2007-10-03 01:07:05 +02:00
|
|
|
/***************************************************************************
|
|
|
|
* 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
|
|
|
|
|
2007-10-08 00:15:31 +02:00
|
|
|
#define VOLTAGE_CONNECT 9000
|
|
|
|
#define VOLTAGE_CHARGE 12450
|
|
|
|
#define CURRENT_CHARGE 16000
|
|
|
|
#define CURRENT_READY 2000
|
|
|
|
#define VOLTAGE_REMOVE 1000
|
|
|
|
|
2007-10-03 01:07:05 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-10-05 23:18:51 +02:00
|
|
|
/* start ADC again */
|
2007-10-03 01:07:05 +02:00
|
|
|
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
|
2007-10-05 23:18:51 +02:00
|
|
|
* and a current limit of 1.6A
|
2007-10-03 01:07:05 +02:00
|
|
|
*/
|
|
|
|
if (mode == MOD_CHARGING) {
|
2007-10-08 00:15:31 +02:00
|
|
|
if (voltage < (VOLTAGE_CHARGE -25) && current < (CURRENT_CHARGE -500))
|
2007-10-03 01:07:05 +02:00
|
|
|
if (pwm < 0xff)
|
|
|
|
pwm++;
|
|
|
|
|
2007-10-08 00:15:31 +02:00
|
|
|
if (voltage > VOLTAGE_CHARGE || current > CURRENT_CHARGE)
|
2007-10-03 01:07:05 +02:00
|
|
|
if (pwm > 0x00)
|
|
|
|
pwm--;
|
|
|
|
|
|
|
|
OCR1A = pwm;
|
|
|
|
TCCR1A |= (1<<COM1A1);
|
|
|
|
} else {
|
2007-10-05 23:18:51 +02:00
|
|
|
pwm = 0;
|
2007-10-03 01:07:05 +02:00
|
|
|
TCCR1A &= ~(1<<COM1A1);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case MOD_WAITING:
|
2007-10-05 23:18:51 +02:00
|
|
|
/* start charging when a voltage > 9V is detected (lipo connected) */
|
2007-10-08 00:15:31 +02:00
|
|
|
if (voltage > VOLTAGE_CONNECT)
|
2007-10-03 01:07:05 +02:00
|
|
|
mode = MOD_CHARGING;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MOD_CHARGING:
|
2007-10-08 00:15:31 +02:00
|
|
|
/* end charging if voltage > 12.45V and current < 200mA */
|
|
|
|
if (voltage >= VOLTAGE_CHARGE && current < CURRENT_READY)
|
2007-10-03 01:07:05 +02:00
|
|
|
mode = MOD_READY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MOD_READY:
|
|
|
|
/* wait for lipo disconnect */
|
2007-10-08 00:15:31 +02:00
|
|
|
if (voltage < VOLTAGE_REMOVE)
|
2007-10-03 01:07:05 +02:00
|
|
|
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;
|
|
|
|
}
|