home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
listings
/
v_11_11
/
watson
/
record.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-09-03
|
15KB
|
558 lines
/*
listing1
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;
SampleFrequency = 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 from 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 DMABPtrPorts[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 DMAModePorts[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 * DMAChannel2QNode;
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 overrun */
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 empty queue. A far pointer
to each small buffer is created with DPMI functions.
Returns non-zero if an error occurs. */
int AllocateDMABuffers (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-16 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, O_BINARY | O_CREAT | O_RDWR | O_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);
}