From 8175eab76ababb7f9630f783660ed05d973df6df Mon Sep 17 00:00:00 2001
From: Alberto Nidasio <alberto.nidasio@skywarder.eu>
Date: Sat, 1 Jul 2023 17:14:15 +0200
Subject: [PATCH] [ADS131] Fixed offset calibration and implemented self test

---
 src/shared/sensors/ADS131M04/ADS131M04.cpp | 162 +++++++++++++----
 src/shared/sensors/ADS131M04/ADS131M04.h   |  10 ++
 src/shared/sensors/ADS131M08/ADS131M08.cpp | 200 +++++++++++++++------
 src/shared/sensors/ADS131M08/ADS131M08.h   |  10 ++
 src/tests/sensors/test-ads131m08.cpp       |  17 +-
 5 files changed, 307 insertions(+), 92 deletions(-)

diff --git a/src/shared/sensors/ADS131M04/ADS131M04.cpp b/src/shared/sensors/ADS131M04/ADS131M04.cpp
index 3b7387c41..797d9ba46 100644
--- a/src/shared/sensors/ADS131M04/ADS131M04.cpp
+++ b/src/shared/sensors/ADS131M04/ADS131M04.cpp
@@ -26,6 +26,7 @@
 #include <util/crc16.h>
 #include <utils/CRC.h>
 
+using namespace miosix;
 using namespace Boardcore::ADS131M04RegisterBitMasks;
 
 namespace Boardcore
@@ -100,32 +101,47 @@ bool ADS131M04::reset()
 
 void ADS131M04::calibrateOffset()
 {
-    int32_t averageValues[4] = {0};
+    // Reset all offsets and gains
+    for (int i = 0; i < 8; i++)
+    {
+        setChannelOffset(static_cast<Channel>(i), 0);
+    }
 
     // Sample the channels and average the samples
-    for (int i = 0; i < 1000; i++)
+    int32_t averageValues[4] = {0};
+    for (int i = 0; i < 250; i++)
     {
-        sample();
-        miosix::delayUs(1);
+        Thread::sleep(4);
 
-        for (int ii = 0; ii < 4; ii++)
-            averageValues[ii] +=
-                lastSample.voltage[ii] /
-                PGA_LSB_SIZE[static_cast<uint16_t>(channelsPGAGain[ii])];
-    }
+        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++)
+        {
+            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
+
+            averageValues[j] += tmp;
+        }
+    }
     for (int i = 0; i < 4; i++)
-        averageValues[i] /= 1000;
+        averageValues[i] /= 250;
 
     // Configure the offset registers
-    writeRegister(Registers::REG_CH0_OCAL_MSB, averageValues[0] >> 8);
-    writeRegister(Registers::REG_CH0_OCAL_LSB, averageValues[0] << 16);
-    writeRegister(Registers::REG_CH1_OCAL_MSB, averageValues[1] >> 8);
-    writeRegister(Registers::REG_CH1_OCAL_LSB, averageValues[1] << 16);
-    writeRegister(Registers::REG_CH2_OCAL_MSB, averageValues[2] >> 8);
-    writeRegister(Registers::REG_CH2_OCAL_LSB, averageValues[2] << 16);
-    writeRegister(Registers::REG_CH3_OCAL_MSB, averageValues[3] >> 8);
-    writeRegister(Registers::REG_CH3_OCAL_LSB, averageValues[3] << 16);
+    for (int i = 0; i < 4; i++)
+    {
+        setChannelOffset(static_cast<Channel>(i), averageValues[i]);
+    }
 }
 
 void ADS131M04::setChannelPGA(Channel channel, PGA gain)
@@ -162,8 +178,8 @@ void ADS131M04::setChannelOffset(Channel channel, uint32_t offset)
             break;
     }
 
-    writeRegister(regMSB, static_cast<uint16_t>(offset) >> 16);
-    writeRegister(regLSB, static_cast<uint16_t>(offset) << 8);
+    writeRegister(regMSB, offset >> 8);
+    writeRegister(regLSB, offset << 16);
 }
 
 void ADS131M04::setChannelGainCalibration(Channel channel, double gain)
@@ -201,6 +217,32 @@ void ADS131M04::setChannelGainCalibration(Channel channel, double gain)
     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);
