/* Copyright 1999 Novatech Instruments, Inc. Shoreline, WA 98133 FILE: dds7pc.c (also dds7pc8.c) This file, written for and compiled by Power C and djgpp, controls the Novatech Instruments, Inc. model DDS7pc Direct Digital Synthesizer. It is provided for exclusive use with Novatech Instruments, Inc. products and is without warranty, expressed or implied. Two executable versions are created. One-- dds7pc.exe-- from djgpp is for 386 and up microprocessors. dds7pc8.exe is compiled using Power C and is intended for 286 and below (8 and 16-bit applications). Author: Steven D. Swift, P.E. Principal Engineer Novatech Instruments, Inc. Revision History: Rev 0 December 5, 1998 Created from DDS3pc.c Rev 0.1 January 1, 1999 DOS Version finished. */ #include #include #include #include #include #define NO 0 #define YES 1 #define BUFSIZE 80 #define MAXOUT 1000 #define BASE 0x0338 /* BASE is the factory set address of the DDS7pc board */ FILE *fileptr, *fopen(); void main( int argc, char *argv[] ) { char buf[BUFSIZE]; /* buffer for each text input line */ char *bufp; /* pointer into text buffer */ int ch; /* character holder */ int count=0; /* index number of out records read in file */ int START=0; /* value of count at the start of a sweep file */ int STOP=0; /* value of count at the end of a sweep file */ int RESET=NO; /* if YES then a reset command is requested */ int i; /* dummy counter used as necessary */ int control; /* bin control word with clock and atten */ int eof=NO; double DDS_base = 4294967296.0; /* AD9851 chip is 32-bits */ unsigned int addr=BASE; /*address of board*/ double defclock=DDS_base/150.0; /*clock input freq, if <1 then default */ double clock; static double freq[MAXOUT]; static unsigned long DDS_m[MAXOUT]; /*binary value of programming word*/ static double amp[MAXOUT]; static int atten[MAXOUT]; static double dwell[MAXOUT]; static unsigned long msec[MAXOUT]; /*dwell in integer msec for each out */ /* variables used in immediate mode only */ double ampi; int atteni; double freqi; unsigned long DDS_mi; /*programmed value sent to DDS IC */ /* address definitions: BASE+0 FS0-FS7 BASE+1 FS8-FS15 BASE+2 FS16-FS23 BASE+3 FS24-FS31 (msb) BASE+4 AMP0-AMP3, Bit 7=HIGH -> Ext Clk Sel BASE+5 PS0-PS5, bit7=HIGH -> DDS chip reset. BASE+6 unused BASE+7 data not used, write to here generates update. */ /* timing parameters */ struct timeb start, end; /* used for measuring time of system */ unsigned long counter=0x049a; /* number to give 1ms with 486/33 LB */ unsigned int c1 = 0; /*loop counter in timing adjust routine */ int c; /* lsb of timer */ int d; /* msb of timer */ unsigned long DEFCOUNT = 10000 ; /* default timing adjust for 10 seconds */ /* change DEFCOUNT to 0 to skip this adjustment */ double startime=0; double endtime=0; double fudge; double scratch; /* scratch variable used where needed */ /* MAIN BEGINS HERE */ /* get an estimate of system speed by measuring length of time it takes to execute this loop and calculate a factor for use in the timing of "dwell." */ printf("\n\n\nModel DDS7pc Direct Digital Synthesizer Control Software\n"); printf("\nCopyright 1999 NOVATECH INSTRUMENTS, INC., Seattle, WA 98133\n\n"); if (DEFCOUNT > 0 && argc > 1) { printf("\nPlease wait %ld seconds while I adjust to your system speed...\n", DEFCOUNT/1000 ); /* get counter as two bytes */ c = (int) 0x0ff & counter; /*lsb*/ d = (int) (0x0ff & ( counter >> 8) ); /*msb*/ ftime(&start); setdwell( DEFCOUNT, c, d ) ; ftime(&end); startime = ( (double) start.time ) + ( (double) start.millitm)/1000.0; endtime = ( (double) end.time ) + ( (double) end.millitm)/1000.0; fudge = ( (double) DEFCOUNT/1000 ) / ( endtime - startime); fudge = ( (double) counter ) * fudge; counter = (unsigned long) fudge; if ( fudge - (double) counter >= 0.5 ) counter++; /* find new integers for use in setdwell() function */ c = (int) 0x0ff & counter; /*lsb*/ d = (int) (0x0ff & ( counter >> 8) ); /*msb*/ } /* use default values if DEFCOUNT = 0 */ /* end of system measurement */ /******* IMMEDIATE MODE OF OPERATION ************/ if (argc == 1) /* must be in immediate mode */ { printf("\nInput your board address in Hex (0=default): "); scanf("%lx", &addr); if ( addr < 1) addr = BASE; if( (addr < 0x0200) || (addr > 0x03f8) ) { printf("\nBOGUS Address: address out of range. Check hardware.\n"); exit(); } printf("Input your clock frequency in Hz (0=default): "); scanf("%lf", &clock); if (clock < 1.0) clock = defclock; if (clock == defclock) control = 0x00; else control = 0x80; /* set clock for external input */ if( clock > 30.0e6 ) { printf("\nBOGUS Clock: greater than 30MHz. Exceeds specifications.\n"); exit(); } if( clock < 5.0e6 ) { printf("\nBOGUS Clock: less than 5MHz. Below specifications.\n"); exit(); } reset(addr); /* sends a reset to addr*/ while(1) /* always loop until exit() is signalled */ { printf("\nInput your output frequency in Hz (0=quit, 1=reset): "); scanf("%lf", &freqi); if ( freqi <= 0.0 ) exit(); /* add reset for low values of frequency. If reset twice, then exit */ if (freqi < 5.0 ) { reset(addr); printf("\n\nInput your output frequency in Hz (0=quit, 1=reset): "); scanf("%lf", &freqi); if ( freqi <= 0.0 ) exit(); if (freqi < 5.0 && freqi ) exit(); } scratch = (DDS_base)*freqi/(6.0*clock); /* 6.0 factor because AD9851 has internal mult */ DDS_mi = (unsigned long) scratch; if ( (scratch - ( (double) DDS_mi ) ) >= 0.5 ) DDS_mi++; if ( DDS_mi >= DDS_base/2.5 ) { printf("BOGUS frequency, violates Nyquist\n"); exit(); } printf("Input your attenuation in dB (max 60dB): "); scanf("%lf", &i); ampi = ampi/4.0; /* change to # of steps */ atteni = (int) ampi; /* cast to integer */ if( (ampi - ((double)atteni)) >= 0.5 ) atteni++; if (atteni > 15) atteni=15; /* invert as 0=atten in place, XOR with FF */ atteni = atteni^0x0ff ; atteni = atteni&0x0f; /* mask off upper bits */ atteni = atteni | control ; /* combine in clock bits */ writeout(DDS_mi,atteni,addr); /*send to output-- note that clock and attenuation are set at same time */ } exit(); /*this is normal exit from immediate mode*/ } /********** FILE MODE OF OPERATION **************/ if (argc == 2) /* must be in file mode */ { if (( fileptr = fopen(argv[1],"r")) == NULL) { printf("CAN'T OPEN FILE %s\n", argv[1]); exit(); } clock = 0.0; /* set up to get default clock unless changed */ /* read file and store all the values */ while (!eof) { bufp = buf; /* get a line of text */ do { if ((ch = getc(fileptr)) == EOF) eof = YES; else *bufp = tolower(ch); /*convert to lower to accept either */ } while ((!eof) && (*bufp++ != '\n')); *bufp = '\0'; /* need null terminator for puts() */ /* if 'out' process the freq record */ if (!strncmp (buf, "out", 3)) { if (count > MAXOUT) { printf("\nERROR-- too many output points.\n",buf); exit(); } if (sscanf (buf,"out %lf %lf %lf", &freq[count], &[count], &dwell[count]) != 3) { fprintf (stderr, "ERROR! - Bad output record: %s", buf); exit(); } count++; } /* else ignore commented lines */ else if (*buf == '#') ; /* else if 'clock' process the clock select input */ else if (!strncmp (buf, "clock", 5)) { if (sscanf (buf, "clock %lf",&clock) != 1) { fprintf (stderr, "ERROR! - Bad clock record: %s", buf); exit(); } } /* else if 'start' process a start flag input */ else if (!strncmp (buf, "start", 5)) { START = count; } /* else if 'stop' process an stop flag input */ else if (!strncmp (buf, "stop", 4)) { STOP = count; } /* else if 'addr' process the addr select input */ else if (!strncmp (buf, "addr", 4)) { if (sscanf (buf, "addr %lx",&addr) != 1) { fprintf (stderr, "ERROR! - Bad addr record: %s", buf); exit(); } if( (addr < 0x0200) || (addr > 0x03f8) ) { fprintf (stderr, "ERROR! - Bad addr record: %s", buf); exit(); } } /* else if 'reset', process a reset record */ else if (!strncmp (buf, "reset", 5)) { RESET = YES; } } } fclose(fileptr); /* close the file as we are finished with it */ /* all data is now collected so process the outputs this area uses functions to output the addresses and binary data to the pc-card */ /* only the last clock record counts */ reset(addr); /*sends a reset to addr*/ if (clock < 1.0) clock = defclock; if (clock > 30.0e6) { printf("BOGUS: Clock > 30 MHz exceeds specifications.\n"); exit(); } if (clock < 5.0e6) { printf("BOGUS: Clock < 5 MHz below specifications.\n"); exit(); } if (clock == defclock) control = 0x00; else control = 0x80; /* set clock for external input */ if ( RESET == YES ) reset(addr); /* convert all frequency inputs into a long integer representing the value to send to the DDS IC */ i = 0; while ( i < count ) { msec[i] = (unsigned long) ( 1000.0*dwell[i] ); /* convert to integer */ if ( freq[i] <= 0.0 ) { printf("BOGUS Frequency: zero or negative!\n"); exit(); } scratch = (DDS_base)*freq[i]/(6.0*clock); /* x6 because of internal multiplier on AD9851 */ DDS_m[i] = (unsigned long) scratch; if ( (scratch - ( (double) DDS_m[i] ) ) >= 0.5 ) DDS_m[i]++; if ( DDS_m[i] >= DDS_base/2.5 ) { printf("BOGUS frequency, violates Nyquist\n"); exit(); } amp[i] = amp[i]/4.0; /* change to # of steps */ atten[i] = (int) amp[i]; /* cast to integer */ if( (amp[i] - ((double)atten[i])) >= 0.5 ) atten[i]++; if (atten[i] > 15) atten[i]=15; /* invert as 0=atten in place, XOR with FF */ atten[i] = atten[i]^0x0ff ; atten[i] = atten[i]&0x0f; /* mask off upper bits */ atten[i] = atten[i] | control ; /* combine in clock bits */ i++; } /* now output the values and time them appropriately */ /* this is a once through loop and is executed one time only. if START or STOP records exists then the values between start and stop records are continuously swept */ if( (START==0) && (STOP==0) ) { i = 0; printf("\nBegin once-through output\n"); while ( i < count ) { writeout(DDS_m[i],atten[i],addr); if (msec[i] < 1 ) { printf("Output is %lf Hz. Hit return to resume, ^c to abort.\n",freq[i]); while(!getchar()); } else setdwell(msec[i],c,d); i++; } printf("\nEnd once-through output\n"); } /* this is the continuous sweep mode */ if( (START>0) || (STOP>0) ) { if(START >= STOP) printf("BOGUS Sweep Record: check output file!\n"); printf("\nSweeping...\n"); printf("Unless paused, hit any key to abort sweep\n"); while(!kbhit()){ i = START; while ( i < STOP ) { writeout(DDS_m[i],atten[i],addr); if ( msec[i] < 1 ) { printf("Sweep paused at %lf Hz. Hit return key to resume, ^c to abort.\n",freq[i]); while(!getchar()); } else setdwell(msec[i],c,d); if(kbhit()) exit(); i++; } } } exit(); /*this is normal exit from file mode*/ if (argc > 2) /* must be bogus input command line */ { printf("\nBOGUS INPUT LINE: check your command and run again.\n"); exit(); } } /* end of main() */ /***************FUNCTIONS FOLLOW************************/ /* No values are returned by any of these function */ /* WRITE THE OUTPUT SIGNAL FUNCTION */ /* this function assumes that the amplitude value has been converted to an attenuation value and combined with the clock select control lines to prevent problems with the clock lines */ writeout(unsigned long DDS_m, int atten, unsigned int addr) { int lowbyte; int midbyte; int highbyte; int highestbyte; unsigned long mask=0x0ff; lowbyte = (int) ( mask & DDS_m ); midbyte = (int) ( mask & (DDS_m >> 8) ); highbyte = (int) ( mask & (DDS_m >> 16) ); highestbyte = (int) ( mask & (DDS_m >> 24) ); outp(addr+4,atten); /* send the attenuation value */ outp(addr+0,lowbyte); /* send DDS_m a byte at a time */ outp(addr+1,midbyte); outp(addr+2,highbyte); outp(addr+3,highestbyte); outp(addr+7,0xff); /* send update pulse, value is don't care */ } /* RESET THE BOARD FUNCTION */ reset(unsigned int addr) { outp(addr+4,0x00); /* send max attenuation value, int clock */ sleep(1); /* wait for int clock to stabilize */ outp(addr+5,0x00); /* phase =0, reset=low*/ outp(addr+5,0x80); /*phase =0, reset=HIGH*/ outp(addr+5,0x00); /* phase =0, reset=low*/ writeout(0x00,0x00,addr); /* dummy write to clear registers */ } /* SET THE DWELL TIME FUNCTION */ /* This timing function reprograms timer #2 in the PC. No attempt is made to control other timing functions going on inside the PC, so at best it is only good to 1-2%. This should be fine for most applications. The default timing parameters give good timing on fast machines. The slower your PC, the worse the overhead */ setdwell(unsigned long counter, int lsb, int msb) { int a,b; /* overhead function to make sure this is cool */ /* set up control register in PC */ a = inp(0x061); /* get control register status from PC */ b = a; /* save it for now */ a = a & 0xfc; /*enable clock, disable speaker*/ a = a | 0x01; outp(0x061,a); /* write it */ /* set timer control word to 1011 0000: counter 2, lsb-msb, mode 0, binary */ outp(0x043,0xb0); /* two byte mode*/ while( counter > 0 ) { outp(0x042,lsb); outp(0x042,msb+1); /*msb to msb + 1 because of > 0 compare */ do { outp(0x43,0x80); /*latch timer value */ inp(0x42); } /*get lsb*/ while ( inp(0x042) ) ; /*repeat until msb goes to zero*/ counter--; } outp(0x061,b); /* put back control register as it was */ }