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 */