+}
+
 void ADS131M04::enableChannel(Channel channel)
 {
     changeRegister(Registers::REG_CLOCK, 1 << (static_cast<int>(channel) + 8),
@@ -233,7 +275,65 @@ ADCData ADS131M04::getVoltage(Channel channel)
 
 bool ADS131M04::selfTest()
 {
-    // TODO
+    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};
+
+    // Connect all channels to the positive DC test signal
+    for (int i = 0; i < 4; i++)
+    {
+        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++)
+    {
+        Thread::sleep(4);
+        auto newSample = sampleImpl();
+
+        for (int j = 0; j < 4; j++)
+        {
+            voltage[j] += newSample.voltage[j];
+        }
+    }
+
+    // Check values
+    for (int i = 0; i < 4; i++)
+    {
+        voltage[i] /= 100;
+        if (voltage[i] < V_REF * TEST_SIGNAL_FACTOR - TEST_SIGNAL_SLACK)
+            return false;
+    }
+
+    // Connect all channels to the negative DC test signal
+    for (int i = 0; i < 4; i++)
+    {
+        setChannelInput(static_cast<Channel>(i), Input::NEGATIVE_DC_TEST);
+    }
+
+    // Take some samples
+    for (int i = 0; i < 100; i++)
+    {
+        Thread::sleep(4);
+        auto newSample = sampleImpl();
+
+        for (int j = 0; j < 4; j++)
+        {
+            voltage[j] += newSample.voltage[j];
+        }
+    }
+
+    // Check values
+    for (int i = 0; i < 4; i++)
+    {
+        voltage[i] /= 100;
+        if (voltage[i] > -V_REF * TEST_SIGNAL_FACTOR + TEST_SIGNAL_SLACK)
+            return false;
+    }
+
     return true;
 }
 
@@ -250,18 +350,12 @@ ADS131M04Data ADS131M04::sampleImpl()
 
     // Extract each channel value
     int32_t rawValue[4];
-    rawValue[0] = static_cast<uint32_t>(data[3]) << 16 |
-                  static_cast<uint32_t>(data[4]) << 8 |
-                  static_cast<uint32_t>(data[5]);
-    rawValue[1] = static_cast<uint32_t>(data[6]) << 16 |
-                  static_cast<uint32_t>(data[7]) << 8 |
-                  static_cast<uint32_t>(data[8]);
-    rawValue[2] = static_cast<uint32_t>(data[9]) << 16 |
-                  static_cast<uint32_t>(data[10]) << 8 |
-                  static_cast<uint32_t>(data[11]);
-    rawValue[3] = static_cast<uint32_t>(data[12]) << 16 |
-                  static_cast<uint32_t>(data[13]) << 8 |
-                  static_cast<uint32_t>(data[14]);
+    for (int i = 0; i < 4; i++)
+    {
+        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]);
+    }
 
     // Extract and verify the CRC
     uint16_t dataCrc =
@@ -281,7 +375,7 @@ ADS131M04Data ADS131M04::sampleImpl()
     for (int i = 0; i < 4; i++)
     {
         // Check for the sign bit
-        if (rawValue[i] & 0x800000)
+        if (rawValue[i] & 0x00800000)
             rawValue[i] |= 0xFF000000;  // Extend the sign bit
     }
 
diff --git a/src/shared/sensors/ADS131M04/ADS131M04.h b/src/shared/sensors/ADS131M04/ADS131M04.h
index 9d948a288..c2857a4f8 100644
--- a/src/shared/sensors/ADS131M04/ADS131M04.h
+++ b/src/shared/sensors/ADS131M04/ADS131M04.h
@@ -116,6 +116,14 @@ public:
         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,
               SPIBusConfig config = getDefaultSPIConfig());
 
@@ -164,6 +172,8 @@ public:
      */
     void setChannelGainCalibration(Channel channel, double gain);
 
+    void setChannelInput(Channel channel, Input input);
+
     void enableChannel(Channel channel);
 
     void disableChannel(Channel channel);
diff --git a/src/shared/sensors/ADS131M08/ADS131M08.cpp b/src/shared/sensors/ADS131M08/ADS131M08.cpp
index 9ea5204cc..21c30d1e3 100644
--- a/src/shared/sensors/ADS131M08/ADS131M08.cpp
+++ b/src/shared/sensors/ADS131M08/ADS131M08.cpp
@@ -26,6 +26,7 @@
 #include <util/crc16.h>
 #include <utils/CRC.h>
 
+using namespace miosix;
 using namespace Boardcore::ADS131M08RegisterBitMasks;
 
 namespace Boardcore
