initial commit
This commit is contained in:
commit
18cb2f29c7
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
*.o
|
||||
*.elf
|
||||
*.bin
|
||||
*.hex
|
||||
*.lst
|
||||
*.map
|
51
Makefile
Normal file
51
Makefile
Normal file
@ -0,0 +1,51 @@
|
||||
PRG = lipo-charger
|
||||
OBJ = lipo-charger.o
|
||||
MCU_TARGET = atmega8
|
||||
OPTIMIZE = -Os
|
||||
|
||||
DEFS =
|
||||
LIBS =
|
||||
|
||||
# You should not have to change anything below here.
|
||||
|
||||
CC = avr-gcc
|
||||
|
||||
# Override is only needed by avr-lib build system.
|
||||
|
||||
override CFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS)
|
||||
override LDFLAGS = -Wl,-Map,$(PRG).map
|
||||
|
||||
OBJCOPY = avr-objcopy
|
||||
OBJDUMP = avr-objdump
|
||||
SIZE = avr-size
|
||||
|
||||
all: $(PRG).elf lst text
|
||||
$(SIZE) -x -A $(PRG).elf
|
||||
|
||||
$(PRG).elf: $(OBJ)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -rf *.o *.lst *.map $(PRG).elf *.hex *.bin
|
||||
|
||||
lst: $(PRG).lst
|
||||
|
||||
%.lst: %.elf
|
||||
$(OBJDUMP) -h -S $< > $@
|
||||
|
||||
# Rules for building the .text rom images
|
||||
|
||||
text: hex bin
|
||||
|
||||
hex: $(PRG).hex
|
||||
bin: $(PRG).bin
|
||||
|
||||
%.hex: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -O ihex $< $@
|
||||
|
||||
%.bin: %.elf
|
||||
$(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
|
354
lipo-charger.c
Normal file
354
lipo-charger.c
Normal file
@ -0,0 +1,354 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 09/2007 by Olaf Rempel *
|
||||
* razzor@kopf-tisch.de *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; version 2 of the License, *
|
||||
* *
|
||||
* This program 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 General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
|
||||
#define F_CPU 8000000
|
||||
#include <util/delay.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define BAUDRATE 19200
|
||||
#define UART_CALC_BAUDRATE(baudRate) ((uint32_t)(F_CPU) / ((uint32_t)(baudRate)*16) -1)
|
||||
|
||||
/*
|
||||
* 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))
|
||||
|
||||
/*
|
||||
* power supply:
|
||||
* - CH0 - voltage with 10:1 prescaler
|
||||
* - CH1 - current with 10mA -> 1mA
|
||||
* - PWM - high-active output to buckconverter
|
||||
*/
|
||||
#define CH0 PORTC0
|
||||
#define CH1 PORTC1
|
||||
#define PWM PORTB1
|
||||
|
||||
#define NOP asm volatile ("nop")
|
||||
|
||||
#define MOD_WAITING 0x00
|
||||
#define MOD_CHARGING 0x01
|
||||
#define MOD_READY 0x02
|
||||
|
||||
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) {
|
||||
current = ADCW * 25;
|
||||
ADMUX = CH0;
|
||||
|
||||
} else {
|
||||
voltage = ADCW * 15;
|
||||
ADMUX = CH1;
|
||||
}
|
||||
|
||||
ADCSRA |= (1<<ADSC);
|
||||
}
|
||||
|
||||
static uint8_t lcd_update;
|
||||
static uint8_t mode = MOD_WAITING;
|
||||
static uint8_t pwm;
|
||||
|
||||
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.6V
|
||||
*/
|
||||
if (mode == MOD_CHARGING) {
|
||||
if (voltage < 12425 && current < 15500)
|
||||
if (pwm < 0xff)
|
||||
pwm++;
|
||||
|
||||
if (voltage > 12450 || current > 16000)
|
||||
if (pwm > 0x00)
|
||||
pwm--;
|
||||
} else {
|
||||
pwm = 0;
|
||||
}
|
||||
|
||||
if (pwm > 0) {
|
||||
OCR1A = pwm;
|
||||
TCCR1A |= (1<<COM1A1);
|
||||
} else {
|
||||
TCCR1A &= ~(1<<COM1A1);
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case MOD_WAITING:
|
||||
/*
|
||||
* start charging when a voltage > 9V is detected (lipo connected)
|
||||
*/
|
||||
if (voltage > 9000)
|
||||
mode = MOD_CHARGING;
|
||||
break;
|
||||
|
||||
case MOD_CHARGING:
|
||||
/*
|
||||
* end charging if voltage > 12.42V and current < 200mA
|
||||
*/
|
||||
if (voltage > 12425 && current < 2000)
|
||||
mode = MOD_READY;
|
||||
break;
|
||||
|
||||
case MOD_READY:
|
||||
/* wait for lipo disconnect */
|
||||
if (voltage < 1000)
|
||||
mode = MOD_WAITING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
DDRB = (1<<CS) | (1<<RS) | (1<<PWM);
|
||||
DDRD = (LCD_DATA_MASK) | (1<<RW);
|
||||
|
||||
/* Set baud rate 19200 */
|
||||
UBRRH = (UART_CALC_BAUDRATE(BAUDRATE)>>8) & 0xFF;
|
||||
UBRRL = (UART_CALC_BAUDRATE(BAUDRATE) & 0xFF);
|
||||
|
||||
/* USART: rx/tx enable, 19200, 8n1 */
|
||||
UCSRB = (1<<TXEN) | (1<<RXEN);
|
||||
UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);
|
||||
|
||||
/* timer0: running with F_CPU/64 (125kHz) */
|
||||
TCCR0 = (1<<CS01) | (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 ADC with interrupts, 125kHz clk */
|
||||
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;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user