User Tools

Site Tools


info:feb_2014_bms_using_avr

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

info:feb_2014_bms_using_avr [2014/02/10 15:13] – created tomgeeinfo:feb_2014_bms_using_avr [2014/02/10 15:14] (current) tomgee
Line 9: Line 9:
 {{:info:bms_diy_schematic_labeled.gif|}} {{:info:bms_diy_schematic_labeled.gif|}}
 {{:info:diy_bms_img.jpg|}} {{:info:diy_bms_img.jpg|}}
-     // 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 <avr/io.h> 
-#include <avr/interrupt.h> 
-#include <util/delay.h> 
-#include <avr/wdt.h> 
- 
-#define ADC_VREF_TYPE 0x40 // AVCC, with capacitor between AREF and GND 
- 
-// 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 HV_SIG (1<<PD4) 
-#define LV_SIG (1<<PD3) 
-#define SHUNT_SIG (1<<PD2) 
- 
-#define TEMP1_IN (1<<PB1); 
-#define TEMP2_IN (1<<PB0); 
- 
-// HV sample enable pins 
-// 1 2 3 4 5 6 7 8 
-// PD6 PC0 PC2 PC4 PC6 PA7 PA5 PA3 
- 
-// Shunt enable pins 
-// 1 2 3 4 5 6 7 8 
-// PD5 PD7 PC1 PC3 PC5 PC7 PA6 PA4 
- 
-// Status LED outputs 
-// 1 2 3 4 5 6 7 8 
-// PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0 
- 
-#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); // Delay needed for the stabilization of the ADC input voltage 
- ADCSRA|=0x40; // Start the AD conversion 
- 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); // Turn sampling off 
- 
- 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, shunts[n]); 
-  
- if (lows[n] || highs[n]) 
- SetLED(n, false); // LED off 
- else if (shunts[n]) 
- SetLED(n, counter&1); // blink 
- else 
- SetLED(n, true); // Constant on = no errors 
-  
- } 
- 
- 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, ADC0 = Temp2, ADC1 = Temp1 
- // 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); // slow iterations down a little, slightly dirty coding 
- } 
-} 
- 
-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<<PD6); break; 
- case 2: PORTC |= (1<<PC0); break; 
- case 3: PORTC |= (1<<PC2); break; 
- case 4: PORTC |= (1<<PC4); break; 
- case 5: PORTC |= (1<<PC6); break; 
- case 6: PORTA |= (1<<PA7); break; 
- case 7: PORTA |= (1<<PA5); break; 
- case 8: PORTA |= (1<<PA3); break; 
- default: return 0; 
- } 
- 
- _delay_ms(1); // Allow filter capacitor to stabilise 
- 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<<PD5); else PORTD &= ~(1<<PD5); break; 
- case 2: if (state) PORTD |= (1<<PD7); else PORTD &= ~(1<<PD7); break; 
- case 3: if (state) PORTC |= (1<<PC1); else PORTC &= ~(1<<PC1); break; 
- case 4: if (state) PORTC |= (1<<PC3); else PORTC &= ~(1<<PC3); break; 
- case 5: if (state) PORTC |= (1<<PC5); else PORTC &= ~(1<<PC5); break; 
- case 6: if (state) PORTC |= (1<<PC7); else PORTC &= ~(1<<PC7); break; 
- case 7: if (state) PORTA |= (1<<PA6); else PORTA &= ~(1<<PA6); break; 
- case 8: if (state) PORTA |= (1<<PA4); else PORTA &= ~(1<<PA4); break; 
- } 
-} 
- 
-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<<PB7); else PORTB &= ~(1<<PB7); break; 
- case 2: if (state) PORTB |= (1<<PB6); else PORTB &= ~(1<<PB6); break; 
- case 3: if (state) PORTB |= (1<<PB5); else PORTB &= ~(1<<PB5); break; 
- case 4: if (state) PORTB |= (1<<PB4); else PORTB &= ~(1<<PB4); break; 
- case 5: if (state) PORTB |= (1<<PB3); else PORTB &= ~(1<<PB3); break; 
- case 6: if (state) PORTB |= (1<<PB2); else PORTB &= ~(1<<PB2); break; 
- case 7: if (state) PORTB |= (1<<PB1); else PORTB &= ~(1<<PB1); break; 
- case 8: if (state) PORTB |= (1<<PB0); else PORTB &= ~(1<<PB0); break; 
- } 
-} 
  
info/feb_2014_bms_using_avr.1392063193.txt.gz · Last modified: 2014/02/10 15:13 by tomgee