Link here

DA 24/7 Forth Demo 3

Forth demonstration code for the DA 24/7 Data Acquisition Wildcard: faster conversion times

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.


DA 24/7 Forth Demo 3

\ ---------------------------------------------------------------------
\                          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

 
This page is about: Forth Language Example Program, 24-bit A/D Conversion, Self-calibration – 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.
 
 
Navigation