Pwr firmware improvements, Test shunt at startup and report
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Tue, 24 Sep 2019 07:07:04 +0000 (00:07 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Tue, 24 Sep 2019 07:07:04 +0000 (00:07 -0700)
CHANGELOG.md
src/pug/templates/indicators.pug
src/pwr/config.h
src/pwr/main.c
src/py/bbctrl/AVR.py
src/py/bbctrl/Pwr.py

index faad1ccb6fd2fb1cc07ba0f2220142a56a9ac3ed..0ba506f8a7e23d4a6c5424e713da4f42bdb12e14 100644 (file)
@@ -10,6 +10,7 @@ Buildbotics CNC Controller Firmware Changelog
  - Improved GCode error messages.
  - Put controller into estop when in power shutdown.
  - Don't reset global offsets on M2.
+ - Test shunt and show error on failure.
 
 ## v0.4.9
  - Enforce 6A per motor channel peak current limit.
index 500ab482fd3483979f77cf958390eb1a2e104c16..1f0864374bc0b783579f747573f65585bafbf096 100644 (file)
@@ -151,9 +151,9 @@ script#indicators-template(type="text/x-template")
         td(:class="{error: state.shunt_overload}")
           | {{state.shunt_overload ? 'True' : 'False'}}
         th.separator
-        th(:class="{error: state.motor_overload}") Motor overload
-        td(:class="{error: state.motor_overload}")
-          | {{state.motor_overload ? 'True' : 'False'}}
+        th(:class="{error: state.shunt_error}") Shunt error
+        td(:class="{error: state.shunt_error}")
+          | {{state.shunt_error ? 'True' : 'False'}}
       tr
         th(:class="{error: state.load1_shutdown}") Load 1 shutdown
         td(:class="{error: state.load1_shutdown}")
@@ -167,9 +167,17 @@ script#indicators-template(type="text/x-template")
         td(:class="{error: state.motor_under_voltage}")
           | {{state.motor_under_voltage ? 'True' : 'False'}}
         th.separator
+        th(:class="{error: state.motor_overload}") Motor overload
+        td(:class="{error: state.motor_overload}")
+          | {{state.motor_overload ? 'True' : 'False'}}
+
+      tr
         th(:class="{error: state.power_shutdown}") Power shutdown
         td(:class="{error: state.power_shutdown}")
           | {{state.power_shutdown ? 'True' : 'False'}}
+        th.separator
+        th
+        td
 
     table.motor_fault
       tr
index b90f48b7a05a1ab21e26845b1efb1ecf13b4d05c..cb6adb50ff79662f27ffb36eb5706efb5b993f1f 100644 (file)
@@ -30,7 +30,7 @@
 #include "pins.h"
 
 
-#define VERSION 4
+#define VERSION 5
 
 
 // Pins
@@ -78,43 +78,44 @@ enum {
 };
 
 
-#define CAP_CHARGE_TIME 50 // ms
-#define VOLTAGE_MIN 11
-#define VOLTAGE_MAX 39
-#define CURRENT_MAX 25
-#define CURRENT_OVERTEMP 19 // Should read as ~21A but over 11.86A in an error
-#define LOAD_OVERTEMP_MAX 10
-#define MOTOR_SHUTDOWN_THRESH 15
-#define VOLTAGE_SETTLE_COUNT 5
-#define VOLTAGE_SETTLE_PERIOD 20 // ms
+#define SHUNT_FAIL_VOLTAGE       5
+#define CAP_CHARGE_TIME          20 // ms
+#define VOLTAGE_MIN              11
+#define VOLTAGE_MAX              39
+#define CURRENT_MAX              25
+#define CURRENT_OVERTEMP         19 // Should read ~21A but > 11.86A is error
+#define LOAD_OVERTEMP_MAX        10
+#define MOTOR_SHUTDOWN_THRESH    15
+#define VOLTAGE_SETTLE_COUNT     5
+#define VOLTAGE_SETTLE_PERIOD    20 // ms
 #define VOLTAGE_SETTLE_TOLERANCE 0.01
-#define VOLTAGE_EXP 0.01
-
-#define SHUNT_WATTS 5
-#define SHUNT_OHMS  5.1
-#define SHUNT_PERIOD 65000 // ms
-#define SHUNT_JOULES 25    // Power per shunt period
-#define SHUNT_JOULES_PER_MS ((float)SHUNT_JOULES / SHUNT_PERIOD)
-#define SHUNT_MIN_V 2
-
-#define VOLTAGE_REF 1.1
-#define VOLTAGE_REF_R1 37400
-#define VOLTAGE_REF_R2 1000
-#define CURRENT_REF_R2 137
+#define VOLTAGE_EXP              0.01
+
+#define SHUNT_WATTS              5
+#define SHUNT_OHMS               5.1
+#define SHUNT_PERIOD             65000 // ms
+#define SHUNT_JOULES             25    // Power per shunt period
+#define SHUNT_JOULES_PER_MS      ((float)SHUNT_JOULES / SHUNT_PERIOD)
+#define SHUNT_MIN_V              2
+
+#define VOLTAGE_REF              1.1
+#define VOLTAGE_REF_R1           37400
+#define VOLTAGE_REF_R2           1000
+#define CURRENT_REF_R2           137
 #define CURRENT_REF_MUL (100.0 * 2700 / CURRENT_REF_R2) // 2700 from datasheet
 
-#define AVG_SCALE 3
-#define BUCKETS (1 << AVG_SCALE)
+#define AVG_SCALE                3
+#define BUCKETS                  (1 << AVG_SCALE)
 
 // Addresses 0x60 to 0x67
-#define I2C_ADDR 0x60
-#define I2C_MASK 0b00001111
+#define I2C_ADDR                 0x60
+#define I2C_MASK                 0b00001111
 
-#define I2C_ERROR_BM (1 << TWBE)
-#define I2C_DATA_INT_BM (1 << TWDIF)
-#define I2C_READ_BM (1 << TWDIR)
-#define I2C_ADDRESS_STOP_INT_BM (1 << TWASIF)
-#define I2C_ADDRESS_MATCH_BM (1 << TWAS)
+#define I2C_ERROR_BM             (1 << TWBE)
+#define I2C_DATA_INT_BM          (1 << TWDIF)
+#define I2C_READ_BM              (1 << TWDIR)
+#define I2C_ADDRESS_STOP_INT_BM  (1 << TWASIF)
+#define I2C_ADDRESS_MATCH_BM     (1 << TWAS)
 
 
 typedef enum {
@@ -144,6 +145,7 @@ enum {
   LOAD1_SHUTDOWN_FLAG            = 1 << 6,
   LOAD2_SHUTDOWN_FLAG            = 1 << 7,
   MOTOR_UNDER_VOLTAGE_FLAG       = 1 << 8,
+  SHUNT_ERROR_FLAG               = 1 << 15,
 
   // Sense errors
   MOTOR_VOLTAGE_SENSE_ERROR_FLAG = 1 << 9,
index 7ea387a802910b9fce3570c3e3c01c0c53e92d5a..60eff534365ea12541ae265f6ff7ab495f920387 100644 (file)
@@ -66,9 +66,16 @@ static volatile uint16_t reg_index[NUM_REGS] = {0};
 static volatile uint64_t time = 0; // ms
 static volatile uint8_t motor_overload = 0;
 static volatile float shunt_joules = 0;
+static volatile bool auto_shunt = false;
 static volatile float vnom = 0;
 
 
+void delay(uint16_t ms) {
+  uint64_t end = time + ms;
+  while (time < end) continue;
+}
+
+
 static void shutdown();
 
 
@@ -144,7 +151,7 @@ static float get_reg(int reg) {
 
 
 static void update_shunt() {
-  if (flags_get(POWER_SHUTDOWN_FLAG)) return;
+  if (!auto_shunt || flags_get(POWER_SHUTDOWN_FLAG)) return;
 
   static float joules = SHUNT_JOULES; // Power disipation budget
 
@@ -158,7 +165,7 @@ static void update_shunt() {
 
 
 static void update_shunt_power() {
-  if (flags_get(POWER_SHUTDOWN_FLAG)) return;
+  if (!auto_shunt || flags_get(POWER_SHUTDOWN_FLAG)) return;
 
   float vout = get_reg(VOUT_REG);
 
@@ -323,7 +330,7 @@ static void validate_input_voltage() {
   float vlast = 0;
 
   while (settle < VOLTAGE_SETTLE_COUNT) {
-    _delay_ms(VOLTAGE_SETTLE_PERIOD);
+    delay(VOLTAGE_SETTLE_PERIOD);
 
     // Check that voltage is with in range and settled
     float vin = get_reg(VIN_REG);
@@ -339,7 +346,19 @@ static void validate_input_voltage() {
 static void charge_caps() {
   IO_PORT_SET(SHUNT_PIN); // Disable shunt (hi)
   IO_PORT_SET(MOTOR_PIN); // Motor voltage on
-  _delay_ms(CAP_CHARGE_TIME);
+  delay(CAP_CHARGE_TIME);
+}
+
+
+static void shunt_test() {
+  charge_caps();
+
+  // Discharge caps
+  IO_PORT_CLR(MOTOR_PIN); // Motor voltage off
+  IO_PORT_CLR(SHUNT_PIN); // Enable shunt (lo)
+  delay(CAP_CHARGE_TIME);
+
+  if (SHUNT_FAIL_VOLTAGE < get_reg(VOUT_REG)) flags_set(SHUNT_ERROR_FLAG);
 }
 
 
@@ -378,8 +397,8 @@ void init() {
     (1 << ADPS2) | (1 << ADPS1) | (0 << ADPS0);
   ADCSRB = 0;
 
-  // Timer 0 (Fast PWM, clk/1)
-  TCCR0A = (1 << WGM01) | (1 << WGM00);
+  // Timer 0, normal, clk/1
+  TCCR0A = (0 << WGM01) | (0 << WGM00);
   TCCR0B = (0 << WGM02) | (0 << CS02) | (0 << CS01) | (1 << CS00);
   TIMSK  = 1 << TOIE0; // Enable overflow interrupt
 
@@ -427,8 +446,10 @@ int main() {
   init();
   adc_conversion(); // Start ADC
   validate_input_voltage();
+  shunt_test();
   charge_caps();
   validate_measurements();
+  auto_shunt = true;
 
   while (true) continue;
 
index 6ef0db7e1da5ea0376c1d5271be2ba3c43a5e552..211fe6e46835b7a09c12b0bce6c8f2592c4a44be 100644 (file)
@@ -141,7 +141,7 @@ class AVR(object):
 
     def i2c_command(self, cmd, byte = None, word = None, block = None):
         self.log.info('I2C: %s b=%s w=%s d=%s' % (cmd, byte, word, block))
-        retry = 5
+        retry = 10
         cmd = ord(cmd[0])
 
         while True:
@@ -154,7 +154,7 @@ class AVR(object):
 
                 if retry:
                     self.log.warning('I2C failed, retrying: %s' % e)
-                    time.sleep(0.1)
+                    time.sleep(0.25)
                     continue
 
                 else:
index b2ec945a295e894b298e9ee75786ffd987ad4994..9c4579aa8983d16b7c33364536e1de4bd46c8df7 100644 (file)
@@ -56,6 +56,7 @@ LOAD1_SENSE_ERROR_FLAG         = 1 << 11
 LOAD2_SENSE_ERROR_FLAG         = 1 << 12
 VDD_CURRENT_SENSE_ERROR_FLAG   = 1 << 13
 POWER_SHUTDOWN_FLAG            = 1 << 14
+SHUNT_ERROR_FLAG               = 1 << 15
 
 reg_names = 'temp vin vout motor load1 load2 vdd pwr_flags pwr_version'.split()
 
@@ -87,57 +88,60 @@ class Pwr():
         flags = self.regs[FLAGS_REG]
 
         if self.check_fault('under_voltage', flags & UNDER_VOLTAGE_FLAG):
-            self.log.error('Device under voltage')
+            self.log.warning('Device under voltage')
 
         if self.check_fault('over_voltage', flags & OVER_VOLTAGE_FLAG):
-            self.log.error('Device over voltage')
+            self.log.warning('Device over voltage')
 
         if self.check_fault('over_current', flags & OVER_CURRENT_FLAG):
-            self.log.error('Device total current limit exceeded')
+            self.log.warning('Device total current limit exceeded')
 
         if self.check_fault('sense_error', flags & SENSE_ERROR_FLAG):
-            self.log.error('Power sense error')
+            self.log.warning('Power sense error')
 
         if self.check_fault('shunt_overload', flags & SHUNT_OVERLOAD_FLAG):
-            self.log.error('Power shunt overload')
+            self.log.warning('Power shunt overload')
 
         if self.check_fault('motor_overload', flags & MOTOR_OVERLOAD_FLAG):
-            self.log.error('Motor power overload')
+            self.log.warning('Motor power overload')
 
         if self.check_fault('load1_shutdown', flags & LOAD1_SHUTDOWN_FLAG):
-            self.log.error('Load 1 over temperature shutdown')
+            self.log.warning('Load 1 over temperature shutdown')
 
         if self.check_fault('load2_shutdown', flags & LOAD2_SHUTDOWN_FLAG):
-            self.log.error('Load 2 over temperature shutdown')
+            self.log.warning('Load 2 over temperature shutdown')
 
         if self.check_fault('motor_under_voltage',
                             flags & MOTOR_UNDER_VOLTAGE_FLAG):
-            self.log.error('Motor under voltage')
+            self.log.warning('Motor under voltage')
 
         if self.check_fault('motor_voltage_sense_error',
                             flags & MOTOR_VOLTAGE_SENSE_ERROR_FLAG):
-            self.log.error('Motor voltage sense error')
+            self.log.warning('Motor voltage sense error')
 
         if self.check_fault('motor_current_sense_error',
                             flags & MOTOR_CURRENT_SENSE_ERROR_FLAG):
-            self.log.error('Motor current sense error')
+            self.log.warning('Motor current sense error')
 
         if self.check_fault('load1_sense_error',
                             flags & LOAD1_SENSE_ERROR_FLAG):
-            self.log.error('Load1 sense error')
+            self.log.warning('Load1 sense error')
 
         if self.check_fault('load2_sense_error',
                             flags & LOAD2_SENSE_ERROR_FLAG):
-            self.log.error('Load2 sense error')
+            self.log.warning('Load2 sense error')
 
         if self.check_fault('vdd_current_sense_error',
                             flags & VDD_CURRENT_SENSE_ERROR_FLAG):
-            self.log.error('Vdd current sense error')
+            self.log.warning('Vdd current sense error')
 
         if self.check_fault('power_shutdown', flags & POWER_SHUTDOWN_FLAG):
-            self.log.error('Power shutdown')
+            self.log.warning('Power shutdown')
             self.ctrl.mach.i2c_command(Cmd.SHUTDOWN)
 
+        if self.check_fault('shunt_error', flags & SHUNT_ERROR_FLAG):
+            self.log.warning('Shunt error')
+
 
     def _update_cb(self, now = True):
         if now: self._update()