From 8ce2c31080ebc94ce45e55669410d4212778fb02 Mon Sep 17 00:00:00 2001 From: Joseph Coffland Date: Tue, 24 Sep 2019 00:07:04 -0700 Subject: [PATCH] Pwr firmware improvements, Test shunt at startup and report --- CHANGELOG.md | 1 + src/pug/templates/indicators.pug | 14 +++++-- src/pwr/config.h | 66 ++++++++++++++++---------------- src/pwr/main.c | 33 +++++++++++++--- src/py/bbctrl/AVR.py | 4 +- src/py/bbctrl/Pwr.py | 34 ++++++++-------- 6 files changed, 94 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faad1cc..0ba506f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/src/pug/templates/indicators.pug b/src/pug/templates/indicators.pug index 500ab48..1f08643 100644 --- a/src/pug/templates/indicators.pug +++ b/src/pug/templates/indicators.pug @@ -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 diff --git a/src/pwr/config.h b/src/pwr/config.h index b90f48b..cb6adb5 100644 --- a/src/pwr/config.h +++ b/src/pwr/config.h @@ -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, diff --git a/src/pwr/main.c b/src/pwr/main.c index 7ea387a..60eff53 100644 --- a/src/pwr/main.c +++ b/src/pwr/main.c @@ -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; diff --git a/src/py/bbctrl/AVR.py b/src/py/bbctrl/AVR.py index 6ef0db7..211fe6e 100644 --- a/src/py/bbctrl/AVR.py +++ b/src/py/bbctrl/AVR.py @@ -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: diff --git a/src/py/bbctrl/Pwr.py b/src/py/bbctrl/Pwr.py index b2ec945..9c4579a 100644 --- a/src/py/bbctrl/Pwr.py +++ b/src/py/bbctrl/Pwr.py @@ -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() -- 2.27.0