info:feb_2014_bms_using_avr
Differences
This shows you the differences between two versions of the page.
info:feb_2014_bms_using_avr [2014/02/10 15:13] – created tomgee | info:feb_2014_bms_using_avr [2014/02/10 15:14] (current) – tomgee | ||
---|---|---|---|
Line 9: | Line 9: | ||
{{: | {{: | ||
{{: | {{: | ||
- | // 8-cell Lithium BMS module | ||
- | // Code for ATmega16A | ||
- | // by Ian Hooper, (C) ZEVA 2012 | ||
- | |||
- | // Fuses: 1Mhz clock, JTAGEN disabled | ||
- | // And brown-out at 4 volts to help with the eeprom? | ||
- | |||
- | #include < | ||
- | #include < | ||
- | #include < | ||
- | #include < | ||
- | |||
- | # | ||
- | |||
- | // division of 10K high : 1.5K low = 0.130 scaling factor | ||
- | // 1V full -> 0.13V scaled -> ADC 26.60 per volt | ||
- | // or, 37.435mV per ADC step | ||
- | #define LV_THRESHOLD 66 // ~2.5V | ||
- | #define SHUNT_THRESHOLD 96 // ~3.6V | ||
- | #define HV_THRESHOLD 106 // ~4.0V | ||
- | |||
- | #define TEMP_THRESHOLD 350 // ~1.75V on the ADC, hopefully about 50 degrees | ||
- | |||
- | # | ||
- | #define LV_SIG (1<< | ||
- | # | ||
- | |||
- | #define TEMP1_IN (1<< | ||
- | # | ||
- | |||
- | // HV sample enable pins | ||
- | // | ||
- | // | ||
- | |||
- | // Shunt enable pins | ||
- | // | ||
- | // | ||
- | |||
- | // Status LED outputs | ||
- | // | ||
- | // | ||
- | |||
- | #ifndef true | ||
- | #define true 1 | ||
- | #define false 0 | ||
- | #endif | ||
- | |||
- | // Function declarations | ||
- | void SetupPorts(); | ||
- | int GetCell(int n); // 0-8 | ||
- | void SetShunt(int n, char state); | ||
- | void SetLED(int n, char state); | ||
- | |||
- | // Global variables | ||
- | int level[9]; | ||
- | char lows[9]; | ||
- | char highs[9]; | ||
- | char shunts[9]; | ||
- | char temps[2]; | ||
- | |||
- | unsigned char counter; | ||
- | |||
- | unsigned short ReadADC(unsigned char adc_input) | ||
- | { | ||
- | ADMUX=adc_input | (ADC_VREF_TYPE & 0xff); | ||
- | _delay_us(10); | ||
- | ADCSRA|=0x40; | ||
- | while ((ADCSRA & 0x10)==0); // Wait for the AD conversion to complete | ||
- | ADCSRA|=0x10; | ||
- | return ADCW; | ||
- | } | ||
- | |||
- | int main(void) | ||
- | { | ||
- | SetupPorts(); | ||
- | wdt_enable(WDTO_250MS); | ||
- | |||
- | level[0] = -16; // Fixed reference point | ||
- | // Opto has a ~0.6V forward drop? | ||
- | // 37mV per step -> 16 extra needed for level zero | ||
- | |||
- | while (1) | ||
- | { | ||
- | wdt_reset(); | ||
- | counter++; | ||
- | |||
- | for (int n=1; n<=8; n++) | ||
- | level[n] = GetCell(n); | ||
- | GetCell(0); | ||
- | |||
- | for (int n=1; n<=8; n++) | ||
- | { | ||
- | int diff = level[n] - level[n-1]; | ||
- | |||
- | // Tiny bit of hysteresis around the threshold value | ||
- | if (diff < LV_THRESHOLD) | ||
- | lows[n] = true; | ||
- | else if (diff > LV_THRESHOLD) | ||
- | lows[n] = false; | ||
- | |||
- | if (diff > HV_THRESHOLD) | ||
- | highs[n] = true; | ||
- | else if (diff < HV_THRESHOLD) | ||
- | highs[n] = false; | ||
- | |||
- | if (diff > SHUNT_THRESHOLD) | ||
- | shunts[n] = true; | ||
- | else if (diff < SHUNT_THRESHOLD) | ||
- | shunts[n] = false; | ||
- | SetShunt(n, | ||
- | |||
- | if (lows[n] || highs[n]) | ||
- | SetLED(n, | ||
- | else if (shunts[n]) | ||
- | SetLED(n, | ||
- | else | ||
- | SetLED(n, | ||
- | |||
- | } | ||
- | |||
- | char low = false; | ||
- | char high = false; | ||
- | char shunt = false; | ||
- | for (int n=1; n<=8; n++) | ||
- | { | ||
- | if (lows[n]) low = true; | ||
- | if (highs[n]) high = true; | ||
- | if (shunts[n]) shunt = true; | ||
- | } | ||
- | |||
- | |||
- | // Temperatures, | ||
- | // Altronics thermistor is about 10K at room temp, i.e ADC 512 will be room temp | ||
- | // Voltage goes DOWN as it gets hotter | ||
- | // 0.68V is about 80degC - TOO HOT | ||
- | // 1.6V is about 45degC - bit hotter is fine | ||
- | // I'm thinking about 1V would be ~60degC which is a good upper limit for shunts and batteries | ||
- | for (int n=0; n<2; n++) | ||
- | { | ||
- | if (ReadADC(n) < TEMP_THRESHOLD-10) | ||
- | temps[n] = true; | ||
- | else if (ReadADC(n) > TEMP_THRESHOLD+10) | ||
- | temps[n] = false; | ||
- | } | ||
- | |||
- | if (temps[0] || temps[1]) | ||
- | { low = true; high = true; } // Shut down both charge and drive | ||
- | |||
- | // Reverse logic, so it holds a daisy chain closed whenever all modules are happy | ||
- | if (low) PORTD &= ~LV_SIG; else PORTD |= LV_SIG;; | ||
- | if (high) PORTD &= ~HV_SIG; else PORTD |= HV_SIG; | ||
- | if (shunt) PORTD &= ~SHUNT_SIG; else PORTD |= SHUNT_SIG; | ||
- | |||
- | _delay_ms(100); | ||
- | } | ||
- | } | ||
- | |||
- | void SetupPorts() | ||
- | { | ||
- | // I/O direction registers; 0 means input, 1 means output | ||
- | DDRA = 0b11111000; | ||
- | DDRB = 0b11111111; | ||
- | DDRC = 0b11111111; | ||
- | DDRD = 0b11111100; | ||
- | |||
- | ADCSRA = 0b10000011; // ADC clock prescaler is /8 with 011 as final bits, ADC clock should be between 50khz and 200khz and we're running 1Mhz uC clock | ||
- | } | ||
- | |||
- | int GetCell(int n) | ||
- | { | ||
- | // Turn all HV sampler pins off | ||
- | PORTA &= ~0b10101000; | ||
- | PORTC &= ~0b01010101; | ||
- | PORTD &= ~0b01000000; | ||
- | |||
- | switch (n) // Turn selected sampler on | ||
- | { | ||
- | case 1: PORTD |= (1<< | ||
- | case 2: PORTC |= (1<< | ||
- | case 3: PORTC |= (1<< | ||
- | case 4: PORTC |= (1<< | ||
- | case 5: PORTC |= (1<< | ||
- | case 6: PORTA |= (1<< | ||
- | case 7: PORTA |= (1<< | ||
- | case 8: PORTA |= (1<< | ||
- | default: return 0; | ||
- | } | ||
- | |||
- | _delay_ms(1); | ||
- | return ReadADC(2); // TODO: We may need to acount for optotransistor voltage drop here too | ||
- | } | ||
- | |||
- | void SetShunt(int n, char state) // state should be on or off / true or false | ||
- | { | ||
- | switch (n) | ||
- | { | ||
- | case 1: if (state) PORTD |= (1<< | ||
- | case 2: if (state) PORTD |= (1<< | ||
- | case 3: if (state) PORTC |= (1<< | ||
- | case 4: if (state) PORTC |= (1<< | ||
- | case 5: if (state) PORTC |= (1<< | ||
- | case 6: if (state) PORTC |= (1<< | ||
- | case 7: if (state) PORTA |= (1<< | ||
- | case 8: if (state) PORTA |= (1<< | ||
- | } | ||
- | } | ||
- | |||
- | void SetLED(int n, char state) // state should be on or off / true or false | ||
- | { | ||
- | switch (n) // TODO: faster algorithm | ||
- | { | ||
- | case 1: if (state) PORTB |= (1<< | ||
- | case 2: if (state) PORTB |= (1<< | ||
- | case 3: if (state) PORTB |= (1<< | ||
- | case 4: if (state) PORTB |= (1<< | ||
- | case 5: if (state) PORTB |= (1<< | ||
- | case 6: if (state) PORTB |= (1<< | ||
- | case 7: if (state) PORTB |= (1<< | ||
- | case 8: if (state) PORTB |= (1<< | ||
- | } | ||
- | } | ||
info/feb_2014_bms_using_avr.1392063193.txt.gz · Last modified: 2014/02/10 15:13 by tomgee