diff --git a/src/shared/sensors/ADS131M04/ADS131M04.cpp b/src/shared/sensors/ADS131M04/ADS131M04.cpp index e8619ac47f4c7afcd999fe4a49959df38edbd19e..bb614f6cef4029e39f0d869011422141108158b9 100644 --- a/src/shared/sensors/ADS131M04/ADS131M04.cpp +++ b/src/shared/sensors/ADS131M04/ADS131M04.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2020 Skyward Experimental Rocketry +/* Copyright (c) 2023 Skyward Experimental Rocketry * Author: Alberto Nidasio * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -27,7 +27,7 @@ #include <utils/CRC.h> using namespace miosix; -using namespace Boardcore::ADS131M04RegisterBitMasks; +using namespace Boardcore::ADS131M04Defs; namespace Boardcore { @@ -38,7 +38,16 @@ ADS131M04::ADS131M04(SPIBusInterface &bus, miosix::GpioPin cs, { } -ADS131M04::ADS131M04(SPISlave spiSlave) : spiSlave(spiSlave) {} +ADS131M04::ADS131M04(SPISlave spiSlave) : spiSlave(spiSlave) +{ + // Initialize the channel configurations + for (int i = 0; i < CHANNELS_NUM; i++) + { + channelsPGAGain[i] = PGA::PGA_1; + channelsOffset[i] = 0; + channelsGain[i] = 1.0; + } +} SPIBusConfig ADS131M04::getDefaultSPIConfig() { @@ -48,28 +57,22 @@ SPIBusConfig ADS131M04::getDefaultSPIConfig() return spiConfig; } -void ADS131M04::setOversamplingRatio(OversamplingRatio ratio) -{ - changeRegister(Registers::REG_CLOCK, static_cast<uint16_t>(ratio), - REG_CLOCK_OSR); -} - bool ADS131M04::init() { return true; } bool ADS131M04::reset() { - // Write all the communication frame which is maximum 6 24bit words - uint8_t data[18] = {0}; + SPITransaction transaction(spiSlave); - // Prepare the command - data[1] = static_cast<uint16_t>(Commands::RESET); + uint8_t data[ADS131M04Defs::FULL_FRAME_SIZE] = {0}; + sendCommand(transaction, Command::RESET, data); - SPITransaction transaction(spiSlave); - transaction.write(data, sizeof(data)); + // The reset command takes typically 5us to reload the register contents. + // We wait 10us to be sure miosix::delayUs(10); - transaction.read(data, 3); - uint16_t response = data[0] << 8 | data[1]; + // To read the response we need to read the full 3 bytes word. The response + // code is contained in the first 2 bytes, the last byte is padding + uint16_t response = transaction.read24() >> 8; // Check for the correct response if (response != RESET_CMD_RESPONSE) @@ -84,242 +87,189 @@ bool ADS131M04::reset() void ADS131M04::calibrateOffset() { - // Reset all offsets and gains - for (int i = 0; i < 8; i++) + // Reset all offsets and gains, otherwise the calibration would be wrong + for (int i = 0; i < CHANNELS_NUM; i++) { - setChannelOffset(static_cast<Channel>(i), 0); + setChannelOffsetImpl(static_cast<Channel>(i), 0); + setChannelGainCalibrationImpl(static_cast<Channel>(i), 1.0); } // Sample the channels and average the samples - int32_t averageValues[4] = {0}; - for (int i = 0; i < 250; i++) + int32_t averageValues[CHANNELS_NUM] = {0}; + int realSampleCount = 0; + for (int i = 0; i < CALIBRATION_SAMPLES; i++) { + // Wait for a sample to be ready Thread::sleep(4); - uint8_t data[18] = {0}; - - data[0] = static_cast<uint16_t>(Commands::NULL_CMD) >> 8; - data[1] = static_cast<uint16_t>(Commands::NULL_CMD); - - SPITransaction transaction(spiSlave); - transaction.transfer(data, sizeof(data)); - - for (int j = 0; j < 4; j++) + // Sample the channels + int32_t rawValues[CHANNELS_NUM]; + if (!readSamples(rawValues)) { - int32_t tmp = static_cast<uint32_t>(data[j * 3 + 3]) << 16 | - static_cast<uint32_t>(data[j * 3 + 4]) << 8 | - static_cast<uint32_t>(data[j * 3 + 5]); - - // Check for the sign bit - if (tmp & 0x00800000) - tmp |= 0xFF000000; // Extend the sign bit + // If the CRC failed we skip this sample + continue; + } - averageValues[j] += tmp; + for (int j = 0; j < CHANNELS_NUM; j++) + { + averageValues[j] += rawValues[j]; } - } - for (int i = 0; i < 4; i++) - averageValues[i] /= 250; - // Configure the offset registers - for (int i = 0; i < 4; i++) + realSampleCount++; + } + for (int i = 0; i < CHANNELS_NUM; i++) { + // Compute the average + averageValues[i] /= realSampleCount; + LOG_INFO(logger, "Channel {} average offset: {}", i, + averageValues[i] * getLSBSizeFromGain(channelsPGAGain[i])); + + // The new offset is the average value setChannelOffset(static_cast<Channel>(i), averageValues[i]); + + // Reset the gain as it was before + setChannelGainCalibrationImpl(static_cast<Channel>(i), channelsGain[i]); } } +void ADS131M04::setOversamplingRatio(OversamplingRatio ratio) +{ + changeRegister(Register::REG_CLOCK, static_cast<uint16_t>(ratio), + RegClockMasks::OSR); +} + void ADS131M04::setChannelPGA(Channel channel, PGA gain) { + setChannelPGAImpl(channel, gain); channelsPGAGain[static_cast<int>(channel)] = gain; - - int shift = static_cast<int>(channel) * 4; - changeRegister(Registers::REG_GAIN, static_cast<uint16_t>(gain) << shift, - REG_GAIN_PGAGAIN0 << shift); } void ADS131M04::setChannelOffset(Channel channel, uint32_t offset) { - // Set the correct registers based on the selected channel - Registers regMSB, regLSB; - switch (static_cast<int>(channel)) - { - case 0: - regMSB = Registers::REG_CH0_OCAL_MSB; - regLSB = Registers::REG_CH0_OCAL_LSB; - break; - case 1: - regMSB = Registers::REG_CH1_OCAL_MSB; - regLSB = Registers::REG_CH1_OCAL_LSB; - break; - case 2: - regMSB = Registers::REG_CH2_OCAL_MSB; - regLSB = Registers::REG_CH2_OCAL_LSB; - break; - default: - regMSB = Registers::REG_CH3_OCAL_MSB; - regLSB = Registers::REG_CH3_OCAL_LSB; - break; - } - - writeRegister(regMSB, offset >> 8); - writeRegister(regLSB, offset << 16); + setChannelOffsetImpl(channel, offset); + channelsOffset[static_cast<int>(channel)] = offset; } void ADS131M04::setChannelGainCalibration(Channel channel, double gain) { - // Check gain value - if (gain < 0 || gain > 2) - return; - - // Set the correct registers based on the selected channel - Registers regMSB, regLSB; - switch (static_cast<int>(channel)) - { - case 0: - regMSB = Registers::REG_CH0_GCAL_MSB; - regLSB = Registers::REG_CH0_GCAL_LSB; - break; - case 1: - regMSB = Registers::REG_CH1_GCAL_MSB; - regLSB = Registers::REG_CH1_GCAL_LSB; - break; - case 2: - regMSB = Registers::REG_CH2_GCAL_MSB; - regLSB = Registers::REG_CH2_GCAL_LSB; - break; - default: - regMSB = Registers::REG_CH3_GCAL_MSB; - regLSB = Registers::REG_CH3_GCAL_LSB; - break; - } - - // Compute the correct gain register parameter - uint32_t rawGain = gain * 8388608; // 2^23 - - writeRegister(regMSB, rawGain >> 16); - writeRegister(regLSB, rawGain << 8); -} - -void ADS131M04::setChannelInput(Channel channel, Input input) -{ - // Set the correct register based on the selected channel - Registers reg; - switch (static_cast<int>(channel)) - { - case 0: - reg = Registers::REG_CH0_CFG; - break; - case 1: - reg = Registers::REG_CH1_CFG; - break; - case 2: - reg = Registers::REG_CH2_CFG; - break; - default: - reg = Registers::REG_CH3_CFG; - break; - } - - uint16_t value = readRegister(reg); - value &= REG_CHx_CFG_MUX; - value |= static_cast<uint16_t>(input); - writeRegister(reg, value); + setChannelGainCalibrationImpl(channel, gain); + channelsGain[static_cast<int>(channel)] = gain; } void ADS131M04::enableChannel(Channel channel) { - changeRegister(Registers::REG_CLOCK, 1 << (static_cast<int>(channel) + 8), + changeRegister(Register::REG_CLOCK, 1 << (static_cast<int>(channel) + 8), 1 << (static_cast<int>(channel) + 8)); } void ADS131M04::disableChannel(Channel channel) { - changeRegister(Registers::REG_CLOCK, 0, + changeRegister(Register::REG_CLOCK, 0, 1 << (static_cast<int>(channel) + 8)); } void ADS131M04::enableGlobalChopMode() { - changeRegister(Registers::REG_CFG, ADS131M04RegisterBitMasks::REG_CFG_GC_EN, - ADS131M04RegisterBitMasks::REG_CFG_GC_EN); + changeRegister(Register::REG_CFG, RegConfigurationMasks::GC_EN, + RegConfigurationMasks::GC_EN); } void ADS131M04::disableGlobalChopMode() { - changeRegister(Registers::REG_CFG, 0, - ADS131M04RegisterBitMasks::REG_CFG_GC_EN); -} - -ADCData ADS131M04::getVoltage(Channel channel) -{ - return {lastSample.timestamp, static_cast<uint8_t>(channel), - lastSample.voltage[static_cast<uint8_t>(channel)]}; + changeRegister(Register::REG_CFG, 0, RegConfigurationMasks::GC_EN); } bool ADS131M04::selfTest() { - static constexpr float V_REF = 1.2; - static constexpr float TEST_SIGNAL_FACTOR = 2 / 15; - static constexpr float TEST_SIGNAL_SLACK = 0.1; // Not defined in DS - - float voltage[4] = {0}; + float voltage[CHANNELS_NUM] = {0}; + bool success = true; - // Connect all channels to the positive DC test signal - for (int i = 0; i < 4; i++) + // Reset PGA, offsets and gains and connect the positive test signal + for (int i = 0; i < CHANNELS_NUM; i++) { + setChannelPGAImpl(static_cast<Channel>(i), PGA::PGA_1); + setChannelOffsetImpl(static_cast<Channel>(i), 0); + setChannelGainCalibrationImpl(static_cast<Channel>(i), 1.0); setChannelInput(static_cast<Channel>(i), Input::POSITIVE_DC_TEST); - setChannelPGA(static_cast<Channel>(i), PGA::PGA_1); } // Take some samples - for (int i = 0; i < 100; i++) + for (int i = 0; i < SELF_TEST_SAMPLES; i++) { Thread::sleep(4); auto newSample = sampleImpl(); - for (int j = 0; j < 4; j++) + for (int j = 0; j < CHANNELS_NUM; j++) { voltage[j] += newSample.voltage[j]; } } - // Check values - for (int i = 0; i < 4; i++) + // Check the values + for (int i = 0; i < CHANNELS_NUM; i++) { - voltage[i] /= 100; + // Compute the average + voltage[i] /= SELF_TEST_SAMPLES; + + // Check if the value is in the acceptable range if (voltage[i] < V_REF * TEST_SIGNAL_FACTOR - TEST_SIGNAL_SLACK) - return false; + { + LOG_ERR(logger, + "Self test failed on channel {} on positive test signal, " + "value was {}", + i, voltage[i]); + success = false; + } } // Connect all channels to the negative DC test signal - for (int i = 0; i < 4; i++) + for (int i = 0; i < CHANNELS_NUM; i++) { setChannelInput(static_cast<Channel>(i), Input::NEGATIVE_DC_TEST); } // Take some samples - for (int i = 0; i < 100; i++) + for (int i = 0; i < SELF_TEST_SAMPLES; i++) { Thread::sleep(4); auto newSample = sampleImpl(); - for (int j = 0; j < 4; j++) + for (int j = 0; j < CHANNELS_NUM; j++) { voltage[j] += newSample.voltage[j]; } } - // Check values - for (int i = 0; i < 4; i++) + // Check the values + for (int i = 0; i < CHANNELS_NUM; i++) { - voltage[i] /= 100; + // Compute the average + voltage[i] /= SELF_TEST_SAMPLES; + + // Check if the value is in the acceptable range if (voltage[i] > -V_REF * TEST_SIGNAL_FACTOR + TEST_SIGNAL_SLACK) - return false; + { + LOG_ERR(logger, + "Self test failed on channel {} on negative test signal, " + "value was {}", + i, voltage[i]); + success = false; + } } - // Reset channel connections - for (int i = 0; i < 8; i++) + // If any channel failed we return false + if (!success) { - setChannelInput(static_cast<Channel>(i), Input::POSITIVE_DC_TEST); + return false; + } + + // Reset channels previous configuration + for (int i = 0; i < CHANNELS_NUM; i++) + { + setChannelPGAImpl(static_cast<Channel>(i), channelsPGAGain[i]); + setChannelOffsetImpl(static_cast<Channel>(i), channelsOffset[i]); + setChannelGainCalibrationImpl(static_cast<Channel>(i), channelsGain[i]); + setChannelInput(static_cast<Channel>(i), Input::DEFAULT); } return true; @@ -327,27 +277,184 @@ bool ADS131M04::selfTest() ADS131M04Data ADS131M04::sampleImpl() { - // Send the NULL command and read response - uint8_t data[18] = {0}; + // Read the samples and check if the CRC is correct + int32_t rawValues[CHANNELS_NUM]; + if (!readSamples(rawValues)) + { + // The CRC failed, return the last sample + return lastSample; + } - data[0] = static_cast<uint16_t>(Commands::NULL_CMD) >> 8; - data[1] = static_cast<uint16_t>(Commands::NULL_CMD); + // Convert the raw values to voltages + ADS131M04Data adcData; + adcData.timestamp = TimestampTimer::getTimestamp(); + for (int i = 0; i < CHANNELS_NUM; i++) + { + adcData.voltage[i] = + rawValues[i] * getLSBSizeFromGain(channelsPGAGain[i]); + } - SPITransaction transaction(spiSlave); - transaction.transfer(data, sizeof(data)); + return adcData; +} - // Extract each channel value - int32_t rawValue[4]; - for (int i = 0; i < 4; i++) +void ADS131M04::setChannelInput(Channel channel, Input input) +{ + Register reg = getChannelConfigRegister(channel); + changeRegister(reg, static_cast<uint16_t>(input), RegChannelMasks::CFG_MUX); +} + +void ADS131M04::setChannelPGAImpl(Channel channel, PGA gain) +{ + int shift = static_cast<int>(channel) * 4; + changeRegister(Register::REG_GAIN, static_cast<uint16_t>(gain) << shift, + RegGainMasks::PGA_GAIN_0 << shift); +} + +void ADS131M04::setChannelOffsetImpl(Channel channel, uint32_t offset) +{ + // The offset is a 24 bit value. Its two most significant bytes are stored + // in the MSB register, and the least significant in the LSB register, like + // this: + // +----------+-------+ + // | | 23-16 | + // | OCAL_MSB +-------+ + // | | 15-8 | + // +----------+-------+ + // | | 7-0 | + // | OCAL_LSB +-------+ + // | | | + // +----------+-------+ + writeRegister(getChannelOffsetRegisterMSB(channel), offset >> 8); + writeRegister(getChannelOffsetRegisterLSB(channel), offset << 8); +} + +void ADS131M04::setChannelGainCalibrationImpl(Channel channel, double gain) +{ + // If the user passes a value outside the range [0, 2] we cap it. + if (gain < 0) + { + gain = 0; + } + else if (gain > 2) { - rawValue[i] = static_cast<uint32_t>(data[i * 3 + 3]) << 16 | - static_cast<uint32_t>(data[i * 3 + 4]) << 8 | - static_cast<uint32_t>(data[i * 3 + 5]); + gain = 2; } + // The ADS131M04 corrects for gain errors by multiplying the ADC conversion + // result using the gain calibration registers. + // The contents of the gain calibration registers are interpreted by the + // dives as 24-bit unsigned values corresponding to linear steps from 0 to + // 2-(1/2^23). + // So effectively the register value is a fixed point number with 1bit for + // the integer part and 23 bits for the fractional part. + // So to convert the gain value to the register value we multiply it by 2^23 + uint32_t rawGain = gain * (1 << 23); + + // The gain is a 24 bit value. Its two most significant bytes are stored + // in the MSB register, and the least significant in the LSB register, like + // this: + // +----------+-------+ + // | | 23-16 | + // | GCAL_MSB +-------+ + // | | 15-8 | + // +----------+-------+ + // | | 7-0 | + // | GCAL_LSB +-------+ + // | | | + // +----------+-------+ + writeRegister(getChannelGainRegisterMSB(channel), rawGain >> 8); + writeRegister(getChannelGainRegisterLSB(channel), rawGain << 8); +} + +Register ADS131M04::getChannelConfigRegister(Channel channel) +{ + switch (static_cast<int>(channel)) + { + case 0: + return Register::REG_CH0_CFG; + case 1: + return Register::REG_CH1_CFG; + case 2: + return Register::REG_CH2_CFG; + default: + return Register::REG_CH3_CFG; + } +} + +Register ADS131M04::getChannelOffsetRegisterMSB(Channel channel) +{ + switch (static_cast<int>(channel)) + { + case 0: + return Register::REG_CH0_OCAL_MSB; + case 1: + return Register::REG_CH1_OCAL_MSB; + case 2: + return Register::REG_CH2_OCAL_MSB; + default: + return Register::REG_CH3_OCAL_MSB; + } +} + +Register ADS131M04::getChannelOffsetRegisterLSB(Channel channel) +{ + switch (static_cast<int>(channel)) + { + case 0: + return Register::REG_CH0_OCAL_LSB; + case 1: + return Register::REG_CH1_OCAL_LSB; + case 2: + return Register::REG_CH2_OCAL_LSB; + default: + return Register::REG_CH3_OCAL_LSB; + } +} + +Register ADS131M04::getChannelGainRegisterMSB(Channel channel) +{ + switch (static_cast<int>(channel)) + { + case 0: + return Register::REG_CH0_GCAL_MSB; + case 1: + return Register::REG_CH1_GCAL_MSB; + case 2: + return Register::REG_CH2_GCAL_MSB; + default: + return Register::REG_CH3_GCAL_MSB; + } +} + +Register ADS131M04::getChannelGainRegisterLSB(Channel channel) +{ + switch (static_cast<int>(channel)) + { + case 0: + return Register::REG_CH0_GCAL_LSB; + case 1: + return Register::REG_CH1_GCAL_LSB; + case 2: + return Register::REG_CH2_GCAL_LSB; + default: + return Register::REG_CH3_GCAL_LSB; + } +} + +bool ADS131M04::readSamples(int32_t rawValues[CHANNELS_NUM]) +{ + // Send the NULL command and read response + uint8_t data[FULL_FRAME_SIZE] = {0}; + + data[0] = (static_cast<uint16_t>(Command::NULL_CMD) & 0xff00) >> 8; + data[1] = (static_cast<uint16_t>(Command::NULL_CMD) & 0x00ff); + + SPITransaction transaction(spiSlave); + transaction.transfer(data, sizeof(data)); + // Extract and verify the CRC uint16_t dataCrc = - static_cast<uint32_t>(data[15]) << 8 | static_cast<uint32_t>(data[16]); + static_cast<uint16_t>(data[15]) << 8 | static_cast<uint16_t>(data[16]); uint16_t calculatedCrc = CRCUtils::crc16(data, sizeof(data) - 3); if (dataCrc != calculatedCrc) @@ -356,36 +463,30 @@ ADS131M04Data ADS131M04::sampleImpl() LOG_ERR(logger, "Failed CRC check during sensor sampling"); // Return and don't convert the corrupted data - return lastSample; + return false; } - // Set the two complement - for (int i = 0; i < 4; i++) + // Extract each channel value + for (int i = 0; i < CHANNELS_NUM; i++) { - // Check for the sign bit - if (rawValue[i] & 0x00800000) - rawValue[i] |= 0xFF000000; // Extend the sign bit - } + rawValues[i] = static_cast<uint32_t>(data[i * 3 + 3]) << 16 | + static_cast<uint32_t>(data[i * 3 + 4]) << 8 | + static_cast<uint32_t>(data[i * 3 + 5]); - // Convert values - ADS131M04Data adcData; - adcData.timestamp = TimestampTimer::getTimestamp(); - for (int i = 0; i < 4; i++) - { - adcData.voltage[i] = - rawValue[i] * - PGA_LSB_SIZE[static_cast<uint16_t>(channelsPGAGain[i])]; + // Extend the sign bit with a double shift + rawValues[i] <<= 8; + rawValues[i] >>= 8; } - return adcData; + return true; } -uint16_t ADS131M04::readRegister(Registers reg) +uint16_t ADS131M04::readRegister(Register reg) { uint8_t data[3] = {0}; // Prepare the command - data[0] = static_cast<uint16_t>(Commands::RREG) >> 8 | + data[0] = static_cast<uint16_t>(Command::RREG) >> 8 | static_cast<uint16_t>(reg) >> 1; data[1] = static_cast<uint16_t>(reg) << 7; @@ -396,12 +497,14 @@ uint16_t ADS131M04::readRegister(Registers reg) return data[0] << 8 | data[1]; } -void ADS131M04::writeRegister(Registers reg, uint16_t data) +void ADS131M04::writeRegister(Register reg, uint16_t data) { + // The write command uses two communication words (3 bytes each), one for + // the register address and one for the data to write uint8_t writeCommand[6] = {0}; // Prepare the command - writeCommand[0] = static_cast<uint16_t>(Commands::WREG) >> 8 | + writeCommand[0] = static_cast<uint16_t>(Command::WREG) >> 8 | static_cast<uint16_t>(reg) >> 1; writeCommand[1] = static_cast<uint16_t>(reg) << 7; writeCommand[3] = data >> 8; @@ -410,29 +513,47 @@ void ADS131M04::writeRegister(Registers reg, uint16_t data) SPITransaction transaction(spiSlave); transaction.write(writeCommand, sizeof(writeCommand)); - // Check response - transaction.read(writeCommand, 3); - uint16_t response = writeCommand[0] << 8 | writeCommand[1]; - if (response != (0x4000 | (static_cast<uint16_t>(reg) << 7))) + // The response contains a fixed part and the register address. + uint16_t response = transaction.read24() >> 8; + if (response != (WRITE_CMD_RESPONSE | (static_cast<uint16_t>(reg) << 7))) { lastError = SensorErrors::COMMAND_FAILED; LOG_ERR(logger, "Write command failed, response was {:X}", response); } } -void ADS131M04::changeRegister(Registers reg, uint16_t newValue, uint16_t mask) +void ADS131M04::changeRegister(Register reg, uint16_t newValue, uint16_t mask) { // Read the clock register uint16_t regValue = readRegister(reg); - // Remove the OSR configuration + // Remove the target configuration regValue &= ~mask; - // Set the OSR + // Set the new value regValue |= newValue; // Write the new value writeRegister(reg, regValue); } +void ADS131M04::sendCommand(SPITransaction &transaction, Command command, + uint8_t data[FULL_FRAME_SIZE]) +{ + // All commands (a part from read and write) needs the full 10 words + // communication frame. Each word is (by default) 3 bytes long + + // The command is 16 bits long and goes in the most significant bytes of the + // first communication word + data[0] = (static_cast<uint16_t>(command) & 0xff00) >> 8; + data[1] = (static_cast<uint16_t>(command) & 0x00ff); + + transaction.write(data, FULL_FRAME_SIZE); +} + +float ADS131M04::getLSBSizeFromGain(PGA gain) +{ + return PGA_LSB_SIZE[static_cast<uint16_t>(gain)]; +} + } // namespace Boardcore diff --git a/src/shared/sensors/ADS131M04/ADS131M04.h b/src/shared/sensors/ADS131M04/ADS131M04.h index 4f4615ac1a44c0adc702a62537720b9727627a98..5140345736481fa8ab203cf70f9f6c6f2c840eac 100644 --- a/src/shared/sensors/ADS131M04/ADS131M04.h +++ b/src/shared/sensors/ADS131M04/ADS131M04.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2021 Skyward Experimental Rocketry +/* Copyright (c) 2023 Skyward Experimental Rocketry * Author: Alberto Nidasio * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -27,6 +27,7 @@ #include <sensors/Sensor.h> #include "ADS131M04Data.h" +#include "ADS131M04Defs.h" namespace Boardcore { @@ -74,57 +75,7 @@ namespace Boardcore class ADS131M04 : public Sensor<ADS131M04Data> { public: - /** - * @brief ADC's oversampling ratio configurations. - * - * The OSR determins the output data rate, depending on the master clock - * frequency. - * - * ODR = f_CLK / 2 / OSR - * - * On Skyward's boards an 8.192MHz clock is used. - */ - enum class OversamplingRatio : uint16_t - { - OSR_128 = 0, // ODR is 32KHz - OSR_256 = 0x1 << 2, // ODR is 16KHz - OSR_512 = 0x2 << 2, // ODR is 8KHz - OSR_1024 = 0x3 << 2, // ODR is 4KHz - OSR_2048 = 0x4 << 2, // ODR is 2KHz - OSR_4096 = 0x5 << 2, // ODR is 1KHz - OSR_8192 = 0x6 << 2, // ODR is 500Hz - OSR_16256 = 0x7 << 2 // ODR is 250Hz - }; - - enum class PGA : uint16_t - { - PGA_1 = 0, ///< Full scale resolution is ±1.2V - PGA_2 = 0x1, ///< Full scale resolution is ±600mV - PGA_4 = 0x2, ///< Full scale resolution is ±300mV - PGA_8 = 0x3, ///< Full scale resolution is ±150mV - PGA_16 = 0x4, ///< Full scale resolution is ±75mV - PGA_32 = 0x5, ///< Full scale resolution is ±37.5mV - PGA_64 = 0x6, ///< Full scale resolution is ±18.75mV - PGA_128 = 0x7 ///< Full scale resolution is ±9.375mV - }; - - enum class Channel : uint8_t - { - CHANNEL_0 = 0, - CHANNEL_1 = 1, - CHANNEL_2 = 2, - CHANNEL_3 = 3 - }; - - enum class Input : uint8_t - { - DEFAULT = 0, // AINxP and AINxN (default) - SHORTED = 1, // ADC inputs shorted - POSITIVE_DC_TEST = 2, // Positive DC test signal - NEGATIVE_DC_TEST = 3 // Negative DC test signal - }; - - ADS131M04(SPIBusInterface &bus, miosix::GpioPin cs, + ADS131M04(SPIBusInterface& bus, miosix::GpioPin cs, SPIBusConfig config = getDefaultSPIConfig()); explicit ADS131M04(SPISlave spiSlave); @@ -146,16 +97,16 @@ public: */ void calibrateOffset(); - void setOversamplingRatio(OversamplingRatio ratio); + void setOversamplingRatio(ADS131M04Defs::OversamplingRatio ratio); - void setChannelPGA(Channel channel, PGA gain); + void setChannelPGA(ADS131M04Defs::Channel channel, ADS131M04Defs::PGA gain); /** * @brief Sets the channel offset. * * Note that the device offset is a 24bit two complement. */ - void setChannelOffset(Channel channel, uint32_t offset); + void setChannelOffset(ADS131M04Defs::Channel channel, uint32_t offset); /** * @brief Sets the channel gain calibration. @@ -170,152 +121,69 @@ public: * * @param gain Must be between 0 and 2. */ - void setChannelGainCalibration(Channel channel, double gain); - - void setChannelInput(Channel channel, Input input); + void setChannelGainCalibration(ADS131M04Defs::Channel channel, double gain); - void enableChannel(Channel channel); + void enableChannel(ADS131M04Defs::Channel channel); - void disableChannel(Channel channel); + void disableChannel(ADS131M04Defs::Channel channel); void enableGlobalChopMode(); void disableGlobalChopMode(); - ADCData getVoltage(Channel channel); - bool selfTest() override; -protected: +private: ADS131M04Data sampleImpl() override; -private: - enum class Registers : uint16_t - { - // Device settings and indicators - REG_ID = 0, - REG_STATUS = 0x1, - - // Global settings across channels - REG_MODE = 0x2, - REG_CLOCK = 0x3, - REG_GAIN = 0x4, - REG_CFG = 0x6, - REG_THRSHLD_MSB = 0x7, - REG_THRSHLD_LSB = 0x8, - - // Channel specific settings - REG_CH0_CFG = 0x9, - REG_CH0_OCAL_MSB = 0xA, - REG_CH0_OCAL_LSB = 0xB, - REG_CH0_GCAL_MSB = 0xC, - REG_CH0_GCAL_LSB = 0xD, - REG_CH1_CFG = 0xE, - REG_CH1_OCAL_MSB = 0xF, - REG_CH1_OCAL_LSB = 0x10, - REG_CH1_GCAL_MSB = 0x11, - REG_CH1_GCAL_LSB = 0x12, - REG_CH2_CFG = 0x13, - REG_CH2_OCAL_MSB = 0x14, - REG_CH2_OCAL_LSB = 0x15, - REG_CH2_GCAL_MSB = 0x16, - REG_CH2_GCAL_LSB = 0x17, - REG_CH3_CFG = 0x18, - REG_CH3_OCAL_MSB = 0x19, - REG_CH3_OCAL_LSB = 0x1A, - REG_CH3_GCAL_MSB = 0x1B, - REG_CH3_GCAL_LSB = 0x1C, - - // Register map CRC - REG_REGMAP_CRC = 0x3E - }; - - uint16_t readRegister(Registers reg); - - void writeRegister(Registers reg, uint16_t data); - - void changeRegister(Registers reg, uint16_t newValue, uint16_t mask); - - enum class Commands : uint16_t - { - NULL_CMD = 0, - RESET = 0x11, - STANDBY = 0x22, - WAKEUP = 0x33, - LOCK = 0x555, - UNLOCK = 0x655, - RREG = 0xA000, - WREG = 0x6000 - }; + void setChannelInput(ADS131M04Defs::Channel channel, + ADS131M04Defs::Input input); - SPISlave spiSlave; + void setChannelPGAImpl(ADS131M04Defs::Channel channel, + ADS131M04Defs::PGA gain); - PGA channelsPGAGain[4] = {PGA::PGA_1, PGA::PGA_1, PGA::PGA_1, PGA::PGA_1}; + void setChannelOffsetImpl(ADS131M04Defs::Channel channel, uint32_t offset); - PrintLogger logger = Logging::getLogger("ads131m04"); + void setChannelGainCalibrationImpl(ADS131M04Defs::Channel channel, + double gain); - static constexpr uint16_t RESET_CMD_RESPONSE = 0xFF24; + ADS131M04Defs::Register getChannelConfigRegister( + ADS131M04Defs::Channel channel); - ///< Digit value in mV for each pga configurations - const float PGA_LSB_SIZE[8] = {143.0511e-9, 71.5256e-9, 35.7628e-9, - 17.8814e-9, 8.9407e-9, 4.4703e-9, - 2.2352e-9, 1.1176e-9}; -}; + ADS131M04Defs::Register getChannelOffsetRegisterMSB( + ADS131M04Defs::Channel channel); -namespace ADS131M04RegisterBitMasks -{ + ADS131M04Defs::Register getChannelOffsetRegisterLSB( + ADS131M04Defs::Channel channel); -// Status register -constexpr uint16_t REG_STATUS_LOCK = 1 << 15; -constexpr uint16_t REG_STATUS_F_RESYNC = 1 << 14; -constexpr uint16_t REG_STATUS_REG_MAP = 1 << 13; -constexpr uint16_t REG_STATUS_CRC_ERR = 1 << 12; -constexpr uint16_t REG_STATUS_CRC_TYPE = 1 << 11; -constexpr uint16_t REG_STATUS_RESET = 1 << 10; -constexpr uint16_t REG_STATUS_WLENGTH = 3 << 8; -constexpr uint16_t REG_STATUS_DRDY3 = 1 << 3; -constexpr uint16_t REG_STATUS_DRDY2 = 1 << 2; -constexpr uint16_t REG_STATUS_DRDY1 = 1 << 1; -constexpr uint16_t REG_STATUS_DRDY0 = 1; - -// Mode register -constexpr uint16_t REG_MODE_REG_CRC_EN = 1 << 13; -constexpr uint16_t REG_MODE_RX_CRC_EN = 1 << 12; -constexpr uint16_t REG_MODE_CRC_TYPE = 1 << 11; -constexpr uint16_t REG_MODE_RESET = 1 << 10; -constexpr uint16_t REG_MODE_WLENGTH = 3 << 8; -constexpr uint16_t REG_MODE_TIMEOUT = 1 << 4; -constexpr uint16_t REG_MODE_DRDY_SEL = 3 << 2; -constexpr uint16_t REG_MODE_DRDY_HiZ = 1 << 1; -constexpr uint16_t REG_MODE_DRDY_FMT = 1 << 0; - -// Clock register -constexpr uint16_t REG_CLOCK_CH3_EN = 1 << 11; -constexpr uint16_t REG_CLOCK_CH2_EN = 1 << 10; -constexpr uint16_t REG_CLOCK_CH1_EN = 1 << 9; -constexpr uint16_t REG_CLOCK_CH0_EN = 1 << 8; -constexpr uint16_t REG_CLOCK_OSR = 7 << 2; -constexpr uint16_t REG_CLOCK_PWR = 3; - -// Gain register -constexpr uint16_t REG_GAIN_PGAGAIN3 = 7 << 12; -constexpr uint16_t REG_GAIN_PGAGAIN2 = 7 << 8; -constexpr uint16_t REG_GAIN_PGAGAIN1 = 7 << 4; -constexpr uint16_t REG_GAIN_PGAGAIN0 = 7; - -// Configuration register -constexpr uint16_t REG_CFG_GC_DLY = 0xF << 9; -constexpr uint16_t REG_CFG_GC_EN = 1 << 8; -constexpr uint16_t REG_CFG_CD_ALLCH = 1 << 7; -constexpr uint16_t REG_CFG_CD_NUM = 7 << 4; -constexpr uint16_t REG_CFG_CD_LEN = 7 << 1; -constexpr uint16_t REG_CFG_CD_EN = 1; - -// Channel configuration register -constexpr uint16_t REG_CHx_CFG_PHASE = 0x3FF << 6; -constexpr uint16_t REG_CHx_CFG_DCBLK_DIS = 1 << 2; -constexpr uint16_t REG_CHx_CFG_MUX = 3; - -} // namespace ADS131M04RegisterBitMasks + ADS131M04Defs::Register getChannelGainRegisterMSB( + ADS131M04Defs::Channel channel); + + ADS131M04Defs::Register getChannelGainRegisterLSB( + ADS131M04Defs::Channel channel); + + bool readSamples(int32_t rawValues[ADS131M04Defs::CHANNELS_NUM]); + + uint16_t readRegister(ADS131M04Defs::Register reg); + + void writeRegister(ADS131M04Defs::Register reg, uint16_t data); + + void changeRegister(ADS131M04Defs::Register reg, uint16_t newValue, + uint16_t mask); + + void sendCommand(SPITransaction& transaction, ADS131M04Defs::Command cmd, + uint8_t data[ADS131M04Defs::FULL_FRAME_SIZE]); + + float getLSBSizeFromGain(ADS131M04Defs::PGA gain); + + SPISlave spiSlave; + + // Current channels configuration + ADS131M04Defs::PGA channelsPGAGain[ADS131M04Defs::CHANNELS_NUM]; + uint32_t channelsOffset[ADS131M04Defs::CHANNELS_NUM]; + double channelsGain[ADS131M04Defs::CHANNELS_NUM]; + + PrintLogger logger = Logging::getLogger("ads131m04"); +}; } // namespace Boardcore diff --git a/src/shared/sensors/ADS131M04/ADS131M04Defs.h b/src/shared/sensors/ADS131M04/ADS131M04Defs.h new file mode 100644 index 0000000000000000000000000000000000000000..c90555df41eacec8f8ff6a32a75fb391a89dfbfb --- /dev/null +++ b/src/shared/sensors/ADS131M04/ADS131M04Defs.h @@ -0,0 +1,216 @@ +/* Copyright (c) 2023 Skyward Experimental Rocketry + * Author: Alberto Nidasio + * + * 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. + */ + +#pragma once + +#include <stdint.h> + +namespace Boardcore +{ + +namespace ADS131M04Defs +{ + +static constexpr int CHANNELS_NUM = 4; +static constexpr int CALIBRATION_SAMPLES = 250; +static constexpr int SELF_TEST_SAMPLES = 250; +static constexpr int FULL_FRAME_SIZE = 18; +static constexpr uint16_t RESET_CMD_RESPONSE = 0xFF24; +static constexpr uint16_t WRITE_CMD_RESPONSE = 0x4000; + +///< Digit value in mV for each pga configurations +constexpr float PGA_LSB_SIZE[8] = {143.0511e-9, 71.5256e-9, 35.7628e-9, + 17.8814e-9, 8.9407e-9, 4.4703e-9, + 2.2352e-9, 1.1176e-9}; + +static constexpr float V_REF = 1.2; +static constexpr float TEST_SIGNAL_FACTOR = 2 / 15; +static constexpr float TEST_SIGNAL_SLACK = 0.1; // Not defined in DS + +/** + * @brief ADC's oversampling ratio configurations. + * + * The OSR determins the output data rate, depending on the master clock + * frequency. + * + * ODR = f_CLK / 2 / OSR + * + * On Skyward's boards an 8.192MHz clock is used. + */ +enum class OversamplingRatio : uint16_t +{ + OSR_128 = 0, // ODR is 32KHz + OSR_256 = 0x1 << 2, // ODR is 16KHz + OSR_512 = 0x2 << 2, // ODR is 8KHz + OSR_1024 = 0x3 << 2, // ODR is 4KHz + OSR_2048 = 0x4 << 2, // ODR is 2KHz + OSR_4096 = 0x5 << 2, // ODR is 1KHz + OSR_8192 = 0x6 << 2, // ODR is 500Hz + OSR_16256 = 0x7 << 2 // ODR is 250Hz +}; + +enum class PGA : uint16_t +{ + PGA_1 = 0, ///< Full scale resolution is ±1.2V + PGA_2 = 0x1, ///< Full scale resolution is ±600mV + PGA_4 = 0x2, ///< Full scale resolution is ±300mV + PGA_8 = 0x3, ///< Full scale resolution is ±150mV + PGA_16 = 0x4, ///< Full scale resolution is ±75mV + PGA_32 = 0x5, ///< Full scale resolution is ±37.5mV + PGA_64 = 0x6, ///< Full scale resolution is ±18.75mV + PGA_128 = 0x7 ///< Full scale resolution is ±9.375mV +}; + +enum class Channel : uint8_t +{ + CHANNEL_0 = 0, + CHANNEL_1 = 1, + CHANNEL_2 = 2, + CHANNEL_3 = 3 +}; + +enum class Input : uint8_t +{ + DEFAULT = 0, // AINxP and AINxN (default) + SHORTED = 1, // ADC inputs shorted + POSITIVE_DC_TEST = 2, // Positive DC test signal + NEGATIVE_DC_TEST = 3 // Negative DC test signal +}; + +enum class Register : uint16_t +{ + // Device settings and indicators + REG_ID = 0, + REG_STATUS = 0x1, + + // Global settings across channels + REG_MODE = 0x2, + REG_CLOCK = 0x3, + REG_GAIN = 0x4, + REG_CFG = 0x6, + REG_THRSHLD_MSB = 0x7, + REG_THRSHLD_LSB = 0x8, + + // Channel specific settings + REG_CH0_CFG = 0x9, + REG_CH0_OCAL_MSB = 0xA, + REG_CH0_OCAL_LSB = 0xB, + REG_CH0_GCAL_MSB = 0xC, + REG_CH0_GCAL_LSB = 0xD, + REG_CH1_CFG = 0xE, + REG_CH1_OCAL_MSB = 0xF, + REG_CH1_OCAL_LSB = 0x10, + REG_CH1_GCAL_MSB = 0x11, + REG_CH1_GCAL_LSB = 0x12, + REG_CH2_CFG = 0x13, + REG_CH2_OCAL_MSB = 0x14, + REG_CH2_OCAL_LSB = 0x15, + REG_CH2_GCAL_MSB = 0x16, + REG_CH2_GCAL_LSB = 0x17, + REG_CH3_CFG = 0x18, + REG_CH3_OCAL_MSB = 0x19, + REG_CH3_OCAL_LSB = 0x1A, + REG_CH3_GCAL_MSB = 0x1B, + REG_CH3_GCAL_LSB = 0x1C, + + // Register map CRC + REG_REGMAP_CRC = 0x3E +}; + +enum class Command : uint16_t +{ + NULL_CMD = 0x0000, + RESET = 0x0011, + STANDBY = 0x0022, + WAKEUP = 0x0033, + LOCK = 0x0555, + UNLOCK = 0x0655, + RREG = 0xA000, + WREG = 0x6000 +}; + +namespace RegStatusMasks +{ +constexpr uint16_t LOCK = 0x1 << 15; +constexpr uint16_t F_RESYNC = 0x1 << 14; +constexpr uint16_t REG_MAP = 0x1 << 13; +constexpr uint16_t CRC_ERR = 0x1 << 12; +constexpr uint16_t CRC_TYPE = 0x1 << 11; +constexpr uint16_t RESET = 0x1 << 10; +constexpr uint16_t WLENGTH = 0x3 << 8; +constexpr uint16_t DRDY3 = 0x1 << 3; +constexpr uint16_t DRDY2 = 0x1 << 2; +constexpr uint16_t DRDY1 = 0x1 << 1; +constexpr uint16_t DRDY0 = 0x1 << 0; +} // namespace RegStatusMasks + +namespace RegModeMasks +{ +constexpr uint16_t REG_CRC_EN = 0x1 << 13; +constexpr uint16_t RX_CRC_EN = 0x1 << 12; +constexpr uint16_t CRC_TYPE = 0x1 << 11; +constexpr uint16_t RESET = 0x1 << 10; +constexpr uint16_t WLENGTH = 0x3 << 8; +constexpr uint16_t TIMEOUT = 0x1 << 4; +constexpr uint16_t DRDY_SEL = 0x3 << 2; +constexpr uint16_t DRDY_HiZ = 0x1 << 1; +constexpr uint16_t DRDY_FMT = 0x1 << 0; +} // namespace RegModeMasks + +namespace RegClockMasks +{ +constexpr uint16_t CH3_EN = 0x1 << 11; +constexpr uint16_t CH2_EN = 0x1 << 10; +constexpr uint16_t CH1_EN = 0x1 << 9; +constexpr uint16_t CH0_EN = 0x1 << 8; +constexpr uint16_t OSR = 0x7 << 2; +constexpr uint16_t POWER_MODE = 0x3 << 0; +} // namespace RegClockMasks + +namespace RegGainMasks +{ +constexpr uint16_t PGA_GAIN_3 = 0x7 << 12; +constexpr uint16_t PGA_GAIN_2 = 0x7 << 8; +constexpr uint16_t PGA_GAIN_1 = 0x7 << 4; +constexpr uint16_t PGA_GAIN_0 = 0x7 << 0; +} // namespace RegGainMasks + +namespace RegConfigurationMasks +{ +constexpr uint16_t GC_DLY = 0xF << 9; +constexpr uint16_t GC_EN = 0x1 << 8; +constexpr uint16_t CD_ALLCH = 0x1 << 7; +constexpr uint16_t CD_NUM = 0x7 << 4; +constexpr uint16_t CD_LEN = 0x7 << 1; +constexpr uint16_t CD_EN = 0x1 << 0; +} // namespace RegConfigurationMasks + +namespace RegChannelMasks +{ +constexpr uint16_t CFG_PHASE = 0x3FF << 6; +constexpr uint16_t CFG_DCBLK_DIS = 0x001 << 2; +constexpr uint16_t CFG_MUX = 0x003 << 0; +} // namespace RegChannelMasks + +} // namespace ADS131M04Defs + +} // namespace Boardcore diff --git a/src/shared/sensors/ADS131M08/ADS131M08.cpp b/src/shared/sensors/ADS131M08/ADS131M08.cpp index 667ccd2449109ad690b190ed5ed62338e76ba54d..dfcda68abb9c52b4754ca6ca1e71057c1ed1136b 100644 --- a/src/shared/sensors/ADS131M08/ADS131M08.cpp +++ b/src/shared/sensors/ADS131M08/ADS131M08.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2020 Skyward Experimental Rocketry +/* Copyright (c) 2023 Skyward Experimental Rocketry * Author: Alberto Nidasio * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -27,7 +27,7 @@ #include <utils/CRC.h> using namespace miosix; -using namespace Boardcore::ADS131M08RegisterBitMasks; +using namespace Boardcore::ADS131M08Defs; namespace Boardcore { @@ -38,7 +38,16 @@ ADS131M08::ADS131M08(SPIBusInterface &bus, miosix::GpioPin cs, { } -ADS131M08::ADS131M08(SPISlave spiSlave) : spiSlave(spiSlave) {} +ADS131M08::ADS131M08(SPISlave spiSlave) : spiSlave(spiSlave) +{ + // Initialize the channel configurations + for (int i = 0; i < CHANNELS_NUM; i++) + { + channelsPGAGain[i] = PGA::PGA_1; + channelsOffset[i] = 0; + channelsGain[i] = 1.0; + } +} SPIBusConfig ADS131M08::getDefaultSPIConfig() { @@ -48,28 +57,22 @@ SPIBusConfig ADS131M08::getDefaultSPIConfig() return spiConfig; } -void ADS131M08::setOversamplingRatio(OversamplingRatio ratio) -{ - changeRegister(Registers::REG_CLOCK, static_cast<uint16_t>(ratio), - REG_CLOCK_OSR); -} - bool ADS131M08::init() { return true; } bool ADS131M08::reset() { - // Write all the communication frame which is maximum 6 24bit words - uint8_t data[30] = {0}; + SPITransaction transaction(spiSlave); - // Prepare the command - data[1] = static_cast<uint16_t>(Commands::RESET); + uint8_t data[ADS131M08Defs::FULL_FRAME_SIZE] = {0}; + sendCommand(transaction, Command::RESET, data); - SPITransaction transaction(spiSlave); - transaction.write(data, sizeof(data)); + // The reset command takes typically 5us to reload the register contents. + // We wait 10us to be sure miosix::delayUs(10); - transaction.read(data, 3); - uint16_t response = data[0] << 8 | data[1]; + // To read the response we need to read the full 3 bytes word. The response + // code is contained in the first 2 bytes, the last byte is padding + uint16_t response = transaction.read24() >> 8; // Check for the correct response if (response != RESET_CMD_RESPONSE) @@ -84,297 +87,189 @@ bool ADS131M08::reset() void ADS131M08::calibrateOffset() { - // Reset all offsets and gains - for (int i = 0; i < 8; i++) + // Reset all offsets and gains, otherwise the calibration would be wrong + for (int i = 0; i < CHANNELS_NUM; i++) { - setChannelOffset(static_cast<Channel>(i), 0); + setChannelOffsetImpl(static_cast<Channel>(i), 0); + setChannelGainCalibrationImpl(static_cast<Channel>(i), 1.0); } // Sample the channels and average the samples - int32_t averageValues[8] = {0}; - for (int i = 0; i < 250; i++) + int32_t averageValues[CHANNELS_NUM] = {0}; + int realSampleCount = 0; + for (int i = 0; i < CALIBRATION_SAMPLES; i++) { + // Wait for a sample to be ready Thread::sleep(4); - uint8_t data[30] = {0}; - - data[0] = static_cast<uint16_t>(Commands::NULL_CMD) >> 8; - data[1] = static_cast<uint16_t>(Commands::NULL_CMD); - - SPITransaction transaction(spiSlave); - transaction.transfer(data, sizeof(data)); - - for (int j = 0; j < 8; j++) + // Sample the channels + int32_t rawValues[CHANNELS_NUM]; + if (!readSamples(rawValues)) { - int32_t tmp = static_cast<uint32_t>(data[j * 3 + 3]) << 16 | - static_cast<uint32_t>(data[j * 3 + 4]) << 8 | - static_cast<uint32_t>(data[j * 3 + 5]); - - // Check for the sign bit - if (tmp & 0x00800000) - tmp |= 0xFF000000; // Extend the sign bit + // If the CRC failed we skip this sample + continue; + } - averageValues[j] += tmp; + for (int j = 0; j < CHANNELS_NUM; j++) + { + averageValues[j] += rawValues[j]; } - } - for (int i = 0; i < 8; i++) - averageValues[i] /= 250; - // Configure the offset registers - for (int i = 0; i < 8; i++) + realSampleCount++; + } + for (int i = 0; i < CHANNELS_NUM; i++) { + // Compute the average + averageValues[i] /= realSampleCount; + LOG_INFO(logger, "Channel {} average offset: {}", i, + averageValues[i] * getLSBSizeFromGain(channelsPGAGain[i])); + + // The new offset is the average value setChannelOffset(static_cast<Channel>(i), averageValues[i]); + + // Reset the gain as it was before + setChannelGainCalibrationImpl(static_cast<Channel>(i), channelsGain[i]); } } +void ADS131M08::setOversamplingRatio(OversamplingRatio ratio) +{ + changeRegister(Register::REG_CLOCK, static_cast<uint16_t>(ratio), + RegClockMasks::OSR); +} + void ADS131M08::setChannelPGA(Channel channel, PGA gain) { + setChannelPGAImpl(channel, gain); channelsPGAGain[static_cast<int>(channel)] = gain; - - if (channel <= Channel::CHANNEL_3) - { - int shift = static_cast<int>(channel) * 4; - changeRegister(Registers::REG_GAIN_1, - static_cast<uint16_t>(gain) << shift, - REG_GAIN_PGAGAIN0 << shift); - } - else - { - int shift = (static_cast<int>(channel) - 4) * 4; - changeRegister(Registers::REG_GAIN_2, - static_cast<uint16_t>(gain) << shift, - REG_GAIN_PGAGAIN0 << shift); - } } void ADS131M08::setChannelOffset(Channel channel, uint32_t offset) { - // Set the correct registers based on the selected channel - Registers regMSB, regLSB; - switch (static_cast<int>(channel)) - { - case 0: - regMSB = Registers::REG_CH0_OCAL_MSB; - regLSB = Registers::REG_CH0_OCAL_LSB; - break; - case 1: - regMSB = Registers::REG_CH1_OCAL_MSB; - regLSB = Registers::REG_CH1_OCAL_LSB; - break; - case 2: - regMSB = Registers::REG_CH2_OCAL_MSB; - regLSB = Registers::REG_CH2_OCAL_LSB; - break; - case 3: - regMSB = Registers::REG_CH3_OCAL_MSB; - regLSB = Registers::REG_CH3_OCAL_LSB; - break; - case 4: - regMSB = Registers::REG_CH4_OCAL_MSB; - regLSB = Registers::REG_CH4_OCAL_LSB; - break; - case 5: - regMSB = Registers::REG_CH5_OCAL_MSB; - regLSB = Registers::REG_CH5_OCAL_LSB; - break; - case 6: - regMSB = Registers::REG_CH6_OCAL_MSB; - regLSB = Registers::REG_CH6_OCAL_LSB; - break; - default: - regMSB = Registers::REG_CH7_OCAL_MSB; - regLSB = Registers::REG_CH7_OCAL_LSB; - break; - } - - writeRegister(regMSB, offset >> 8); - writeRegister(regLSB, offset << 16); + setChannelOffsetImpl(channel, offset); + channelsOffset[static_cast<int>(channel)] = offset; } void ADS131M08::setChannelGainCalibration(Channel channel, double gain) { - // Check gain value - if (gain < 0 || gain > 2) - return; - - // Set the correct registers based on the selected channel - Registers regMSB, regLSB; - switch (static_cast<int>(channel)) - { - case 0: - regMSB = Registers::REG_CH0_GCAL_MSB; - regLSB = Registers::REG_CH0_GCAL_LSB; - break; - case 1: - regMSB = Registers::REG_CH1_GCAL_MSB; - regLSB = Registers::REG_CH1_GCAL_LSB; - break; - case 2: - regMSB = Registers::REG_CH2_GCAL_MSB; - regLSB = Registers::REG_CH2_GCAL_LSB; - break; - case 3: - regMSB = Registers::REG_CH3_GCAL_MSB; - regLSB = Registers::REG_CH3_GCAL_LSB; - break; - case 4: - regMSB = Registers::REG_CH4_GCAL_MSB; - regLSB = Registers::REG_CH4_GCAL_LSB; - break; - case 5: - regMSB = Registers::REG_CH5_GCAL_MSB; - regLSB = Registers::REG_CH5_GCAL_LSB; - break; - case 6: - regMSB = Registers::REG_CH6_GCAL_MSB; - regLSB = Registers::REG_CH6_GCAL_LSB; - break; - default: - regMSB = Registers::REG_CH7_GCAL_MSB; - regLSB = Registers::REG_CH7_GCAL_LSB; - break; - } - - // Compute the correct gain register parameter - uint32_t rawGain = gain * 8388608; // 2^23 - - writeRegister(regMSB, rawGain >> 16); - writeRegister(regLSB, rawGain << 8); -} - -void ADS131M08::setChannelInput(Channel channel, Input input) -{ - // Set the correct register based on the selected channel - Registers reg; - switch (static_cast<int>(channel)) - { - case 0: - reg = Registers::REG_CH0_CFG; - break; - case 1: - reg = Registers::REG_CH1_CFG; - break; - case 2: - reg = Registers::REG_CH2_CFG; - break; - case 3: - reg = Registers::REG_CH3_CFG; - break; - case 4: - reg = Registers::REG_CH4_CFG; - break; - case 5: - reg = Registers::REG_CH5_CFG; - break; - case 6: - reg = Registers::REG_CH6_CFG; - break; - default: - reg = Registers::REG_CH7_CFG; - break; - } - - uint16_t value = readRegister(reg); - value &= REG_CHx_CFG_MUX; - value |= static_cast<uint16_t>(input); - writeRegister(reg, value); + setChannelGainCalibrationImpl(channel, gain); + channelsGain[static_cast<int>(channel)] = gain; } void ADS131M08::enableChannel(Channel channel) { - changeRegister(Registers::REG_CLOCK, 1 << (static_cast<int>(channel) + 8), + changeRegister(Register::REG_CLOCK, 1 << (static_cast<int>(channel) + 8), 1 << (static_cast<int>(channel) + 8)); } void ADS131M08::disableChannel(Channel channel) { - changeRegister(Registers::REG_CLOCK, 0, + changeRegister(Register::REG_CLOCK, 0, 1 << (static_cast<int>(channel) + 8)); } void ADS131M08::enableGlobalChopMode() { - changeRegister(Registers::REG_CFG, ADS131M08RegisterBitMasks::REG_CFG_GC_EN, - ADS131M08RegisterBitMasks::REG_CFG_GC_EN); + changeRegister(Register::REG_CFG, RegConfigurationMasks::GC_EN, + RegConfigurationMasks::GC_EN); } void ADS131M08::disableGlobalChopMode() { - changeRegister(Registers::REG_CFG, 0, - ADS131M08RegisterBitMasks::REG_CFG_GC_EN); -} - -ADCData ADS131M08::getVoltage(Channel channel) -{ - return {lastSample.timestamp, static_cast<uint8_t>(channel), - lastSample.voltage[static_cast<uint8_t>(channel)]}; + changeRegister(Register::REG_CFG, 0, RegConfigurationMasks::GC_EN); } bool ADS131M08::selfTest() { - static constexpr float V_REF = 1.2; - static constexpr float TEST_SIGNAL_FACTOR = 2 / 15; - static constexpr float TEST_SIGNAL_SLACK = 0.1; // Not defined in DS - - float voltage[8] = {0}; + float voltage[CHANNELS_NUM] = {0}; + bool success = true; - // Connect all channels to the positive DC test signal - for (int i = 0; i < 8; i++) + // Reset PGA, offsets and gains and connect the positive test signal + for (int i = 0; i < CHANNELS_NUM; i++) { + setChannelPGAImpl(static_cast<Channel>(i), PGA::PGA_1); + setChannelOffsetImpl(static_cast<Channel>(i), 0); + setChannelGainCalibrationImpl(static_cast<Channel>(i), 1.0); setChannelInput(static_cast<Channel>(i), Input::POSITIVE_DC_TEST); - setChannelPGA(static_cast<Channel>(i), PGA::PGA_1); } // Take some samples - for (int i = 0; i < 100; i++) + for (int i = 0; i < SELF_TEST_SAMPLES; i++) { Thread::sleep(4); auto newSample = sampleImpl(); - for (int j = 0; j < 8; j++) + for (int j = 0; j < CHANNELS_NUM; j++) { voltage[j] += newSample.voltage[j]; } } - // Check values - for (int i = 0; i < 8; i++) + // Check the values + for (int i = 0; i < CHANNELS_NUM; i++) { - voltage[i] /= 100; + // Compute the average + voltage[i] /= SELF_TEST_SAMPLES; + + // Check if the value is in the acceptable range if (voltage[i] < V_REF * TEST_SIGNAL_FACTOR - TEST_SIGNAL_SLACK) - return false; + { + LOG_ERR(logger, + "Self test failed on channel {} on positive test signal, " + "value was {}", + i, voltage[i]); + success = false; + } } // Connect all channels to the negative DC test signal - for (int i = 0; i < 8; i++) + for (int i = 0; i < CHANNELS_NUM; i++) { setChannelInput(static_cast<Channel>(i), Input::NEGATIVE_DC_TEST); } // Take some samples - for (int i = 0; i < 100; i++) + for (int i = 0; i < SELF_TEST_SAMPLES; i++) { Thread::sleep(4); auto newSample = sampleImpl(); - for (int j = 0; j < 8; j++) + for (int j = 0; j < CHANNELS_NUM; j++) { voltage[j] += newSample.voltage[j]; } } - // Check values - for (int i = 0; i < 8; i++) + // Check the values + for (int i = 0; i < CHANNELS_NUM; i++) { - voltage[i] /= 100; + // Compute the average + voltage[i] /= SELF_TEST_SAMPLES; + + // Check if the value is in the acceptable range if (voltage[i] > -V_REF * TEST_SIGNAL_FACTOR + TEST_SIGNAL_SLACK) - return false; + { + LOG_ERR(logger, + "Self test failed on channel {} on negative test signal, " + "value was {}", + i, voltage[i]); + success = false; + } } - // Reset channel connections - for (int i = 0; i < 8; i++) + // If any channel failed we return false + if (!success) { - setChannelInput(static_cast<Channel>(i), Input::POSITIVE_DC_TEST); + return false; + } + + // Reset channels previous configuration + for (int i = 0; i < CHANNELS_NUM; i++) + { + setChannelPGAImpl(static_cast<Channel>(i), channelsPGAGain[i]); + setChannelOffsetImpl(static_cast<Channel>(i), channelsOffset[i]); + setChannelGainCalibrationImpl(static_cast<Channel>(i), channelsGain[i]); + setChannelInput(static_cast<Channel>(i), Input::DEFAULT); } return true; @@ -382,27 +277,235 @@ bool ADS131M08::selfTest() ADS131M08Data ADS131M08::sampleImpl() { - // Send the NULL command and read response - uint8_t data[30] = {0}; + // Read the samples and check if the CRC is correct + int32_t rawValues[CHANNELS_NUM]; + if (!readSamples(rawValues)) + { + // The CRC failed, return the last sample + return lastSample; + } + + // Convert the raw values to voltages + ADS131M08Data adcData; + adcData.timestamp = TimestampTimer::getTimestamp(); + for (int i = 0; i < CHANNELS_NUM; i++) + { + adcData.voltage[i] = + rawValues[i] * getLSBSizeFromGain(channelsPGAGain[i]); + } - data[0] = static_cast<uint16_t>(Commands::NULL_CMD) >> 8; - data[1] = static_cast<uint16_t>(Commands::NULL_CMD); + return adcData; +} - SPITransaction transaction(spiSlave); - transaction.transfer(data, sizeof(data)); +void ADS131M08::setChannelInput(Channel channel, Input input) +{ + Register reg = getChannelConfigRegister(channel); + changeRegister(reg, static_cast<uint16_t>(input), RegChannelMasks::CFG_MUX); +} - // Extract each channel value - int32_t rawValue[8]; - for (int i = 0; i < 8; i++) +void ADS131M08::setChannelPGAImpl(Channel channel, PGA gain) +{ + if (channel <= Channel::CHANNEL_3) { - rawValue[i] = static_cast<uint32_t>(data[i * 3 + 3]) << 16 | - static_cast<uint32_t>(data[i * 3 + 4]) << 8 | - static_cast<uint32_t>(data[i * 3 + 5]); + int shift = static_cast<int>(channel) * 4; + changeRegister(Register::REG_GAIN_1, + static_cast<uint16_t>(gain) << shift, + RegGainMasks::PGA_GAIN_0 << shift); + } + else + { + int shift = (static_cast<int>(channel) - 4) * 4; + changeRegister(Register::REG_GAIN_2, + static_cast<uint16_t>(gain) << shift, + RegGainMasks::PGA_GAIN_0 << shift); + } +} + +void ADS131M08::setChannelOffsetImpl(Channel channel, uint32_t offset) +{ + // The offset is a 24 bit value. Its two most significant bytes are stored + // in the MSB register, and the least significant in the LSB register, like + // this: + // +----------+-------+ + // | | 23-16 | + // | OCAL_MSB +-------+ + // | | 15-8 | + // +----------+-------+ + // | | 7-0 | + // | OCAL_LSB +-------+ + // | | | + // +----------+-------+ + writeRegister(getChannelOffsetRegisterMSB(channel), offset >> 8); + writeRegister(getChannelOffsetRegisterLSB(channel), offset << 8); +} + +void ADS131M08::setChannelGainCalibrationImpl(Channel channel, double gain) +{ + // If the user passes a value outside the range [0, 2] we cap it. + if (gain < 0) + { + gain = 0; + } + else if (gain > 2) + { + gain = 2; + } + + // The ADS131M08 corrects for gain errors by multiplying the ADC conversion + // result using the gain calibration registers. + // The contents of the gain calibration registers are interpreted by the + // dives as 24-bit unsigned values corresponding to linear steps from 0 to + // 2-(1/2^23). + // So effectively the register value is a fixed point number with 1bit for + // the integer part and 23 bits for the fractional part. + // So to convert the gain value to the register value we multiply it by 2^23 + uint32_t rawGain = gain * (1 << 23); + + // The gain is a 24 bit value. Its two most significant bytes are stored + // in the MSB register, and the least significant in the LSB register, like + // this: + // +----------+-------+ + // | | 23-16 | + // | GCAL_MSB +-------+ + // | | 15-8 | + // +----------+-------+ + // | | 7-0 | + // | GCAL_LSB +-------+ + // | | | + // +----------+-------+ + writeRegister(getChannelGainRegisterMSB(channel), rawGain >> 8); + writeRegister(getChannelGainRegisterLSB(channel), rawGain << 8); +} + +Register ADS131M08::getChannelConfigRegister(Channel channel) +{ + switch (static_cast<int>(channel)) + { + case 0: + return Register::REG_CH0_CFG; + case 1: + return Register::REG_CH1_CFG; + case 2: + return Register::REG_CH2_CFG; + case 3: + return Register::REG_CH3_CFG; + case 4: + return Register::REG_CH4_CFG; + case 5: + return Register::REG_CH5_CFG; + case 6: + return Register::REG_CH6_CFG; + default: + return Register::REG_CH7_CFG; + } +} + +Register ADS131M08::getChannelOffsetRegisterMSB(Channel channel) +{ + switch (static_cast<int>(channel)) + { + case 0: + return Register::REG_CH0_OCAL_MSB; + case 1: + return Register::REG_CH1_OCAL_MSB; + case 2: + return Register::REG_CH2_OCAL_MSB; + case 3: + return Register::REG_CH3_OCAL_MSB; + case 4: + return Register::REG_CH4_OCAL_MSB; + case 5: + return Register::REG_CH5_OCAL_MSB; + case 6: + return Register::REG_CH6_OCAL_MSB; + default: + return Register::REG_CH7_OCAL_MSB; + } +} + +Register ADS131M08::getChannelOffsetRegisterLSB(Channel channel) +{ + switch (static_cast<int>(channel)) + { + case 0: + return Register::REG_CH0_OCAL_LSB; + case 1: + return Register::REG_CH1_OCAL_LSB; + case 2: + return Register::REG_CH2_OCAL_LSB; + case 3: + return Register::REG_CH3_OCAL_LSB; + case 4: + return Register::REG_CH4_OCAL_LSB; + case 5: + return Register::REG_CH5_OCAL_LSB; + case 6: + return Register::REG_CH6_OCAL_LSB; + default: + return Register::REG_CH7_OCAL_LSB; } +} + +Register ADS131M08::getChannelGainRegisterMSB(Channel channel) +{ + switch (static_cast<int>(channel)) + { + case 0: + return Register::REG_CH0_GCAL_MSB; + case 1: + return Register::REG_CH1_GCAL_MSB; + case 2: + return Register::REG_CH2_GCAL_MSB; + case 3: + return Register::REG_CH3_GCAL_MSB; + case 4: + return Register::REG_CH4_GCAL_MSB; + case 5: + return Register::REG_CH5_GCAL_MSB; + case 6: + return Register::REG_CH6_GCAL_MSB; + default: + return Register::REG_CH7_GCAL_MSB; + } +} + +Register ADS131M08::getChannelGainRegisterLSB(Channel channel) +{ + switch (static_cast<int>(channel)) + { + case 0: + return Register::REG_CH0_GCAL_LSB; + case 1: + return Register::REG_CH1_GCAL_LSB; + case 2: + return Register::REG_CH2_GCAL_LSB; + case 3: + return Register::REG_CH3_GCAL_LSB; + case 4: + return Register::REG_CH4_GCAL_LSB; + case 5: + return Register::REG_CH5_GCAL_LSB; + case 6: + return Register::REG_CH6_GCAL_LSB; + default: + return Register::REG_CH7_GCAL_LSB; + } +} + +bool ADS131M08::readSamples(int32_t rawValues[CHANNELS_NUM]) +{ + // Send the NULL command and read response + uint8_t data[FULL_FRAME_SIZE] = {0}; + + data[0] = (static_cast<uint16_t>(Command::NULL_CMD) & 0xff00) >> 8; + data[1] = (static_cast<uint16_t>(Command::NULL_CMD) & 0x00ff); + + SPITransaction transaction(spiSlave); + transaction.transfer(data, sizeof(data)); // Extract and verify the CRC uint16_t dataCrc = - static_cast<uint32_t>(data[27]) << 8 | static_cast<uint32_t>(data[28]); + static_cast<uint16_t>(data[27]) << 8 | static_cast<uint16_t>(data[28]); uint16_t calculatedCrc = CRCUtils::crc16(data, sizeof(data) - 3); if (dataCrc != calculatedCrc) @@ -411,36 +514,30 @@ ADS131M08Data ADS131M08::sampleImpl() LOG_ERR(logger, "Failed CRC check during sensor sampling"); // Return and don't convert the corrupted data - return lastSample; + return false; } - // Set the two complement - for (int i = 0; i < 8; i++) + // Extract each channel value + for (int i = 0; i < CHANNELS_NUM; i++) { - // Check for the sign bit - if (rawValue[i] & 0x00800000) - rawValue[i] |= 0xFF000000; // Extend the sign bit - } + rawValues[i] = static_cast<uint32_t>(data[i * 3 + 3]) << 16 | + static_cast<uint32_t>(data[i * 3 + 4]) << 8 | + static_cast<uint32_t>(data[i * 3 + 5]); - // Convert values - ADS131M08Data adcData; - adcData.timestamp = TimestampTimer::getTimestamp(); - for (int i = 0; i < 8; i++) - { - adcData.voltage[i] = - rawValue[i] * - PGA_LSB_SIZE[static_cast<uint16_t>(channelsPGAGain[i])]; + // Extend the sign bit with a double shift + rawValues[i] <<= 8; + rawValues[i] >>= 8; } - return adcData; + return true; } -uint16_t ADS131M08::readRegister(Registers reg) +uint16_t ADS131M08::readRegister(Register reg) { uint8_t data[3] = {0}; // Prepare the command - data[0] = static_cast<uint16_t>(Commands::RREG) >> 8 | + data[0] = static_cast<uint16_t>(Command::RREG) >> 8 | static_cast<uint16_t>(reg) >> 1; data[1] = static_cast<uint16_t>(reg) << 7; @@ -451,12 +548,14 @@ uint16_t ADS131M08::readRegister(Registers reg) return data[0] << 8 | data[1]; } -void ADS131M08::writeRegister(Registers reg, uint16_t data) +void ADS131M08::writeRegister(Register reg, uint16_t data) { + // The write command uses two communication words (3 bytes each), one for + // the register address and one for the data to write uint8_t writeCommand[6] = {0}; // Prepare the command - writeCommand[0] = static_cast<uint16_t>(Commands::WREG) >> 8 | + writeCommand[0] = static_cast<uint16_t>(Command::WREG) >> 8 | static_cast<uint16_t>(reg) >> 1; writeCommand[1] = static_cast<uint16_t>(reg) << 7; writeCommand[3] = data >> 8; @@ -465,29 +564,47 @@ void ADS131M08::writeRegister(Registers reg, uint16_t data) SPITransaction transaction(spiSlave); transaction.write(writeCommand, sizeof(writeCommand)); - // Check response - transaction.read(writeCommand, 3); - uint16_t response = writeCommand[0] << 8 | writeCommand[1]; - if (response != (0x4000 | (static_cast<uint16_t>(reg) << 7))) + // The response contains a fixed part and the register address. + uint16_t response = transaction.read24() >> 8; + if (response != (WRITE_CMD_RESPONSE | (static_cast<uint16_t>(reg) << 7))) { lastError = SensorErrors::COMMAND_FAILED; LOG_ERR(logger, "Write command failed, response was {:X}", response); } } -void ADS131M08::changeRegister(Registers reg, uint16_t newValue, uint16_t mask) +void ADS131M08::changeRegister(Register reg, uint16_t newValue, uint16_t mask) { // Read the clock register uint16_t regValue = readRegister(reg); - // Remove the OSR configuration + // Remove the target configuration regValue &= ~mask; - // Set the OSR + // Set the new value regValue |= newValue; // Write the new value writeRegister(reg, regValue); } +void ADS131M08::sendCommand(SPITransaction &transaction, Command command, + uint8_t data[FULL_FRAME_SIZE]) +{ + // All commands (a part from read and write) needs the full 10 words + // communication frame. Each word is (by default) 3 bytes long + + // The command is 16 bits long and goes in the most significant bytes of the + // first communication word + data[0] = (static_cast<uint16_t>(command) & 0xff00) >> 8; + data[1] = (static_cast<uint16_t>(command) & 0x00ff); + + transaction.write(data, FULL_FRAME_SIZE); +} + +float ADS131M08::getLSBSizeFromGain(PGA gain) +{ + return PGA_LSB_SIZE[static_cast<uint16_t>(gain)]; +} + } // namespace Boardcore diff --git a/src/shared/sensors/ADS131M08/ADS131M08.h b/src/shared/sensors/ADS131M08/ADS131M08.h index 374288a80525f288c71d89e647e67126b11fd1bc..705cf29dd3e7d71ad5a406a53ee0b4d9c5e0f014 100644 --- a/src/shared/sensors/ADS131M08/ADS131M08.h +++ b/src/shared/sensors/ADS131M08/ADS131M08.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2021 Skyward Experimental Rocketry +/* Copyright (c) 2023 Skyward Experimental Rocketry * Author: Alberto Nidasio * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -27,6 +27,7 @@ #include <sensors/Sensor.h> #include "ADS131M08Data.h" +#include "ADS131M08Defs.h" namespace Boardcore { @@ -74,61 +75,7 @@ namespace Boardcore class ADS131M08 : public Sensor<ADS131M08Data> { public: - /** - * @brief ADC's oversampling ratio configurations. - * - * The OSR determins the output data rate, depending on the master clock - * frequency. - * - * ODR = f_CLK / 2 / OSR - * - * On Skyward's boards an 8.192MHz clock is used. - */ - enum class OversamplingRatio : uint16_t - { - OSR_128 = 0, // ODR is 32KHz - OSR_256 = 0x1 << 2, // ODR is 16KHz - OSR_512 = 0x2 << 2, // ODR is 8KHz - OSR_1024 = 0x3 << 2, // ODR is 4KHz - OSR_2048 = 0x4 << 2, // ODR is 2KHz - OSR_4096 = 0x5 << 2, // ODR is 1KHz - OSR_8192 = 0x6 << 2, // ODR is 500Hz - OSR_16256 = 0x7 << 2 // ODR is 250Hz - }; - - enum class PGA : uint16_t - { - PGA_1 = 0, ///< Full scale resolution is ±1.2V - PGA_2 = 0x1, ///< Full scale resolution is ±600mV - PGA_4 = 0x2, ///< Full scale resolution is ±300mV - PGA_8 = 0x3, ///< Full scale resolution is ±150mV - PGA_16 = 0x4, ///< Full scale resolution is ±75mV - PGA_32 = 0x5, ///< Full scale resolution is ±37.5mV - PGA_64 = 0x6, ///< Full scale resolution is ±18.75mV - PGA_128 = 0x7 ///< Full scale resolution is ±9.375mV - }; - - enum class Channel : uint8_t - { - CHANNEL_0 = 0, - CHANNEL_1 = 1, - CHANNEL_2 = 2, - CHANNEL_3 = 3, - CHANNEL_4 = 4, - CHANNEL_5 = 5, - CHANNEL_6 = 6, - CHANNEL_7 = 7 - }; - - enum class Input : uint8_t - { - DEFAULT = 0, // AINxP and AINxN (default) - SHORTED = 1, // ADC inputs shorted - POSITIVE_DC_TEST = 2, // Positive DC test signal - NEGATIVE_DC_TEST = 3 // Negative DC test signal - }; - - ADS131M08(SPIBusInterface &bus, miosix::GpioPin cs, + ADS131M08(SPIBusInterface& bus, miosix::GpioPin cs, SPIBusConfig config = getDefaultSPIConfig()); explicit ADS131M08(SPISlave spiSlave); @@ -150,16 +97,16 @@ public: */ void calibrateOffset(); - void setOversamplingRatio(OversamplingRatio ratio); + void setOversamplingRatio(ADS131M08Defs::OversamplingRatio ratio); - void setChannelPGA(Channel channel, PGA gain); + void setChannelPGA(ADS131M08Defs::Channel channel, ADS131M08Defs::PGA gain); /** * @brief Sets the channel offset. * * Note that the device offset is a 24bit two complement. */ - void setChannelOffset(Channel channel, uint32_t offset); + void setChannelOffset(ADS131M08Defs::Channel channel, uint32_t offset); /** * @brief Sets the channel gain calibration. @@ -174,174 +121,69 @@ public: * * @param gain Must be between 0 and 2. */ - void setChannelGainCalibration(Channel channel, double gain); - - void setChannelInput(Channel channel, Input input); + void setChannelGainCalibration(ADS131M08Defs::Channel channel, double gain); - void enableChannel(Channel channel); + void enableChannel(ADS131M08Defs::Channel channel); - void disableChannel(Channel channel); + void disableChannel(ADS131M08Defs::Channel channel); void enableGlobalChopMode(); void disableGlobalChopMode(); - ADCData getVoltage(Channel channel); - bool selfTest() override; -protected: +private: ADS131M08Data sampleImpl() override; -private: - enum class Registers : uint16_t - { - // Device settings and indicators - REG_ID = 0, - REG_STATUS = 0x1, - - // Global settings across channels - REG_MODE = 0x2, - REG_CLOCK = 0x3, - REG_GAIN_1 = 0x4, - REG_GAIN_2 = 0x5, - REG_CFG = 0x6, - REG_THRSHLD_MSB = 0x7, - REG_THRSHLD_LSB = 0x8, - - // Channel specific settings - REG_CH0_CFG = 0x9, - REG_CH0_OCAL_MSB = 0xA, - REG_CH0_OCAL_LSB = 0xB, - REG_CH0_GCAL_MSB = 0xC, - REG_CH0_GCAL_LSB = 0xD, - REG_CH1_CFG = 0xE, - REG_CH1_OCAL_MSB = 0xF, - REG_CH1_OCAL_LSB = 0x10, - REG_CH1_GCAL_MSB = 0x11, - REG_CH1_GCAL_LSB = 0x12, - REG_CH2_CFG = 0x13, - REG_CH2_OCAL_MSB = 0x14, - REG_CH2_OCAL_LSB = 0x15, - REG_CH2_GCAL_MSB = 0x16, - REG_CH2_GCAL_LSB = 0x17, - REG_CH3_CFG = 0x18, - REG_CH3_OCAL_MSB = 0x19, - REG_CH3_OCAL_LSB = 0x1A, - REG_CH3_GCAL_MSB = 0x1B, - REG_CH3_GCAL_LSB = 0x1C, - REG_CH4_CFG = 0x1D, - REG_CH4_OCAL_MSB = 0x1E, - REG_CH4_OCAL_LSB = 0x1F, - REG_CH4_GCAL_MSB = 0x20, - REG_CH4_GCAL_LSB = 0x21, - REG_CH5_CFG = 0x22, - REG_CH5_OCAL_MSB = 0x23, - REG_CH5_OCAL_LSB = 0x24, - REG_CH5_GCAL_MSB = 0x25, - REG_CH5_GCAL_LSB = 0x26, - REG_CH6_CFG = 0x27, - REG_CH6_OCAL_MSB = 0x28, - REG_CH6_OCAL_LSB = 0x29, - REG_CH6_GCAL_MSB = 0x2A, - REG_CH6_GCAL_LSB = 0x2B, - REG_CH7_CFG = 0x2C, - REG_CH7_OCAL_MSB = 0x2D, - REG_CH7_OCAL_LSB = 0x2E, - REG_CH7_GCAL_MSB = 0x2F, - REG_CH7_GCAL_LSB = 0x30, - - // Register map CRC - REG_REGMAP_CRC = 0x3E - }; - - uint16_t readRegister(Registers reg); - - void writeRegister(Registers reg, uint16_t data); - - void changeRegister(Registers reg, uint16_t newValue, uint16_t mask); - - enum class Commands : uint16_t - { - NULL_CMD = 0, - RESET = 0x11, - STANDBY = 0x22, - WAKEUP = 0x33, - LOCK = 0x555, - UNLOCK = 0x655, - RREG = 0xA000, - WREG = 0x6000 - }; + void setChannelInput(ADS131M08Defs::Channel channel, + ADS131M08Defs::Input input); - SPISlave spiSlave; + void setChannelPGAImpl(ADS131M08Defs::Channel channel, + ADS131M08Defs::PGA gain); - PGA channelsPGAGain[8] = {PGA::PGA_1, PGA::PGA_1, PGA::PGA_1, PGA::PGA_1, - PGA::PGA_1, PGA::PGA_1, PGA::PGA_1, PGA::PGA_1}; + void setChannelOffsetImpl(ADS131M08Defs::Channel channel, uint32_t offset); - PrintLogger logger = Logging::getLogger("ads131m08"); + void setChannelGainCalibrationImpl(ADS131M08Defs::Channel channel, + double gain); - static constexpr uint16_t RESET_CMD_RESPONSE = 0xFF28; + ADS131M08Defs::Register getChannelConfigRegister( + ADS131M08Defs::Channel channel); - ///< Digit value in mV for each pga configurations - const float PGA_LSB_SIZE[8] = {143.0511e-9, 71.5256e-9, 35.7628e-9, - 17.8814e-9, 8.9407e-9, 4.4703e-9, - 2.2352e-9, 1.1176e-9}; -}; + ADS131M08Defs::Register getChannelOffsetRegisterMSB( + ADS131M08Defs::Channel channel); -namespace ADS131M08RegisterBitMasks -{ + ADS131M08Defs::Register getChannelOffsetRegisterLSB( + ADS131M08Defs::Channel channel); -// Status register -constexpr uint16_t REG_STATUS_LOCK = 1 << 15; -constexpr uint16_t REG_STATUS_F_RESYNC = 1 << 14; -constexpr uint16_t REG_STATUS_REG_MAP = 1 << 13; -constexpr uint16_t REG_STATUS_CRC_ERR = 1 << 12; -constexpr uint16_t REG_STATUS_CRC_TYPE = 1 << 11; -constexpr uint16_t REG_STATUS_RESET = 1 << 10; -constexpr uint16_t REG_STATUS_WLENGTH = 3 << 8; -constexpr uint16_t REG_STATUS_DRDY3 = 1 << 3; -constexpr uint16_t REG_STATUS_DRDY2 = 1 << 2; -constexpr uint16_t REG_STATUS_DRDY1 = 1 << 1; -constexpr uint16_t REG_STATUS_DRDY0 = 1; - -// Mode register -constexpr uint16_t REG_MODE_REG_CRC_EN = 1 << 13; -constexpr uint16_t REG_MODE_RX_CRC_EN = 1 << 12; -constexpr uint16_t REG_MODE_CRC_TYPE = 1 << 11; -constexpr uint16_t REG_MODE_RESET = 1 << 10; -constexpr uint16_t REG_MODE_WLENGTH = 3 << 8; -constexpr uint16_t REG_MODE_TIMEOUT = 1 << 4; -constexpr uint16_t REG_MODE_DRDY_SEL = 3 << 2; -constexpr uint16_t REG_MODE_DRDY_HiZ = 1 << 1; -constexpr uint16_t REG_MODE_DRDY_FMT = 1 << 0; - -// Clock register -constexpr uint16_t REG_CLOCK_CH3_EN = 1 << 11; -constexpr uint16_t REG_CLOCK_CH2_EN = 1 << 10; -constexpr uint16_t REG_CLOCK_CH1_EN = 1 << 9; -constexpr uint16_t REG_CLOCK_CH0_EN = 1 << 8; -constexpr uint16_t REG_CLOCK_OSR = 7 << 2; -constexpr uint16_t REG_CLOCK_PWR = 3; - -// Gain register -constexpr uint16_t REG_GAIN_PGAGAIN3 = 7 << 12; -constexpr uint16_t REG_GAIN_PGAGAIN2 = 7 << 8; -constexpr uint16_t REG_GAIN_PGAGAIN1 = 7 << 4; -constexpr uint16_t REG_GAIN_PGAGAIN0 = 7; - -// Configuration register -constexpr uint16_t REG_CFG_GC_DLY = 0xF << 9; -constexpr uint16_t REG_CFG_GC_EN = 1 << 8; -constexpr uint16_t REG_CFG_CD_ALLCH = 1 << 7; -constexpr uint16_t REG_CFG_CD_NUM = 7 << 4; -constexpr uint16_t REG_CFG_CD_LEN = 7 << 1; -constexpr uint16_t REG_CFG_CD_EN = 1; - -// Channel configuration register -constexpr uint16_t REG_CHx_CFG_PHASE = 0x3FF << 6; -constexpr uint16_t REG_CHx_CFG_DCBLK_DIS = 1 << 2; -constexpr uint16_t REG_CHx_CFG_MUX = 3; - -} // namespace ADS131M08RegisterBitMasks + ADS131M08Defs::Register getChannelGainRegisterMSB( + ADS131M08Defs::Channel channel); + + ADS131M08Defs::Register getChannelGainRegisterLSB( + ADS131M08Defs::Channel channel); + + bool readSamples(int32_t rawValues[ADS131M08Defs::CHANNELS_NUM]); + + uint16_t readRegister(ADS131M08Defs::Register reg); + + void writeRegister(ADS131M08Defs::Register reg, uint16_t data); + + void changeRegister(ADS131M08Defs::Register reg, uint16_t newValue, + uint16_t mask); + + void sendCommand(SPITransaction& transaction, ADS131M08Defs::Command cmd, + uint8_t data[ADS131M08Defs::FULL_FRAME_SIZE]); + + float getLSBSizeFromGain(ADS131M08Defs::PGA gain); + + SPISlave spiSlave; + + // Current channels configuration + ADS131M08Defs::PGA channelsPGAGain[ADS131M08Defs::CHANNELS_NUM]; + uint32_t channelsOffset[ADS131M08Defs::CHANNELS_NUM]; + double channelsGain[ADS131M08Defs::CHANNELS_NUM]; + + PrintLogger logger = Logging::getLogger("ads131m08"); +}; } // namespace Boardcore diff --git a/src/shared/sensors/ADS131M08/ADS131M08Data.h b/src/shared/sensors/ADS131M08/ADS131M08Data.h index 355bcd4dc944197999a379a4eb0e043babd83033..ac28f168b6dc248e23f5e5ebfa566ced2353c17a 100644 --- a/src/shared/sensors/ADS131M08/ADS131M08Data.h +++ b/src/shared/sensors/ADS131M08/ADS131M08Data.h @@ -26,6 +26,8 @@ #include <ostream> +#include "ADS131M08Defs.h" + namespace Boardcore { @@ -64,6 +66,12 @@ struct ADS131M08Data << voltage[2] << "," << voltage[3] << "," << voltage[4] << "," << voltage[5] << "," << voltage[6] << "," << voltage[7] << "\n"; } + + ADCData getVoltage(ADS131M08Defs::Channel channel) + { + return {timestamp, static_cast<uint8_t>(channel), + voltage[static_cast<uint8_t>(channel)]}; + } }; } // namespace Boardcore diff --git a/src/shared/sensors/ADS131M08/ADS131M08Defs.h b/src/shared/sensors/ADS131M08/ADS131M08Defs.h new file mode 100644 index 0000000000000000000000000000000000000000..8600aaedab7ed1068621da5ebf3aca24419d9084 --- /dev/null +++ b/src/shared/sensors/ADS131M08/ADS131M08Defs.h @@ -0,0 +1,241 @@ +/* Copyright (c) 2023 Skyward Experimental Rocketry + * Author: Alberto Nidasio + * + * 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. + */ + +#pragma once + +#include <stdint.h> + +namespace Boardcore +{ + +namespace ADS131M08Defs +{ + +static constexpr int CHANNELS_NUM = 8; +static constexpr int CALIBRATION_SAMPLES = 250; +static constexpr int SELF_TEST_SAMPLES = 250; +static constexpr int FULL_FRAME_SIZE = 30; +static constexpr uint16_t RESET_CMD_RESPONSE = 0xFF28; +static constexpr uint16_t WRITE_CMD_RESPONSE = 0x4000; + +///< Digit value in mV for each pga configurations +constexpr float PGA_LSB_SIZE[8] = {143.0511e-9, 71.5256e-9, 35.7628e-9, + 17.8814e-9, 8.9407e-9, 4.4703e-9, + 2.2352e-9, 1.1176e-9}; + +static constexpr float V_REF = 1.2; +static constexpr float TEST_SIGNAL_FACTOR = 2 / 15; +static constexpr float TEST_SIGNAL_SLACK = 0.1; // Not defined in DS + +/** + * @brief ADC's oversampling ratio configurations. + * + * The OSR determins the output data rate, depending on the master clock + * frequency. + * + * ODR = f_CLK / 2 / OSR + * + * On Skyward's boards an 8.192MHz clock is used. + */ +enum class OversamplingRatio : uint16_t +{ + OSR_128 = 0, // ODR is 32KHz + OSR_256 = 0x1 << 2, // ODR is 16KHz + OSR_512 = 0x2 << 2, // ODR is 8KHz + OSR_1024 = 0x3 << 2, // ODR is 4KHz + OSR_2048 = 0x4 << 2, // ODR is 2KHz + OSR_4096 = 0x5 << 2, // ODR is 1KHz + OSR_8192 = 0x6 << 2, // ODR is 500Hz + OSR_16256 = 0x7 << 2 // ODR is 250Hz +}; + +enum class PGA : uint16_t +{ + PGA_1 = 0, ///< Full scale resolution is ±1.2V + PGA_2 = 0x1, ///< Full scale resolution is ±600mV + PGA_4 = 0x2, ///< Full scale resolution is ±300mV + PGA_8 = 0x3, ///< Full scale resolution is ±150mV + PGA_16 = 0x4, ///< Full scale resolution is ±75mV + PGA_32 = 0x5, ///< Full scale resolution is ±37.5mV + PGA_64 = 0x6, ///< Full scale resolution is ±18.75mV + PGA_128 = 0x7 ///< Full scale resolution is ±9.375mV +}; + +enum class Channel : uint8_t +{ + CHANNEL_0 = 0, + CHANNEL_1 = 1, + CHANNEL_2 = 2, + CHANNEL_3 = 3, + CHANNEL_4 = 4, + CHANNEL_5 = 5, + CHANNEL_6 = 6, + CHANNEL_7 = 7 +}; + +enum class Input : uint8_t +{ + DEFAULT = 0, // AINxP and AINxN (default) + SHORTED = 1, // ADC inputs shorted + POSITIVE_DC_TEST = 2, // Positive DC test signal + NEGATIVE_DC_TEST = 3 // Negative DC test signal +}; + +enum class Register : uint16_t +{ + // Device settings and indicators + REG_ID = 0, + REG_STATUS = 0x1, + + // Global settings across channels + REG_MODE = 0x2, + REG_CLOCK = 0x3, + REG_GAIN_1 = 0x4, + REG_GAIN_2 = 0x5, + REG_CFG = 0x6, + REG_THRSHLD_MSB = 0x7, + REG_THRSHLD_LSB = 0x8, + + // Channel specific settings + REG_CH0_CFG = 0x9, + REG_CH0_OCAL_MSB = 0xA, + REG_CH0_OCAL_LSB = 0xB, + REG_CH0_GCAL_MSB = 0xC, + REG_CH0_GCAL_LSB = 0xD, + REG_CH1_CFG = 0xE, + REG_CH1_OCAL_MSB = 0xF, + REG_CH1_OCAL_LSB = 0x10, + REG_CH1_GCAL_MSB = 0x11, + REG_CH1_GCAL_LSB = 0x12, + REG_CH2_CFG = 0x13, + REG_CH2_OCAL_MSB = 0x14, + REG_CH2_OCAL_LSB = 0x15, + REG_CH2_GCAL_MSB = 0x16, + REG_CH2_GCAL_LSB = 0x17, + REG_CH3_CFG = 0x18, + REG_CH3_OCAL_MSB = 0x19, + REG_CH3_OCAL_LSB = 0x1A, + REG_CH3_GCAL_MSB = 0x1B, + REG_CH3_GCAL_LSB = 0x1C, + REG_CH4_CFG = 0x1D, + REG_CH4_OCAL_MSB = 0x1E, + REG_CH4_OCAL_LSB = 0x1F, + REG_CH4_GCAL_MSB = 0x20, + REG_CH4_GCAL_LSB = 0x21, + REG_CH5_CFG = 0x22, + REG_CH5_OCAL_MSB = 0x23, + REG_CH5_OCAL_LSB = 0x24, + REG_CH5_GCAL_MSB = 0x25, + REG_CH5_GCAL_LSB = 0x26, + REG_CH6_CFG = 0x27, + REG_CH6_OCAL_MSB = 0x28, + REG_CH6_OCAL_LSB = 0x29, + REG_CH6_GCAL_MSB = 0x2A, + REG_CH6_GCAL_LSB = 0x2B, + REG_CH7_CFG = 0x2C, + REG_CH7_OCAL_MSB = 0x2D, + REG_CH7_OCAL_LSB = 0x2E, + REG_CH7_GCAL_MSB = 0x2F, + REG_CH7_GCAL_LSB = 0x30, + + // Register map CRC + REG_REGMAP_CRC = 0x3E +}; + +enum class Command : uint16_t +{ + NULL_CMD = 0x0000, + RESET = 0x0011, + STANDBY = 0x0022, + WAKEUP = 0x0033, + LOCK = 0x0555, + UNLOCK = 0x0655, + RREG = 0xA000, + WREG = 0x6000 +}; + +namespace RegStatusMasks +{ +constexpr uint16_t LOCK = 0x1 << 15; +constexpr uint16_t F_RESYNC = 0x1 << 14; +constexpr uint16_t REG_MAP = 0x1 << 13; +constexpr uint16_t CRC_ERR = 0x1 << 12; +constexpr uint16_t CRC_TYPE = 0x1 << 11; +constexpr uint16_t RESET = 0x1 << 10; +constexpr uint16_t WLENGTH = 0x3 << 8; +constexpr uint16_t DRDY3 = 0x1 << 3; +constexpr uint16_t DRDY2 = 0x1 << 2; +constexpr uint16_t DRDY1 = 0x1 << 1; +constexpr uint16_t DRDY0 = 0x1 << 0; +} // namespace RegStatusMasks + +namespace RegModeMasks +{ +constexpr uint16_t REG_CRC_EN = 0x1 << 13; +constexpr uint16_t RX_CRC_EN = 0x1 << 12; +constexpr uint16_t CRC_TYPE = 0x1 << 11; +constexpr uint16_t RESET = 0x1 << 10; +constexpr uint16_t WLENGTH = 0x3 << 8; +constexpr uint16_t TIMEOUT = 0x1 << 4; +constexpr uint16_t DRDY_SEL = 0x3 << 2; +constexpr uint16_t DRDY_HiZ = 0x1 << 1; +constexpr uint16_t DRDY_FMT = 0x1 << 0; +} // namespace RegModeMasks + +namespace RegClockMasks +{ +constexpr uint16_t CH3_EN = 0x1 << 11; +constexpr uint16_t CH2_EN = 0x1 << 10; +constexpr uint16_t CH1_EN = 0x1 << 9; +constexpr uint16_t CH0_EN = 0x1 << 8; +constexpr uint16_t OSR = 0x7 << 2; +constexpr uint16_t POWER_MODE = 0x3 << 0; +} // namespace RegClockMasks + +namespace RegGainMasks +{ +constexpr uint16_t PGA_GAIN_3 = 0x7 << 12; +constexpr uint16_t PGA_GAIN_2 = 0x7 << 8; +constexpr uint16_t PGA_GAIN_1 = 0x7 << 4; +constexpr uint16_t PGA_GAIN_0 = 0x7 << 0; +} // namespace RegGainMasks + +namespace RegConfigurationMasks +{ +constexpr uint16_t GC_DLY = 0xF << 9; +constexpr uint16_t GC_EN = 0x1 << 8; +constexpr uint16_t CD_ALLCH = 0x1 << 7; +constexpr uint16_t CD_NUM = 0x7 << 4; +constexpr uint16_t CD_LEN = 0x7 << 1; +constexpr uint16_t CD_EN = 0x1 << 0; +} // namespace RegConfigurationMasks + +namespace RegChannelMasks +{ +constexpr uint16_t CFG_PHASE = 0x3FF << 6; +constexpr uint16_t CFG_DCBLK_DIS = 0x001 << 2; +constexpr uint16_t CFG_MUX = 0x003 << 0; +} // namespace RegChannelMasks + +} // namespace ADS131M08Defs + +} // namespace Boardcore diff --git a/src/tests/sensors/test-ads131m04.cpp b/src/tests/sensors/test-ads131m04.cpp index 89deaf0d6e8a20d8f89d7f7cabd86ea27dcbc78b..6c001ae8edfd83eb48b4f19c9c53f186ce5d0530 100644 --- a/src/tests/sensors/test-ads131m04.cpp +++ b/src/tests/sensors/test-ads131m04.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2021 Skyward Experimental Rocketry +/* Copyright (c) 2023 Skyward Experimental Rocketry * Author: Alberto Nidasio * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -36,14 +36,14 @@ GpioPin csPin = GpioPin(GPIOE_BASE, 4); void initBoard() { // Setup gpio pins - csPin.mode(Mode::OUTPUT); - csPin.high(); sckPin.mode(Mode::ALTERNATE); sckPin.alternateFunction(5); misoPin.mode(Mode::ALTERNATE); misoPin.alternateFunction(5); mosiPin.mode(Mode::ALTERNATE); mosiPin.alternateFunction(5); + csPin.mode(Mode::OUTPUT); + csPin.high(); } int main() @@ -53,20 +53,30 @@ int main() // SPI configuration setup SPIBus spiBus(SPI4); - SPIBusConfig spiConfig = {}; - spiConfig.mode = SPI::Mode::MODE_1; - spiConfig.clockDivider = SPI::ClockDivider::DIV_64; - SPISlave spiSlave(spiBus, csPin, spiConfig); + SPISlave spiSlave(spiBus, csPin, ADS131M04::getDefaultSPIConfig()); // Device initialization ADS131M04 ads131(spiSlave); - // Initialize the device - ads131.init(); + ads131.reset(); + ads131.enableGlobalChopMode(); - ads131.setOversamplingRatio(ADS131M04::OversamplingRatio::OSR_16256); + ads131.setOversamplingRatio(ADS131M04Defs::OversamplingRatio::OSR_16256); + + // WARNING: After changing the OSR the device needs some time to settle + delayMs(20); ads131.calibrateOffset(); + printf("Now performing self test...\n"); + if (ads131.selfTest()) + { + printf("Self test failed!\n"); + } + else + { + printf("Self test succeeded\n"); + } + while (true) { ads131.sample(); diff --git a/src/tests/sensors/test-ads131m08.cpp b/src/tests/sensors/test-ads131m08.cpp index 37728444090c7d7b5b38ca2ee1f7de4da22293af..180ec04b59d8466c731620223352b93eec2c47d0 100644 --- a/src/tests/sensors/test-ads131m08.cpp +++ b/src/tests/sensors/test-ads131m08.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2021 Skyward Experimental Rocketry +/* Copyright (c) 2023 Skyward Experimental Rocketry * Author: Alberto Nidasio * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -58,26 +58,24 @@ int main() // Device initialization ADS131M08 ads131(spiSlave); - // Initialize the device - if (!ads131.init()) - { - printf("Initialization failed!\n"); - } - else - { - printf("Initialization done\n"); - } - if (!ads131.selfTest()) + ads131.reset(); + + ads131.enableGlobalChopMode(); + ads131.setOversamplingRatio(ADS131M08Defs::OversamplingRatio::OSR_16256); + + // WARNING: After changing the OSR the device needs some time to settle + delayMs(20); + ads131.calibrateOffset(); + + printf("Now performing self test...\n"); + if (ads131.selfTest()) { printf("Self test failed!\n"); } else { - printf("Self test done\n"); + printf("Self test succeeded\n"); } - ads131.enableGlobalChopMode(); - ads131.setOversamplingRatio(ADS131M08::OversamplingRatio::OSR_16256); - ads131.calibrateOffset(); while (true) {