From 576df19a72706351373021d5027937a47046f8b4 Mon Sep 17 00:00:00 2001 From: Philipp Klaus Date: Thu, 19 Dec 2013 16:10:27 +0100 Subject: [PATCH] preliminary support for LTC1867L --- firmware/src/Makefile | 1 + firmware/src/ltc1867l.c | 64 ++++++++++++++++++ firmware/src/ltc1867l.h | 144 ++++++++++++++++++++++++++++++++++++++++ firmware/src/main.c | 38 +++++++++++ firmware/src/spi.c | 17 ++++- 5 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 firmware/src/ltc1867l.c create mode 100644 firmware/src/ltc1867l.h diff --git a/firmware/src/Makefile b/firmware/src/Makefile index 2495997..ce27cc4 100644 --- a/firmware/src/Makefile +++ b/firmware/src/Makefile @@ -9,6 +9,7 @@ OBJS+=periph_conf.o OBJS+=CB_functions.o OBJS+=usart1.o OBJS+=spi2.o +OBJS+=ltc1867l.o # OBJS+=keypins.o diff --git a/firmware/src/ltc1867l.c b/firmware/src/ltc1867l.c new file mode 100644 index 0000000..0f2eb53 --- /dev/null +++ b/firmware/src/ltc1867l.c @@ -0,0 +1,64 @@ + +/*! +LTC1867: 16-Bit 8-Channel 200ksps ADC + +The LTC1863/LTC1867 are pin-compatible, 8-channel 12-/16-bit A/D converters with +serial I/O, and an internal reference. The ADCs typically draw only 1.3mA from a +single 5V supply. The 8-channel input multiplexer can be configured for either +single-ended or differential inputs and unipolar or bipolar conversions (or +combinations thereof). The automatic nap and sleep modes benefit power sensitive +applications. + +The LTC1867's DC performance is outstanding with a +/-2LSB INL specification and +no missing codes over temperature. The signal-to-noise ratio (SNR) for the +LTC1867 is typically 89dB, with the internal reference. + +*/ + +#include +#include "ltc1867l.h" +#include "spi.h" + + +// Reads the ADC and returns 16-bit data +void LTC1867_read(uint8_t adc_command, uint16_t *adc_code) +{ + spi_writeTwoBytes((uint8_t)(adc_command & 0xFF), 0x00); + *adc_code = 0; +} + +// Calculates the LTC1867 input's unipolar voltage given the binary data and lsb weight. +float LTC1867_unipolar_code_to_voltage(uint16_t adc_code, float LTC1867_lsb, int32_t LTC1867_offset_unipolar_code) +{ + float adc_voltage; + adc_voltage=((float)(adc_code+LTC1867_offset_unipolar_code))*LTC1867_lsb; //! 1) Calculate voltage from ADC code, lsb, offset. + return(adc_voltage); +} + +// Calculates the LTC1867 input's bipolar voltage given the two's compliment data and lsb weight +float LTC1867_bipolar_code_to_voltage(uint16_t adc_code, float LTC1867_lsb, int32_t LTC1867_offset_bipolar_code) +{ + float adc_voltage, sign = 1.0; + if (adc_code>>15) + { + adc_code = (adc_code ^ 0xFFFF)+1; //! 1) Convert ADC code from two's complement to binary + sign = -1; + } + adc_voltage=((float)(adc_code+LTC1867_offset_bipolar_code))*LTC1867_lsb*sign; //! 2) Calculate voltage from ADC code, lsb, offset. + return(adc_voltage); +} + +// Calibrate the lsb +void LTC1867_cal_voltage(uint16_t zero_unipolar_code, uint16_t zero_bipolar_code, uint16_t fs_code, float zero_voltage, float fs_voltage, float *LTC1867_lsb, int32_t *LTC1867_offset_unipolar_code, int32_t *LTC1867_offset_bipolar_code) +{ + float temp_offset; + *LTC1867_lsb = (fs_voltage-zero_voltage)/((float)(fs_code - zero_unipolar_code)); //! 1) Calculate the LSB + + temp_offset = (zero_voltage/ *LTC1867_lsb) - zero_unipolar_code; //! 2) Calculate Unipolar offset + //temp_offset = (temp_offset > (floor(temp_offset) + 0.5)) ? ceil(temp_offset) : floor(temp_offset); //! 3) Round + *LTC1867_offset_unipolar_code = (int32_t)temp_offset; //! 4) Cast as int32_t + + temp_offset = (zero_voltage / *LTC1867_lsb) - zero_bipolar_code ; //! 5) Calculate Bipolar offset + //temp_offset = (temp_offset > (floor(temp_offset) + 0.5)) ? ceil(temp_offset) : floor(temp_offset); //! 6) Round + *LTC1867_offset_bipolar_code = (int32_t)temp_offset; //! 7) cast as int32_t +} diff --git a/firmware/src/ltc1867l.h b/firmware/src/ltc1867l.h new file mode 100644 index 0000000..c205113 --- /dev/null +++ b/firmware/src/ltc1867l.h @@ -0,0 +1,144 @@ + +/*! +LTC1867: 16-bit 8-channel 200ksps ADC + +The LTC1863/LTC1867 are pin-compatible, 8-channel 12-/16-bit A/D converters +with serial I/O, and an internal reference. The ADCs typically draw only 1.3mA +from a single 5V supply. The 8-channel input multiplexer can be configured for +either single-ended or differential inputs and unipolar or bipolar conversions +(or combinations thereof). The automatic nap and sleep modes benefit power +sensitive applications. + +The LTC1867's DC performance is outstanding with a +/-2LSB INL specification and +no missing codes over temperature. The signal-to-noise ratio (SNR) for the +LTC1867 is typically 89dB, with the internal reference. + +SPI DATA FORMAT (MSB First): + + Byte #1 Byte #2 +Data Out : D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 +Data In : SD OS S1 S0 COM UNI SLP X X X X X X X X X + +SD : Single/Differential Bit +OS : ODD/Sign Bit +Sx : Address Select Bit +COM : CH7/COM Configuration Bit +UNI : Unipolar/Bipolar Bit +SLP : Sleep Mode Bit +Dx : Data Bits +X : Don't care + + +Example Code: + +Read Channel 0 in Single-Ended Unipolar mode when input is with respect to GND + + adc_command = LTC1867_CH0 | LTC1867_UNIPOLAR_MODE; // Build ADC command for channel 0 + LTC1867_read(LTC1867_CS, adc_command, &adc_code); // Throws out last reading + LTC1867_read(LTC1867_CS, adc_command, &adc_code); // Obtains the current reading and stores to adc_code variable + + // Convert adc_code to voltage + adc_voltage = LTC1867_unipolar_code_to_voltage(adc_code, LTC1867_lsb, LTC1867_offset_unipolar_code); + +*/ + +#ifndef LTC1867L_H +#define LTC1867L_H + +//#include + +//! Define the SPI CS pin +#ifndef LTC1867_CS +#define LTC1867_CS QUIKEVAL_CS +#endif + +//! @name LTC1867 Single-Ended Channel Addresses, COM=GND +//! @{ +// Single-Ended Channel Address When CH7/COM Pin Is used As CH7 +#define LTC1867_CH0 0x80 +#define LTC1867_CH1 0xC0 +#define LTC1867_CH2 0x90 +#define LTC1867_CH3 0xD0 +#define LTC1867_CH4 0xA0 +#define LTC1867_CH5 0xE0 +#define LTC1867_CH6 0xB0 +#define LTC1867_CH7 0xF0 +//!@} + +//! @name LTC1867 Differential Channel Addresses +//! @{ +// Differential Channel Address When CH7/COM Pin Is Used As CH7 +#define LTC1867_P0_N1 0x00 +#define LTC1867_P1_N0 0x40 + +#define LTC1867_P2_N3 0x10 +#define LTC1867_P3_N2 0x50 + +#define LTC1867_P4_N5 0x20 +#define LTC1867_P5_N4 0x60 + +#define LTC1867_P6_N7 0x30 +#define LTC1867_P7_N6 0x70 +//!@} + +//! @name LTC1867 Single-Ended Channel Addresses, COM=CH7 +//! @{ +// Channel Address When CH7/COM Pin Is Used As Common +#define LTC1867_CH0_7COM 0x88 +#define LTC1867_CH1_7COM 0xC8 +#define LTC1867_CH2_7COM 0x98 +#define LTC1867_CH3_7COM 0xD8 +#define LTC1867_CH4_7COM 0xA8 +#define LTC1867_CH5_7COM 0xE8 +#define LTC1867_CH6_7COM 0xB8 +//!@} + +//! @name LTC1867 Sleep / Unipolar/Bipolar config bits +//! @{ +// Sleep Mode Command +#define LTC1867_SLEEP_MODE 0x02 +#define LTC1867_EXIT_SLEEP_MODE 0x00 +#define LTC1867_UNIPOLAR_MODE 0x04 // Bitwise OR with channel commands for unipolar mode +#define LTC1867_BIPOLAR_MODE 0x00 +//!@} + +/* + Example command + adc_command = LTC1867_P0_N1; // Differential Bipolar Mode with CH0 as positive and CH1 as negative. + adc_command = LTC1867_P0_N1 | LTC1867_UNIPOLAR_MODE; // Differential Unipolar Mode with CH0 as positive and CH1 as negative. + */ + +//! Reads the ADC and returns 16-bit data +//! @return void +void LTC1867_read(uint8_t adc_command, //!< Channel address, config bits ORed together + uint16_t *adc_code //!< Returns code read from ADC (from previous conversion) + ); + + +//! Calculates the LTC1867 input's unipolar voltage given the binary data and lsb weight. +//! @return Floating point voltage +float LTC1867_unipolar_code_to_voltage(uint16_t adc_code, //!< Raw ADC code + float LTC1867_lsb, //!< LSB value (volts) + int32_t LTC1867_offset_unipolar_code //!< Offset code + ); + +//! Calculates the LTC1867 input's bipolar voltage given the two's compliment data and lsb weight +//! @return Floating point voltage +float LTC1867_bipolar_code_to_voltage(uint16_t adc_code, //!< Raw ADC code + float LTC1867_lsb, //!< LSB value (volts) + int32_t LTC1867_offset_bipolar_code //!< Offset code + ); + +//! Calibrate the offset and LSB voltage given two measured offset codes, and a full-scale voltage with the corresponding code. +//! @return Void +void LTC1867_cal_voltage(uint16_t zero_unipolar_code, //!< Code from a unipolar zero reading + uint16_t zero_bipolar_code, //!< Code from a bipolar zero reading + uint16_t fs_code, //!< full-scale code + float zero_voltage, //!< Measured zero voltage + float fs_voltage, //!< Measured full-scale voltage + float *LTC1867_lsb, //!< Return LSB value (volts) + int32_t *LTC1867_offset_unipolar_code, //!< Return Unipolar Offset code, in floating point + int32_t *LTC1867_offset_bipolar_code //!< Return Bipolar Offset code, in floating point + ); + +#endif // LTC1867_H diff --git a/firmware/src/main.c b/firmware/src/main.c index cee88b5..8e4540a 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -16,7 +16,9 @@ #include "CB_functions.h" #include "usart1.h" #include "spi2.h" +#include "spi.h" #include "periph_conf.h" +#include "ltc1867l.h" #define UC_NO_REGS 17 @@ -85,7 +87,9 @@ int main(int argc, char *argv[]) { // SPIBuffer[1] = 0x5678; init_USART1(); + + spi_create(SPI1, GPIOA, GPIO_Pin_8); @@ -146,6 +150,40 @@ void SysTick_Handler(void) spi_dma_shovel(); spi_addr=(spi_addr+1)%16; + + + static uint8_t uni_bi_polar = LTC1867_UNIPOLAR_MODE; //!< The LTC1867 unipolar/bipolar mode selection + static float LTC1867_lsb = 6.25009537E-5; //!< Ideal LSB voltage for a perfect part + static int32_t LTC1867_offset_unipolar_code = 0; //!< Ideal unipolar offset for a perfect part + static int32_t LTC1867_offset_bipolar_code = 0; //!< Ideal bipolar offset for a perfect part + // Constants + //! Lookup table to build the command for single-ended mode, input with respect to GND + const uint8_t BUILD_COMMAND_SINGLE_ENDED[8] = {LTC1867_CH0, LTC1867_CH1, LTC1867_CH2, LTC1867_CH3, + LTC1867_CH4, LTC1867_CH5, LTC1867_CH6, LTC1867_CH7 + }; //!< Builds the command for single-ended mode, input with respect to GND + ////! Lookup table to build the command for single-ended mode with channel 7 as common pin + //const uint8_t BUILD_COMMAND_SINGLE_ENDED_COM7[7] = {LTC1867_CH0_7COM, LTC1867_CH1_7COM, LTC1867_CH2_7COM, LTC1867_CH3_7COM, + // LTC1867_CH4_7COM, LTC1867_CH5_7COM, LTC1867_CH6_7COM + // }; //!< Builds the command for single-ended mode, input with respect to CH7 + ////! Lookup table to build the command for differential mode with the selected uni/bipolar mode + //const uint8_t BUILD_COMMAND_DIFF[8] = {LTC1867_P0_N1, LTC1867_P2_N3, LTC1867_P4_N5, LTC1867_P6_N7, + // LTC1867_P1_N0, LTC1867_P3_N2, LTC1867_P5_N4, LTC1867_P7_N6 + // }; //!< Build the command for differential mode + + + uint8_t channel = 0; + uint8_t adc_command; // The LTC1867 command byte + uint16_t adc_code = 0; // The LTC1867 code + //float adc_voltage; // The LTC1867 voltage + adc_command = BUILD_COMMAND_SINGLE_ENDED[channel] | uni_bi_polar; + LTC1867_read(adc_command, &adc_code); // Throws out last reading + //delay(100); + LTC1867_read(adc_command, &adc_code); // returnes ADC reading in adc_code + + //if (uni_bi_polar == LTC1867_UNIPOLAR_MODE) + // adc_voltage = LTC1867_unipolar_code_to_voltage(adc_code, LTC1867_lsb, LTC1867_offset_unipolar_code); + //else + // adc_voltage = LTC1867_bipolar_code_to_voltage(adc_code, LTC1867_lsb, LTC1867_offset_bipolar_code); } diff --git a/firmware/src/spi.c b/firmware/src/spi.c index 805b1c4..a29f97e 100644 --- a/firmware/src/spi.c +++ b/firmware/src/spi.c @@ -1,6 +1,6 @@ #include "spi.h" -#define BUFFER_SIZE 2 +#define BUFFER_SIZE 4 SPI_TypeDef * SPI_Module; GPIO_TypeDef * CS_GPIO; @@ -44,6 +44,19 @@ void spi_writeTwoBytes(uint8_t byte1, uint8_t byte0){ spi_chipSelect(); spi_enableTxInterrupt(); } + +void spi_writeFourBytes(uint8_t byte3, uint8_t byte2, uint8_t byte1, uint8_t byte0){ + while(spiBusyFlag){} + spiTxCounter = 4; + spiRxCounter = 4; + spiBusyFlag = 1; + spiDataBuffer[0] = byte0; + spiDataBuffer[1] = byte1; + spiDataBuffer[2] = byte2; + spiDataBuffer[3] = byte3; + spi_chipSelect(); + spi_enableTxInterrupt(); +} void spi_handleSPI1Interrupt(void){ if(SPI_I2S_GetFlagStatus(SPI_Module, SPI_I2S_FLAG_RXNE) == SET){ @@ -64,4 +77,4 @@ void spi_handleSPI1Interrupt(void){ spi_disableTxInterrupt(); } } -} \ No newline at end of file +} -- 2.43.0