187 lines
4.6 KiB
C
187 lines
4.6 KiB
C
/***************************************************************************
|
|
* 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>
|
|
|
|
#define ADC_REF PORTA0
|
|
#define ADC_U PORTA1
|
|
#define ADC_IM PORTA2
|
|
#define ADC_IP PORTA3
|
|
#define SUMMER PORTA7
|
|
|
|
#define LEDRT PORTB0
|
|
#define LEDGN PORTB1
|
|
#define PWM PORTB2
|
|
|
|
/* ADC_U vs GND: voltage with prescaler (12V -> 2V; 15mV -> 2.5mV/bit), ref 2.56V */
|
|
#define CH_U ((1<<REFS0) | 0x01)
|
|
|
|
/* 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
|
|
#define MOD_READY 0x02
|
|
|
|
#define VOLTAGE_CONNECT 9000
|
|
#define VOLTAGE_CHARGE 12450
|
|
#define CURRENT_CHARGE 17500
|
|
#define CURRENT_READY 2000
|
|
#define VOLTAGE_REMOVE 1000
|
|
|
|
static uint16_t voltage;
|
|
static uint16_t current;
|
|
|
|
ISR(ADC_vect)
|
|
{
|
|
if (ADMUX == CH_I) {
|
|
current = ADCW * 25;
|
|
ADMUX = CH_U;
|
|
|
|
} else {
|
|
voltage = ADCW * 15;
|
|
ADMUX = CH_I;
|
|
}
|
|
}
|
|
|
|
ISR(TIM1_OVF_vect)
|
|
{
|
|
/* Come back in 1ms */
|
|
TCNT1 = 0xFFFF - 8000;
|
|
|
|
/* start ADC again */
|
|
ADCSRA |= (1<<ADSC);
|
|
|
|
static uint8_t pwm;
|
|
static uint8_t mode = MOD_WAITING;
|
|
|
|
/* charge with constant voltage and current limit */
|
|
if (mode == MOD_CHARGING) {
|
|
if (voltage < (VOLTAGE_CHARGE -25) && current < (CURRENT_CHARGE -500))
|
|
if (pwm < 0xff)
|
|
pwm++;
|
|
|
|
if (voltage > VOLTAGE_CHARGE || current > CURRENT_CHARGE)
|
|
if (pwm > 0x00)
|
|
pwm--;
|
|
|
|
OCR0A = pwm;
|
|
TCCR0A |= (1<<COM1A1);
|
|
|
|
} else {
|
|
pwm = 0;
|
|
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;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
/* digital outputs */
|
|
DDRA = (1<<SUMMER);
|
|
DDRB = (1<<LEDGN) | (1<<LEDRT) | (1<<PWM);
|
|
|
|
/* analog inputs */
|
|
DIDR0 = (1<<ADC_REF) | (1<<ADC_U) | (1<<ADC_IM) | (1<<ADC_IP);
|
|
|
|
/* timer0: running with F_CPU, 8bit Fast PWM (32khz) */
|
|
TCCR0A = (1<<WGM00) | (1<<WGM01);
|
|
TCCR0B = (1<<CS00);
|
|
|
|
/* timer1: running with F_CPU */
|
|
TCCR1B = (1<<CS00);
|
|
|
|
/* 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();
|
|
|
|
while (1);
|
|
return 0;
|
|
}
|