From 84ae7ae2a86cfadc219acb2d21c65fa70a63fd7a 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] Added documentation throughout the code and improved self test sequence --- src/shared/sensors/ADS131M04/ADS131M04.cpp | 154 +++++++++++++++------ src/shared/sensors/ADS131M04/ADS131M04.h | 78 +++++++++-- src/shared/sensors/ADS131M08/ADS131M08.cpp | 152 ++++++++++++++------ src/shared/sensors/ADS131M08/ADS131M08.h | 78 +++++++++-- src/tests/sensors/test-ads131m04.cpp | 4 +- src/tests/sensors/test-ads131m08.cpp | 4 +- 6 files changed, 357 insertions(+), 113 deletions(-) diff --git a/src/shared/sensors/ADS131M04/ADS131M04.cpp b/src/shared/sensors/ADS131M04/ADS131M04.cpp index bb614f6ce..727ed16a8 100644 --- a/src/shared/sensors/ADS131M04/ADS131M04.cpp +++ b/src/shared/sensors/ADS131M04/ADS131M04.cpp @@ -40,7 +40,7 @@ ADS131M04::ADS131M04(SPIBusInterface &bus, miosix::GpioPin cs, ADS131M04::ADS131M04(SPISlave spiSlave) : spiSlave(spiSlave) { - // Initialize the channel configurations + // Initialize the channel configurations to the default values for (int i = 0; i < CHANNELS_NUM; i++) { channelsPGAGain[i] = PGA::PGA_1; @@ -87,7 +87,10 @@ bool ADS131M04::reset() void ADS131M04::calibrateOffset() { - // Reset all offsets and gains, otherwise the calibration would be wrong + // The device internal data chain firsts subtracts the offset and then + // multiplies for the gain. To calibrate the offset we first reset it, then + // take some samples and apply the average as the new offset. + // So we need to reset the offset and gain for (int i = 0; i < CHANNELS_NUM; i++) { setChannelOffsetImpl(static_cast<Channel>(i), 0); @@ -117,17 +120,32 @@ void ADS131M04::calibrateOffset() 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]); + if (realSampleCount == 0) + { + LOG_ERR(logger, "Calibration failed, no valid samples"); + } + else + { + 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])); + + // Set the new offset only if valid, otherwise keep the old one + if (averageValues[i] != 0) + { + channelsOffset[i] = averageValues[i]; + } + } + } - // Reset the gain as it was before + // Update the offset values and reset the gain as it was before + for (int i = 0; i < CHANNELS_NUM; i++) + { + setChannelOffset(static_cast<Channel>(i), channelsOffset[i]); setChannelGainCalibrationImpl(static_cast<Channel>(i), channelsGain[i]); } } @@ -181,8 +199,7 @@ void ADS131M04::disableGlobalChopMode() bool ADS131M04::selfTest() { - float voltage[CHANNELS_NUM] = {0}; - bool success = true; + bool success = true; // Reset PGA, offsets and gains and connect the positive test signal for (int i = 0; i < CHANNELS_NUM; i++) @@ -194,31 +211,59 @@ bool ADS131M04::selfTest() } // Take some samples + int32_t averageValues[CHANNELS_NUM] = {0}; + int realSampleCount = 0; for (int i = 0; i < SELF_TEST_SAMPLES; i++) { + // Wait for a sample to be ready Thread::sleep(4); - auto newSample = sampleImpl(); + + // Sample the channels + int32_t rawValues[CHANNELS_NUM]; + if (!readSamples(rawValues)) + { + // If the CRC failed we skip this sample + continue; + } for (int j = 0; j < CHANNELS_NUM; j++) { - voltage[j] += newSample.voltage[j]; + averageValues[j] += rawValues[j]; } + + realSampleCount++; } - // Check the values - for (int i = 0; i < CHANNELS_NUM; i++) + if (realSampleCount == 0) { - // 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) + LOG_ERR(logger, + "Failed self test with positive DC signal, no valid samples"); + success = false; + } + else + { + // Check the values + for (int i = 0; i < CHANNELS_NUM; i++) { - LOG_ERR(logger, + // Compute the average + averageValues[i] /= realSampleCount; + + // Convert the value to Volts + float volts = averageValues[i] * getLSBSizeFromGain(PGA::PGA_1); + + // Check if the value is in the acceptable range + if (volts < V_REF * TEST_SIGNAL_FACTOR - TEST_SIGNAL_SLACK) + { + LOG_ERR( + logger, "Self test failed on channel {} on positive test signal, " "value was {}", - i, voltage[i]); - success = false; + i, volts); + success = false; + } + + // Reset the raw value + averageValues[i] = 0; } } @@ -229,40 +274,58 @@ bool ADS131M04::selfTest() } // Take some samples + realSampleCount = 0; for (int i = 0; i < SELF_TEST_SAMPLES; i++) { + // Wait for a sample to be ready Thread::sleep(4); - auto newSample = sampleImpl(); + + // Sample the channels + int32_t rawValues[CHANNELS_NUM]; + if (!readSamples(rawValues)) + { + // If the CRC failed we skip this sample + continue; + } for (int j = 0; j < CHANNELS_NUM; j++) { - voltage[j] += newSample.voltage[j]; + averageValues[j] += rawValues[j]; } + + realSampleCount++; } - // Check the values - for (int i = 0; i < CHANNELS_NUM; i++) + if (realSampleCount == 0) { - // 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) + LOG_ERR(logger, + "Failed self test with positive DC signal, no valid samples"); + success = false; + } + else + { + // Check the values + for (int i = 0; i < CHANNELS_NUM; i++) { - LOG_ERR(logger, + // Compute the average + averageValues[i] /= realSampleCount; + + // Convert the value to Volts + float volts = averageValues[i] * getLSBSizeFromGain(PGA::PGA_1); + + // Check if the value is in the acceptable range + if (volts > -V_REF * TEST_SIGNAL_FACTOR + TEST_SIGNAL_SLACK) + { + LOG_ERR( + logger, "Self test failed on channel {} on negative test signal, " "value was {}", - i, voltage[i]); - success = false; + i, volts); + success = false; + } } } - // If any channel failed we return false - if (!success) - { - return false; - } - // Reset channels previous configuration for (int i = 0; i < CHANNELS_NUM; i++) { @@ -272,7 +335,8 @@ bool ADS131M04::selfTest() setChannelInput(static_cast<Channel>(i), Input::DEFAULT); } - return true; + // We fail even if one channel didn't pass the test + return success; } ADS131M04Data ADS131M04::sampleImpl() @@ -340,7 +404,7 @@ void ADS131M04::setChannelGainCalibrationImpl(Channel channel, double gain) gain = 2; } - // The ADS131M04 corrects for gain errors by multiplying the ADC conversion + // 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 diff --git a/src/shared/sensors/ADS131M04/ADS131M04.h b/src/shared/sensors/ADS131M04/ADS131M04.h index 514034573..c5ac7068f 100644 --- a/src/shared/sensors/ADS131M04/ADS131M04.h +++ b/src/shared/sensors/ADS131M04/ADS131M04.h @@ -97,8 +97,24 @@ public: */ void calibrateOffset(); + /** + * @brief changes the oversampling ratio. + * + * Keep in mind that after changing the oversampling ratio the device resets + * the internal digital filter and needs some time to settle. + * So if you immediately take a sample you'll read zeros. + * + * @warning This is especially important if you perform an offset + * calibration right after changing the oversampling ratio. + */ void setOversamplingRatio(ADS131M04Defs::OversamplingRatio ratio); + /** + * @brief Sets the channel programmable gain amplifier. + * + * The programmable gain amplifier allows the ADC to measure low level + * signals with the full resolution. + */ void setChannelPGA(ADS131M04Defs::Channel channel, ADS131M04Defs::PGA gain); /** @@ -111,15 +127,8 @@ public: /** * @brief Sets the channel gain calibration. * - * The ADS131M04 corrects for gain errors by multiplying the ADC conversion - * result using the gain calibration registers. - * The gain calibration value is interpreted as a 24bit unsigned. The values - * corresponds to n * (1/2^23), ranging from 0 to 2 - (1/2^23). - * - * This function accepts a value between 0 and 2, it then compute the - * correct gain register value. - * - * @param gain Must be between 0 and 2. + * @param gain Gain value between 0 and 2. Values outside this range will be + * capped. */ void setChannelGainCalibration(ADS131M04Defs::Channel channel, double gain); @@ -127,10 +136,35 @@ public: void disableChannel(ADS131M04Defs::Channel channel); + /** + * @brief Enables the global chop mode. + * + * When global chop mode is enabled the ADC uses the conversion results from + * two consecutive internal conversions taken with opposite input polarity + * to cancel the device internal offset voltage. + * + * The drawback of this mode is that the sampling rate is reduced. The + * conversion period is reduced because every time the device swaps the + * input polarity, the internal filter is reset. The ADC then always takes + * three internal conversions to produce one result. + * + * For more details see chapter 8.4.3.2 of the datasheet. + */ void enableGlobalChopMode(); + /** + * @brief Disables the global chop mode. + * + * See enableGlobalChopMode() for more details on the global chop mode. + */ void disableGlobalChopMode(); + /** + * @brief The self test samples internally connects each channel to known + * test signals and verifies if the sampled values are in an expected range. + * + * @returns True if the self test is successful, false otherwise. + */ bool selfTest() override; private: @@ -139,11 +173,23 @@ private: void setChannelInput(ADS131M04Defs::Channel channel, ADS131M04Defs::Input input); + /** + * setChannelPGS() implementation without saving the gain value in + * the local variable. + */ void setChannelPGAImpl(ADS131M04Defs::Channel channel, ADS131M04Defs::PGA gain); + /** + * setChannelOffset() implementation without saving the offset value in + * the local variable. + */ void setChannelOffsetImpl(ADS131M04Defs::Channel channel, uint32_t offset); + /** + * setChannelGainCalibration() implementation without saving the gain value + * in the local variable. + */ void setChannelGainCalibrationImpl(ADS131M04Defs::Channel channel, double gain); @@ -162,6 +208,12 @@ private: ADS131M04Defs::Register getChannelGainRegisterLSB( ADS131M04Defs::Channel channel); + /** + * @brief Sends a NULL command, reads the channels samples and stores the + * values in the given array. + * + * @returns Returns true if the CRC is correct, false otherwise. + */ bool readSamples(int32_t rawValues[ADS131M04Defs::CHANNELS_NUM]); uint16_t readRegister(ADS131M04Defs::Register reg); @@ -174,11 +226,17 @@ private: void sendCommand(SPITransaction& transaction, ADS131M04Defs::Command cmd, uint8_t data[ADS131M04Defs::FULL_FRAME_SIZE]); + /** + * @brief Given a gain value returns the conversion value for a raw data + * sample. + */ float getLSBSizeFromGain(ADS131M04Defs::PGA gain); SPISlave spiSlave; - // Current channels configuration + // Saving the current configuration of the device + // This is necessary because the selfTest and calibrateOffset functions + // temporarily resets the channels configuration ADS131M04Defs::PGA channelsPGAGain[ADS131M04Defs::CHANNELS_NUM]; uint32_t channelsOffset[ADS131M04Defs::CHANNELS_NUM]; double channelsGain[ADS131M04Defs::CHANNELS_NUM]; diff --git a/src/shared/sensors/ADS131M08/ADS131M08.cpp b/src/shared/sensors/ADS131M08/ADS131M08.cpp index dfcda68ab..4dbff38b1 100644 --- a/src/shared/sensors/ADS131M08/ADS131M08.cpp +++ b/src/shared/sensors/ADS131M08/ADS131M08.cpp @@ -40,7 +40,7 @@ ADS131M08::ADS131M08(SPIBusInterface &bus, miosix::GpioPin cs, ADS131M08::ADS131M08(SPISlave spiSlave) : spiSlave(spiSlave) { - // Initialize the channel configurations + // Initialize the channel configurations to the default values for (int i = 0; i < CHANNELS_NUM; i++) { channelsPGAGain[i] = PGA::PGA_1; @@ -87,7 +87,10 @@ bool ADS131M08::reset() void ADS131M08::calibrateOffset() { - // Reset all offsets and gains, otherwise the calibration would be wrong + // The device internal data chain firsts subtracts the offset and then + // multiplies for the gain. To calibrate the offset we first reset it, then + // take some samples and apply the average as the new offset. + // So we need to reset the offset and gain for (int i = 0; i < CHANNELS_NUM; i++) { setChannelOffsetImpl(static_cast<Channel>(i), 0); @@ -117,17 +120,32 @@ void ADS131M08::calibrateOffset() 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]); + if (realSampleCount == 0) + { + LOG_ERR(logger, "Calibration failed, no valid samples"); + } + else + { + 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])); + + // Set the new offset only if valid, otherwise keep the old one + if (averageValues[i] != 0) + { + channelsOffset[i] = averageValues[i]; + } + } + } - // Reset the gain as it was before + // Update the offset values and reset the gain as it was before + for (int i = 0; i < CHANNELS_NUM; i++) + { + setChannelOffset(static_cast<Channel>(i), channelsOffset[i]); setChannelGainCalibrationImpl(static_cast<Channel>(i), channelsGain[i]); } } @@ -181,8 +199,7 @@ void ADS131M08::disableGlobalChopMode() bool ADS131M08::selfTest() { - float voltage[CHANNELS_NUM] = {0}; - bool success = true; + bool success = true; // Reset PGA, offsets and gains and connect the positive test signal for (int i = 0; i < CHANNELS_NUM; i++) @@ -194,31 +211,59 @@ bool ADS131M08::selfTest() } // Take some samples + int32_t averageValues[CHANNELS_NUM] = {0}; + int realSampleCount = 0; for (int i = 0; i < SELF_TEST_SAMPLES; i++) { + // Wait for a sample to be ready Thread::sleep(4); - auto newSample = sampleImpl(); + + // Sample the channels + int32_t rawValues[CHANNELS_NUM]; + if (!readSamples(rawValues)) + { + // If the CRC failed we skip this sample + continue; + } for (int j = 0; j < CHANNELS_NUM; j++) { - voltage[j] += newSample.voltage[j]; + averageValues[j] += rawValues[j]; } + + realSampleCount++; } - // Check the values - for (int i = 0; i < CHANNELS_NUM; i++) + if (realSampleCount == 0) { - // 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) + LOG_ERR(logger, + "Failed self test with positive DC signal, no valid samples"); + success = false; + } + else + { + // Check the values + for (int i = 0; i < CHANNELS_NUM; i++) { - LOG_ERR(logger, + // Compute the average + averageValues[i] /= realSampleCount; + + // Convert the value to Volts + float volts = averageValues[i] * getLSBSizeFromGain(PGA::PGA_1); + + // Check if the value is in the acceptable range + if (volts < V_REF * TEST_SIGNAL_FACTOR - TEST_SIGNAL_SLACK) + { + LOG_ERR( + logger, "Self test failed on channel {} on positive test signal, " "value was {}", - i, voltage[i]); - success = false; + i, volts); + success = false; + } + + // Reset the raw value + averageValues[i] = 0; } } @@ -229,40 +274,58 @@ bool ADS131M08::selfTest() } // Take some samples + realSampleCount = 0; for (int i = 0; i < SELF_TEST_SAMPLES; i++) { + // Wait for a sample to be ready Thread::sleep(4); - auto newSample = sampleImpl(); + + // Sample the channels + int32_t rawValues[CHANNELS_NUM]; + if (!readSamples(rawValues)) + { + // If the CRC failed we skip this sample + continue; + } for (int j = 0; j < CHANNELS_NUM; j++) { - voltage[j] += newSample.voltage[j]; + averageValues[j] += rawValues[j]; } + + realSampleCount++; } - // Check the values - for (int i = 0; i < CHANNELS_NUM; i++) + if (realSampleCount == 0) { - // 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) + LOG_ERR(logger, + "Failed self test with positive DC signal, no valid samples"); + success = false; + } + else + { + // Check the values + for (int i = 0; i < CHANNELS_NUM; i++) { - LOG_ERR(logger, + // Compute the average + averageValues[i] /= realSampleCount; + + // Convert the value to Volts + float volts = averageValues[i] * getLSBSizeFromGain(PGA::PGA_1); + + // Check if the value is in the acceptable range + if (volts > -V_REF * TEST_SIGNAL_FACTOR + TEST_SIGNAL_SLACK) + { + LOG_ERR( + logger, "Self test failed on channel {} on negative test signal, " "value was {}", - i, voltage[i]); - success = false; + i, volts); + success = false; + } } } - // If any channel failed we return false - if (!success) - { - return false; - } - // Reset channels previous configuration for (int i = 0; i < CHANNELS_NUM; i++) { @@ -272,7 +335,8 @@ bool ADS131M08::selfTest() setChannelInput(static_cast<Channel>(i), Input::DEFAULT); } - return true; + // We fail even if one channel didn't pass the test + return success; } ADS131M08Data ADS131M08::sampleImpl() diff --git a/src/shared/sensors/ADS131M08/ADS131M08.h b/src/shared/sensors/ADS131M08/ADS131M08.h index 705cf29dd..01ad2c733 100644 --- a/src/shared/sensors/ADS131M08/ADS131M08.h +++ b/src/shared/sensors/ADS131M08/ADS131M08.h @@ -97,8 +97,24 @@ public: */ void calibrateOffset(); + /** + * @brief changes the oversampling ratio. + * + * Keep in mind that after changing the oversampling ratio the device resets + * the internal digital filter and needs some time to settle. + * So if you immediately take a sample you'll read zeros. + * + * @warning This is especially important if you perform an offset + * calibration right after changing the oversampling ratio. + */ void setOversamplingRatio(ADS131M08Defs::OversamplingRatio ratio); + /** + * @brief Sets the channel programmable gain amplifier. + * + * The programmable gain amplifier allows the ADC to measure low level + * signals with the full resolution. + */ void setChannelPGA(ADS131M08Defs::Channel channel, ADS131M08Defs::PGA gain); /** @@ -111,15 +127,8 @@ public: /** * @brief Sets the channel gain calibration. * - * The ADS131M08 corrects for gain errors by multiplying the ADC conversion - * result using the gain calibration registers. - * The gain calibration value is interpreted as a 24bit unsigned. The values - * corresponds to n * (1/2^23), ranging from 0 to 2 - (1/2^23). - * - * This function accepts a value between 0 and 2, it then compute the - * correct gain register value. - * - * @param gain Must be between 0 and 2. + * @param gain Gain value between 0 and 2. Values outside this range will be + * capped. */ void setChannelGainCalibration(ADS131M08Defs::Channel channel, double gain); @@ -127,10 +136,35 @@ public: void disableChannel(ADS131M08Defs::Channel channel); + /** + * @brief Enables the global chop mode. + * + * When global chop mode is enabled the ADC uses the conversion results from + * two consecutive internal conversions taken with opposite input polarity + * to cancel the device internal offset voltage. + * + * The drawback of this mode is that the sampling rate is reduced. The + * conversion period is reduced because every time the device swaps the + * input polarity, the internal filter is reset. The ADC then always takes + * three internal conversions to produce one result. + * + * For more details see chapter 8.4.3.2 of the datasheet. + */ void enableGlobalChopMode(); + /** + * @brief Disables the global chop mode. + * + * See enableGlobalChopMode() for more details on the global chop mode. + */ void disableGlobalChopMode(); + /** + * @brief The self test samples internally connects each channel to known + * test signals and verifies if the sampled values are in an expected range. + * + * @returns True if the self test is successful, false otherwise. + */ bool selfTest() override; private: @@ -139,11 +173,23 @@ private: void setChannelInput(ADS131M08Defs::Channel channel, ADS131M08Defs::Input input); + /** + * setChannelPGS() implementation without saving the gain value in + * the local variable. + */ void setChannelPGAImpl(ADS131M08Defs::Channel channel, ADS131M08Defs::PGA gain); + /** + * setChannelOffset() implementation without saving the offset value in + * the local variable. + */ void setChannelOffsetImpl(ADS131M08Defs::Channel channel, uint32_t offset); + /** + * setChannelGainCalibration() implementation without saving the gain value + * in the local variable. + */ void setChannelGainCalibrationImpl(ADS131M08Defs::Channel channel, double gain); @@ -162,6 +208,12 @@ private: ADS131M08Defs::Register getChannelGainRegisterLSB( ADS131M08Defs::Channel channel); + /** + * @brief Sends a NULL command, reads the channels samples and stores the + * values in the given array. + * + * @returns Returns true if the CRC is correct, false otherwise. + */ bool readSamples(int32_t rawValues[ADS131M08Defs::CHANNELS_NUM]); uint16_t readRegister(ADS131M08Defs::Register reg); @@ -174,11 +226,17 @@ private: void sendCommand(SPITransaction& transaction, ADS131M08Defs::Command cmd, uint8_t data[ADS131M08Defs::FULL_FRAME_SIZE]); + /** + * @brief Given a gain value returns the conversion value for a raw data + * sample. + */ float getLSBSizeFromGain(ADS131M08Defs::PGA gain); SPISlave spiSlave; - // Current channels configuration + // Saving the current configuration of the device + // This is necessary because the selfTest and calibrateOffset functions + // temporarily resets the channels configuration ADS131M08Defs::PGA channelsPGAGain[ADS131M08Defs::CHANNELS_NUM]; uint32_t channelsOffset[ADS131M08Defs::CHANNELS_NUM]; double channelsGain[ADS131M08Defs::CHANNELS_NUM]; diff --git a/src/tests/sensors/test-ads131m04.cpp b/src/tests/sensors/test-ads131m04.cpp index 6c001ae8e..cda44e9bc 100644 --- a/src/tests/sensors/test-ads131m04.cpp +++ b/src/tests/sensors/test-ads131m04.cpp @@ -70,11 +70,11 @@ int main() printf("Now performing self test...\n"); if (ads131.selfTest()) { - printf("Self test failed!\n"); + printf("Self test succeeded\n"); } else { - printf("Self test succeeded\n"); + printf("Self test failed!\n"); } while (true) diff --git a/src/tests/sensors/test-ads131m08.cpp b/src/tests/sensors/test-ads131m08.cpp index 180ec04b5..ee199d39c 100644 --- a/src/tests/sensors/test-ads131m08.cpp +++ b/src/tests/sensors/test-ads131m08.cpp @@ -70,11 +70,11 @@ int main() printf("Now performing self test...\n"); if (ads131.selfTest()) { - printf("Self test failed!\n"); + printf("Self test succeeded\n"); } else { - printf("Self test succeeded\n"); + printf("Self test failed!\n"); } while (true) -- GitLab