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