Show power shutdown on indicators page, Show all motors in shutdown when in power...
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Sat, 31 Aug 2019 21:55:21 +0000 (14:55 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Sat, 31 Aug 2019 21:55:21 +0000 (14:55 -0700)
12 files changed:
CHANGELOG.md
src/avr/src/command.def
src/avr/src/estop.c
src/avr/src/messages.def
src/avr/src/motor.c
src/js/axis-vars.js
src/pug/templates/indicators.pug
src/pwr/config.h
src/pwr/main.c
src/py/bbctrl/Cmd.py
src/py/bbctrl/Planner.py
src/py/bbctrl/Pwr.py

index 6aac9280def43fea741b6a2ca069ac670b948451..06863c2c682c02af107647b95b66499e3e26bd3d 100644 (file)
@@ -5,6 +5,10 @@ Buildbotics CNC Controller Firmware Changelog
  - Fix demo password check
  - Fix bug were fast clicks could cause jog commands to arrive out of order.
  - Fix bug bug where planner position may not sync after jog.
+ - Show power shutdown on indicators page.
+ - Show all motors in shutdown when in power shutdown.
+ - Improved GCode error messages.
+ - Don't reset global offsets on M2.
 
 ## v0.4.9
  - Enforce 6A per motor channel peak current limit.
index 7296df3385ae38c872e39acdc59fabed0d189dff..c413a9013a89cf70519c28b24133b08e382ef868 100644 (file)
@@ -43,6 +43,7 @@ CMD('r', report,       0) // <0|1>[var] Enable or disable var reporting
 CMD('R', reboot,       0) // Reboot the controller
 CMD('c', resume,       0) // Continue processing after a flush
 CMD('E', estop,        0) // Emergency stop
+CMD('X', shutdown,     0) // Power shutdown
 CMD('C', clear,        0) // Clear estop
 CMD('F', flush,        0) // Flush command queue
 CMD('D', dump,         0) // Report all variables
index d111c0b1a85d3421d4f145af6a5c68c536d2e220..1e9bacb2870450282eba74660e84cf67bc7afd4d 100644 (file)
@@ -117,4 +117,10 @@ stat_t command_estop(char *cmd) {
 }
 
 
+stat_t command_shutdown(char *cmd) {
+  estop_trigger(STAT_POWER_SHUTDOWN);
+  return STAT_OK;
+}
+
+
 stat_t command_clear(char *cmd) {estop_clear(); return STAT_OK;}
index 8f1be68be9115d8f8b338727689c530a08ea56c5..2f9aa33102ea40b9b3c2e11c27ef64a78c5a951c 100644 (file)
@@ -31,6 +31,7 @@ STAT_MSG(NOP,                   "No op")
 STAT_MSG(INTERNAL_ERROR,        "Internal error")
 STAT_MSG(ESTOP_USER,            "User triggered EStop")
 STAT_MSG(ESTOP_SWITCH,          "Switch triggered EStop")
+STAT_MSG(POWER_SHUTDOWN,        "Power shutdown")
 STAT_MSG(UNRECOGNIZED_NAME,     "Unrecognized command or variable name")
 STAT_MSG(INVALID_COMMAND,       "Invalid command")
 STAT_MSG(INVALID_ARGUMENTS,     "Invalid arguments")
index b032568d71a8227a52c8b2a56d309bf504057aa6..2edc42c98592b4d01b07bda9ed65d19a45e3201f 100644 (file)
@@ -427,7 +427,7 @@ void set_motor_axis(int motor, uint8_t axis) {
       set_travel(motor, motors[m].travel_rev);
       set_microstep(motor, motors[m].microsteps);
       set_motor_enabled(motor, motors[m].enabled);
-      motors[motor].slave = true;
+      motors[motor].slave = true; // Must be last
       break;
     }
 }
