Improved power spike shunting
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Mon, 10 Sep 2018 23:48:33 +0000 (16:48 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Mon, 10 Sep 2018 23:48:33 +0000 (16:48 -0700)
src/pwr/config.h
src/pwr/main.c

index 854ef38aaece3b8a89b7b7638469e4567e275208..759ef9cc8d7e35eb62779c933ace4724260bfe6c 100644 (file)
@@ -89,14 +89,21 @@ enum {
 #define VOLTAGE_EXP 0.01
 
 #define SHUNT_WATTS 5
-#define SHUNT_OHMS 10
+#define SHUNT_OHMS  5.1
+#define SHUNT_PERIOD 65000 // ms
+#define SHUNT_JOULES 50    // Power per shunt period
+#define SHUNT_JOULES_PER_MS ((float)SHUNT_JOULES / SHUNT_PERIOD)
 #define SHUNT_MIN_V 1
 #define SHUNT_MAX_V 3
 
 #define VOLTAGE_REF 1.1
 #define VOLTAGE_REF_R1 37400
 #define VOLTAGE_REF_R2 1000
-#define CURRENT_REF_MUL 1970
+#define CURRENT_REF_R2 137
+#define CURRENT_REF_MUL (100.0 * 2700 / CURRENT_REF_R2) // 2700 from datasheet
+
+#define AVG_SCALE 2
+#define BUCKETS (1 << AVG_SCALE)
 
 // Addresses 0x60 to 0x67
 #define I2C_ADDR 0x60
index c5b1113ab634c34cfa6f59b1c2f3b262b29122b9..b68f26a74894eb500dee0bfcf331e99386c514da 100644 (file)
@@ -62,10 +62,12 @@ static const uint8_t ch_schedule[] = {
 
 
 static volatile uint16_t regs[NUM_REGS] = {0};
+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_ms_power = 0;
+static volatile float shunt_joules = 0;
 static volatile float vnom = 0;
 
 
@@ -135,19 +137,20 @@ static float get_reg(int reg) {
 
 
 static void update_shunt() {
-  static float watts = SHUNT_WATTS;
+  static float joules = SHUNT_JOULES; // Power disipation budget
 
-  // Add power dissipation credit
-  watts += SHUNT_WATTS / 1000.0;
-  if (SHUNT_WATTS < watts) watts = SHUNT_WATTS;
+  // Add power dissipation credit for the 1ms that elapsed
+  joules += SHUNT_JOULES_PER_MS;
+  if (SHUNT_JOULES < joules) joules = SHUNT_JOULES; // Max
 
-  // Remove power dissipation credit
-  watts -= shunt_ms_power;
-  if (watts < 0) shunt_overload = true;
+  if (joules < shunt_joules) shunt_overload = true;
+  else if (shunt_joules) {
+    joules -= shunt_joules; // Subtract power dissipated
+    IO_DDR_SET(SHUNT_PIN);  // Enable
+    return;
+  }
 
-  // Enable shunt when requested
-  if (shunt_ms_power) IO_DDR_SET(SHUNT_PIN); // Enable
-  else IO_DDR_CLR(SHUNT_PIN);                // Disable
+  IO_DDR_CLR(SHUNT_PIN); // Disable
 }
 
 
@@ -156,12 +159,13 @@ static void update_shunt_power(float vout, float vnom) {
     float duty = (vout - vnom - SHUNT_MIN_V) / SHUNT_MAX_V;
     if (duty < 0) duty = 0;
     if (1 < duty) duty = 1;
+    if (VOLTAGE_MAX <= vout) duty = 1; // Full shunt at max voltage
 
-    // Compute the power credits used per ms
-    shunt_ms_power = vout * vout * duty * (1.0 / SHUNT_OHMS / 1000.0);
+    // Compute joules shunted this cycle: J = V^2 / RT
+    shunt_joules = duty * vout * vout / (SHUNT_OHMS * 1000.0);
     OCR1A = 0xff * duty;
 
-  } else shunt_ms_power = 0;
+  } else shunt_joules = 0;
 }
 
 
@@ -220,6 +224,18 @@ inline static uint16_t convert_current(uint16_t sample) {
 }
 
 
+inline static uint16_t 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;
+
+  uint32_t sum = 0;
+  for (int i = 0; i < BUCKETS; i++)
+    sum += reg_avg[reg][i];
+
+  return regs[reg] = sum >> AVG_SCALE;
+}
+
+
 static void read_conversion(uint8_t ch) {
   uint16_t data = ADC;
 
@@ -229,23 +245,23 @@ static void read_conversion(uint8_t ch) {
   case VOUT_ADC: regs[VOUT_REG] = convert_voltage(data); break;
 
   case CS1_ADC: {
-    regs[MOTOR_REG] = convert_current(data);
-    bool overtemp = CURRENT_OVERTEMP * 100 < regs[MOTOR_REG];
+    uint16_t current = update_current(MOTOR_REG, data);
+    bool overtemp = CURRENT_OVERTEMP * 100 < current;
     if (overtemp) {
       if (motor_overload < MOTOR_SHUTDOWN_THRESH) motor_overload++;
     } else if (motor_overload) motor_overload--;
     break;
   }
 
-  case CS2_ADC: regs[VDD_REG] = convert_current(data); break;
+  case CS2_ADC: update_current(VDD_REG, data); break;
 
   case CS3_ADC:
-    regs[LOAD2_REG] = convert_current(data);
+    update_current(LOAD2_REG, data);
     check_load(&loads[1]);
     break;
 
   case CS4_ADC:
-    regs[LOAD1_REG] = convert_current(data);
+    update_current(LOAD1_REG, data);
     check_load(&loads[0]);
     break;
   }
@@ -360,19 +376,13 @@ void init() {
 }
 
 
-static void shutdown(uint16_t flags) {
-  // Disable timers
-  TCCR0B = TCCR1B = 0;
-
+static void shutdown() {
   // Disable outputs
-  IO_DDR_CLR(SHUNT_PIN);  // Input
   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
-
-  while (true) continue;
 }
 
 
@@ -384,8 +394,10 @@ static void validate_measurements() {
       max_current < regs[MOTOR_REG] ||
       max_current < regs[LOAD1_REG] ||
       max_current < regs[LOAD2_REG] ||
-      max_current < regs[VDD_REG])
-    shutdown(SENSE_ERROR_FLAG);
+      max_current < regs[VDD_REG]) {
+    regs[FLAGS_REG] |= SENSE_ERROR_FLAG;
+    shutdown();
+  }
 }
 
 
@@ -417,7 +429,7 @@ int main() {
     if (loads[1].shutdown) flags |= LOAD2_SHUTDOWN_FLAG;
 
     regs[FLAGS_REG] = flags;
-    if (flags & FATAL_FLAG_MASK) shutdown(flags);
+    if (flags & FATAL_FLAG_MASK) shutdown();
   }
 
   return 0;