@@ -79,7 +80,7 @@ bool ADS131M08::init()
 bool ADS131M08::reset()
 {
     // Write all the communication frame which is maximum 6 24bit words
-    uint8_t data[18] = {0};
+    uint8_t data[30] = {0};
 
     // Prepare the command
     data[1] = static_cast<uint16_t>(Commands::RESET);
@@ -92,7 +93,7 @@ bool ADS131M08::reset()
     uint16_t response = data[0] << 8 | data[1];
 
     // Check for the correct response
-    if (response != 0xFF24)
+    if (response != 0xFF28)
     {
         lastError = SensorErrors::COMMAND_FAILED;
         LOG_ERR(logger, "Reset command failed, response was {:X}", response);
@@ -104,40 +105,47 @@ bool ADS131M08::reset()
 
 void ADS131M08::calibrateOffset()
 {
-    int32_t averageValues[8] = {0};
+    // Reset all offsets and gains
+    for (int i = 0; i < 8; i++)
+    {
+        setChannelOffset(static_cast<Channel>(i), 0);
+    }
 
     // Sample the channels and average the samples
-    for (int i = 0; i < 1000; i++)
+    int32_t averageValues[8] = {0};
+    for (int i = 0; i < 250; i++)
     {
-        sample();
-        miosix::delayUs(1);
+        Thread::sleep(4);
 
-        for (int ii = 0; ii < 4; ii++)
-            averageValues[ii] +=
-                lastSample.voltage[ii] /
-                PGA_LSB_SIZE[static_cast<uint16_t>(channelsPGAGain[ii])];
-    }
+        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 i = 0; i < 4; i++)
-        averageValues[i] /= 1000;
+        for (int j = 0; j < 8; j++)
+        {
+            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
+
+            averageValues[j] += tmp;
+        }
+    }
+    for (int i = 0; i < 8; i++)
+        averageValues[i] /= 250;
 
     // Configure the offset registers
-    writeRegister(Registers::REG_CH0_OCAL_MSB, averageValues[0] >> 8);
-    writeRegister(Registers::REG_CH0_OCAL_LSB, averageValues[0] << 16);
-    writeRegister(Registers::REG_CH1_OCAL_MSB, averageValues[1] >> 8);
-    writeRegister(Registers::REG_CH1_OCAL_LSB, averageValues[1] << 16);
-    writeRegister(Registers::REG_CH2_OCAL_MSB, averageValues[2] >> 8);
-    writeRegister(Registers::REG_CH2_OCAL_LSB, averageValues[2] << 16);
-    writeRegister(Registers::REG_CH3_OCAL_MSB, averageValues[3] >> 8);
-    writeRegister(Registers::REG_CH3_OCAL_LSB, averageValues[3] << 16);
-    writeRegister(Registers::REG_CH3_OCAL_MSB, averageValues[4] >> 8);
-    writeRegister(Registers::REG_CH3_OCAL_LSB, averageValues[4] << 16);
-    writeRegister(Registers::REG_CH3_OCAL_MSB, averageValues[5] >> 8);
-    writeRegister(Registers::REG_CH3_OCAL_LSB, averageValues[5] << 16);
-    writeRegister(Registers::REG_CH3_OCAL_MSB, averageValues[6] >> 8);
-    writeRegister(Registers::REG_CH3_OCAL_LSB, averageValues[6] << 16);
-    writeRegister(Registers::REG_CH3_OCAL_MSB, averageValues[7] >> 8);
-    writeRegister(Registers::REG_CH3_OCAL_LSB, averageValues[7] << 16);
+    for (int i = 0; i < 8; i++)
+    {
+        setChannelOffset(static_cast<Channel>(i), averageValues[i]);
+    }
 }
 
 void ADS131M08::setChannelPGA(Channel channel, PGA gain)
@@ -200,8 +208,8 @@ void ADS131M08::setChannelOffset(Channel channel, uint32_t offset)
             break;
     }
 
-    writeRegister(regMSB, static_cast<uint16_t>(offset) >> 16);
-    writeRegister(regLSB, static_cast<uint16_t>(offset) << 8);
+    writeRegister(regMSB, offset >> 8);
+    writeRegister(regLSB, offset << 16);
 }
 
 void ADS131M08::setChannelGainCalibration(Channel channel, double gain)
@@ -255,6 +263,44 @@ void ADS131M08::setChannelGainCalibration(Channel channel, double gain)
     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);