index 6f0b7c43de3c7c97a51674180211a741fb87c55d..40ddf439ad1bd1d14cba0f7edb35b12e9d900a4f 100644 (file)
@@ -78,10 +78,11 @@ module.exports = {
       var state      = 'UNHOMED';
       var icon       = 'question-circle';
       var fault      = this.state[motor_id + 'df'] & 0x1f;
+      var shutdown   = this.state.power_shutdown;
       var title;
 
-      if (fault) {
-        state = 'FAULT';
+      if (fault || shutdown) {
+        state = shutdown ? 'SHUTDOWN' : 'FAULT';
         klass += ' error';
         icon = 'exclamation-circle';
 
@@ -102,7 +103,7 @@ module.exports = {
       }
 
       switch (state) {
-      case 'UNHOMED':  title = 'Click the home button to home axis.'; break;
+      case 'UNHOMED': title = 'Click the home button to home axis.'; break;
       case 'HOMED': title = 'Axis successfuly homed.'; break;
 
       case 'OVER':
@@ -124,9 +125,14 @@ module.exports = {
         title = 'Motor driver fault.  A potentially damaging electrical ' +
           'condition was detected and the motor driver was shutdown.  ' +
           'Please power down the controller and check your motor cabling.  ' +
-          'See the "Motor Faults" table on the "Indicators" for more ' +
+          'See the "Motor Faults" table on the "Indicators" tab for more ' +
           'information.';
         break;
+
+      case 'SHUTDOWN':
+        title = 'Motor power fault.  All motors in shutdown.  ' +
+          'See the "Power Faults" table on the "Indicators" tab for more ' +
+          'information.  Reboot controller to reset.';
       }
 
       return {
index 70821c55fa098df7bc6866ffdfbb312a474d6bce..500ab482fd3483979f77cf958390eb1a2e104c16 100644 (file)
@@ -167,8 +167,9 @@ script#indicators-template(type="text/x-template")
         td(:class="{error: state.motor_under_voltage}")
           | {{state.motor_under_voltage ? 'True' : 'False'}}
         th.separator
-        th
-        td
+        th(:class="{error: state.power_shutdown}") Power shutdown
+        td(:class="{error: state.power_shutdown}")
+          | {{state.power_shutdown ? 'True' : 'False'}}
 
     table.motor_fault
       tr
index 132f54030a6dee96ebbb5ec976d12d89330ad12e..b90f48b7a05a1ab21e26845b1efb1ecf13b4d05c 100644 (file)
@@ -30,7 +30,7 @@
 #include "pins.h"
 
 
-#define VERSION 3
+#define VERSION 4
 
 
 // Pins
@@ -133,17 +133,17 @@ typedef enum {
 
 enum {
   // Fatal
-  UNDER_VOLTAGE_FLAG  = 1 << 0,
-  OVER_VOLTAGE_FLAG   = 1 << 1,
-  OVER_CURRENT_FLAG   = 1 << 2,
-  SENSE_ERROR_FLAG    = 1 << 3,
-  SHUNT_OVERLOAD_FLAG = 1 << 4,
-  MOTOR_OVERLOAD_FLAG = 1 << 5,
+  UNDER_VOLTAGE_FLAG             = 1 << 0,
+  OVER_VOLTAGE_FLAG              = 1 << 1,
+  OVER_CURRENT_FLAG              = 1 << 2,
+  SENSE_ERROR_FLAG               = 1 << 3,
+  SHUNT_OVERLOAD_FLAG            = 1 << 4,
+  MOTOR_OVERLOAD_FLAG            = 1 << 5,
 
   // Non fatal
-  LOAD1_SHUTDOWN_FLAG      = 1 << 6,
-  LOAD2_SHUTDOWN_FLAG      = 1 << 7,
-  MOTOR_UNDER_VOLTAGE_FLAG = 1 << 8,
+  LOAD1_SHUTDOWN_FLAG            = 1 << 6,
+  LOAD2_SHUTDOWN_FLAG            = 1 << 7,
+  MOTOR_UNDER_VOLTAGE_FLAG       = 1 << 8,
 
   // Sense errors
   MOTOR_VOLTAGE_SENSE_ERROR_FLAG = 1 << 9,
@@ -151,7 +151,11 @@ enum {
   LOAD1_SENSE_ERROR_FLAG         = 1 << 11,
   LOAD2_SENSE_ERROR_FLAG         = 1 << 12,
   VDD_CURRENT_SENSE_ERROR_FLAG   = 1 << 13,
+
+  // State flags
+  POWER_SHUTDOWN_FLAG            = 1 << 14,
 };
 
 
-#define FATAL_FLAG_MASK ((1 << 6) - 1)
+#define FATAL_FLAGS       0x003f
+#define SENSE_ERROR_FLAGS 0x3e00
index 01160e704d7223a545f171266e7ad732d3d39cd8..7ea387a802910b9fce3570c3e3c01c0c53e92d5a 100644 (file)
@@ -30,7 +30,6 @@
 #include <avr/interrupt.h>
 #include <avr/pgmspace.h>
 #include <avr/io.h>
-#include <avr/wdt.h>
 
 #include <util/delay.h>
 
@@ -41,13 +40,13 @@ typedef struct {
   const regs_t reg;
   const uint8_t pin;
   volatile uint8_t overtemp;
-  volatile bool shutdown;
+  volatile uint16_t shutdown_flag;
 } load_t;
 
 
 load_t loads[2] = {
-  {LOAD1_REG, LOAD1_PIN, 0, false},
-  {LOAD2_REG, LOAD2_PIN, 0, false},
+  {LOAD1_REG, LOAD1_PIN, 0, LOAD1_SHUTDOWN_FLAG},
+  {LOAD2_REG, LOAD2_PIN, 0, LOAD2_SHUTDOWN_FLAG},
 };
 
 
@@ -66,13 +65,31 @@ static volatile uint16_t reg_avg[NUM_REGS][BUCKETS] = {{0}};
 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 bool shunt_overload = false;
 static volatile float shunt_joules = 0;
 static volatile float vnom = 0;
 
 
-void i2c_ack() {TWSCRB = (1 << TWCMD1) | (1 << TWCMD0);}
-void i2c_nack() {TWSCRB = (1 << TWAA) | (1 << TWCMD1) | (1 << TWCMD0);}
+static void shutdown();
+
+
+static uint16_t flags_get(uint16_t flags) {return regs[FLAGS_REG] & flags;}
+static void flags_clear(uint16_t flags) {regs[FLAGS_REG] &= ~flags;}
+
+
+static void flags_set(uint16_t flags) {
+  regs[FLAGS_REG] |= flags;
+  if (flags & FATAL_FLAGS) shutdown();
+}
+
+
+static void flags(uint16_t flags, bool enable) {
+  if (enable) flags_set(flags);
+  else flags_clear(flags);
+}
+
+
+static void i2c_ack()  {TWSCRB = (1 << TWCMD1) | (1 << TWCMD0);}
+static void i2c_nack() {TWSCRB = (1 << TWAA) | (1 << TWCMD1) | (1 << TWCMD0);}
 
 
 ISR(TWI_SLAVE_vect) {
@@ -117,16 +134,6 @@ ISR(TWI_SLAVE_vect) {
 }
 
 
-static float get_total_current() {
-  cli();
-  float current =
-    regs[MOTOR_REG] + regs[VDD_REG] + regs[LOAD1_REG] + regs[LOAD2_REG];
-  sei();
-
-  return current / 100;
-}
-
-
 static float get_reg(int reg) {
   cli();
   float value = regs[reg];
@@ -137,27 +144,32 @@ static float get_reg(int reg) {
 
 
 static void update_shunt() {
+  if (flags_get(POWER_SHUTDOWN_FLAG)) return;
+
   static float joules = SHUNT_JOULES; // Power disipation budget
 
   // Add power dissipation credit for the 1ms that elapsed
   joules += SHUNT_JOULES_PER_MS;
   if (SHUNT_JOULES < joules) joules = SHUNT_JOULES; // Max
 
-  if (joules < shunt_joules) shunt_overload = true;
+  if (joules < shunt_joules) flags_set(SHUNT_OVERLOAD_FLAG);
   else joules -= shunt_joules; // Subtract power dissipated
 }
 
 
-static void update_shunt_power(float vout, float vnom) {
+static void update_shunt_power() {
+  if (flags_get(POWER_SHUTDOWN_FLAG)) return;
+
+  float vout = get_reg(VOUT_REG);
+
   if (vnom + SHUNT_MIN_V < vout) {
     // Compute joules shunted this cycle: J = V^2 / RT
     shunt_joules = vout * vout / (SHUNT_OHMS * 1000.0);
-    IO_DDR_SET(SHUNT_PIN);  // Enable
-    IO_PORT_CLR(SHUNT_PIN); // Lo
+    IO_PORT_CLR(SHUNT_PIN); // Enable (lo)
 
   } else {
     shunt_joules = 0;
-    IO_DDR_CLR(SHUNT_PIN); // Disable
+    IO_PORT_SET(SHUNT_PIN); // Disable (hi)
   }
 }
 
@@ -173,20 +185,6 @@ static void measure_nominal_voltage() {
 }
 
 
-static void check_load(load_t *load) {
-  if (load->shutdown) return;
-
-  bool overtemp = CURRENT_OVERTEMP * 100 < regs[load->reg];
-  if (overtemp) {
-    if (++load->overtemp == LOAD_OVERTEMP_MAX) {
-      load->shutdown = true;
-      IO_PORT_CLR(load->pin); // Lo
-      IO_DDR_SET(load->pin);  // Output
-    }
-  } else if (load->overtemp) load->overtemp--;
-}
-
-
 ISR(TIMER0_OVF_vect) {
   static uint8_t tick = 0;
 
@@ -200,24 +198,18 @@ ISR(TIMER0_OVF_vect) {
 }
 
 
-static void delay_ms(uint16_t ms) {
-  uint64_t start = time;
-  while (time < start + ms) continue;
-}
-
-
-inline static uint16_t convert_voltage(uint16_t sample) {
+static uint16_t convert_voltage(uint16_t sample) {
   return sample * (VOLTAGE_REF / 1024.0 *
                    (VOLTAGE_REF_R1 + VOLTAGE_REF_R2) / VOLTAGE_REF_R2 * 100);
 }
 
 
-inline static uint16_t convert_current(uint16_t sample) {
+static uint16_t convert_current(uint16_t sample) {
   return sample * (VOLTAGE_REF / 1024.0 * CURRENT_REF_MUL);
 }
 
 
-inline static void update_current(int reg, uint16_t sample) {
+static void update_current(int reg, uint16_t sample) {
   reg_avg[reg][reg_index[reg]] = convert_current(sample);
   if (++reg_index[reg] == BUCKETS) reg_index[reg] = 0;
 
@@ -226,43 +218,82 @@ inline static void update_current(int reg, uint16_t sample) {
     sum += reg_avg[reg][i];
 
   regs[reg] = sum >> AVG_SCALE;
+
+  // Check total current
+  uint16_t total_current =
+    regs[MOTOR_REG] + regs[VDD_REG] + regs[LOAD1_REG] + regs[LOAD2_REG];
+  if (CURRENT_MAX * 100 < total_current) flags_set(OVER_CURRENT_FLAG);
 }
 
 
-static void read_conversion(uint8_t ch) {
-  uint16_t data = ADC;
+static void update_vin(uint16_t sample) {
+  uint16_t vin = regs[VIN_REG] = convert_voltage(sample);
 
-  switch (ch) {
-  case TEMP_ADC: regs[TEMP_REG] = data; break; // in Kelvin
-  case VIN_ADC:  regs[VIN_REG]  = convert_voltage(data); break;
-
-  case VOUT_ADC:
-    regs[VOUT_REG] = convert_voltage(data);
-    update_shunt_power(regs[VOUT_REG] / 100.0, vnom);
-    break;
-
-  case CS1_ADC: {
-    update_current(MOTOR_REG, data);
-    bool overtemp = CURRENT_OVERTEMP * 100 < regs[MOTOR_REG];
-
-    if (overtemp) {
-      if (motor_overload < MOTOR_SHUTDOWN_THRESH) motor_overload++;
-    } else if (motor_overload != MOTOR_SHUTDOWN_THRESH && motor_overload)
-      motor_overload--;
-    break;
-  }
+  // Check voltage
+  if (vin < (VOLTAGE_MIN * 100)) flags_set(UNDER_VOLTAGE_FLAG);
+  if ((VOLTAGE_MAX * 100) < vin) flags_set(OVER_VOLTAGE_FLAG);
+}
 
-  case CS2_ADC: update_current(VDD_REG, data); break;
 
-  case CS3_ADC:
-    update_current(LOAD2_REG, data);
-    check_load(&loads[1]);
-    break;
+static void update_vout(uint16_t sample) {
+  uint16_t vout = regs[VOUT_REG] = convert_voltage(sample);
 
-  case CS4_ADC:
-    update_current(LOAD1_REG, data);
-    check_load(&loads[0]);
-    break;
+  update_shunt_power();
+
+  // Check voltage
+  if ((VOLTAGE_MAX * 100) < vout) flags_set(OVER_VOLTAGE_FLAG);
+  flags(MOTOR_UNDER_VOLTAGE_FLAG,
+        vout < (VOLTAGE_MIN * 100) && !flags_get(POWER_SHUTDOWN_FLAG));
+}
+
+
+static void update_motor_current(uint16_t sample) {
+  update_current(MOTOR_REG, sample);
+
+  if (flags_get(MOTOR_OVERLOAD_FLAG)) return;
+
+  bool overtemp = CURRENT_OVERTEMP * 100 < regs[MOTOR_REG];
+
+  if (overtemp) {
+    if (motor_overload < MOTOR_SHUTDOWN_THRESH) motor_overload++;
+    if (motor_overload == MOTOR_SHUTDOWN_THRESH) flags_set(MOTOR_OVERLOAD_FLAG);
+
+  } else if (motor_overload != MOTOR_SHUTDOWN_THRESH && motor_overload)
+    motor_overload--;
+}
+
+
+static void load_shutdown(load_t *load) {
+  flags_set(load->shutdown_flag);
+  IO_PORT_CLR(load->pin); // Lo
+  IO_DDR_SET(load->pin);  // Output
+}
+
+
+static void update_load_current(load_t *load, uint16_t sample) {
+  update_current(load->reg, sample);
+
+  if (flags_get(load->shutdown_flag)) return;
+
+  bool overtemp = CURRENT_OVERTEMP * 100 < regs[load->reg];
+
+  if (overtemp) {
+    if (++load->overtemp == LOAD_OVERTEMP_MAX) load_shutdown(load);
+  } else if (load->overtemp) load->overtemp--;
+}
+
+
+static void read_conversion(uint8_t ch) {
+  uint16_t sample = ADC;
+
+  switch (ch) {
+  case TEMP_ADC: regs[TEMP_REG] = sample;                break; // in Kelvin
+  case VIN_ADC:  update_vin(sample);                     break;
+  case VOUT_ADC: update_vout(sample);                    break;
+  case CS1_ADC:  update_motor_current(sample);           break;
+  case CS2_ADC:  update_current(VDD_REG, sample);        break;
+  case CS3_ADC:  update_load_current(&loads[1], sample); break;
+  case CS4_ADC:  update_load_current(&loads[0], sample); break;
   }
 }
 
@@ -292,12 +323,10 @@ static void validate_input_voltage() {
   float vlast = 0;
 
   while (settle < VOLTAGE_SETTLE_COUNT) {
-    wdt_reset();
-    delay_ms(VOLTAGE_SETTLE_PERIOD);
-
-    float vin = get_reg(VIN_REG);
+    _delay_ms(VOLTAGE_SETTLE_PERIOD);
 
     // Check that voltage is with in range and settled
+    float vin = get_reg(VIN_REG);
     if (VOLTAGE_MIN < vin && vin < VOLTAGE_MAX &&
         is_within(vlast, vin, VOLTAGE_SETTLE_TOLERANCE)) settle++;
     else settle = 0;
@@ -308,10 +337,9 @@ static void validate_input_voltage() {
 
 
 static void charge_caps() {
-  uint64_t now = time;
-
+  IO_PORT_SET(SHUNT_PIN); // Disable shunt (hi)
   IO_PORT_SET(MOTOR_PIN); // Motor voltage on
-  while (time < now + CAP_CHARGE_TIME) continue;
+  _delay_ms(CAP_CHARGE_TIME);
 }
 
 
@@ -319,9 +347,9 @@ void init() {
   cli();
 
   // CPU Clock, disable CKOUT
-  CCP = 0xd8;
+  CCP   = 0xd8;
   CLKSR = (1 << CSTR) | (1 << CKOUT_IO) | 0b0010; // 8Mhz internal clock
-  CCP = 0xd8;
+  CCP   = 0xd8;
   CLKPR = 0; // div 1
   while (!((1 << 7) & CLKSR)) continue; // Wait for clock to stabilize
 
@@ -335,6 +363,8 @@ void init() {
   IO_DDR_CLR(LOAD1_PIN);  // Tri-state
   IO_DDR_CLR(LOAD2_PIN);  // Tri-state
   IO_PUE_SET(PWR_RESET);  // Pull up reset line
+  IO_PORT_CLR(SHUNT_PIN); // Enable shunt
+  IO_DDR_SET(SHUNT_PIN);  // Output
 
   // Disable digital IO on ADC lines
   DIDR0 = (1 << ADC4D) | (1 << ADC3D) | (1 << ADC2D) | (1 << ADC1D) |
@@ -343,7 +373,7 @@ void init() {
 
   // ADC internal 1.1v, enable, with interrupt, prescale 64
   // Note, a conversion takes ~200uS
-  ADMUX = (1 << REFS1) | (0 << REFS0);
+  ADMUX  = (1 << REFS1) | (0 << REFS0);
   ADCSRA = (1 << ADEN) | (1 << ADIE) |
     (1 << ADPS2) | (1 << ADPS1) | (0 << ADPS0);
   ADCSRB = 0;
@@ -351,78 +381,56 @@ void init() {
   // Timer 0 (Fast PWM, clk/1)
   TCCR0A = (1 << WGM01) | (1 << WGM00);
   TCCR0B = (0 << WGM02) | (0 << CS02) | (0 << CS01) | (1 << CS00);
-  TIMSK = 1 << TOIE0; // Enable overflow interrupt
+  TIMSK  = 1 << TOIE0; // Enable overflow interrupt
 
   // I2C, enable, enable address/stop interrupt
   TWSCRA = (1 << TWEN) | (1 << TWASIE) | (1 << TWDIE);
-  TWSA = I2C_ADDR << 1;
-  TWSAM = I2C_MASK << 1;
+  TWSA   = I2C_ADDR << 1;
+  TWSAM  = I2C_MASK << 1;
 
   sei();
 }
 
 
 static void shutdown() {
-  // Disable outputs
-  IO_DDR_CLR(MOTOR_PIN);  // Input
-  IO_PORT_CLR(LOAD1_PIN); // Lo
-  IO_PORT_CLR(LOAD2_PIN); // Lo
-  IO_DDR_SET(LOAD1_PIN);  // Output
-  IO_DDR_SET(LOAD2_PIN);  // Output
+  if (flags_get(POWER_SHUTDOWN_FLAG)) return;
+  flags_set(POWER_SHUTDOWN_FLAG);
+
+  // Disable loads
+  load_shutdown(&loads[0]);
+  load_shutdown(&loads[1]);
+
+  // Motor power off
+  IO_PORT_CLR(MOTOR_PIN); // Lo
+
+  // Turn shunt on
+  IO_PORT_CLR(SHUNT_PIN); // Lo
 }
 
 
-static uint16_t validate_measurements() {
+static void validate_measurements() {
   const float max_voltage = 0.99 * convert_voltage(0x3ff);
   const float max_current = 0.99 * convert_current(0x3ff);
-  uint16_t flags = 0;
-
-  if (max_voltage < regs[VOUT_REG])  flags |= MOTOR_VOLTAGE_SENSE_ERROR_FLAG;
-  if (max_current < regs[MOTOR_REG]) flags |= MOTOR_CURRENT_SENSE_ERROR_FLAG;
-  if (max_current < regs[LOAD1_REG]) flags |= LOAD1_SENSE_ERROR_FLAG;
-  if (max_current < regs[LOAD2_REG]) flags |= LOAD2_SENSE_ERROR_FLAG;
-  if (max_current < regs[VDD_REG])   flags |= VDD_CURRENT_SENSE_ERROR_FLAG;
 
-  return flags ? SENSE_ERROR_FLAG | flags : 0;
+  if (max_voltage < regs[VOUT_REG])  flags_set(MOTOR_VOLTAGE_SENSE_ERROR_FLAG);
+  if (max_current < regs[MOTOR_REG]) flags_set(MOTOR_CURRENT_SENSE_ERROR_FLAG);
+  if (max_current < regs[LOAD1_REG]) flags_set(LOAD1_SENSE_ERROR_FLAG);
+  if (max_current < regs[LOAD2_REG]) flags_set(LOAD2_SENSE_ERROR_FLAG);
+  if (max_current < regs[VDD_REG])   flags_set(VDD_CURRENT_SENSE_ERROR_FLAG);
+  if (flags_get(SENSE_ERROR_FLAGS))  flags_set(SENSE_ERROR_FLAG);
 }
 
 
 int main() {
-  wdt_enable(WDTO_8S);
-
   regs[VERSION_REG] = VERSION;
 
   init();
   adc_conversion(); // Start ADC
   validate_input_voltage();
   charge_caps();
-  uint16_t fatal = validate_measurements();
-
-  while (true) {
-    wdt_reset();
+  validate_measurements();
 
-    float vin = get_reg(VIN_REG);
-    float vout = get_reg(VOUT_REG);
-
-    //update_shunt_power(vout, vnom);
-
-    // Fatal conditions
-    if (vin < VOLTAGE_MIN) fatal |= UNDER_VOLTAGE_FLAG;
-    if (VOLTAGE_MAX < vin || VOLTAGE_MAX < vout) fatal |= OVER_VOLTAGE_FLAG;
-    if (CURRENT_MAX < get_total_current()) fatal |= OVER_CURRENT_FLAG;
-    if (shunt_overload) fatal |= SHUNT_OVERLOAD_FLAG;
-    if (motor_overload == MOTOR_SHUTDOWN_THRESH) fatal |= MOTOR_OVERLOAD_FLAG;
-    if (fatal) shutdown();
-
-    // Nonfatal conditions
-    uint16_t nonfatal = 0;
-    if (loads[0].shutdown) nonfatal |= LOAD1_SHUTDOWN_FLAG;
-    if (loads[1].shutdown) nonfatal |= LOAD2_SHUTDOWN_FLAG;
-    if (vout < VOLTAGE_MIN) nonfatal |= MOTOR_UNDER_VOLTAGE_FLAG;
-
-    // Update flags
-    regs[FLAGS_REG] = fatal | nonfatal;
-  }
+  while (true) continue;
 
   return 0;
 }
index 169f92391cd6b2d1564f48e6421258184d4b5ffb..7413760a347c8d96012cf972eb47588c589fd06b 100755 (executable)
@@ -51,6 +51,7 @@ REPORT       = 'r'
 REBOOT       = 'R'
 RESUME       = 'c'
 ESTOP        = 'E'
+SHUTDOWN     = 'X'
 CLEAR        = 'C'
 FLUSH        = 'F'
 DUMP         = 'D'
@@ -239,13 +240,14 @@ def decode_command(cmd):
             if name in 'xyzabcuvw': data['target'][name] = value
             else: data['times'][int(name)] = value
 
-    elif cmd[0] == REPORT:  data['type'] = 'report'
-    elif cmd[0] == PAUSE:   data['type'] = 'pause'
-    elif cmd[0] == UNPAUSE: data['type'] = 'unpause'
-    elif cmd[0] == ESTOP:   data['type'] = 'estop'
-    elif cmd[0] == CLEAR:   data['type'] = 'clear'
-    elif cmd[0] == FLUSH:   data['type'] = 'flush'
-    elif cmd[0] == RESUME:  data['type'] = 'resume'
+    elif cmd[0] == REPORT:   data['type'] = 'report'
+    elif cmd[0] == PAUSE:    data['type'] = 'pause'
+    elif cmd[0] == UNPAUSE:  data['type'] = 'unpause'
+    elif cmd[0] == ESTOP:    data['type'] = 'estop'
+    elif cmd[0] == SHUTDOWN: data['type'] = 'shutdown'
+    elif cmd[0] == CLEAR:    data['type'] = 'clear'
+    elif cmd[0] == FLUSH:    data['type'] = 'flush'
+    elif cmd[0] == RESUME:   data['type'] = 'resume'
 
     return data
 
index fcb19af723cd350b14ba993adc89d8c0d7610135..abd223c8565b79d386a0ce1a1d382f5e15e14b2c 100644 (file)
@@ -362,6 +362,11 @@ class Planner():
                 cmd = self._encode(cmd)
                 if cmd is not None: return cmd
 
+        except RuntimeError as e:
+            # Pass on the planner message
+            self.log.error(str(e));
+            self.stop()
+
         except:
             self.log.exception()
             self.stop()
index ded8aac06ec10dfd9974d7107d8165d3d617b72e..b2ec945a295e894b298e9ee75786ffd987ad4994 100644 (file)
@@ -26,6 +26,7 @@
 ################################################################################
 
 import bbctrl
+import bbctrl.Cmd as Cmd
 
 
 # Must match regs in pwr firmware
@@ -40,20 +41,21 @@ FLAGS_REG       = 7
 VERSION_REG     = 8
 
 # Must be kept in sync with pwr firmware
-UNDER_VOLTAGE_FLAG       = 1 << 0
-OVER_VOLTAGE_FLAG        = 1 << 1
-OVER_CURRENT_FLAG        = 1 << 2
-SENSE_ERROR_FLAG         = 1 << 3
-SHUNT_OVERLOAD_FLAG      = 1 << 4
-MOTOR_OVERLOAD_FLAG      = 1 << 5
-LOAD1_SHUTDOWN_FLAG      = 1 << 6
-LOAD2_SHUTDOWN_FLAG      = 1 << 7
-MOTOR_UNDER_VOLTAGE_FLAG = 1 << 8
+UNDER_VOLTAGE_FLAG             = 1 << 0
+OVER_VOLTAGE_FLAG              = 1 << 1
+OVER_CURRENT_FLAG              = 1 << 2
+SENSE_ERROR_FLAG               = 1 << 3
+SHUNT_OVERLOAD_FLAG            = 1 << 4
+MOTOR_OVERLOAD_FLAG            = 1 << 5
+LOAD1_SHUTDOWN_FLAG            = 1 << 6
+LOAD2_SHUTDOWN_FLAG            = 1 << 7
+MOTOR_UNDER_VOLTAGE_FLAG       = 1 << 8
 MOTOR_VOLTAGE_SENSE_ERROR_FLAG = 1 << 9
 MOTOR_CURRENT_SENSE_ERROR_FLAG = 1 << 10
 LOAD1_SENSE_ERROR_FLAG         = 1 << 11
 LOAD2_SENSE_ERROR_FLAG         = 1 << 12
 VDD_CURRENT_SENSE_ERROR_FLAG   = 1 << 13
+POWER_SHUTDOWN_FLAG            = 1 << 14
 
 reg_names = 'temp vin vout motor load1 load2 vdd pwr_flags pwr_version'.split()
 
@@ -132,6 +134,10 @@ class Pwr():
                             flags & VDD_CURRENT_SENSE_ERROR_FLAG):
             self.log.error('Vdd current sense error')
 
+        if self.check_fault('power_shutdown', flags & POWER_SHUTDOWN_FLAG):
+            self.log.error('Power shutdown')
+            self.ctrl.mach.i2c_command(Cmd.SHUTDOWN)
+
 
     def _update_cb(self, now = True):
         if now: self._update()