⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 adc_elmb.c

📁 AVR平台下的CanOpen协议桟源码包括应用
💻 C
📖 第 1 页 / 共 5 页
字号:
/* ------------------------------------------------------------------------
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 + -