+}
+
 void ADS131M08::enableChannel(Channel channel)
 {
     changeRegister(Registers::REG_CLOCK, 1 << (static_cast<int>(channel) + 8),
@@ -287,7 +333,65 @@ ADCData ADS131M08::getVoltage(Channel channel)
 
 bool ADS131M08::selfTest()
 {
-    // TODO
+    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};
+
+    // Connect all channels to the positive DC test signal
+    for (int i = 0; i < 8; i++)
+    {
+        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++)
+    {
+        Thread::sleep(4);
+        auto newSample = sampleImpl();
+
+        for (int j = 0; j < 8; j++)
+        {
+            voltage[j] += newSample.voltage[j];
+        }
+    }
+
+    // Check values
+    for (int i = 0; i < 8; i++)
+    {
+        voltage[i] /= 100;
+        if (voltage[i] < V_REF * TEST_SIGNAL_FACTOR - TEST_SIGNAL_SLACK)
+            return false;
+    }
+
+    // Connect all channels to the negative DC test signal
+    for (int i = 0; i < 8; i++)
+    {
+        setChannelInput(static_cast<Channel>(i), Input::NEGATIVE_DC_TEST);
+    }
+
+    // Take some samples
+    for (int i = 0; i < 100; i++)
+    {
+        Thread::sleep(4);
+        auto newSample = sampleImpl();
+
+        for (int j = 0; j < 8; j++)
+        {
+            voltage[j] += newSample.voltage[j];
+        }
+    }
+
+    // Check values
+    for (int i = 0; i < 8; i++)
+    {
+        voltage[i] /= 100;
+        if (voltage[i] > -V_REF * TEST_SIGNAL_FACTOR + TEST_SIGNAL_SLACK)
+            return false;
+    }
+
     return true;
 }
 
@@ -304,30 +408,12 @@ ADS131M08Data ADS131M08::sampleImpl()
 
     // Extract each channel value
     int32_t rawValue[8];
-    rawValue[0] = static_cast<uint32_t>(data[3]) << 16 |
-                  static_cast<uint32_t>(data[4]) << 8 |
-                  static_cast<uint32_t>(data[5]);
-    rawValue[1] = static_cast<uint32_t>(data[6]) << 16 |
-                  static_cast<uint32_t>(data[7]) << 8 |
-                  static_cast<uint32_t>(data[8]);
-    rawValue[2] = static_cast<uint32_t>(data[9]) << 16 |
-                  static_cast<uint32_t>(data[10]) << 8 |
-                  static_cast<uint32_t>(data[11]);
-    rawValue[3] = static_cast<uint32_t>(data[12]) << 16 |
-                  static_cast<uint32_t>(data[13]) << 8 |
-                  static_cast<uint32_t>(data[14]);
-    rawValue[4] = static_cast<uint32_t>(data[15]) << 16 |
-                  static_cast<uint32_t>(data[16]) << 8 |
-                  static_cast<uint32_t>(data[17]);
-    rawValue[5] = static_cast<uint32_t>(data[18]) << 16 |
-                  static_cast<uint32_t>(data[19]) << 8 |
-                  static_cast<uint32_t>(data[20]);
-    rawValue[6] = static_cast<uint32_t>(data[21]) << 16 |
-                  static_cast<uint32_t>(data[22]) << 8 |
-                  static_cast<uint32_t>(data[23]);
-    rawValue[7] = static_cast<uint32_t>(data[24]) << 16 |
-                  static_cast<uint32_t>(data[25]) << 8 |
-                  static_cast<uint32_t>(data[26]);
+    for (int i = 0; i < 8; i++)
+    {
+        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]);
+    }
 
     // Extract and verify the CRC
     uint16_t dataCrc =
@@ -347,7 +433,7 @@ ADS131M08Data ADS131M08::sampleImpl()
     for (int i = 0; i < 8; i++)
     {
         // Check for the sign bit
-        if (rawValue[i] & 0x800000)
+        if (rawValue[i] & 0x00800000)
             rawValue[i] |= 0xFF000000;  // Extend the sign bit
     }
 
diff --git a/src/shared/sensors/ADS131M08/ADS131M08.h b/src/shared/sensors/ADS131M08/ADS131M08.h
index 0d2d3c733..6dca0801f 100644
--- a/src/shared/sensors/ADS131M08/ADS131M08.h
+++ b/src/shared/sensors/ADS131M08/ADS131M08.h
@@ -120,6 +120,14 @@ public:
         CHANNEL_7 = 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
+    };
+
     ADS131M08(SPIBusInterface &bus, miosix::GpioPin cs,
               SPIBusConfig config = getDefaultSPIConfig());
 
@@ -168,6 +176,8 @@ public:
      */
     void setChannelGainCalibration(Channel channel, double gain);
 
+    void setChannelInput(Channel channel, Input input);
+
     void enableChannel(Channel channel);
 
     void disableChannel(Channel channel);
diff --git a/src/tests/sensors/test-ads131m08.cpp b/src/tests/sensors/test-ads131m08.cpp
index 5deb400da..cd4dd482d 100644
--- a/src/tests/sensors/test-ads131m08.cpp
+++ b/src/tests/sensors/test-ads131m08.cpp
@@ -59,7 +59,22 @@ int main()
     ADS131M08 ads131(spiSlave);
 
     // Initialize the device
-    ads131.init();
+    if (!ads131.init())
+    {
+        printf("Initialization failed!\n");
+    }
+    else
+    {
+        printf("Initialization done\n");
+    }
+    if (!ads131.selfTest())
+    {
+        printf("Self test failed!\n");
+    }
+    else
+    {
+        printf("Self test done\n");
+    }
     ads131.enableGlobalChopMode();
     ads131.setOversamplingRatio(ADS131M08::OversamplingRatio::OSR_16256);
     ads131.calibrateOffset();
-- 
GitLab