User Tools

Site Tools


info:feb_2014_bms_using_avr

This is an old revision of the document!


AVR BMS

   // 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