📄 adc_elmb.c
字号:
/* ------------------------------------------------------------------------
File : adc_elmb.c
Descr : Functions for controlling the ELMB onboard CRYSTAL CS5523 16-bit ADC.
History: 19JUL.00; Henk B&B; Start of development, based on a similar module
with routines for CS5525 ADCs for ATLAS MDT-
monitoring applications and an existing set of
basic routines for operating the CS5523.
18SEP.00; Henk B&B; Add routine 'adc_chan_cnt()';
add recalibration to 'adc_set_config()';
in the PDO replace the ADC status byte with
a byte containing the current ADC-configuration
and status-bit.
16JAN.00; Henk B&B; Go to Motherboard V3 configuration, but
allow backwards compatibility using compile
options __MOTHERBOARD1__ or __ALL_MOTHERBOARDS__.
23FEB.01; Henk B&B; Keep some variables in EEPROM (optionally).
02APR.01; Henk B&B; Refresh the ADC Configuration Register before
every scan cycle (in adc_pdo_scan_start()).
18APR.01; Henk B&B; Perform a complete reset and calibration procedure
before every scan cycle (in adc_pdo_scan_start()),
which includes a Config Register refresh.
25JUL.01; Henk B&B; Make the above optional/configurable...
AUG.02; Henk B&B; Add readout-on-change mechanism with adjustable
per-channel delta parameter and optional
forced readout scan cycles.
OCT.02; Henk B&B; Add stuff for voltage range calibration
and calibrated readout.
MAR.03; Henk B&B; Add upper- and lower-limit checking and stuff for
storing and administering these limit values
per-channel.
16OCT.03; Henk B&B; Window width must at least be 1 for window check
to take place.
--------------------------------------------------------------------------- */
#include "general.h"
#include "adc.h"
#include "can.h"
#include "crc.h"
#include "cs5523.h"
#include "eeprom.h"
#include "objects.h"
#include "store.h"
#include "timer103.h"
/* ------------------------------------------------------------------------ */
/* Globals */
/* Total number of ADC channels */
static BYTE AdcChans; /* (stored in EEPROM) */
/* ADC configuration to use (word rate, voltage range, unipolar/bipolar) */
static BYTE AdcConfig; /* (stored in EEPROM) */
/* ADC Configuration Register to use
(chop frequency: depends on selected wordrate) */
static BYTE AdcConfReg_2; /* (stored in EEPROM) */
/* Storage space for error bits concerning the ADC */
static BYTE AdcError;
/* Signal hold time due to opto-coupler (in microseconds) */
BYTE AdcOptoDelay; /* (stored in EEPROM) */
/* ------------------------------------------------------------------------ */
/* Global variables for ADC channel scanning operations */
/* ADC-channel index */
static BYTE AdcChanNo;
/* ADC scanning-operation-in-progress boolean */
static BOOL AdcScanInProgress;
/* ADC conversion-in-progress boolean */
BOOL AdcConvInProgress;
/* ADC conversion-in-progress-to-set-latch booleans */
static BOOL AdcLatchBeingSet;
static BOOL AdcLatchSet;
/* ADC recalibrate before every scan cycle */
static BOOL AdcCalibBeforeScan;
/* ADC-readout-on-change enable */
static BOOL AdcReadoutOnChange;
/* ADC delta-scan enable */
static BOOL AdcDeltaScanEnabled;
/* Last sent ADC channel values in ADC-readout-on-change scanning operation */
static UINT16 AdcRefCount[ADC_MAX_INPUTS];
/* When ADC-readout-on-change is enabled, setting this boolean to TRUE
forces initalization of the reference value of all channels
in 'AdcRefCount[]' */
static BOOL AdcInitRefCount;
/* ADC upper/lower-limit-scan enable */
static BOOL AdcWindowScanEnabled;
/* Inital value for ADC upper/lower-limit inside/outside window counter;
also used for the delta-change mode */
static BYTE AdcWindowCounterInit;
/* Inside/outside-window counter per channel */
static CHAR AdcWindowCounter[ADC_MAX_INPUTS];
/* Delta-change counter per channel */
static BYTE AdcDeltaCounter[ADC_MAX_INPUTS];
/* When ADC-readout-on-change is enabled, the setting of this boolean
forces the readout values of all channels to be sent once */
static BOOL AdcForcedReadout;
/* Readout of ADC channels during a scan is in (micro)Volts
when this boolean is set */
static BOOL AdcScanVolts;
/* ------------------------------------------------------------------------ */
/* Global variables for ADC calibration and volts-conversion */
/* To enable a single write to an calibration constant location in EEPROM */
static BOOL AdcCalibConstWriteEnabled;
/* ------------------------------------------------------------------------ */
/* Useful constants */
/* Map from calibration constants OD index to
(3-bit) ADC voltage range code (and vice-versa) */
const BYTE RANGE_ID[] = { 2, 1, 0, 3, 5, 4 };
/* ADC full-scale voltage ranges in microVolts */
const float V_FULLSCALE[] = { 100000.0, 55000.0, 25000.0,
1000000.0, 5000000.0, 2500000.0 };
/* Map from ADC wordrate code/id to approximate wordrate in Hz */
const BYTE HZ_WORDRATE[] = { 15, 30, 62, 85, 101, 2, 4, 8 };
/* ------------------------------------------------------------------------ */
/* Local prototypes */
static void adc_convert_to_volts ( UINT16 adc_count, BYTE *microvolts );
static BOOL adc_set_calib_regs ( BOOL send_emergency );
static BOOL adc_self_calibrate ( BOOL send_emergency );
static void adc_init_confreg ( void );
static void adc_init_csr ( void );
static BOOL adc_scan_next ( void );
static BOOL adc_delta_check ( UINT16 adc_count );
static BOOL adc_window_check ( UINT16 adc_count );
static BOOL adc_load_config ( void );
static UINT16 adc_get_delta_cnt ( BYTE chan_no );
static UINT16 adc_get_upperlimit_cnt( BYTE chan_no );
static UINT16 adc_get_lowerlimit_cnt( BYTE chan_no );
static UINT16 adc_get_limit_cnt ( BYTE chan_no, UINT16 ee_copy_addr );
static BOOL adc_set_limit ( BYTE chan_no, BYTE *limit,
UINT16 base_addr );
static void adc_get_limit ( BYTE chan_no, BYTE *limit,
UINT16 base_addr );
static BOOL adc_load_deltas ( void );
static BOOL adc_load_upperlimits ( void );
static BOOL adc_load_lowerlimits ( void );
static BOOL adc_load_limits ( UINT16 ee_storage_addr,
UINT16 ee_copy_addr,
BYTE err_id,
BYTE dflt );
static BOOL adc_store_limits ( UINT16 ee_storage_addr,
UINT16 ee_copy_addr );
static BOOL adc_invalidate_limits ( UINT16 ee_storage_addr );
static BOOL adc_valid_calib_const ( BYTE od_range_id );
/* ------------------------------------------------------------------------ */
#ifdef __ALL_MOTHERBOARDS__
static void motherboard_select( BYTE version );
/* Pointers to the functions doing the actual I/O pin operations:
the pointers get set depending on the Motherboard type used */
void (*ADC_SET_SCLK)( void );
void (*ADC_CLEAR_SCLK)( void );
void (*ADC_SET_SDI)( void );
void (*ADC_CLEAR_SDI)( void );
BOOL (*ADC_SDO_HIGH)( void );
BOOL (*ADC_SDO_LOW)( void );
void (*ADC_SET_MUX_LATCH)( void );
void (*ADC_CLEAR_MUX_LATCH)( void );
void (*ADC_SELECT)( void );
void (*ADC_DESELECT)( void );
#endif /* __ALL_MOTHERBOARDS__ */
/* ------------------------------------------------------------------------ */
BOOL adc_init( void )
{
BYTE wordrate;
BOOL result = TRUE;
AdcError = 0;
/* Initialise ADC configuration parameters */
if( adc_load_config() == FALSE ) result = FALSE;
/* Set wordrate-dependent setting for ADC Configuration Register:
Chop Frequency: 256 Hz (at freq <= 30 Hz), 4096 Hz (other freq)
(recommended by Crystal, CS5523 datasheet pg 14) */
wordrate = ((AdcConfig & CS23_CSR_WORDRATE_MASK) >> CS23_CSR_WORDRATE_SHIFT);
if( wordrate == CS23_WORDRATE_61 ||
wordrate == CS23_WORDRATE_84 ||
wordrate == CS23_WORDRATE_101 )
AdcConfReg_2 = ADC_CNFREG_2_CHOP4096;
else
AdcConfReg_2 = ADC_CNFREG_2_CHOP256;
#ifdef __VARS_IN_EEPROM__
/* Create working copies of configuration globals in EEPROM */
if( eeprom_read( EE_ADCCONFREG2 ) != AdcConfReg_2 )
eeprom_write( EE_ADCCONFREG2, AdcConfReg_2 );
#endif /* __VARS_IN_EEPROM__ */
/* Initialize variables for scanning operations */
AdcChanNo = 0;
AdcScanInProgress = FALSE;
AdcConvInProgress = FALSE;
AdcLatchBeingSet = FALSE;
AdcLatchSet = FALSE;
AdcForcedReadout = FALSE;
AdcInitRefCount = FALSE;
AdcScanVolts = FALSE;
/* Other */
AdcCalibConstWriteEnabled = FALSE;
#ifdef __MOTHERBOARD1__
/* Initialise I/O-pins for ADC: old Motherboard configuration */
ADC_CLEAR_SCLK_V1();
ADC_SET_SDI_V1();
ADC_SET_MUX_LATCH_V1();
ADC_DESELECT_V1();
ADC_INIT_DDR_V1();
#else
/* Initialise I/O-pins for ADC: assume Motherboard V3 configuration */
ADC_CLEAR_SCLK_V3();
ADC_SET_SDI_V3();
ADC_SET_MUX_LATCH_V3();
ADC_DESELECT_V3();
ADC_INIT_DDR_V3();
#endif
#ifdef __ALL_MOTHERBOARDS__
/* Initialise pointers to I/O-pin access functions */
motherboard_select( 3 );
#endif /* __ALL_MOTHERBOARDS__ */
#ifndef __ALL_MOTHERBOARDS__
/* Reset and calibrate (but don't send Emergency messages) */
if( adc_reset_and_calibrate( FALSE ) == FALSE ) result = FALSE;
#else
if( adc_reset_and_calibrate( FALSE ) == FALSE )
{
/* Problem with accessing ADC? :
are we dealing with Motherboard V1/2 perhaps ? */
/* Initialise I/O-pins for ADC: Old Motherboard configuration */
ADC_CLEAR_SCLK_V1();
ADC_SET_SDI_V1();
ADC_SET_MUX_LATCH_V1();
ADC_DESELECT_V1();
ADC_INIT_DDR_V1();
/* Initialise pointers to I/O-pin access functions */
motherboard_select( 1 );
/* Try again, now assuming the ADC is connected differently:
reset and calibrate (but don't send Emergency messages) */
if( adc_reset_and_calibrate( FALSE ) == FALSE ) result = FALSE;
}
#endif /* __ALL_MOTHERBOARDS__ */
adc_init_delta_references();
return result;
}
/* ------------------------------------------------------------------------ */
BOOL adc_read( BYTE chan_no, BYTE *conversion_data )
{
BYTE bank_no;
BOOL result;
if( (AdcConvInProgress & TRUE) == TRUE )
{
/* Can't do anything if the ADC is in use... */
/*conversion_data[0] = 0xFF;
conversion_data[1] = 0xFF;
conversion_data[2] = 0xFE;*/
return FALSE;
}
else
{
/*#define __DO_NOT_CONVERT_WHEN_ERROR__ */
#ifdef __DO_NOT_CONVERT_WHEN_ERROR__
if( (AdcError & ADC_ERR_IN_HARDWARE) != 0 )
{
/* Something has gone wrong with this ADC previously */
/*conversion_data[0] = 0xFF;
conversion_data[1] = 0xFF;
conversion_data[2] = 0xFD;*/
return FALSE;
}
else
#endif /* __DO_NOT_CONVERT_WHEN_ERROR__ */
{
/* Perform an ADC conversion */
#ifdef __VARS_IN_EEPROM__
AdcOptoDelay = eeprom_read( EE_ADCOPTODELAY );
#endif
ADC_SELECT();
/* =================================== */
/* Select the proper set of 16 channels */
bank_no = chan_no/16;
/* Change of 16-chan inputs bank: select bank,
by initiating a (dummy) conversion;
LC 5 to 8 contain the proper setting of A1-A0
(set during initialization) to accomplish this */
result = cs5523_read_adc( 4 + bank_no, conversion_data );
/* Now latch this data */
ADC_CLEAR_MUX_LATCH();
//timer2_delay_mus( CS23_ELMB_SIGNAL_RISETIME );
timer2_delay_mus( AdcOptoDelay );
ADC_SET_MUX_LATCH();
/* =================================== */
/* Select the proper channel within the chosen set of 16 */
if( result == TRUE )
{
BYTE a1a0, cs1cs0;
BYTE csr_data[1*2*2];
#ifdef __VARS_IN_EEPROM__
AdcConfig = eeprom_read( EE_ADCCONFIG );
#endif
/* Set A1-A0 and physical channel in LC 1 (+2)
while maintaining proper wordrate and gain */
a1a0 = chan_no & 3;
cs1cs0 = (chan_no>>2) & 3;
csr_data[0] = (AdcConfig |
((cs1cs0 << CS23_CSR_PHYSCHAN_SEL_LO_SHIFT) &
CS23_CSR_PHYSCHAN_SEL_LO_MASK));
csr_data[1] = ((a1a0 << CS23_CSR_A1A0_SHIFT) |
((cs1cs0 >> CS23_CSR_PHYSCHAN_SEL_HI_SHIFT) &
CS23_CSR_PHYSCHAN_SEL_HI_MASK));
csr_data[2] = 0x00;
csr_data[3] = 0x00;
cs5523_write_csr( 1, csr_data );
/* Do a conversion of LC 1 and read the result */
result = cs5523_read_adc( 0, conversion_data );
}
/* =================================== */
ADC_DESELECT();
if( result == FALSE )
{
/* Conversion timed out ! */
AdcError |= ADC_ERR_TIMEOUT;
/* CANopen Error Code 0x5000: device hardware */
can_write_emergency( 0x00, 0x50, EMG_ADC_CONVERSION, chan_no,
0, 0, ERRREG_MANUFACTURER );
adc_serial_init();
return FALSE;
}
}
}
return TRUE;
}
/* ------------------------------------------------------------------------ */
BOOL adc_read_volts( BYTE chan_no, BYTE *data )
{
BYTE conversion_data[3];
BYTE microvolts[4];
UINT16 adc_count;
/* Readout in volts only when calibration constants are there
(for the current ADC setting) */
if( adc_calibrated() == FALSE ) return FALSE;
/* Read out the ADC-count and status flags */
if( adc_read( chan_no, conversion_data ) == FALSE ) return FALSE;
/* The ADC status flags */
data[0] = conversion_data[0];
/* The ADC-count as a 16-bit (unsigned) integer */
adc_count = (((UINT16) conversion_data[1]) |
(((UINT16) conversion_data[2]) << 8));
/* Convert the 16-bit ADC-count to microVolts units (signed) */
adc_convert_to_volts( adc_count, microvolts );
/* The microvolts value is sent as a 3-byte integer,
right behind the status flags byte */
data[1] = microvolts[0];
data[2] = microvolts[1];
data[3] = microvolts[2];
return TRUE;
}
/* ------------------------------------------------------------------------ */
static void adc_convert_to_volts( UINT16 adc_count, BYTE *microvolts )
{
BOOL unipolar;
BYTE adc_range_id;
float fullscale;
/* Get the ADC settings */
unipolar = (AdcConfig & CS23_CSR_UNIPOLAR);
adc_range_id = ((AdcConfig & CS23_CSR_GAIN_MASK) >> CS23_CSR_GAIN_SHIFT);
fullscale = V_FULLSCALE[adc_range_id];
if( unipolar )
{
/* Unipolar measurement: use unsigned numbers ! */
UINT32 adc_volts_ul;
float adc_volts_f;
UINT32 *ptr;
/* Do the ADC-count to microVolts calculation */
adc_volts_f = (((float) adc_count) / 65535.0) * fullscale;
/* Cast to a UINT32 */
adc_volts_ul = (UINT32) adc_volts_f;
/* Copy the UINT32 into the BYTE array */
ptr = (UINT32 *) microvolts;
*ptr = adc_volts_ul;
// The following also works(?):
//BYTE i, *ptr = (BYTE *) &adc_volts_l;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -