Site Archive (Complete)
C++
Email
Print
Reprint

add to:
Del.icio.us
Digg
Google
Furl
Slashdot
Y! MyWeb
Blink
November 01, 1993
DMA Controller Programming in C

(Page 1 of 5)

Listing 1 DMA control program

/*
record.c - Single channel recorder for DAQ-16
Written by Robert Watson
(C) Copyright Robert Watson 1993
*/

#include <conio.h>
#include <ctype.h>
#include <dos.h>
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys\stat.h>

#include "vds.h"

#define MAXFILENAME 128
char FileName [MAXFILENAME];
int File;
int Channel;
long SampleFrequency;

void interrupt (far * OldVectors[16])();

/* DMA controller mode values */
#define DMAMODE_AUTOINIT  0x10
#define DMAMODE_WRITE     0x04
#define DMAMODE_SINGLE    0x40

/* Size of DMA buffers in bytes */
unsigned DMABufferSize = 0x4000;
VDS_DDS DDS;

/* DAQ-16 configuration parameters */
#define INTCHANNEL  9
#define DMACHANNEL1 5
#define DMACHANNEL2 6
#define DAQ16PORT    0x300

/* SignOn - Writes initial signon message */
void SignOn (void) {
   clrscr();
   puts ("RECORD");
   puts ("Single channel recorder for DAQ-16 A/D/A "
         "interface.");
   puts ("Written by Robert Watson");
   puts ("(C) Copyright Robert Watson 1993.\n");
}

/* PrintHelp - Prints help message if command line is
   in error */
void PrintHelp (void) {
   puts ("Syntax: record [-cn] [-fnn] filename");
   puts (" where -c indicates the channel number and "
         "n is a single digit");
   puts ("           in the range of 0 through 7.");
   puts ("        -f is the recording sample frequency "
         "(in hertz) and nn is");
   puts ("           an unsigned integer in the range "
         "of 1 to 100000");
   puts ("        filename is the name of the output "
         "file.\n");
}

/* ProcessArgs - Interpret command line arguments */
int ProcessArgs (int argc, char * argv[]) {
   int i,j;

   FileName[0] = 0;
   File = -1;
   Channel = 0;
   Sample Frequency = 48000;

   if (argc == 1) return (1);

   for (i=1; i<argc; i++) {
      if (argv[i][0] == '-') {
         switch (argv[i][1]) {
            case 'c' :
            case 'C' :
               Channel = argv[i][2] - '0';
               if ((Channel < 0) || (Channel > 7) || (argv[i][3] != 0)) {
                  printf ("Error - Illegal channel "
                          "number: %s\n", argv[i]);
                  return (1);
               }
               break;
            case 'f' :
            case 'F' :
               for (j=2; isdigit(argv[i][j]); j++);
                  if (argv[i][j] || (strlen(argv[i])>8)) {
                     printf ("Error - Illegal frequency "
                             "value: %s\n", argv[i]);
                     return (1);
                  }
               SampleFrequency = atol (argv[i]+2);
               if (SampleFrequency > 100000L) {
                  printf ("Error - Frequency value too "
                          "large: %s\n", argv[i]);
                  return (1);
               }
               break;
            default :
               printf ("Error - Unknown switch "
                       "argument: %s\n", argv[i]);
               return (1);
         }
      } else strcpy (FileName, argv[i]);
   }

   if (strlen(FileName) == 0) {
      printf ("Error - Output file name must be "
              "specified.\n");
      return (1);
   }

   return (0);
}

/* Queue functions used to communicate between
   application and interrupt handler */

typedef struct QTAG {
   char * Buffer;
   long Address;
   struct QTAG * Next;
} QueueNode;

QueueNode QueueNodeList[8];

QueueNode * EmptyQueue; /* Queue of empty buffers */
QueueNode * Queue;       /* Queue of full buffers */

/* EnqEmpty - Place an empty buffer in a queue of
   empty buffers. Function is reentrant and can be
   called from application time code, not from an
   interrupt handler. */
