/* * The MIT License (MIT) * * Copyright (c) 2019 Matthias P. Braendli * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* Essential information from datasheet: * * SCK mode: * The internal or external SCK mode is selected on power-up and then * reselected every time a HIGH-to-LOW transition is detected at the CS pin. If * SCK is HIGH or floating at power-up or during this transition, the * converter enters the internal SCK mode. If SCK is LOW at power-up or * during this transition, the converter enters the external SCK mode. * * CS: * At any time during the conversion cycle, CS may be pulled LOW in order to * monitor the state of the converter. While CS is pulled LOW, EOC is output * to the SDO pin. EOC = 1 while a conversion is in progress and EOC = 0 if the * device is in the sleep state. Independent of CS, the device automatically * enters the low power sleep state once the conversion is complete. * * The device remains in the sleep state until the first rising edge of SCK is * seen while CS is LOW. * * We must latch incoming data on the rising edge of SCK. * * On the 32nd falling edge of SCK, the device begins a new conversion. * * The sub LSBs are valid conversion results beyond the 24-bit level that may * be included in averaging or discarded without loss of resolution. * * Output format, 32 bits in total: * EOC, DMY, SIG, EXR, MSB..LSB (24 bits), SUB-LSB(4 bits) * * - EOC goes low when the conversion is complete * - DMY is always low * - SIG is 1 when Vin > 0 * - EXR is 0 when 0 <= Vin <= Vref * * * We will use External SCK, Single Cycle Conversion (see table 4): * - SCK must be low on falling edge of CSn * - We will discard the sub LSB * - We will check that DMY is always 0, SIG always 1 and EXR always 0 * */ #include #include #include #include "ltc2400.h" static void cs_low() { cli(); PORTB &= ~PINB_SPI_LTC_CSn; sei(); } static void cs_high() { cli(); PORTB |= PINB_SPI_LTC_CSn; sei(); } static void spi_en() { cli(); // Set SPI Enable and Master mode, // bit order=MSB first (DORD=0), // SPI mode=0 (CPOL=0, CPHA=0) // // clock divider: // SPR1 and SPR0 are the two LSB bits in SPCR // SPR1 SPR0 ~SPI2X Freq // 0 0 0 fosc/2 // 0 1 0 fosc/8 // 1 0 0 fosc/32 // 1 1 0 fosc/64 // double speed mode: // 0 0 1 fosc/4 // 0 1 1 fosc/16 // 1 0 1 fosc/64 // 1 1 1 fosc/128 // // Set SPR0 for /8 SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPR0); SPSR = 0; // clear SPI2X sei(); } static void spi_dis() { cli(); PORTB &= ~PINB_SPI_SCK; SPCR = _BV(MSTR) | _BV(SPR1); sei(); } void ltc2400_init() { cli(); PORTB |= PINB_SPI_LTC_CSn; // Ensure CLK is low so that the device doesn't enter internal SCK mode PORTB &= ~PINB_SPI_SCK; sei(); } bool ltc2400_conversion_ready() { cs_low(); _delay_us(100); // EOC == 0 means conversion is complete and device in sleep mode return not (PINB & PINB_SPI_MISO); } double ltc2400_get_conversion_result(bool& dmy_fault, bool& exr_fault, uint32_t& raw_value) { spi_en(); cs_low(); _delay_us(100); uint8_t data[4] = {}; for (int i = 0; i < 4; i++) { SPDR = 0x0; // always output 0 while (!(SPSR & _BV(SPIF))) { /* wait */ } data[i] = SPDR; } spi_dis(); cs_high(); raw_value = ((uint32_t)data[0] << 24) | ((uint32_t)data[1] << 16) | ((uint32_t)data[2] << 8) | ((uint32_t)data[3]); dmy_fault = raw_value & (1uL << 30); exr_fault = raw_value & (1uL << 28); // Mask 4 MSB status bits, and shift out 4 sub-LSB bits const uint32_t adc_value = (raw_value >> 4) & 0x00FFFFFF; // Convert ADC value to voltage return (double)adc_value / ((double)0x00FFFFFF / 5.0f); }