Bladeren bron

Merge pull request #185 from ZuluSCSI/dev_vdd_monitoring

Implement supply voltage monitoring
Alex Perez 2 jaren geleden
bovenliggende
commit
bc26a6a9ae

+ 49 - 0
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.cpp

@@ -266,6 +266,54 @@ void platform_disable_led(void)
     logmsg("Disabling status LED");
 }
 
+/*****************************************/
+/* Supply voltage monitor                */
+/*****************************************/
+
+// Use ADC to implement supply voltage monitoring for the +3.0V rail.
+// This works by sampling the Vrefint, which has
+// a voltage of 1.2 V, allowing to calculate the VDD voltage.
+static void adc_poll()
+{
+#if PLATFORM_VDD_WARNING_LIMIT_mV > 0
+    static bool initialized = false;
+    static int lowest_vdd_seen = PLATFORM_VDD_WARNING_LIMIT_mV;
+
+    if (!initialized)
+    {
+        rcu_periph_clock_enable(RCU_ADC0);
+        adc_enable(ADC0);
+        adc_calibration_enable(ADC0);
+        adc_tempsensor_vrefint_enable();
+        adc_inserted_channel_config(ADC0, 0, ADC_CHANNEL_17, ADC_SAMPLETIME_239POINT5);
+        adc_external_trigger_source_config(ADC0, ADC_INSERTED_CHANNEL, ADC0_1_2_EXTTRIG_INSERTED_NONE);
+        adc_external_trigger_config(ADC0, ADC_INSERTED_CHANNEL, ENABLE);
+        adc_software_trigger_enable(ADC0, ADC_INSERTED_CHANNEL);
+        initialized = true;
+    }
+
+    // Read previous result and start new one
+    int adc_value = ADC_IDATA0(ADC0);
+    adc_software_trigger_enable(ADC0, ADC_INSERTED_CHANNEL);
+
+    // adc_value = 1200mV * 4096 / Vdd
+    // => Vdd = 1200mV * 4096 / adc_value
+    // To avoid wasting time on division, compare against
+    // limit directly.
+    const int limit = (1200 * 4096) / PLATFORM_VDD_WARNING_LIMIT_mV;
+    if (adc_value > limit)
+    {
+        // Warn once, and then again if we detect even a lower drop.
+        int vdd_mV = (1200 * 4096) / adc_value;
+        if (vdd_mV < lowest_vdd_seen)
+        {
+            logmsg("WARNING: Detected supply voltage drop to ", vdd_mV, "mV. Verify power supply is adequate.");
+            lowest_vdd_seen = vdd_mV - 50; // Small hysteresis to avoid excessive warnings
+        }
+    }
+#endif
+}
+
 /*****************************************/
 /* Crash handlers                        */
 /*****************************************/
@@ -436,6 +484,7 @@ void platform_reset_watchdog()
     // It gives us opportunity to collect better debug info than the
     // full hardware reset that would be caused by hardware watchdog.
     g_watchdog_timeout = WATCHDOG_CRASH_TIMEOUT;
+    adc_poll();
 }
 
 /***********************/

+ 4 - 0
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.h

@@ -54,6 +54,10 @@ extern const char *g_platform_name;
 #   include "ZuluSCSI_v1_1_gpio.h"
 #endif
 
+#ifndef PLATFORM_VDD_WARNING_LIMIT_mV
+#define PLATFORM_VDD_WARNING_LIMIT_mV 2800
+#endif
+
 // Debug logging functions
 void platform_log(const char *s);
 

+ 48 - 0
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.cpp

@@ -28,6 +28,7 @@
 #include <hardware/gpio.h>
 #include <hardware/uart.h>
 #include <hardware/spi.h>
+#include <hardware/adc.h>
 #include <hardware/flash.h>
 #include <hardware/structs/xip_ctrl.h>
 #include <hardware/structs/usb.h>
@@ -396,6 +397,51 @@ static void usb_log_poll()
     }
 }
 
+// Use ADC to implement supply voltage monitoring for the +3.0V rail.
+// This works by sampling the temperature sensor channel, which has
+// a voltage of 0.7 V, allowing to calculate the VDD voltage.
+static void adc_poll()
+{
+#if PLATFORM_VDD_WARNING_LIMIT_mV > 0
+    static bool initialized = false;
+    static int lowest_vdd_seen = PLATFORM_VDD_WARNING_LIMIT_mV;
+
+    if (!initialized)
+    {
+        adc_init();
+        adc_set_temp_sensor_enabled(true);
+        adc_set_clkdiv(65535); // Lowest samplerate, about 2 kHz
+        adc_select_input(4);
+        adc_fifo_setup(true, false, 0, false, false);
+        adc_run(true);
+        initialized = true;
+    }
+
+    int adc_value_max = 0;
+    while (!adc_fifo_is_empty())
+    {
+        int adc_value = adc_fifo_get();
+        if (adc_value > adc_value_max) adc_value_max = adc_value;
+    }
+
+    // adc_value = 700mV * 4096 / Vdd
+    // => Vdd = 700mV * 4096 / adc_value
+    // To avoid wasting time on division, compare against
+    // limit directly.
+    const int limit = (700 * 4096) / PLATFORM_VDD_WARNING_LIMIT_mV;
+    if (adc_value_max > limit)
+    {
+        // Warn once, and then again if we detect even a lower drop.
+        int vdd_mV = (700 * 4096) / adc_value_max;
+        if (vdd_mV < lowest_vdd_seen)
+        {
+            logmsg("WARNING: Detected supply voltage drop to ", vdd_mV, "mV. Verify power supply is adequate.");
+            lowest_vdd_seen = vdd_mV - 50; // Small hysteresis to avoid excessive warnings
+        }
+    }
+#endif
+}
+
 // This function is called for every log message.
 void platform_log(const char *s)
 {
@@ -488,6 +534,8 @@ void platform_reset_watchdog()
     }
 
     usb_log_poll();
+
+    adc_poll();
 }
 
 /*****************************************/

+ 4 - 0
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.h

@@ -60,6 +60,10 @@ extern const char *g_platform_name;
 #define SD_USE_SDIO 1
 #define PLATFORM_HAS_PARITY_CHECK 1
 
+#ifndef PLATFORM_VDD_WARNING_LIMIT_mV
+#define PLATFORM_VDD_WARNING_LIMIT_mV 2800
+#endif
+
 // NOTE: The driver supports synchronous speeds higher than 10MB/s, but this
 // has not been tested due to lack of fast enough SCSI adapter.
 // #define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_TURBO