/* .nf .pl 66 .po 10 Copyright 1994 Novatech Instruments, Inc. Seattle, WA 98102 FILE: dds4pc.c This file, written for and compiled by Power C, controls the Novatech Instruments, Inc. model DDS4-PC Direct Digital Synthesizer. It is provided for exclusive use with Novatech Instruments, Inc. products and is without warranty, expressed or implied. Author: Steven D. Swift, P.E. Principal Engineer Novatech Instruments, Inc. */ #include #include #include #include #define NO 0 #define YES 1 #define BUFSIZE 80 #define MAXOUT 1000 #define BASE 0x0338 #define SPECIAL 0 /* BASE is the factory set address of the DDS4-PC board */ /* SPECIAL is set to one if external clock special is provided. This is a factory modification only. */ 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; unsigned long DDS_base = 4294967295; /* analog devices chip is 32-bits */ unsigned int addr=BASE; /*address of board*/ double defclock=85899345.92; /*clock input frequency,if <1 then default */ double Defstep = 0.02; /* default step size using internal clock */ 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 */ /* 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, normally 10000 */ double startime=0; double endtime=0; double fudge; double scratch; /* scratch variable used where needed */ /* MAIN BEGINS HERE */ reset(addr); /*sends a reset to BASE addr, doesn't affect changed addr*/ /* 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 DDS4-PC Direct Digital Synthesizer Control Software\n"); printf("\nCopyright 1994 NOVATECH INSTRUMENTS, INC., Seattle, WA 98102\n\n"); /* get counter as two bytes */ c = (int) 0x0ff & counter; /*lsb*/ d = (int) (0x0ff & ( counter >> 8) ); /*msb*/ if (DEFCOUNT > 0 && argc > 1) { printf("\nPlease wait %ld seconds while I adjust to your system speed...\n", DEFCOUNT/1000 ); 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(); } if (SPECIAL) { printf("Input your clock frequency in Hz: "); scanf("%lf", &clock); } else clock = defclock; if( clock > defclock ) { printf("\nBOGUS Clock: greater than 85.9MHz. Exceeds specifications.\n"); exit(); } while(1) /* always loop until exit() is signalled */ { printf("\nInput your output frequency in Hz (0=quit): "); scanf("%lf", &freqi); if ( freqi >= clock/2.0 ) { printf("BOGUS frequency, violates Nyquist: %lf\n",freqi); exit(); } if ( freqi <= 0.0 ) exit(); scratch = freqi / Defstep; if (SPECIAL) scratch = ( (double) DDS_base ) * freqi/clock; /*printf("%lf\n",scratch);*/ DDS_mi = (unsigned long) scratch; if ( (scratch - ( (double) DDS_mi ) ) >= 0.5 ) DDS_mi++; /*printf("%ld\n",DDS_mi);*/ printf("Input your attenuation in dB (max 70db): "); scanf("%lf", &i); ampi = ampi/10.0; /* change to # of steps */ atteni = (int) ampi; /* cast to integer */ if( (ampi - ((double)atteni)) >= 0.5 ) atteni++; if (atteni > 7) atteni=7; /* adjust for bit order in output attenuator */ atteni = ((atteni<<2)&0x04)|((atteni>>2)&0x01)|(atteni&0x02); /* bits 0 and 2 now swapped */ atteni = atteni << 5 ; /* shift bits to proper position, low bits=0 */ 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 = defclock; /* 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 */ if (SPECIAL) { if (clock > 86.0e6) { printf("BOGUS: Clock > 86 MHz exceeds specifications.\n"); exit(); } } 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 = freq[i] / Defstep; if (SPECIAL) scratch = ( (double) DDS_base )*freq[i]/clock; DDS_m[i] = (unsigned long) scratch; if ( (scratch - ( (double) DDS_m[i] ) ) >= 0.5 ) DDS_m[i]++; if ( DDS_m[i] >= DDS_base/2 ) { printf("BOGUS frequency, violates Nyquist\n"); exit(); } amp[i] = amp[i]/10.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] > 7) atten[i]=7; /* adjust for bit order in output attenuator */ atten[i] = ((atten[i]<<2)&0x04)|((atten[i]>>2)&0x01)|(atten[i]&0x02); /* bits 0 and 2 now swapped */ atten[i] = atten[i] << 5 ; /*shift bits to proper position, low bits=0 */ 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 */ void writeout(unsigned long DDS_m, int atten, unsigned int addr) { int lowbyte; int midbyte; int highbyte; int highest; unsigned long mask=0x0ff; lowbyte = (int) ( mask & DDS_m ); midbyte = (int) ( mask & (DDS_m >> 8) ); highbyte = (int) ( mask & (DDS_m >> 16) ); highest = (int) ( mask & (DDS_m >> 24) ); outp(addr+3,lowbyte); /* send DDS_m a byte at a time */ outp(addr+4,midbyte); outp(addr+5,highbyte); outp(addr+2,highest); outp(addr+1,0x00); /* send hop clock, value is don't care */ outp(addr+0,atten); /* send the attenuation value */ } /* RESET THE BOARD FUNCTION */ void reset(unsigned int addr) { outp(addr+0,0xe0); /* max attenuation */ outp(addr+3,0x00); /* send all ZEROES for freq */ outp(addr+4,0x00); outp(addr+5,0x00); outp(addr+2,0x00); outp(addr+1,0x00); /* send hop clock, value is don't care */ } /* 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 10-20% on fast PCs. 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 */ void 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 */ }