From bcf2207b7e99ed08572e22a3f21d66a440543b99 Mon Sep 17 00:00:00 2001 From: Joseph Coffland Date: Sun, 3 Dec 2017 19:33:46 -0800 Subject: [PATCH] Improved overvoltage shunting --- src/pwr/main.c | 248 +++++++++++++++++++++++++++++-------------------- src/pwr/pins.c | 31 +++++++ src/pwr/pins.h | 63 +++++++++++++ 3 files changed, 243 insertions(+), 99 deletions(-) create mode 100644 src/pwr/pins.c create mode 100644 src/pwr/pins.h diff --git a/src/pwr/main.c b/src/pwr/main.c index 7ca03f6..793e07d 100644 --- a/src/pwr/main.c +++ b/src/pwr/main.c @@ -25,6 +25,8 @@ \******************************************************************************/ +#include "pins.h" + #include #include #include @@ -35,32 +37,49 @@ #include -#define TEMP_ADC 14 - -// Port A -#define MOTOR_PIN 3 -//#define LOAD1_PIN 5 -#define VOUT_PIN 5 -#define LOAD2_PIN 6 -#define MOTOR_ADC 0 -//#define LOAD1_ADC 2 -#define VOUT_ADC 2 -#define LOAD2_ADC 3 - -// Port B -#define VIN_PIN 0 -#define VIN_ADC 5 - -// Port C -//#define VOUT_PIN 2 -//#define VOUT_ADC 11 - -#define GATE_PORT PORTC -#define GATE_DDR DDRC -#define GATE1_PIN 0 -//#define GATE2_PIN 4 -#define SHUNT_PIN 4 -#define GATE3_PIN 5 +// Pins +enum { + AREF_PIN = PORT_A << 3, + PA1_PIN, // NC + PA2_PIN, // NC + CS1_PIN, + CS2_PIN, + CS3_PIN, + CS4_PIN, + VOUT_REF_PIN, + + VIN_REF_PIN = PORT_B << 3, + PWR_MOSI_PIN, + PWR_MISO_PIN, + SHUNT_PIN, + + MOTOR_PIN = PORT_C << 3, // IN1 + PWR_SCK_PIN, + PC2_PIN, // NC + PWR_RESET, + LOAD1_PIN, // IN3 + LOAD2_PIN, // IN4 +}; + + +// ADC +enum { + CS1_ADC, // Motor current + CS2_ADC, // Vdd current + CS3_ADC, // Load 1 current + CS4_ADC, // Load 2 current + VOUT_ADC, // Motor voltage + VIN_ADC, // Input voltage + NC6_ADC, + NC7_ADC, + NC8_ADC, + NC9_ADC, + NC10_ADC, + NC11_ADC, + NC12_ADC, + NC13_ADC, + TEMP_ADC, // Temperature +}; #define I2C_ADDR 0x60 #define I2C_MASK 0b00000111 @@ -79,10 +98,21 @@ typedef enum { MOTOR_REG, LOAD1_REG, LOAD2_REG, + VDD_REG, NUM_REGS } regs_t; +static const uint8_t ch_schedule[] = { + TEMP_ADC, VOUT_ADC, + VIN_ADC, VOUT_ADC, + CS1_ADC, VOUT_ADC, + CS2_ADC, VOUT_ADC, + CS3_ADC, VOUT_ADC, + CS4_ADC, VOUT_ADC, + 0 +}; + volatile uint16_t regs[NUM_REGS] = {0}; @@ -134,7 +164,7 @@ ISR(TWI_SLAVE_vect) { inline static uint16_t convert_voltage(uint16_t sample) { #define VREF 1.1 -#define VR1 34800 +#define VR1 34800 // TODO v10 will have 37.4k #define VR2 1000 return sample * (VREF / 1024.0 * (VR1 + VR2) / VR2 * 100); @@ -146,62 +176,30 @@ inline static uint16_t convert_current(uint16_t sample) { } -void adc_conversion() { - regs[VOUT_REG] = convert_voltage(ADC); - - // Start next conversion - ADMUX = (ADMUX & 0xf0) | VOUT_ADC; - ADCSRA |= 1 << ADSC; - -#if 0 +static void read_conversion(uint8_t ch) { uint16_t data = ADC; - uint8_t ch = ADMUX & 0xf; switch (ch) { - case TEMP_ADC: - regs[TEMP_REG] = data; // Temp in Kelvin - ch = VIN_ADC; - break; - - case VIN_ADC: - regs[VIN_REG] = convert_voltage(data); - ch = VOUT_ADC; - break; - - case VOUT_ADC: - regs[VOUT_REG] = convert_voltage(data); - ch = MOTOR_ADC; - break; - - case MOTOR_ADC: - regs[MOTOR_REG] = convert_current(data); - ch = LOAD2_ADC; - break; - -#if 0 - ch = LOAD1_ADC; - break; - - case LOAD1_ADC: - regs[LOAD1_REG] = convert_current(data); - ch = LOAD2_ADC; - break; -#endif - - case LOAD2_ADC: - regs[LOAD2_REG] = convert_current(data); - ch = TEMP_ADC; - break; - - default: - ch = TEMP_ADC; - break; + 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); break; + case CS1_ADC: regs[MOTOR_REG] = convert_current(data); break; + case CS2_ADC: regs[VDD_REG] = convert_current(data); break; + case CS3_ADC: regs[LOAD1_REG] = convert_current(data); break; + case CS4_ADC: regs[LOAD2_REG] = convert_current(data); break; } +} + + +void adc_conversion() { + static int8_t i = 0; + + read_conversion(ch_schedule[i]); + if (!ch_schedule[++i]) i = 0; // Start next conversion - ADMUX = (ADMUX & 0xf0) | ch; + ADMUX = (ADMUX & 0xf0) | ch_schedule[i]; ADCSRA |= 1 << ADSC; -#endif } @@ -220,16 +218,18 @@ void init() { // Power reduction PRR = (0 << PRADC) | (1 << PRUSART0) | (1 << PRUSART1) | (1 << PRUSI) | - (0 << PRTIM0) | (1 << PRTIM1) | (0 << PRTWI); + (0 << PRTIM0) | (0 << PRTIM1) | (0 << PRTWI); // IO - GATE_DDR = (1 << GATE1_PIN) | (1 << SHUNT_PIN) | (1 << GATE3_PIN); // Out - PUEC = 1 << 3; // Pull up reset line + IO_DDR_SET(MOTOR_PIN); // Output + IO_DDR_SET(LOAD1_PIN); // Output + IO_DDR_SET(LOAD2_PIN); // Output + IO_PUE_SET(PWR_RESET); // Pull up reset line // Disable digital IO on ADC lines - DIDR0 = (1 << ADC3D) | (1 << ADC2D) | (1 << ADC0D); + DIDR0 = (1 << ADC4D) | (1 << ADC3D) | (1 << ADC2D)| (1 << ADC1D) | + (1 << ADC0D) | (1 << AREFD); DIDR1 = (1 << ADC5D); - DIDR2 = (1 << ADC11D); // ADC internal 1.1v, enable, with interrupt, prescale 128 ADMUX = (1 << REFS1) | (0 << REFS0); @@ -237,10 +237,15 @@ void init() { (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); ADCSRB = 0; - // Timer (Clear output A on compare match, Fast PWM, disabled) + // Timer 0 (Clear output A on compare match, Fast PWM, disabled) TCCR0A = (1 << COM0A1) | (0 << COM0A0) | (1 << WGM01) | (1 << WGM00); TCCR0B = 0 << WGM02; - OCR0A = 255 * 0.2; // Initial duty cycle + + // Timer 1 (Set output A on compare match, Fast PWM, 8-bit, no prescale) + OCR1A = 0; // Off + TCCR1A = (1 << COM1A1) | (1 << COM1A0) | (0 << WGM11) | (1 << WGM10); + TCCR1B = + (0 << WGM13) | (1 << WGM12) | (0 << CS12) | (0 << CS11) | (1 << CS10); // I2C, enable, enable address/stop interrupt TWSCRA = (1 << TWEN) | (1 << TWASIE) | (1 << TWDIE); @@ -268,42 +273,87 @@ int main() { // Start ADC adc_conversion(); - // Delayed start - _delay_ms(100); + // Validate input voltage + int settle = 0; + float vlast = 0; - // Enable timer with clk/8 - TCCR0B |= (0 << CS02) | (1 << CS01) | (0 << CS00); + while (settle < 5) { + wdt_reset(); + _delay_ms(20); + + float vin = get_reg(VIN_REG); + + // Check that voltage is with in range and settled + if (11 < vin && vin < 39 && vlast * 0.98 < vin && vin < vlast * 1.02) + settle++; + else settle = 0; + + vlast = vin; + } + // Charge caps + OCR0A = 255 * 0.2; // Cap charging duty cycle + TCCR0B |= (0 << CS02) | (1 << CS01) | (0 << CS00); // Enable timer with clk/8 _delay_ms(200); + TCCR0A = 0; // Clock off + IO_PORT_SET(MOTOR_PIN); // Motor voltage on + + _delay_ms(50); // Wait for final charge + + // Measure nominal voltage + float vnom = 0; + settle = 0; + while (settle < 5) { + wdt_reset(); + _delay_ms(20); + + float vout = get_reg(VOUT_REG); + + // Check that voltages are with in range and vout has settled + if (11 < vout && vout < 39 && vout * 0.98 < vnom && vnom < vout * 1.02) + settle++; + else settle = 0; + + vnom = vout; + } + + if (36 < vnom) vnom = 36; // TODO remove this when R27 is updated bool shunt = false; - float vNom = get_reg(VOUT_REG); while (true) { wdt_reset(); - OCR0A = 0xff; // 100% duty cycle + float vout = get_reg(VOUT_REG); - if (!shunt && vNom + 3 < get_reg(VOUT_REG)) { + if (!shunt && vnom + 2 < vout) { shunt = true; - PORTC |= (1 << SHUNT_PIN); + IO_DDR_SET(SHUNT_PIN); // Enable output - } else if (shunt && get_reg(VOUT_REG) < vNom + 1) { - PORTC &= ~(1 << SHUNT_PIN); + } else if (shunt && vout < vnom + 1) { + IO_DDR_CLR(SHUNT_PIN); // Disable output shunt = false; } + + if (shunt) { + float duty = (vout - vnom - 1) / 4; + if (1 < duty) OCR1A = 0xff; + else OCR1A = 0xff * duty; + } + continue; - if (30 < get_reg(VIN_REG) || get_reg(VIN_REG) < 11) { - OCR0A = 0; // 0% duty cycle - GATE_PORT &= ~(1 << GATE1_PIN); + if (39 < get_reg(VIN_REG) || get_reg(VIN_REG) < 11) { + IO_PORT_CLR(MOTOR_PIN); _delay_ms(3000); + IO_PORT_SET(MOTOR_PIN); // Motor voltage on } if (10 < get_reg(MOTOR_REG)) { - OCR0A = 0; // 0% duty cycle - TCCR0A = 0; - GATE_DDR = 0; + IO_PORT_CLR(MOTOR_PIN); + IO_PORT_CLR(LOAD1_PIN); + IO_PORT_CLR(LOAD2_PIN); _delay_ms(1000); + IO_PORT_SET(MOTOR_PIN); // Motor voltage on } } diff --git a/src/pwr/pins.c b/src/pwr/pins.c new file mode 100644 index 0000000..4c20bbb --- /dev/null +++ b/src/pwr/pins.c @@ -0,0 +1,31 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2017 Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "pins.h" + + +uint16_t io_base[] = {0xf, 0xb, 0x7}; diff --git a/src/pwr/pins.h b/src/pwr/pins.h new file mode 100644 index 0000000..e486403 --- /dev/null +++ b/src/pwr/pins.h @@ -0,0 +1,63 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2017 Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +enum {PORT_A = 1, PORT_B, PORT_C}; +enum {IO_REG_PIN, IO_REG_DDR, IO_REG_PORT, IO_REG_PUE}; + +#define IO_REG(PIN, REG) _SFR_IO8(io_base[((PIN) >> 3) - 1] + (REG)) +#define BM(PIN) (1 << ((PIN) & 7)) + +#include + +extern uint16_t io_base[]; + +#define IO_REG_GET(PIN, REG) (!!(IO_REG(PIN, REG) & BM(PIN))) +#define IO_REG_SET(PIN, REG) do {IO_REG(PIN, REG) |= BM(PIN);} while (0) +#define IO_REG_CLR(PIN, REG) do {IO_REG(PIN, REG) &= ~BM(PIN);} while (0) +#define IO_REG_TGL(PIN, REG) do {IO_REG(PIN, REG) ^= BM(PIN);} while (0) + +#define IO_PIN_GET(PIN) IO_REG_GET(PIN, IO_REG_PIN) +#define IO_PIN_SET(PIN) IO_REG_SET(PIN, IO_REG_PIN) +#define IO_PIN_CLR(PIN) IO_REG_CLR(PIN, IO_REG_PIN) +#define IO_PIN_TGL(PIN) IO_REG_TGL(PIN, IO_REG_PIN) + +#define IO_DDR_GET(PIN) IO_REG_GET(PIN, IO_REG_DDR) +#define IO_DDR_SET(PIN) IO_REG_SET(PIN, IO_REG_DDR) +#define IO_DDR_CLR(PIN) IO_REG_CLR(PIN, IO_REG_DDR) +#define IO_DDR_TGL(PIN) IO_REG_TGL(PIN, IO_REG_DDR) + +#define IO_PORT_GET(PIN) IO_REG_GET(PIN, IO_REG_PORT) +#define IO_PORT_SET(PIN) IO_REG_SET(PIN, IO_REG_PORT) +#define IO_PORT_CLR(PIN) IO_REG_CLR(PIN, IO_REG_PORT) +#define IO_PORT_TGL(PIN) IO_REG_TGL(PIN, IO_REG_PORT) + +#define IO_PUE_GET(PIN) IO_REG_GET(PIN, IO_REG_PUE) +#define IO_PUE_SET(PIN) IO_REG_SET(PIN, IO_REG_PUE) +#define IO_PUE_CLR(PIN) IO_REG_CLR(PIN, IO_REG_PUE) +#define IO_PUE_TGL(PIN) IO_REG_TGL(PIN, IO_REG_PUE) -- 2.27.0