Working version
This commit is contained in:
commit
f6e28df0b1
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
*.o
|
||||||
|
*.elf
|
||||||
|
*.bin
|
||||||
|
*.hex
|
||||||
|
*.lst
|
||||||
|
*.map
|
55
Makefile
Normal file
55
Makefile
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
PRG = powermeter
|
||||||
|
OBJ = main.o
|
||||||
|
MCU_TARGET = attiny26
|
||||||
|
OPTIMIZE = -Os
|
||||||
|
|
||||||
|
AVRDUDE_PROG = -c avr910 -b 115200 -P /dev/ttyUSB0
|
||||||
|
#AVRDUDE_PROG = -c dragon_isp -P usb
|
||||||
|
AVRDUDE_MCU = attiny26
|
||||||
|
|
||||||
|
DEFS =
|
||||||
|
LIBS =
|
||||||
|
|
||||||
|
# Override is only needed by avr-lib build system.
|
||||||
|
override CFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS)
|
||||||
|
override LDFLAGS = -Wl,-Map,$(PRG).map
|
||||||
|
|
||||||
|
CC = avr-gcc
|
||||||
|
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)
|
||||||
|
|
||||||
|
%.o: %.c $(MAKEFILE_LIST)
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.o $(PRG).lst $(PRG).map $(PRG).elf $(PRG).hex $(PRG).bin
|
||||||
|
|
||||||
|
lst: $(PRG).lst
|
||||||
|
|
||||||
|
%.lst: %.elf
|
||||||
|
$(OBJDUMP) -h -S $< > $@
|
||||||
|
|
||||||
|
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
|
||||||
|
avrdude $(AVRDUDE_PROG) -p $(AVRDUDE_MCU) -V -U flash:w:$(PRG).hex
|
||||||
|
|
||||||
|
fuses:
|
||||||
|
avrdude $(AVRDUDE_PROG) -p $(AVRDUDE_MCU) -U hfuse:w:0x14:m
|
||||||
|
avrdude $(AVRDUDE_PROG) -p $(AVRDUDE_MCU) -U lfuse:w:0xe4:m
|
550
main.c
Normal file
550
main.c
Normal file
@ -0,0 +1,550 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 11/2012 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>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#include <avr/eeprom.h>
|
||||||
|
#include <util/crc16.h>
|
||||||
|
|
||||||
|
#define F_CPU 8000000
|
||||||
|
#include <util/delay.h>
|
||||||
|
|
||||||
|
#define OSCCAL_VALUE 0xA4
|
||||||
|
|
||||||
|
/*
|
||||||
|
* attiny26
|
||||||
|
* lfuse: 0xe4 (internal 8MHz)
|
||||||
|
* hfuse: 0x14 (BOD enabled)
|
||||||
|
*
|
||||||
|
* PA0 => current sense (0-2.56V => 0-25.6A)
|
||||||
|
* PA1 => volatage sense (0-2.56V => 0-30.72V)
|
||||||
|
* PA2 => mode button (low active)
|
||||||
|
* PA3 => ext. 2.56V reference
|
||||||
|
* PA4-7 => LCD D4-7
|
||||||
|
* PB0 => MOSI (ISP)
|
||||||
|
* PB1 => MISO (ISP)
|
||||||
|
* PB2 => SCK (ISP)
|
||||||
|
* PB3 => free
|
||||||
|
* PB4 => LCD RS
|
||||||
|
* PB5 => LCD RW
|
||||||
|
* PB6 => LCD EN
|
||||||
|
* PB7 => /RST (ISP)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LCD_DATA_MASK 0xF0
|
||||||
|
#define LCD_RS PORTB4
|
||||||
|
#define LCD_RW PORTB5
|
||||||
|
#define LCD_EN PORTB6
|
||||||
|
#define BUTTON PORTA2
|
||||||
|
|
||||||
|
#define ADC_CURRENT 0
|
||||||
|
#define ADC_VOLTAGE 1
|
||||||
|
#define ADC_COMPLETE 0xFE
|
||||||
|
#define ADC_IDLE 0xFF
|
||||||
|
|
||||||
|
/* 8ms @8MHz / 256 */
|
||||||
|
#define TIMER_TICK_RELOAD (256 - 250)
|
||||||
|
|
||||||
|
/* 25 * 8ms => 200ms */
|
||||||
|
#define TIMER_LCD_UPDATE 25
|
||||||
|
|
||||||
|
/* 125 * 8ms => 1s */
|
||||||
|
#define TIMER_SECOND 125
|
||||||
|
|
||||||
|
/* autosave every minute */
|
||||||
|
#define TIMER_NVRAM_SAVE 60
|
||||||
|
|
||||||
|
/* bargraph 20 - 28V */
|
||||||
|
#define VOLTAGE_BAR_MIN 2000
|
||||||
|
#define VOLTAGE_BAR_MAX 2800
|
||||||
|
|
||||||
|
/* bargraph 0 - 20A */
|
||||||
|
#define CURRENT_BAR_MIN 0
|
||||||
|
#define CURRENT_BAR_MAX 2000
|
||||||
|
|
||||||
|
/* minimum discharge current 100mA */
|
||||||
|
#define CURRENT_IDLE 10
|
||||||
|
|
||||||
|
|
||||||
|
#define BUTTON_TIMER_IDLE 0xFF
|
||||||
|
|
||||||
|
#define EVENT_NONE 0
|
||||||
|
#define EVENT_BUTTON_PRESSED 1
|
||||||
|
#define EVENT_BUTTON_RELEASED 2
|
||||||
|
#define EVENT_BUTTON_TIMEOUT 3
|
||||||
|
|
||||||
|
#define STATE_IDLE 0
|
||||||
|
#define STATE_PRESSED 1
|
||||||
|
#define STATE_WARNING 2
|
||||||
|
|
||||||
|
struct _nvdata {
|
||||||
|
uint8_t nvram_size; /* first */
|
||||||
|
uint16_t discharge_time;
|
||||||
|
uint32_t discharge_product;
|
||||||
|
uint16_t nvram_crc; /* last */
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t nvram_write_pos;
|
||||||
|
static struct _nvdata nvram_data;
|
||||||
|
static struct _nvdata nvram_eeprom EEMEM;
|
||||||
|
static struct _nvdata nvram_defaults PROGMEM = { 0 };
|
||||||
|
|
||||||
|
/* create crc and store nvram data to eeprom */
|
||||||
|
static void nvram_start_write(void)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
uint16_t crc = 0x0000;
|
||||||
|
uint8_t *tmp = (uint8_t *)&nvram_data;
|
||||||
|
|
||||||
|
/* write in progress? */
|
||||||
|
if (EECR & (1<<EEWE))
|
||||||
|
return;
|
||||||
|
|
||||||
|
nvram_data.nvram_size = sizeof(struct _nvdata);
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(struct _nvdata) -2; i++) {
|
||||||
|
crc = _crc_ccitt_update(crc, *tmp++);
|
||||||
|
}
|
||||||
|
|
||||||
|
nvram_data.nvram_crc = crc;
|
||||||
|
nvram_write_pos = 0;
|
||||||
|
|
||||||
|
EEAR = nvram_write_pos;
|
||||||
|
EEDR = ((uint8_t *)&nvram_data)[nvram_write_pos++];
|
||||||
|
cli();
|
||||||
|
EECR |= (1<<EEMWE);
|
||||||
|
EECR |= (1<<EEWE);
|
||||||
|
EECR |= (1<<EERIE);
|
||||||
|
sei();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store nvram data to eeprom */
|
||||||
|
ISR(EE_RDY_vect) {
|
||||||
|
if (nvram_write_pos < sizeof(struct _nvdata)) {
|
||||||
|
EEAR = nvram_write_pos;
|
||||||
|
EEDR = ((uint8_t *)&nvram_data)[nvram_write_pos++];
|
||||||
|
EECR |= (1<<EEMWE);
|
||||||
|
EECR |= (1<<EEWE);
|
||||||
|
EECR |= (1<<EERIE);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
EECR &= ~(1<<EERIE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read nvram from eeprom and check crc */
|
||||||
|
static void nvram_read(void)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
uint16_t crc = 0x0000;
|
||||||
|
uint8_t *tmp = (uint8_t *)&nvram_data;
|
||||||
|
|
||||||
|
eeprom_read_block(&nvram_data, &nvram_eeprom, sizeof(struct _nvdata));
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(struct _nvdata); i++) {
|
||||||
|
crc = _crc_ccitt_update(crc, *tmp++);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if nvram content is invalid, overwrite with defaults */
|
||||||
|
if ((nvram_data.nvram_size != sizeof(struct _nvdata)) || (crc != 0x0000)) {
|
||||||
|
memcpy_P(&nvram_data, &nvram_defaults, sizeof(struct _nvdata));
|
||||||
|
nvram_start_write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PROGMEM uint8_t bargraph[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x1c, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x1e, 0x1e, 0x1e, 0x1e, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t lcd_port_write(uint8_t data)
|
||||||
|
{
|
||||||
|
uint8_t retval;
|
||||||
|
|
||||||
|
/* keep button pullup */
|
||||||
|
PORTA = (data & LCD_DATA_MASK) | (1<<BUTTON);
|
||||||
|
|
||||||
|
asm volatile("NOP");
|
||||||
|
asm volatile("NOP");
|
||||||
|
|
||||||
|
PORTB |= (1<<LCD_EN);
|
||||||
|
asm volatile("NOP");
|
||||||
|
asm volatile("NOP");
|
||||||
|
retval = (PINA & LCD_DATA_MASK);
|
||||||
|
PORTB &= ~(1<<LCD_EN);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t lcd_read(uint8_t reg)
|
||||||
|
{
|
||||||
|
uint8_t retval;
|
||||||
|
|
||||||
|
if (reg) {
|
||||||
|
PORTB |= (1<<LCD_RS);
|
||||||
|
} else {
|
||||||
|
PORTB &= ~(1<<LCD_RS);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORTB |= (1<<LCD_RW);
|
||||||
|
DDRA &= ~(LCD_DATA_MASK);
|
||||||
|
|
||||||
|
retval = lcd_port_write(0x00);
|
||||||
|
asm volatile("NOP");
|
||||||
|
asm volatile("NOP");
|
||||||
|
retval |= (lcd_port_write(0x00) >> 4);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lcd_write_no_busy_check(uint8_t reg, uint8_t data)
|
||||||
|
{
|
||||||
|
if (reg) {
|
||||||
|
PORTB |= (1<<LCD_RS);
|
||||||
|
} else {
|
||||||
|
PORTB &= ~(1<<LCD_RS);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORTB &= ~(1<<LCD_RW);
|
||||||
|
DDRA |= LCD_DATA_MASK;
|
||||||
|
|
||||||
|
lcd_port_write((data & 0xF0));
|
||||||
|
asm volatile("NOP");
|
||||||
|
asm volatile("NOP");
|
||||||
|
lcd_port_write(((data & 0x0F) << 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lcd_write(uint8_t reg, uint8_t data)
|
||||||
|
{
|
||||||
|
lcd_write_no_busy_check(reg, data);
|
||||||
|
while ((lcd_read(0x00) & 0x80));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lcd_print_dec2(uint8_t value)
|
||||||
|
{
|
||||||
|
lcd_write(0x01, '0' + (value / 10));
|
||||||
|
lcd_write(0x01, '0' + (value % 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lcd_print_dec2p2(uint16_t value)
|
||||||
|
{
|
||||||
|
lcd_print_dec2(value / 100);
|
||||||
|
lcd_write(0x01, '.');
|
||||||
|
lcd_print_dec2(value % 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lcd_print_dec2p3(uint16_t value)
|
||||||
|
{
|
||||||
|
lcd_print_dec2(value / 1000);
|
||||||
|
lcd_write(0x01, '.');
|
||||||
|
|
||||||
|
value = value % 1000;
|
||||||
|
|
||||||
|
lcd_write(0x01, '0' + (value / 100));
|
||||||
|
lcd_print_dec2(value % 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lcd_print_time(uint16_t time)
|
||||||
|
{
|
||||||
|
lcd_print_dec2(time / 3600);
|
||||||
|
lcd_write(0x01, ':');
|
||||||
|
|
||||||
|
time = time % 3600;
|
||||||
|
|
||||||
|
lcd_print_dec2(time / 60);
|
||||||
|
lcd_write(0x01, ':');
|
||||||
|
|
||||||
|
lcd_print_dec2(time % 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lcd_bargraph(uint8_t len, uint16_t min, uint16_t max, uint16_t value)
|
||||||
|
{
|
||||||
|
if (value < min)
|
||||||
|
value = min;
|
||||||
|
else if (value > max)
|
||||||
|
value = max;
|
||||||
|
|
||||||
|
/* scale to bargraph length */
|
||||||
|
value = ((value - min) * (len * 5)) / (max - min) ;
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
if (value >= 5) {
|
||||||
|
lcd_write(0x01, 0x05);
|
||||||
|
value -= 5;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
lcd_write(0x01, value);
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lcd_write_stringP(const char *ptr)
|
||||||
|
{
|
||||||
|
while (1) {
|
||||||
|
uint8_t data = pgm_read_byte_near(ptr++);
|
||||||
|
|
||||||
|
if (data == 0x00)
|
||||||
|
break;
|
||||||
|
|
||||||
|
lcd_write(0x01, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static volatile uint8_t adc_state;
|
||||||
|
static volatile uint16_t adc_value[2];
|
||||||
|
|
||||||
|
static void adc_start(uint8_t channel)
|
||||||
|
{
|
||||||
|
adc_state = channel;
|
||||||
|
|
||||||
|
/* ext. 2.56V ref, start, irq enable, F_CPU/64 -> ~225us conversion time */
|
||||||
|
ADMUX = (1<<REFS0) | channel;
|
||||||
|
ADCSR = (1<<ADEN) | (1<<ADSC) | (1<<ADIE) | (1<<ADIF) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ISR(ADC_vect)
|
||||||
|
{
|
||||||
|
uint8_t channel = adc_state;
|
||||||
|
adc_value[channel] = ADCW;
|
||||||
|
|
||||||
|
/* get next channel */
|
||||||
|
if (channel == ADC_VOLTAGE) {
|
||||||
|
adc_start(ADC_CURRENT);
|
||||||
|
|
||||||
|
} else if (channel == ADC_CURRENT) {
|
||||||
|
adc_state = ADC_COMPLETE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static volatile uint8_t timer_tick;
|
||||||
|
|
||||||
|
ISR(TIMER0_OVF0_vect)
|
||||||
|
{
|
||||||
|
TCNT0 = TIMER_TICK_RELOAD;
|
||||||
|
|
||||||
|
/* start sampling both channels */
|
||||||
|
adc_start(ADC_VOLTAGE);
|
||||||
|
|
||||||
|
timer_tick = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) __attribute__ ((noreturn));
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
/* calibrate to 8Mhz */
|
||||||
|
OSCCAL = OSCCAL_VALUE;
|
||||||
|
|
||||||
|
DDRA = LCD_DATA_MASK;
|
||||||
|
DDRB = (1<<LCD_RS) | (1<<LCD_RW) | (1<<LCD_EN) | (1<<PORTB3);
|
||||||
|
|
||||||
|
/* pullup for mode button */
|
||||||
|
PORTA = (1<<BUTTON);
|
||||||
|
|
||||||
|
/* F_CPU/256, overflow irq */
|
||||||
|
TCCR0 = (1<<CS02);
|
||||||
|
TIMSK = (1<<TOV0);
|
||||||
|
|
||||||
|
/* execute reset (according to datasheet) */
|
||||||
|
_delay_ms(50);
|
||||||
|
lcd_write_no_busy_check(0x00, 0x30);
|
||||||
|
_delay_ms(5);
|
||||||
|
lcd_write_no_busy_check(0x00, 0x30);
|
||||||
|
_delay_ms(1);
|
||||||
|
lcd_write_no_busy_check(0x00, 0x30);
|
||||||
|
|
||||||
|
/* switch to 4bit */
|
||||||
|
lcd_write(0x00, 0x28); /* 4bit data bus */
|
||||||
|
/* again, now with valid lower nibble */
|
||||||
|
lcd_write(0x00, 0x28); /* 4bit data bus, 2 lines */
|
||||||
|
|
||||||
|
lcd_write(0x00, 0x0C); /* display on, no cursor, no blinking */
|
||||||
|
lcd_write(0x00, 0x06); /* increase address, no shift */
|
||||||
|
lcd_write(0x00, 0x01); /* clear display */
|
||||||
|
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
lcd_write(0x00, 0x40); /* write CGRAM address 0x00 */
|
||||||
|
for (i = 0; i < sizeof(bargraph); i++) {
|
||||||
|
lcd_write(0x01, pgm_read_byte_near(&bargraph[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sei();
|
||||||
|
nvram_read();
|
||||||
|
|
||||||
|
uint8_t sec_timer = 0;
|
||||||
|
uint8_t lcd_timer = 0;
|
||||||
|
uint8_t button_timer = BUTTON_TIMER_IDLE;
|
||||||
|
|
||||||
|
uint8_t lcd_page = 0x02;
|
||||||
|
uint8_t button_prev = 0;
|
||||||
|
uint8_t button_state = 0;
|
||||||
|
uint8_t nvram_save = 0;
|
||||||
|
|
||||||
|
uint16_t voltage = 0;
|
||||||
|
uint16_t current = 0;
|
||||||
|
|
||||||
|
uint8_t discharging = 0;
|
||||||
|
uint16_t discharge_time = nvram_data.discharge_time;
|
||||||
|
uint32_t discharge_product = nvram_data.discharge_product;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (timer_tick) {
|
||||||
|
timer_tick = 0;
|
||||||
|
|
||||||
|
lcd_timer++;
|
||||||
|
|
||||||
|
if (discharging) {
|
||||||
|
sec_timer++;
|
||||||
|
if (sec_timer == TIMER_SECOND) {
|
||||||
|
sec_timer = 0;
|
||||||
|
discharge_time++;
|
||||||
|
|
||||||
|
/* autosave during discharge */
|
||||||
|
if ((discharge_time % TIMER_NVRAM_SAVE) == 0) {
|
||||||
|
nvram_save = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t button_event = EVENT_NONE;
|
||||||
|
|
||||||
|
uint8_t button = PINA & (1<<BUTTON);
|
||||||
|
if (!button && button_prev) {
|
||||||
|
button_event = EVENT_BUTTON_PRESSED;
|
||||||
|
|
||||||
|
} else if (button && !button_prev) {
|
||||||
|
button_event = EVENT_BUTTON_RELEASED;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (button_timer == 0) {
|
||||||
|
button_event = EVENT_BUTTON_TIMEOUT;
|
||||||
|
button_timer = BUTTON_TIMER_IDLE;
|
||||||
|
|
||||||
|
} else if (button_timer != BUTTON_TIMER_IDLE) {
|
||||||
|
button_timer--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button_prev = button;
|
||||||
|
|
||||||
|
if ((button_state == STATE_IDLE) && (button_event == EVENT_BUTTON_PRESSED)) {
|
||||||
|
lcd_write(0x00, 0x01); /* clear display */
|
||||||
|
lcd_timer = TIMER_LCD_UPDATE;
|
||||||
|
lcd_page ^= 0x01;
|
||||||
|
|
||||||
|
button_state = STATE_PRESSED;
|
||||||
|
button_timer = 250; /* 2s timeout */
|
||||||
|
|
||||||
|
} else if ((button_event == EVENT_BUTTON_TIMEOUT) && (button_state == STATE_PRESSED)) {
|
||||||
|
lcd_write(0x00, 0x01); /* clear display */
|
||||||
|
lcd_write(0x00, 0x80 | 1);
|
||||||
|
lcd_write_stringP(PSTR("reset counter?"));
|
||||||
|
lcd_page = 0;
|
||||||
|
|
||||||
|
button_state = STATE_WARNING;
|
||||||
|
button_timer = 250; /* another 2s timeout */
|
||||||
|
|
||||||
|
} else if (((button_event == EVENT_BUTTON_TIMEOUT) && (button_state == STATE_WARNING)) ||
|
||||||
|
(button_event == EVENT_BUTTON_RELEASED)) {
|
||||||
|
if (button_event == EVENT_BUTTON_TIMEOUT) {
|
||||||
|
discharge_time = 0;
|
||||||
|
discharge_product = 0;
|
||||||
|
nvram_save = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button_state == STATE_WARNING) {
|
||||||
|
lcd_write(0x00, 0x01); /* clear display */
|
||||||
|
lcd_timer = TIMER_LCD_UPDATE;
|
||||||
|
lcd_page = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
button_state = STATE_IDLE;
|
||||||
|
button_timer = BUTTON_TIMER_IDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adc_state == ADC_COMPLETE) {
|
||||||
|
adc_state = ADC_IDLE;
|
||||||
|
|
||||||
|
/* voltage in 10mV increments */
|
||||||
|
voltage = adc_value[ADC_VOLTAGE] * 3;
|
||||||
|
|
||||||
|
/* current in 10mA increments */
|
||||||
|
current = (adc_value[ADC_CURRENT] * 5) >> 1;
|
||||||
|
|
||||||
|
if (discharging) {
|
||||||
|
discharge_product += current;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current >= CURRENT_IDLE) {
|
||||||
|
discharging = 1;
|
||||||
|
|
||||||
|
} else if (sec_timer == 0) {
|
||||||
|
discharging = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lcd_timer == TIMER_LCD_UPDATE) {
|
||||||
|
lcd_timer = 0;
|
||||||
|
|
||||||
|
if (lcd_page) {
|
||||||
|
lcd_write(0x00, 0x80 | 0);
|
||||||
|
lcd_print_dec2p2(voltage);
|
||||||
|
lcd_write(0x01, 'V');
|
||||||
|
|
||||||
|
lcd_write(0x00, 0x80 | 40);
|
||||||
|
lcd_print_dec2p2(current);
|
||||||
|
lcd_write(0x01, 'A');
|
||||||
|
|
||||||
|
if (lcd_page == 2) {
|
||||||
|
lcd_write(0x00, 0x80 | 8);
|
||||||
|
lcd_print_time(discharge_time);
|
||||||
|
|
||||||
|
lcd_write(0x00, 0x80 | 48);
|
||||||
|
|
||||||
|
/* 125 ticks per second, 3600s per hour, 10mA Steps */
|
||||||
|
lcd_print_dec2p3(discharge_product / (TIMER_SECOND * 3600ULL / 10ULL));
|
||||||
|
lcd_write(0x01, 'A');
|
||||||
|
lcd_write(0x01, 'h');
|
||||||
|
|
||||||
|
} else if (lcd_page == 3) {
|
||||||
|
lcd_write(0x00, 0x80 | 7);
|
||||||
|
lcd_bargraph(9, VOLTAGE_BAR_MIN, VOLTAGE_BAR_MAX, voltage);
|
||||||
|
|
||||||
|
lcd_write(0x00, 0x80 | 47);
|
||||||
|
lcd_bargraph(9, CURRENT_BAR_MIN, CURRENT_BAR_MAX, current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nvram_save) {
|
||||||
|
nvram_save = 0;
|
||||||
|
|
||||||
|
nvram_data.discharge_time = discharge_time;
|
||||||
|
nvram_data.discharge_product = discharge_product;
|
||||||
|
nvram_start_write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user