tiny24 version
This commit is contained in:
parent
eacfc70af9
commit
cd560f4268
6
Makefile
6
Makefile
@ -1,6 +1,6 @@
|
||||
PRG = lipo-charger
|
||||
OBJ = lipo-charger.o
|
||||
MCU_TARGET = atmega8
|
||||
MCU_TARGET = attiny24
|
||||
OPTIMIZE = -Os
|
||||
|
||||
DEFS =
|
||||
@ -47,5 +47,5 @@ bin: $(PRG).bin
|
||||
$(OBJCOPY) -j .text -j .data -O binary $< $@
|
||||
|
||||
install: text
|
||||
uisp -dprog=avr910 -dserial=/dev/ttyS0 -dspeed=115200 -dpart=M8 --erase --upload if=$(PRG).hex
|
||||
# avrdude -p m8 -c butterfly -b 19200 -P /dev/ttyUSB0 -u -e -U flash:w:$(PRG).hex
|
||||
# uisp -dprog=avr910 -dserial=/dev/ttyS0 -dspeed=115200 -dpart=M8 --erase --upload if=$(PRG).hex
|
||||
avrdude -p t24 -c butterfly -b 115200 -P /dev/ttyUSB0 -u -e -U flash:w:$(PRG).hex
|
||||
|
338
lipo-charger.c
338
lipo-charger.c
@ -22,37 +22,21 @@
|
||||
#define F_CPU 8000000
|
||||
#include <util/delay.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#define ADC_REF PORTA0
|
||||
#define ADC_U PORTA1
|
||||
#define ADC_IM PORTA2
|
||||
#define ADC_IP PORTA3
|
||||
#define SUMMER PORTA7
|
||||
|
||||
#define BAUDRATE 19200
|
||||
#define UART_CALC_BAUDRATE(baudRate) ((uint32_t)(F_CPU) / ((uint32_t)(baudRate)*16) -1)
|
||||
#define LEDRT PORTB0
|
||||
#define LEDGN PORTB1
|
||||
#define PWM PORTB2
|
||||
|
||||
/*
|
||||
* LCD:
|
||||
* - CS Chip Select
|
||||
* - RS Register Select
|
||||
* - RW Read/Write
|
||||
*/
|
||||
#define RS PORTB6
|
||||
#define CS PORTB7
|
||||
#define RW PORTD3
|
||||
#define LCD_D4 PORTD4
|
||||
#define LCD_D5 PORTD5
|
||||
#define LCD_D6 PORTD6
|
||||
#define LCD_D7 PORTD7
|
||||
#define LCD_DATA_MASK ((1<<LCD_D4) | (1<<LCD_D5) | (1<<LCD_D6) | (1<<LCD_D7))
|
||||
/* ADC_U vs GND: voltage with prescaler (12V -> 2V; 15mV -> 2.5mV/bit), ref 2.56V */
|
||||
#define CH_U ((1<<REFS0) | 0x01)
|
||||
|
||||
/*
|
||||
* power supply:
|
||||
* - CH0 - voltage with prescaler (12V -> 2V; 15mV -> 2.5mV/bit)
|
||||
* - CH1 - current with (1A -> 1000mV; 2.5mA -> 2.5mV/bit)
|
||||
* - PWM - high-active output to buckconverter
|
||||
*/
|
||||
#define CH0 PORTC0
|
||||
#define CH1 PORTC1
|
||||
#define PWM PORTB1
|
||||
|
||||
#define NOP asm volatile ("nop")
|
||||
/* ADC_IP vs ADC_IM: current sense (1A -> 100mV; ??mA -> ?mV/bit), gain x20, ref 5V */
|
||||
#define CH_I ((0<<REFS0) | 0x31)
|
||||
|
||||
#define MOD_WAITING 0x00
|
||||
#define MOD_CHARGING 0x01
|
||||
@ -60,173 +44,37 @@
|
||||
|
||||
#define VOLTAGE_CONNECT 9000
|
||||
#define VOLTAGE_CHARGE 12450
|
||||
#define CURRENT_CHARGE 16000
|
||||
#define CURRENT_CHARGE 17500
|
||||
#define CURRENT_READY 2000
|
||||
#define VOLTAGE_REMOVE 1000
|
||||
|
||||
static void lcd_wait_busy(void)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
DDRD &= ~(LCD_DATA_MASK);
|
||||
PORTD |= (1<<RW);
|
||||
|
||||
do {
|
||||
PORTB |= (1<<CS);
|
||||
NOP;
|
||||
NOP;
|
||||
status = (PIND & LCD_DATA_MASK);
|
||||
PORTB &= ~(1<<CS);
|
||||
|
||||
PORTB |= (1<<CS);
|
||||
NOP;
|
||||
NOP;
|
||||
status |= ((PIND & LCD_DATA_MASK) >> 4);
|
||||
PORTB &= ~(1<<CS);
|
||||
|
||||
} while (status & 0x80);
|
||||
|
||||
DDRD |= LCD_DATA_MASK;
|
||||
PORTD &= ~(1<<RW);
|
||||
}
|
||||
|
||||
static void lcd_write_ctrl(uint8_t val)
|
||||
{
|
||||
PORTD &= ~(LCD_DATA_MASK);
|
||||
PORTD |= (val & 0xF0);
|
||||
|
||||
PORTB |= (1<<CS);
|
||||
NOP;
|
||||
NOP;
|
||||
PORTB &= ~(1<<CS);
|
||||
|
||||
PORTD &= ~(LCD_DATA_MASK);
|
||||
PORTD |= ((val<<4) & 0xF0);
|
||||
|
||||
PORTB |= (1<<CS);
|
||||
NOP;
|
||||
NOP;
|
||||
PORTB &= ~(1<<CS);
|
||||
|
||||
lcd_wait_busy();
|
||||
}
|
||||
|
||||
static void lcd_write_data(uint8_t val)
|
||||
{
|
||||
PORTD &= ~(LCD_DATA_MASK);
|
||||
PORTD |= (val & 0xF0);
|
||||
PORTB |= (1<<RS);
|
||||
|
||||
PORTB |= (1<<CS);
|
||||
NOP;
|
||||
NOP;
|
||||
PORTB &= ~(1<<CS);
|
||||
|
||||
PORTD &= ~(LCD_DATA_MASK);
|
||||
PORTD |= ((val<<4) & 0xF0);
|
||||
|
||||
PORTB |= (1<<CS);
|
||||
NOP;
|
||||
NOP;
|
||||
PORTB &= ~(1<<CS);
|
||||
|
||||
PORTB &= ~(1<<RS);
|
||||
|
||||
lcd_wait_busy();
|
||||
}
|
||||
|
||||
/* heartbeat chars */
|
||||
static const uint8_t mychars[] = {
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00,
|
||||
0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static void lcd_init(void)
|
||||
{
|
||||
_delay_ms(25);
|
||||
|
||||
lcd_wait_busy();
|
||||
|
||||
/* 4bit, 2lines, 5x7 font */
|
||||
lcd_write_ctrl(0x28);
|
||||
|
||||
/* display on, cursor off, blink off */
|
||||
lcd_write_ctrl(0x0C);
|
||||
|
||||
/* cursor increments */
|
||||
lcd_write_ctrl(0x06);
|
||||
|
||||
lcd_write_ctrl(0x40 | 0x00);
|
||||
|
||||
uint8_t i;
|
||||
for (i = 0; i < sizeof(mychars); i++)
|
||||
lcd_write_data(mychars[i]);
|
||||
|
||||
/* clear & home pos */
|
||||
lcd_write_ctrl(0x01);
|
||||
}
|
||||
|
||||
static int uart_putchar(char c, FILE *stream)
|
||||
{
|
||||
if (c == '\n')
|
||||
uart_putchar('\r', stream);
|
||||
|
||||
loop_until_bit_is_set(UCSRA, UDRE);
|
||||
UDR = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcd_putchar(char c, FILE *stream)
|
||||
{
|
||||
lcd_write_data(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static FILE lcd = FDEV_SETUP_STREAM(lcd_putchar, NULL, _FDEV_SETUP_WRITE);
|
||||
static FILE log = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
|
||||
|
||||
static uint16_t voltage;
|
||||
static uint16_t current;
|
||||
|
||||
ISR(ADC_vect)
|
||||
{
|
||||
if (ADMUX & 0x01) {
|
||||
if (ADMUX == CH_I) {
|
||||
current = ADCW * 25;
|
||||
ADMUX = CH0;
|
||||
ADMUX = CH_U;
|
||||
|
||||
} else {
|
||||
voltage = ADCW * 15;
|
||||
ADMUX = CH1;
|
||||
ADMUX = CH_I;
|
||||
}
|
||||
}
|
||||
|
||||
ISR(TIM1_OVF_vect)
|
||||
{
|
||||
/* Come back in 1ms */
|
||||
TCNT1 = 0xFFFF - 8000;
|
||||
|
||||
/* start ADC again */
|
||||
ADCSRA |= (1<<ADSC);
|
||||
}
|
||||
|
||||
static uint8_t lcd_update;
|
||||
static uint8_t mode = MOD_WAITING;
|
||||
static uint8_t pwm;
|
||||
static uint8_t mode = MOD_WAITING;
|
||||
|
||||
ISR(TIMER0_OVF_vect)
|
||||
{
|
||||
static uint8_t cnt;
|
||||
|
||||
/* Come back in 1ms */
|
||||
TCNT0 = 0xFF - 125;
|
||||
|
||||
/* update LCD every 250ms */
|
||||
cnt++;
|
||||
if (cnt == 250) {
|
||||
cnt = 0;
|
||||
lcd_update = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* charge with constant voltage of 12.45V
|
||||
* and a current limit of 1.6A
|
||||
*/
|
||||
/* charge with constant voltage and current limit */
|
||||
if (mode == MOD_CHARGING) {
|
||||
if (voltage < (VOLTAGE_CHARGE -25) && current < (CURRENT_CHARGE -500))
|
||||
if (pwm < 0xff)
|
||||
@ -236,27 +84,71 @@ ISR(TIMER0_OVF_vect)
|
||||
if (pwm > 0x00)
|
||||
pwm--;
|
||||
|
||||
OCR1A = pwm;
|
||||
TCCR1A |= (1<<COM1A1);
|
||||
OCR0A = pwm;
|
||||
TCCR0A |= (1<<COM1A1);
|
||||
|
||||
} else {
|
||||
pwm = 0;
|
||||
TCCR1A &= ~(1<<COM1A1);
|
||||
TCCR0A &= ~(1<<COM1A1);
|
||||
}
|
||||
|
||||
static uint16_t led_timer;
|
||||
led_timer = (led_timer +1) & 0x3FF;
|
||||
|
||||
switch (mode) {
|
||||
case MOD_WAITING:
|
||||
/* green flashing, red off */
|
||||
if (led_timer < 0x3F)
|
||||
PORTB &= ~(1<<LEDGN);
|
||||
else
|
||||
PORTB |= (1<<LEDGN) | (1<<LEDRT);
|
||||
|
||||
PORTA |= (1<<SUMMER);
|
||||
|
||||
/* start charging when a voltage > 9V is detected (lipo connected) */
|
||||
if (voltage > VOLTAGE_CONNECT)
|
||||
mode = MOD_CHARGING;
|
||||
|
||||
break;
|
||||
|
||||
case MOD_CHARGING:
|
||||
/* current limit */
|
||||
if (voltage < (VOLTAGE_CHARGE -50)) {
|
||||
/* green on, red fast blinking */
|
||||
if (led_timer & 0x80)
|
||||
PORTB &= ~((1<<LEDGN) | (1<<LEDRT));
|
||||
else
|
||||
PORTB |= (1<<LEDRT);
|
||||
|
||||
/* voltage limit */
|
||||
} else {
|
||||
/* green on, red slow blinking */
|
||||
if (led_timer & 0x200)
|
||||
PORTB &= ~((1<<LEDGN) | (1<<LEDRT));
|
||||
else
|
||||
PORTB |= (1<<LEDRT);
|
||||
}
|
||||
|
||||
PORTA |= (1<<SUMMER);
|
||||
|
||||
/* end charging if voltage > 12.45V and current < 200mA */
|
||||
if (voltage >= VOLTAGE_CHARGE && current < CURRENT_READY)
|
||||
mode = MOD_READY;
|
||||
break;
|
||||
|
||||
case MOD_READY:
|
||||
/* green on, red flashing */
|
||||
if (led_timer < 0x3F)
|
||||
PORTB &= ~((1<<LEDGN) | (1<<LEDRT));
|
||||
else
|
||||
PORTB |= (1<<LEDRT);
|
||||
|
||||
/* beeping */
|
||||
if (led_timer < 0x07)
|
||||
PORTA &= ~(1<<SUMMER);
|
||||
else
|
||||
PORTA |= (1<<SUMMER);
|
||||
|
||||
/* wait for lipo disconnect */
|
||||
if (voltage < VOLTAGE_REMOVE)
|
||||
mode = MOD_WAITING;
|
||||
@ -266,89 +158,29 @@ ISR(TIMER0_OVF_vect)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
DDRB = (1<<CS) | (1<<RS) | (1<<PWM);
|
||||
DDRD = (LCD_DATA_MASK) | (1<<RW);
|
||||
/* digital outputs */
|
||||
DDRA = (1<<SUMMER);
|
||||
DDRB = (1<<LEDGN) | (1<<LEDRT) | (1<<PWM);
|
||||
|
||||
/* Set baud rate 19200 */
|
||||
UBRRH = (UART_CALC_BAUDRATE(BAUDRATE)>>8) & 0xFF;
|
||||
UBRRL = (UART_CALC_BAUDRATE(BAUDRATE) & 0xFF);
|
||||
/* analog inputs */
|
||||
DIDR0 = (1<<ADC_REF) | (1<<ADC_U) | (1<<ADC_IM) | (1<<ADC_IP);
|
||||
|
||||
/* USART: rx/tx enable, 19200, 8n1 */
|
||||
UCSRB = (1<<TXEN) | (1<<RXEN);
|
||||
UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);
|
||||
/* timer0: running with F_CPU, 8bit Fast PWM (32khz) */
|
||||
TCCR0A = (1<<WGM00) | (1<<WGM01);
|
||||
TCCR0B = (1<<CS00);
|
||||
|
||||
/* timer0: running with F_CPU/64 (125kHz) */
|
||||
TCCR0 = (1<<CS01) | (1<<CS00);
|
||||
/* timer1: running with F_CPU */
|
||||
TCCR1B = (1<<CS00);
|
||||
|
||||
/* timer1: running with F_CPU, 8bit Phase Correct PWM (16kHz) */
|
||||
TCCR1A = (1<<WGM10);
|
||||
TCCR1B = (1<<CS10) | (1<<WGM12);
|
||||
|
||||
/* enable Timer0 OVF Interrupt */
|
||||
TIMSK = (1<<TOIE0);
|
||||
|
||||
/* external 2.56V reference, channel 0 */
|
||||
ADMUX = CH0;
|
||||
/* enable iimer1 OVF Interrupt */
|
||||
TIMSK1 = (1<<TOIE1);
|
||||
|
||||
/* enable ADC with interrupts, 125kHz clk */
|
||||
ADMUX = CH_I;
|
||||
ADCSRA = (1<<ADEN) | (1<<ADSC) | (1<<ADIE) | (1<<ADPS2) | (1<<ADPS1);
|
||||
|
||||
sei();
|
||||
|
||||
lcd_init();
|
||||
|
||||
uint8_t step = 0;
|
||||
uint8_t hours = 0, minutes = 0, seconds = 0;
|
||||
|
||||
while (1) {
|
||||
if (lcd_update) {
|
||||
/* first line, first char */
|
||||
lcd_write_ctrl(0x80 | 0x00);
|
||||
fprintf(&lcd, "%2d.%03dV ", voltage / 1000, voltage % 1000);
|
||||
fprintf(&lcd, "%1d.%04dA ", current / 10000, current % 10000);
|
||||
fprintf(&lcd, "0x%02x", pwm);
|
||||
|
||||
step = (step +1) & 0x03;
|
||||
if (step == 0 && mode == MOD_CHARGING) {
|
||||
seconds++;
|
||||
if (seconds == 60) {
|
||||
seconds = 0;
|
||||
minutes++;
|
||||
if (minutes == 60) {
|
||||
minutes = 0;
|
||||
hours++;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(&log, "%02d:%02d:%02d %05d %05d %03d\n",
|
||||
hours, minutes, seconds, voltage, current, pwm);
|
||||
}
|
||||
|
||||
/* second line, first char */
|
||||
lcd_write_ctrl(0x80 | 0x40);
|
||||
fprintf(&lcd, "%02d:%02d:%02d ", hours, minutes, seconds);
|
||||
|
||||
switch (mode) {
|
||||
case MOD_WAITING:
|
||||
fprintf(&lcd, " waiting ");
|
||||
hours = minutes = seconds = 0;
|
||||
break;
|
||||
|
||||
case MOD_CHARGING:
|
||||
fprintf(&lcd, " charging");
|
||||
break;
|
||||
|
||||
case MOD_READY:
|
||||
fprintf(&lcd, " ready ");
|
||||
break;
|
||||
}
|
||||
|
||||
/* second line, last char */
|
||||
lcd_write_ctrl(0x80 | 0x40 | 19);
|
||||
lcd_write_data(step);
|
||||
|
||||
lcd_update = 0;
|
||||
}
|
||||
}
|
||||
while (1);
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user