void EnqEmpty (QueueNode * Node) {
   asm cli
   Node->Next = EmptyQueue;
   EmptyQueue = Node;
   asm sti
}

/* DeqEmpty - Remove an empty buffer from a queue of
   empty buffers. Function is NOT reentrant and must
   be called free an interrupt handler or without
   an active interrupt. */
QueueNode * DeqEmpty (void) {
   QueueNode * n;

   if (!EmptyQueue) return (NULL);
   n = EmptyQueue;
   EmptyQueue = n->Next;
   n->Next = NULL;
   return (n);
}

/* Deque - Remove a full buffer from a queue of full
   buffers. Function is reentrant and can be called
   from application time code only, not an interrupt
   handler. */
QueueNode * Deque (void) {
   QueueNode *n,*p;

   asm cli
   p = 0;
   n = Queue;
   if (Queue) {
      while (n->Next) {
         p = n;
         n = p->Next;
      }
      if (p) p->Next = 0;
      else Queue = 0;
   }
   asm sti
   return (n);
}

/* Enque - Place a full buffer in a queue of full
   buffers. Function is not reentrant and can be
   called from an interrupt handler only. */
void Enque (QueueNode * Node) {
   if (Queue)
      Node->Next = Queue;
   else Node->Next = 0;

   Queue = Node;
}

/* QueueEmpty - Returns non-zero if queue of full
   buffers is empty. Function is reentrant and can
   be called from application time or interrupt time
   code. */
int QueueEmpty (void) {
   return (!Queue);
}

/* Port addresses for various DMA I/O ports */
unsigned DMAPagePorts[8] = {0x00, 0x83 0x81, 0x82,
                            0x88, 0x8B, 0x89, 0x8A};
