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 >
C/C++ Source or Header  |  1993-09-03  |  15KB  |  558 lines

  1. /*
  2. listing1 
  3. record.c - Single channel recorder for DAQ-16
  4. Written by Robert Watson
  5. (C) Copyright Robert Watson 1993
  6. */
  7.  
  8. #include <conio.h>
  9. #include <ctype.h>
  10. #include <dos.h>
  11. #include <fcntl.h>
  12. #include <io.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <sys\stat.h>
  17.  
  18. #include "vds.h"
  19.  
  20. #define MAXFILENAME 128
  21. char FileName [MAXFILENAME];
  22. int File;
  23. int Channel;
  24. long SampleFrequency;
  25.  
  26. void interrupt (far * OldVectors[16])();
  27.  
  28. /* DMA controller mode values */
  29. #define DMAMODE_AUTOINIT  0x10
  30. #define DMAMODE_WRITE     0x04
  31. #define DMAMODE_SINGLE    0x40
  32.  
  33. /* Size of DMA buffers in bytes */
  34. unsigned DMABufferSize = 0x4000;
  35. VDS_DDS DDS;
  36.  
  37. /* DAQ-16 configuration parameters */
  38. #define INTCHANNEL  9
  39. #define DMACHANNEL1 5
  40. #define DMACHANNEL2 6
  41. #define DAQ16PORT   0x300
  42.  
  43. /* SignOn - Writes initial signon message */
  44. void SignOn (void) {
  45.    clrscr();
  46.    puts ("RECORD");
  47.    puts ("Single channel recorder for DAQ-16 A/D/A "
  48.          "interface.");
  49.    puts ("Written by Robert Watson");
  50.    puts ("(C) Copyright Robert Watson 1993.\n");
  51. }
  52.  
  53. /* PrintHelp - Prints help message if command line is
  54.    in error */
  55. void PrintHelp (void) {
  56.    puts ("Syntax: record [-cn] [-fnn] filename");
  57.    puts (" where -c indicates the channel number and "
  58.          "n is a single digit");
  59.    puts ("          in the range of 0 through 7.");
  60.    puts ("       -f is the recording sample frequency "
  61.          "(in hertz) and nn is");
  62.    puts ("          an unsigned integer in the range "
  63.          "of 1 to 100000");
  64.    puts ("       filename is the name of the output "
  65.          "file.\n");
  66. }
  67.  
  68. /* ProcessArgs - Interpret command line arguments */
  69. int ProcessArgs (int argc, char * argv[]) {
  70.    int i,j;
  71.  
  72.    FileName[0] = 0;
  73.    File = -1;
  74.    Channel = 0;
  75.    SampleFrequency = 48000;
  76.  
  77.    if (argc == 1) return (1);
  78.  
  79.    for (i=1; i<argc; i++) {
  80.       if (argv[i][0] == '-') {
  81.          switch (argv[i][1]) {
  82.             case 'c' :
  83.             case 'C' :
  84.                Channel = argv[i][2] - '0';
  85.                if ((Channel < 0) || (Channel > 7) || 
  86.                    (argv[i][3] != 0)) {
  87.                   printf ("Error - Illegal channel "
  88.                           "number: %s\n", argv[i]);
  89.                   return (1);
  90.                }
  91.                break;
  92.             case 'f' :
  93.             case 'F' :
  94.                for (j=2; isdigit(argv[i][j]); j++);
  95.                if (argv[i][j] || (strlen(argv[i])>8)) {
  96.                   printf ("Error - Illegal frequency "
  97.                           "value: %s\n", argv[i]);
  98.                   return (1);
  99.                }
  100.                SampleFrequency = atol (argv[i]+2);
  101.                if (SampleFrequency > 100000L) {
  102.                   printf ("Error - Frequency value too "
  103.                           "large: %s\n", argv[i]);
  104.                   return (1);
  105.                }
  106.                break;
  107.             default :
  108.                printf ("Error - Unknown switch "
  109.                        "argument: %s\n", argv[i]);
  110.                return (1);
  111.          }
  112.       } else strcpy (FileName, argv[i]);
  113.    }
  114.  
  115.    if (strlen(FileName) == 0) {
  116.       printf ("Error - Output file name must be "
  117.               "specified.\n");
  118.       return (1);
  119.    }
  120.  
  121.    return (0);
  122. }
  123.  
  124. /* Queue functions used to communicate between
  125.    application and interrupt handler */
  126.  
  127. typedef struct QTAG {
  128.    char * Buffer;
  129.    long Address;
  130.    struct QTAG * Next;
  131. } QueueNode;
  132.  
  133. QueueNode QueueNodeList[8];
  134.  
  135. QueueNode * EmptyQueue; /* Queue of empty buffers */
  136. QueueNode * Queue;      /* Queue of full buffers */
  137.  
  138. /* EnqEmpty - Place an empty buffer in a queue of
  139.    empty buffers.  Function is reentrant and can be
  140.    called from application time code, not from an
  141.    interrupt handler. */
  142. void EnqEmpty (QueueNode * Node) {
  143.    asm cli
  144.    Node->Next = EmptyQueue;
  145.    EmptyQueue = Node;
  146.    asm sti
  147. }
  148.  
  149. /* DeqEmpty - Remove an empty buffer from a queue of
  150.    empty buffers.  Function is NOT reentrant and must
  151.    be called from an interrupt handler or without
  152.    an active interrupt. */
  153. QueueNode * DeqEmpty (void) {
  154.    QueueNode * n;
  155.  
  156.    if (!EmptyQueue) return (NULL);
  157.    n = EmptyQueue;
  158.    EmptyQueue = n->Next;
  159.    n->Next = NULL;
  160.    return (n);
  161. }
  162.  
  163. /* Deque - Remove a full buffer from a queue of full
  164.    buffers. Function is reentrant and can be called
  165.    from application time code only, not an interrupt
  166.    handler. */
  167. QueueNode * Deque (void) {
  168.    QueueNode *n,*p;
  169.  
  170.    asm cli
  171.    p = 0;
  172.    n = Queue;
  173.    if (Queue) {
  174.       while (n->Next) {
  175.          p = n;
  176.          n = p->Next;
  177.       }
  178.       if (p) p->Next = 0;
  179.       else Queue = 0;
  180.    }
  181.    asm sti
  182.    return (n);
  183. }
  184.  
  185. /* Enque - Place a full buffer in a queue of full 
  186.    buffers.  Function is not reentrant and can be
  187.    called from an interrupt handler only. */
  188. void Enque (QueueNode * Node) {
  189.    if (Queue)
  190.       Node->Next = Queue;
  191.    else Node->Next = 0;
  192.  
  193.    Queue = Node;
  194. }
  195.  
  196. /* QueueEmpty - Returns non-zero if queue of full
  197.    buffers is empty.  Function is reentrant and can
  198.    be called from application time or interrupt time
  199.    code. */
  200. int QueueEmpty (void) {
  201.    return (!Queue);
  202. }
  203.  
  204. /* Port addresses for various DMA I/O ports */
  205. unsigned DMAPagePorts[8] = {0x00, 0x83, 0x81, 0x82,
  206.                             0x88, 0x8B, 0x89, 0x8A};
  207. unsigned DMABPtrPorts[8] = {0x0C, 0x0C, 0x0C, 0x0C,
  208.                             0xD8, 0xD8, 0xD8, 0xD8};
  209. unsigned DMAAddrPorts[8] = {0x00, 0x02, 0x04, 0x06,
  210.                             0xC0, 0xC4, 0xC8, 0xCC};
  211. unsigned DMAMaskPorts[8] = {0x0A, 0x0A, 0x0A, 0x0A,
  212.                             0xD4, 0xD4, 0xD4, 0xD4};
  213. unsigned DMAModePorts[8] = {0x0B, 0x0B, 0x0B, 0x0B,
  214.                             0xD6, 0xD6, 0xD6, 0xD6};
  215. unsigned DMACountPorts[8] = {0x01, 0x03, 0x05, 0x07,
  216.                              0xC2, 0xC6, 0xCA, 0xCE};
  217.  
  218. /* SetDMAAddress - Set the address and page registers
  219.    of a DMA channel to a specified buffer address. */
  220. void SetDMAAddress (int Channel, long Address) {
  221.    /* Set page register value */
  222.    outportb (DMAPagePorts[Channel], Address>>16);
  223.    /* If 16 bit channel, create word address */
  224.    if (Channel > 3) Address >>= 1;
  225.    /* Clear DMA controller byte pointer flip flop */
  226.    outportb (DMABPtrPorts[Channel], 0);
  227.    /* Write DMA controller address register */
  228.    outportb (DMAAddrPorts[Channel], Address);
  229.    outportb (DMAAddrPorts[Channel], Address>>8);
  230. }
  231.  
  232. /* DisableDMA - Disables transfers on a DMA channel */
  233. void DisableDMA (int Channel) {
  234.    outportb (DMAMaskPorts[Channel], Channel | 4);
  235. }
  236.  
  237. /* EnableDMA - Enable transfers on a DMA channel */
  238. void EnableDMA (int Channel) {
  239.    outportb (DMAMaskPorts[Channel], Channel & 3);
  240. }
  241.  
  242. /* SetDMAMode - Set the operating mode of a channel */
  243. void SetDMAMode (int Channel, unsigned Mode) {
  244.    outportb (DMAModePorts[Channel],
  245.              Mode | (Channel & 3));
  246. }
  247.  
  248. /* SetDMABufferLength - Set the word count register
  249.    of a channel to the length of the DMA buffer */
  250. void SetDMABufferLength (int Channel, long Length) {
  251.    outportb (DMABPtrPorts[Channel], 0);
  252.    // Convert byte size to word size if 16 bit */
  253.    if (Channel > 3) Length >>= 1;
  254.    Length--; /* Word count -1 */
  255.    outportb (DMACountPorts[Channel], Length);
  256.    outportb (DMACountPorts[Channel], Length>>8);
  257. }
  258.  
  259. /* ConfigureDMAChannel - Initialize a DMA channel in
  260.    preparation for subsequent transfers. */
  261. void ConfigureDMAChannel (int Channel, unsigned Mode,
  262.                           long Address, long Length) {
  263.    DisableDMA (Channel);
  264.    SetDMAMode (Channel, Mode);
  265.    SetDMAAddress (Channel, Address);
  266.    SetDMABufferLength (Channel, Length);
  267.    EnableDMA (Channel);
  268. }
  269.  
  270. /* InstallInterrupt - Install an interrupt handler */
  271. void InstallInterrupt (int IntNum,
  272.                      void interrupt (far *isr)()) {
  273.    int IntMask;
  274.  
  275.    if (IntNum < 8) {
  276.       OldVectors[IntNum] = getvect (IntNum+8);
  277.       setvect (IntNum+8, isr);
  278.       asm cli
  279.       IntMask = inportb (0x20);
  280.       IntMask &= ~(1<<IntNum);
  281.       outportb (0x20, IntMask);
  282.       asm sti
  283.    } else {
  284.       OldVectors[IntNum] = getvect (IntNum+0x68);
  285.       setvect (IntNum+0x68, isr);
  286.       asm cli
  287.       IntMask = inportb (0xA0);
  288.       IntMask &= ~(1<<(IntNum-8));
  289.       outportb (0xA0, IntMask);
  290.       asm sti
  291.    }
  292. }
  293.  
  294. /* RemoveInterrupt - Reverse the action of
  295.    InstallInterrupt */
  296. void RemoveInterrupt (int IntNum) {
  297.    int IntMask;
  298.  
  299.    if (IntNum < 8) {
  300.       setvect (IntNum+8, OldVectors[IntNum]);
  301.       asm cli
  302.       IntMask = inportb (0x20);
  303.       IntMask |= 1<<IntNum;
  304.       outportb (0x20, IntMask);
  305.       asm sti
  306.    } else {
  307.       setvect (IntNum+0x68, OldVectors[IntNum]);
  308.       asm cli
  309.       IntMask = inportb (0xA0);
  310.       IntMask |= 1<<(IntNum-8);
  311.       outportb (0xA0, IntMask);
  312.       asm sti
  313.    }
  314. }
  315.  
  316. /* AcknowledgeInterrupt - Send an interrupt acknowledge
  317.    command to the interrupt controller(s) */
  318. void AcknowledgeInterrupt (int IntNum) {
  319.    if (IntNum >= 8) {
  320.       outportb (0xA0, 0x20);
  321.       outportb (0xA0, 0x0B);
  322.       if (!inportb (0xA0))
  323.          outportb (0x20, 0x20);
  324.    } else outportb (0x20, 0x20);
  325. }
  326.  
  327. /* SetDAQ16Frequency - Set the sample rate of the
  328.    DAQ-16 clock generator */
  329. void SetDAQ16Frequency (unsigned Port, long Freq) {
  330.    long TimeConst;
  331.    unsigned N1, N2;
  332.    if ((Freq < 1) || (Freq > 100000)) return;
  333.    TimeConst = 10000000/Freq;
  334.    N1 = 2;
  335.    while ((TimeConst/N1) > 0xFFFFL) N1++;
  336.    N2 = TimeConst/N1;
  337.    outportb (Port+0x0F, 0x34);
  338.    outportb (Port+0x0C, N1);
  339.    outportb (Port+0x0C, N1>>8);
  340.    outportb (Port+0x0F, 0x74);
  341.    outportb (Port+0x0D, N2);
  342.    outportb (Port+0x0D, N2>>8);
  343. }
  344.  
  345. /* DMAInterruptHandler - Interrupt handler for DMA
  346.    terminal count interrupts */
  347.  
  348. int BufferOverrun; /* True if DMA buffer overruns */
  349. /* The following variables store the values of the
  350.    two DMA buffers currently in use. */
  351. QueueNode * DMAChannel1QNode;
  352. QueueNode * DMAChannel2QNode;
  353.  
  354. void interrupt DMAInterruptHandler () {
  355.    int Status;
  356.  
  357.    /* Check which DMA port has reached TC */
  358.    Status = inport (DAQ16PORT);
  359.  
  360.    if (Status & 0x0800) {
  361.       if (DMAChannel1QNode) {
  362.          Enque (DMAChannel1QNode);
  363.          DMAChannel1QNode = DeqEmpty ();
  364.          if (DMAChannel1QNode) {
  365.             SetDMAAddress (DMACHANNEL1,
  366.                            DMAChannel1QNode->Address);
  367.          } else {
  368.             BufferOverrun=1;/* Report buffer overrun */
  369.             outport(DAQ16PORT, 0); /* Disable DAQ-16 */
  370.          }
  371.       }
  372.    } else if (DMAChannel2QNode) {
  373.       Enque (DMAChannel2QNode);
  374.       DMAChannel2QNode = DeqEmpty ();
  375.       if (DMAChannel2QNode) {
  376.          SetDMAAddress (DMACHANNEL2,
  377.                         DMAChannel2QNode->Address);
  378.       } else {
  379.          BufferOverrun=1;/* Report buffer overrun */
  380.          outport(DAQ16PORT, 0); /* Disable DAQ-16 */
  381.       }
  382.    }
  383.  
  384.    AcknowledgeInterrupt (INTCHANNEL);
  385. }
  386.  
  387. /* AllocateDMABuffers - Allocates DMA buffer from VDS,
  388.    splits the buffer into smaller buffers that are
  389.    enqueued into the empty queue.  A far pointer
  390.    to each small buffer is created with DPMI functions.
  391.    Returns non-zero if an error occurs. */
  392. int AllocateDMABuffers (void) {
  393.    int i;
  394.    int Error;
  395.  
  396.    if (!IsVDSAvailable()) {
  397.       puts ("Error - VDS services are not available!");
  398.       return (1);
  399.    }
  400.  
  401.    DDS.Size = DMABufferSize * 2;
  402.    if (RequestVDSBuffer (&DDS)) {
  403.       puts ("Error - unable to allocate DMA buffer.");
  404.       return (1);
  405.    }
  406.  
  407.    EmptyQueue = Queue = NULL;
  408.    for (i=0; i<(DDS.Size/DMABufferSize); i++) {
  409.       if (i >= 8) break;
  410.       QueueNodeList[i].Address = DDS.Address +
  411.                    (DMABufferSize * (long) i);
  412.       EnqEmpty (&QueueNodeList[i]);
  413.    }
  414.    DMAChannel1QNode = DeqEmpty();
  415.    DMAChannel2QNode = DeqEmpty();
  416.  
  417.    printf ("Recording with %u DMA buffers\n", i);
  418.    return (0);
  419. }
  420.  
  421. /* Record - Main loop of application. Initializes DMA,
  422.    interrupt, and I/O device activity.  Sits in loop
  423.    writing data buffers to a file until the user
  424.    presses a key or an error occurs.  Before returning,
  425.    DMA, interrupt, and I/O device activity is
  426.    terminated. */
  427. void Record (int File) {
  428.    long BuffersWritten;
  429.    char * DiskBuffer;
  430.    QueueNode * Buff;
  431.    int Error;
  432.    int i;
  433.  
  434.    DiskBuffer = (char *) malloc (DMABufferSize);
  435.    if (!DiskBuffer) {
  436.       puts ("Error - unable to allocate real mode disk "
  437.             "buffer!");
  438.       return;
  439.    }
  440.  
  441.    if (AllocateDMABuffers()) {
  442.       free (DiskBuffer);
  443.       return;
  444.    }
  445.  
  446.    BufferOverrun = 0;
  447.  
  448.    outport (DAQ16PORT, 0); /* Disable DAQ-16 */
  449.    SetDAQ16Frequency (DAQ16PORT, SampleFrequency);
  450.  
  451.    DisableVDSTranslation (DMACHANNEL1);
  452.    DisableVDSTranslation (DMACHANNEL2);
  453.    ConfigureDMAChannel (DMACHANNEL1, DMAMODE_WRITE |
  454.                         DMAMODE_SINGLE | DMAMODE_AUTOINIT,
  455.                         DMAChannel1QNode->Address, DMABufferSize);
  456.    ConfigureDMAChannel (DMACHANNEL2, DMAMODE_WRITE |
  457.                         DMAMODE_SINGLE | DMAMODE_AUTOINIT,
  458.                         DMAChannel2QNode->Address, DMABufferSize);
  459.    InstallInterrupt (INTCHANNEL, DMAInterruptHandler);
  460.  
  461.    /* Configure DAQ-16 operating mode */
  462.    outport (DAQ16PORT, 0xB880 | Channel);
  463.    /* Trigger DAQ-16 operation */
  464.    outport (DAQ16PORT+2, 0);
  465.  
  466.    BuffersWritten = 0;
  467.    do {
  468.       printf ("\rRecording Time: %lu.",
  469.               (BuffersWritten * DMABufferSize / 2) /
  470.                SampleFrequency);
  471.       if (!QueueEmpty()) {
  472.          BuffersWritten++;
  473.          Buff = Deque ();
  474.          if (!Buff) continue;
  475.          DDS.Segment = FP_SEG(DiskBuffer);
  476.          DDS.Offset = FP_OFF(DiskBuffer);
  477.          DDS.Size = DMABufferSize;
  478.          if (CopyFromDMABuffer (&DDS,
  479.                  Buff->Address-DDS.Address)) {
  480.             puts ("Error - VDSCopyFromDMABuffer "
  481.                   "unsuccessful!");
  482.             break;
  483.          }
  484.          EnqEmpty (Buff);
  485.          Error = write (File, DiskBuffer,
  486.                         DMABufferSize);
  487.          if (Error != DMABufferSize) {
  488.             puts ("Error - Disk full!");
  489.             break;
  490.          }
  491.       }
  492.       if (BufferOverrun) break;
  493.    } while (!kbhit());
  494.  
  495.    outport (DAQ16PORT, 0); /* Disable DAQ-16 */
  496.    DisableDMA (DMACHANNEL1);
  497.    DisableDMA (DMACHANNEL2);
  498.    RemoveInterrupt (INTCHANNEL);
  499.    EnableVDSTranslation (DMACHANNEL1);
  500.    EnableVDSTranslation (DMACHANNEL2);
  501.  
  502.    while (!QueueEmpty()) {
  503.       BuffersWritten++;
  504.       Buff = Deque ();
  505.       if (!Buff) continue;
  506.       DDS.Segment = FP_SEG(DiskBuffer);
  507.       DDS.Offset = FP_OFF(DiskBuffer);
  508.       DDS.Size = DMABufferSize;
  509.       Error = CopyFromDMABuffer (&DDS,
  510.               Buff->Address-DDS.Address);
  511.       EnqEmpty (Buff);
  512.       if (Error) {
  513.          puts ("Error - VDSCopyFromDMABuffer "
  514.                "unsuccessful!");
  515.          break;
  516.       }
  517.       Error = write (File, DiskBuffer, DMABufferSize);
  518.       if (Error != DMABufferSize) {
  519.          puts ("Error - Disk full!");
  520.          break;
  521.       }
  522.    }
  523.  
  524.    if (BufferOverrun)
  525.       puts ("Error - DMA buffers overrun while "
  526.             "recording.");
  527.  
  528.    if (ReleaseVDSBuffer (&DDS))
  529.       puts ("Error - DMA buffer release was not "
  530.             "successful.");
  531.  
  532.    free (DiskBuffer);
  533. }
  534.  
  535. /* main - Prints signon message, help message,
  536.    opens and closes output file, and calls
  537.    main loop function. */
  538. int main (int argc, char * argv[]) {
  539.    SignOn ();
  540.    if (ProcessArgs (argc, argv)) {
  541.       PrintHelp ();
  542.       return (0);
  543.    }
  544.  
  545.    File = open (FileName, O_BINARY | O_CREAT | O_RDWR | O_TRUNC,
  546.                 S_IREAD | S_IWRITE);
  547.    if (File == -1) {
  548.       printf ("Error - Unable to open file: %s\n",
  549.               FileName);
  550.       return (1);
  551.    }
  552.  
  553.    Record (File);
  554.  
  555.    close (File);
  556.    return (0);
  557. }
  558.