DA 24/7 Forth Demo 3
The 24/7 Data Acquisition Wildcard™ is a complete analog front end, offering exceptional 24-bit resolution, excellent stability, and remarkable noise rejection for instrumentation applications. Ideal for high resolution, low frequency measurements and data logging, this analog-to-digital converter (ADC) accepts low level signals directly from transducers, amplifies and conditions them, and converts them with 24 bits of resolution with no missing codes performance.
This Forth language demonstration program shows how to read 4 different channels at a fixed sample rate without performing a calibration before each sample and without using interrupts. A self-calibration is performed only once at the outset, resulting in faster conversion times.
\ --------------------------------------------------------------------- \ Example 3 \ --------------------------------------------------------------------- \ The final sample routine uses the timeslice clock to obtain 10 \ samples from 4 different sensors at 60 Hz without using interrupts. \ This routine uses a global structure to contain the settings and \ calibration coefficients of each channel. DECIMAL 0 CONSTANT CH0 \ Constants for channels 0 - 3 1 CONSTANT CH1 2 CONSTANT CH2 3 CONSTANT CH3 320 CONSTANT SAMPLE_FREQ \ freq int corresponding to 60 hz \ 19200 / 60 = 320 [See Table 5] 40 CONSTANT NUM_SAMPLES \ Total number of samples: \ 10 samples for 4 channels 4 CONSTANT NUM_CHANNELS \ Num channels we are sampling array: my_data \ Declare an array for samples structure.BEGIN: ad_channel \ Config options for each channel double-> +ad_zero_cal \ 24-bit zero scale cal val double-> +ad_fs_cal \ 24-bit full scale cal val int-> +ad_freq_int \ Frequency Integer 19 - 4000. byte-> +ad_gain \ Gain 1 to 128. byte-> +ad_polarity \ Bipolar or Unipolar mode. byte-> +ad_res \ Resolution: 16-bit or 24-bit. byte-> +ad_bo \ Burn out current on/off byte-> +ad_fsync \ Sync on/off. byte-> +ad_ch \ Channel. structure.end structure.BEGIN: ad_info \ Global structure. ad_channel struct-> +ch0 ad_channel struct-> +ch1 ad_channel struct-> +ch2 ad_channel struct-> +ch3 byte-> +current_channel \ Current channel being used. int-> +index \ Index into data array structure.end ad_info v.instance: my_struct \ Declare a global instance of \ the structure in variable area. : Init_CH0 ( -- flag ) \ Perform a Full Self Calibration on channel 0-1 for bipolar, unity \ gain, 60 Hz operation and get calibration coefficients. Initialize \ channel 0 of my_struct with calibration coefficients and settings. SELF_CAL 320 GAIN_1 BIPOLAR WORD_24BIT BO_OFF CH_0_1 Start_Conversion -1 = IF SAMPLE_FREQ my_struct +ch0 +ad_freq_int ! GAIN_1 my_struct +ch0 +ad_gain c! BIPOLAR my_struct +ch0 +ad_polarity c! WORD_24BIT my_struct +ch0 +ad_res c! BO_OFF my_struct +ch0 +ad_bo c! FSYNC_OFF my_struct +ch0 +ad_fsync c! CH_0_1 my_struct +ch0 +ad_ch c! Read_Zero_Cal my_struct +ch0 +ad_zero_cal 2! Read_FS_Cal my_struct +ch0 +ad_fs_cal 2! TRUE ELSE FALSE \ Invalid calibration coefficients CH0 ENDIF ; : Init_CH1 ( -- flag ) \ Perform a Full Self Calibration on channel 2-3 for bipolar, unity \ gain, 60 Hz operation and get calibration coefficients. Initialize \ channel 0 of my_struct with calibration coefficients and settings. SELF_CAL 320 GAIN_1 BIPOLAR WORD_24BIT BO_OFF CH_2_3 Start_Conversion -1 = IF SAMPLE_FREQ my_struct +ch1 +ad_freq_int ! GAIN_1 my_struct +ch1 +ad_gain c! BIPOLAR my_struct +ch1 +ad_polarity c! WORD_24BIT my_struct +ch1 +ad_res c! BO_OFF my_struct +ch1 +ad_bo c! FSYNC_OFF my_struct +ch1 +ad_fsync c! CH_2_3 my_struct +ch1 +ad_ch c! Read_Zero_Cal my_struct +ch1 +ad_zero_cal 2! Read_FS_Cal my_struct +ch1 +ad_fs_cal 2! TRUE ELSE FALSE \ Invalid calibration coefficients CH1 ENDIF ; : Init_CH2 ( -- flag ) \ Perform a Full Self Calibration on channel 4-5 for bipolar, unity \ gain, 60 Hz operation and get calibration coefficients. Initialize \ channel 0 of my_struct with calibration coefficients and settings. SELF_CAL 320 GAIN_1 BIPOLAR WORD_24BIT BO_OFF CH_4_5 Start_Conversion -1 = IF SAMPLE_FREQ my_struct +ch2 +ad_freq_int ! GAIN_1 my_struct +ch2 +ad_gain c! BIPOLAR my_struct +ch2 +ad_polarity c! WORD_24BIT my_struct +ch2 +ad_res c! BO_OFF my_struct +ch2 +ad_bo c! FSYNC_OFF my_struct +ch2 +ad_fsync c! CH_4_5 my_struct +ch2 +ad_ch c! Read_Zero_Cal my_struct +ch2 +ad_zero_cal 2! Read_FS_Cal my_struct +ch2 +ad_fs_cal 2! TRUE ELSE FALSE \ Invalid calibration coefficients CH2 ENDIF ; : Init_CH3 ( -- flag ) \ Perform a Full Self Calibration on channel 6-7 for bipolar, unity \ gain, 60 Hz operation and get calibration coefficients. Initialize \ channel 0 of my_struct with calibration coefficients and settings. SELF_CAL 320 GAIN_1 BIPOLAR WORD_24BIT BO_OFF CH_6_7 Start_Conversion -1 = IF SAMPLE_FREQ my_struct +ch3 +ad_freq_int ! GAIN_1 my_struct +ch3 +ad_gain c! BIPOLAR my_struct +ch3 +ad_polarity c! WORD_24BIT my_struct +ch3 +ad_res c! BO_OFF my_struct +ch3 +ad_bo c! FSYNC_OFF my_struct +ch3 +ad_fsync c! CH_6_7 my_struct +ch3 +ad_ch c! Read_Zero_Cal my_struct +ch3 +ad_zero_cal 2! Read_FS_Cal my_struct +ch3 +ad_fs_cal 2! TRUE ELSE FALSE \ Invalid calibration coefficients CH3 ENDIF ; : Do_So_Often ( word_xcfa \ ud -- | ud is in ticks of timeslice clock ) \ This word calls another routine periodically, with a fixed time \ interval between calls of ud ticks of the timeslicer clock. The \ routine is designated by word.xcfa and it should return only a flag \ on the stack. If the flag is true it will continue to be repeatedly \ executed; as soon as it returns with a false flag this routine stops \ calling it and returns immediately. The word.xcfa is called at times \ 0, ud, 2*ud, 3*ud, etc.. measured in units of timeslicer clock ticks. \ If the execution time of word.xcfa is greater than ud ticks of the \ timeslicer then it is just repeatedly called as rapidly as possible. \ With a 5 msec timeslicer period the interval between calls can be up \ to 248 days with a resolution of 5 msec. Because we depend on the \ timeslicer clock that clock should not be stopped or reset while this \ routine is running. To prevent unnoticed rollover if this routine is \ interrupted by another task, the other task should not take longer \ than 248 days; that is, control must return to this routine at least \ once every 248 days. Also word.xcfa should not take longer than 248 \ days to execute either. That should generally not be a problem. \ If another task has control when ud ticks are done and it is time to \ call word.xcfa then the call to word.xcfa will be delayed until this \ routine regains control. However, as long as the other routine and \ the word.xcfa routine together don't take longer than ud then all \ subsequent timing will still occur at integer multiples of ud; there \ is no cumulative timing error. \ There is a PAUSE which may be removed if you don't want any other \ tasks to have a chance at machine time. locals{ d&time_interval x&word_xcfa | d&target_time d&start_time d&elapsed_time } timeslice.COUNT |2@| TO d&start_time \ get the start time BEGIN d&time_interval TO d&target_time x&word_xcfa EXECUTE \ execute the user's word WHILE \ we stop repetitively calling the user's word \ when it returns with a false flag \ D&Target.Time and D&Elapsed.Time are measured from D&Start_Time BEGIN PAUSE timeslice.COUNT |2@| 2dup d&start_time d- TO d&elapsed_time TO d&start_time d&elapsed_time d&target_time du< WHILE \ We readjust the start and target times to maximize \ the time available to other tasks before we \ experience a rollover. This way the rollover \ horizon is always pushed out to the maximum count. d&target_time d&elapsed_time d- TO d&target_time REPEAT d&start_time d&target_time d+ d&elapsed_time d- TO d&start_time REPEAT ; \ This routine takes one sample, stores it to an array, then starts a \ conversion for the next channel. : Get_Sample ( -- flag | flag means NotDone ) my_struct +current_channel c@ \ Get current channel my_struct +index @ \ Get current index TRUE \ initialize flag locals{ &Flag &index &ch | x&struct_base } AD24_Sample_NP \ Get sample from a/d &index &ch my_data 2! \ Store to array &ch 1 + NUM_CHANNELS < IF &ch 1 + \ Increment channel number DUP my_struct +current_channel c! \ Store to structure TO &ch \ Store to local ELSE \ check to see if channel and index have attained their max \ values for the sample just taken, if so we set the \ NotDone flag FALSE to indicate that we are done. &ch 1+ &index 1+ * NUM_SAMPLES >= NOT TO &Flag 0 my_struct +current_channel c! \ Roll over channel 0 TO &ch \ Roll over local &index 1 + my_struct +index ! \ Increment index ENDIF my_struct &ch ad_channel * xn+ \ Get base address of struct TO x&struct_base \ store to local x&struct_base +ad_fs_cal 2@ \ Get settings for next channel x&struct_base +ad_zero_cal 2@ x&struct_base +ad_freq_int @ x&struct_base +ad_gain c@ x&struct_base +ad_polarity c@ x&struct_base +ad_res c@ x&struct_base +ad_bo c@ x&struct_base +ad_fsync c@ x&struct_base +ad_ch c@ Start_Conv_With_Values &Flag \ if FALSE done sampling, if true continue sampling ; HEX : print_routine HEX sp! CR CR ." channel value" CR ." ----------------------" CR NUM_CHANNELS 0 DO \ outer loop goes thru channels I \ put index on stack for inner loop NUM_SAMPLES NUM_CHANNELS / 0 DO \ loop thru samples DUP \ preserve outer index DUP ." " . \ print index ." " I SWAP \ swap outer and inner loop indexes my_data 2@ d. \ fetch value from array and print CR LOOP DROP LOOP ; \ This routine takes 10 samples from 4 sensors at 60 Hz. All of the \ settings for each channel are stored in a global structure. All \ channels must have the same sampling rate! DECIMAL : Sample_Routine3 ( -- flag ) \ Allocate memory for 10 samples from 4 sensors; each sample is \ 4 bytes. NUM_SAMPLES NUM_CHANNELS / NUM_CHANNELS 2 4 ' my_data DIMENSIONED MODULE0 Init_AD24 \ 24/7 Data Acquisition Wildcard is \ the first Wildcard on the stack IF Use_Onboard_Ref \ Use on-board ref for samples CH0 my_struct +current_channel c! \ Set ch0 as the current channel 0 my_struct +index ! \ Init array index number Init_CH1 \ Init global structure Init_CH2 OR Init_CH3 OR Init_CH0 OR \ Init ch 0 last since it will be \ the first channel to be sampled \ Get 1 sample every 60 ms. 60 ms is the fastest we can call \ Get_Sample because the sample rate is 60Hz and the 24-Bit A/D \ takes 3 clock cycles to obtain a sample when using \ Start_Conv_With_Values. This alone is 3/60 or 50 ms. If a \ full Self-Calibration was performed before each conversion, the \ fastest rate you could sample one channel would be 10/60 or 166 \ ms. This would amount to 666 ms for 4 channels or 1.5 Hz per \ channel. cfa.FOR get_sample 12 0 do_so_often \ 12 * 5ms = 60 ms ENDIF print_routine ;
See also →
24/7 Data Acquisition Wildcard Users Guide
24/7 Data Acquisition Wildcard Glossary
DA 24/7 C Demo 1
DA 24/7 C Demo 2
DA 24/7 C Demo 3
DA 24/7 Forth Demo 1
DA 24/7 Forth Demo 2