unsigned DMABP(trPorts[8] = {0x0C, 0x0C 0x0C, 0x0C,
                            0xD8, 0xD8, 0xD8, 0xD8};
unsigned DMAAddrPorts[8] = {0x00, 0x02 0x04, 0x06,
                            0xC0, 0xC4, 0xC8, 0xCC};
unsigned DMAMaskPorts[8] = {0x0A, 0x0A; 0x0A, 0x0A,
                            0xD4, 0xD4, 0xD4, 0xD4};
unsigned DMAM0dePorts[8] = {0x0B, 0x0B, 0x0B, 0x0B,
                            0xD6, 0xD6, 0xD6, 0xD6};
unsigned DMACountPorts[8] = {0x01, 0x03 0x05, 0x07,
                             0xC2, 0xC6, 0xCA, 0xCE};

/* SetDMAAddress - Set the address and page registers
   of a DMA channel to a specified buffer address. */

void SetDMAAddress (int Channel, long Address) {
   /* Set page register value */
   outportb (DMAPagePorts[Channel], Address>>16);
   /* If 16 bit channel, create word address */
   if (Channel > 3) Address >>= 1;
   /* Clear DMA controller byte pointer flip flop */
   outportb (DMABPtrPorts[Channel], 0);
   /* Write DMA controller address register */
   outportb (DMAAddrPorts[Channel], Address);
   outportb (DMAAddrPorts[Channel], Address>>8);
}

/* DisableDMA - Disables transfers on a DMA channel */
void DisableDMA (int Channel) {
   outportb (DMAMaskPorts[Channel], Channel | 4);
}

/* EnableDMA - Enable transfers on a DMA channel */
void EnableDMA (int Channel) {
   outportb (DMAMaskPorts[Channel], Channel & 3);
}

/* SetDMAMode - Set the operating mode of a channel */
void SetDMAMode (int Channel, unsigned Mode) {
   outportb ( DMAModePorts[Channel],
              Mode | (Channel & 3));
}

/* SetDMABufferLength - Set the word count register
   of a channel to the length of the DMA buffer */
void SetDMABufferLength (int Channel, long Length) {
   outportb (DMABPtrPorts[Channel], 0);
   // Convert byte size to word size if 16 bit */
   if (Channel > 3) Length >>= 1;
   Length--; /* Word count -1 */
   outportb (DMACountPorts[Channel], Length);
   outportb (DMACountPorts[Channel], Length>>8);
}

/* ConfigureDMAChannel - Initialize a DMA channel in
   preparation for subsequent transfers. */
void ConfigureDMAChannel (int Channel, unsigned Mode,
                         long Address, long Length) {
   DisableDMA (Channel);
   SetDMAMode (Channel, Mode);
   SetDMAAddress (Channel, Address);
   SetDMABufferLength (Channel, Length);
   EnableDMA (Channel);
}

/* InstallInterrupt - Install an interrupt handler */
void InstallInterrupt (int IntNum,
                     void interrupt (far *isr)()) {

   int IntMask;

   if (IntNum < 8) {
      OldVectors[IntNum] = getvect (IntNum+8);
      setvect (IntNum+8, isr);
      asm cli
      IntMask = inportb (0x20);
      IntMask &= ~(1<<IntNum);
      outportb (0x20, IntMask);
      asm sti
   } else {
      OldVectors[IntNum] = getvect (IntNum+0x68);
      setvect (IntNum+0x68, isr);
      asm cli
      IntMask = inportb (0xA0);
      IntMask &= ~(1<<(IntNum-8));
      outportb (0xA0, IntMask;
      asm sti
   }
}

/* RemoveInterrupt - Reverse the action of
   InstallInterrupt  */
void RemoveInterrupt (int IntNum) {
   int IntMask;

   if (IntNum < 8) {
      setvect (IntNum+8, OldVectors[IntNum]);
      asm cli
      IntMask = inportb (0x20);
      IntMask |= 1<<IntNum;
      outportb (0x20, IntMask);
      asm sti
   } else {
      setvect (IntNum+0x68, OldVectors[IntNum]);
      asm cli
      IntMask = inportb (0xA0);
      IntMask |= 1<<(IntNum-8);
      outportb (0xA0, IntMask);
      asm sti
   }
}

/* AcknowledgeInterrupt - Send an interrupt acknowledge
   command to the interrupt controller(s)  */
void AcknowledgeInterrupt (int IntNum) {
   if (IntNum >= 8) {
      outportb (0xA0, 0x20);
      outportb (0xA0, 0x0B);
      if (!inportb (0xA0))
         outportb (0x20, 0x20);
   } else outportb (0x20, 0x20);
}

/* SetDAQ16Frequency - Set the sample rate of the
   DAQ-16 clock generator  */
void SetDAQ16Frequency (unsigned Port, long Freq) {
   long TimeConst;
   unsigned N1, N2;
   if ((Freq < 1) || (Freq > 100000)) return;
   TimeConst = 10000000/Freq;
   N1 = 2;
   while ((TimeConst/N1) > 0xFFFFL) N1++;
   N2 = TimeConst/N1;
   outportb (Port+0x0F, 0x34);
   outportb (Port+0x0C, N1);
   outportb (Port+0x0C, N1>>8);
   outportb (Port+0x0F, 0x74);
   outportb (Port+0x0D, N2);
   outportb (Port+0x0D, N2>>8);
}

/* DMAInterruptHandler - Interrupt handler for DMA
   terminal count interrupts  */

int BufferOverrun; /* True if DMA buffer overruns */
/* The following variables store the values of the
   two DMA buffers currently in use.  */
QueueNode * DMAChannel1QNode;
QueueNode * DMAChanne12QNode;

void interrupt DMAInterruptHandler () {
   int Status;

   /* Check which DMA port has reached TC */
   Status = inport (DAQ16PORT);

   if (Status & 0x0800) {
      if (DMAChannel1QNode) {
         Enque (DMAChannel1QNode);
         DMAChannel1QNode = DeqEmpty ();
         if (DMAChannel1QNode) {
            SetDMAAddress (DMACHANNEL1,
                           DMAChannel1QNode->Address);
         } else {
            BufferOverrun=1;/* Report buffer overrun */
            outport(DAQ16PORT, 0); /* Disable DAQ-16 */
         }
      }
   } else if (DMAChannel2QNode) {
      Enque (DMAChannel2QNode);
      DMAChannel2QNode = DeqEmpty ();
      if (DMAChannel2QNode) {
         SetDMAAddress (DMACHANNEL2,
                        DMAChannel2QNode->Address);
      } else {
         BufferOverrun=1;/* Report buffer everrun */
         outport(DAQ16PORT, 0); /* Disable DAQ-16 */
      }
   }

   AcknowledgeInterrupt [INTCHANNEL);
}

/* AllocateDMABuffers - Allocates DMA Buffer from VDS,
   splits the buffer into smaller buffers that are
   enqueued into the the empty queue. A far winter
   to each small buffer is created with DPMI functions.
   Returns non-zero if an error occurs.  */
int AllocateBuffers (void) {
   int i;
   int Error;

   if (!IsVDSAvailable()) {
      puts (*Error - VDS services are not available!");
      return (1);
   }

   DDS.Size = DMABufferSize * 2;
   if (RequestVDSBuffer (&DDS)) {
      puts ("Error - unable to allocate DMA buffer.");
      return (1);
   }

   EmptyQueue = Queue = NULL;
   for (i=0; i<(DDS.Size/DMABufferSize); i++) {
      if (i >= 8) break;
      QueueNodeList[i].Address = DDS.Address +
                   (DMABufferSize * (long) i);
      EnqEmpty (&QueueNodeList[i]);
   }
   DMAChannel1QNode = DeqEmpty();
   DMAChannel2QNode = DeqEmpty();

   printf ("Recording with %u DMA buffers\n", i);
   return (0);
}

/* Record - Main loop of application. Initializes DMA,
   interrupt, and I/O device activity. Sits in loop
   writing data buffers to a file until the user
   presses a key or an error occurs. Before returning,
   DMA, interrupt, and I/O device activity is
   terminated.  */
void Record (int File) {
   long BuffersWritten;
   char * DiskBuffer;
   QueueNode * Buff;
   int Error;
   int i;

   DiskBuffer = (char *) malloc (DMABufferSize);
   if (!DiskBuffer) {
      puts ("Error - unable to allocate real mode disk "
            "buffer!");
      return;
   }

   if (AllocateDMABuffers()) {
      free (DiskBuffer);
      return;
   }

   BufferOverrun = 0;

   outport (DAQ16PORT, 0); /* Disable DAQ-16 */
   SetDAQ16Frequency (DAQ16PORT, SampleFrequency);

   DisableVDSTranslation (DMACHANNEL1);
   DisableVDSTranslation (DMACHANNEL2);
   ConfigureDMAChannel (DMACHANNEL1, DMAMODE_WRITE |
                        DMAMODE_SINGLE | DMAMODE_AUTOINIT,
                        DMAChannel1QNode->Address, DMABufferSize);
   ConfigureDMAChannel (DMACHANNEL2, DMAMODE_WRITE |
                        DMAMODE_SINGLE | DMAMODE_AUTOINIT,
                        DMAChannel2QNode->Address, DMABufferSize);
   InstallInterrupt (INTCHANNEL, DMAInterruptHandler);

   /* Configure DAQ-16 operating mode */
   outport (DAQ16PORT, 0xB880 | Channel);
   /* Trigger DAQ-6 operation */
   outport (DAQ16PORT+2, 0);

   BuffersWritten = 0;
   do {
      printf ("\rRecording Time: %lu.",
              (BuffersWritten * DMABufferSize / 2) /SampleFrequency);
      if (!QueueEmpty()) {
         BuffersWritten++;
         Buff = Deque ();
         if (!Buff) continue;
         DDS.Segment = FP_SEG(DiskBuffer);
         DDS.Offset = FP_OFF(DiskBuffer);
         DDS.Size = DMABufferSize;
         if (CopyFromDMABuffer(&DDS,
                 Buff->Address-DDS.Address)) {
            puts ("Error - VDSCopyFromDMABuffer "
                  "unsuccessful!");
            break;
         }
         EnqEmpty (Buff);
         Error = write (File, DiskBuffer,
                       DMABufferSize);

         if (Error != DMABufferSize) {
            puts ("Error - Disk full!");
            break;
         }
      }
      if (BufferOverrun) break;
   } while (!kbhit());

   outport (DAQ16PORT, 0); /* Disable DAQ-16 */
   DisableDMA (DMACHANNEL1);
   DisableDMA (DMACHANNEL2);
   RemoveInterrupt (INTCHANNEL);
   EnableVDSTranslation (DMACHANNEL1);
   EnableVDSTranslation (DMACHANNEL2);

   while (!QueueEmpty()) {
      BuffersWritten++;
      Buff = Deque ();
      if (!Buff) continue;
      DDS.Segment = FP_SEG(DiskBuffer);
      DDS.Offset = FP_OFF(DiskBuffer);
      DDS.Size = DMABufferSize;
      Error = CopyFromDMABuffer (&DDS,
              Buff->Address-DDS.Address);
      EnqEmpty (Buff);
      if (Error) {
         puts ("Error - VDSCopyFromDMABuffer " "unsuccessful!");
         break;
      }
      Error = write (File, DiskBuffer, DMABufferSize);
      if (Error != DMABufferSize) {
         puts ("Error - Disk full!");
         break;
      }
   }

   if (BufferOverrun)
      puts ("Error - DMA buffers overrun while "
            "recording.");

   if (ReleaseVDSBuffer (&DDS))
      puts ("Error - DMA buffer release was not"
            "successful.");

   free (DiskBuffer);
}

/* main - Prints signon message, help message,
   opens and closes output file, and calls
   main loop function. */
int main (int argc, char * argv[]) {
   SignOn ();
   if (ProcessArgs (argc, argv)) {
      PrintHelp ();
      return (0);
   }

   File = open (FileName, 0_BINARY | 0_CREAT | 0_RDWR | 0_TRUNC,
                S_IREAD | S_IWRITE);
   if (File == -1){
      printf ("Error - Unable to open file: %s\n",
              Filename);
      return (1);
   }

   Record (File);

   close (File);
   return (0);
}
/* End of File */
1 | 2 | 3 | 4 | 5 Next Page
RELATED ARTICLES
No Related Articles
TOP 5 ARTICLES
No Top Articles.
SD Best Practices 2008, October 27-30 in Boston, features world-class training on the entire software development lifecycle.
DR. DOBB'S CAREER CENTER
Ready to take that job and shove it? open | close
Search jobs on Dr. Dobb's TechCareers
Function:

Keyword(s):

State:  
  • Post Your Resume
  • Employers Area
  • News & Features
  • Blogs & Forums
  • Career Resources

    Browse By:
    Location | Employer | City
  • Most Recent Posts:
    DC Systems Technician I
    Lowes seeking DC Systems Technician I in Lebanon, OR

    Software Developers
    D. E. Shaw Research seeking Software Developers in New York, NY

    Engineers
    D. E. Shaw Research seeking Engineers in New York, NY

    Sr. Power Electronics Engineers
    Satcon seeking Sr. Power Electronics Engineers in Linthicum, MD

    Systems Administrator
    United Nations Foundation seeking Systems Administrator in Washington, DC




    MICROSITES
    FEATURED TOPIC

    ADDITIONAL TOPICS




    MARKETPLACE (Sponsored Links)
    100% Online programs in Six Sigma, IS Security, CISSP Prep, Business Analysis, Proj. Mgmt. and more!
    Easily create an automated, repeatable process for building and deploying software.
    Automatically capture customer crash data, no debugger required. Support for .NET, C++, OS X, Java.
    Understand C/C++ code in less time. A new team member ? Inherited legacy code ? Get up to speed fast...
    and develop 10 times faster ! ALM, IDE, .Net, PDF, 5GL, Database, 64-bit, etc. Free Express version
    Advertise With Us
     



    Related Sites: DotNetJunkies, SD Expo, SqlJunkies