404 lines
6.4 KiB
C
404 lines
6.4 KiB
C
#include <avr/eeprom.h>
|
|
#include <avr/interrupt.h>
|
|
#include <avr/io.h>
|
|
#include <avr/sleep.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#define VALUE_MIN -99999
|
|
#define VALUE_MAX 999999
|
|
|
|
#define PIN_BTN_DEC PINB3
|
|
#define PIN_BTN_INC PINB4
|
|
|
|
#define BTN_FLAG_DEC 0x02
|
|
#define BTN_FLAG_INC 0x01
|
|
|
|
#define DISPLAY_SIZE 6
|
|
|
|
// Time(ms) to wait before sleep
|
|
#define DISPLAY_DELAY 1000
|
|
|
|
// Time(ms) to assume long button press
|
|
#define LONG_PRESS 500
|
|
|
|
// Event queue macros
|
|
#define MAX_EVENT_COUNT 64
|
|
#define PTR_INC(x) ((x) = events + ((((x) - events) + 1) % MAX_EVENT_COUNT))
|
|
|
|
uint8_t events[MAX_EVENT_COUNT];
|
|
uint8_t event_count = 0;
|
|
uint8_t *event_read = events;
|
|
uint8_t *event_write = events;
|
|
|
|
int32_t counter_value = 0;
|
|
uint8_t btn_flag = 0;
|
|
volatile uint32_t ms = 0;
|
|
uint8_t display_buffer[DISPLAY_SIZE];
|
|
|
|
// Shift way (P is DP)
|
|
// GFAB 1->6
|
|
// PCDE 6->1
|
|
// Therefore - EDCPBAFG
|
|
#define DP 0b00010000
|
|
uint8_t seg_table[11] =
|
|
{
|
|
//EDCPBAFG
|
|
0b11101110, // 0
|
|
0b00101000, // 1
|
|
0b11001101, // 2
|
|
0b01101101, // 3
|
|
0b00101011, // 4
|
|
0b01100111, // 5
|
|
0b11100111, // 6
|
|
0b00101100, // 7
|
|
0b11101111, // 8
|
|
0b01101111, // 9
|
|
0b00000001 // -
|
|
};
|
|
|
|
enum event_e
|
|
{
|
|
EVENT_NONE,
|
|
EVENT_DEC_DOWN,
|
|
EVENT_DEC_UP,
|
|
EVENT_INC_DOWN,
|
|
EVENT_INC_UP
|
|
};
|
|
|
|
// ms counter
|
|
ISR(TIM0_COMPA_vect)
|
|
{
|
|
cli();
|
|
ms += 8;
|
|
sei();
|
|
}
|
|
|
|
// Button interrupt
|
|
ISR(PCINT0_vect)
|
|
{
|
|
uint8_t new_btn_flag = 0;
|
|
uint8_t event = EVENT_NONE;
|
|
|
|
cli();
|
|
if (0 == (PINB & (1 << PIN_BTN_DEC)))
|
|
{
|
|
new_btn_flag |= BTN_FLAG_DEC;
|
|
}
|
|
|
|
if (0 == (PINB & (1 << PIN_BTN_INC)))
|
|
{
|
|
new_btn_flag |= BTN_FLAG_INC;
|
|
}
|
|
|
|
if ((new_btn_flag & BTN_FLAG_DEC) != (btn_flag & BTN_FLAG_DEC))
|
|
{
|
|
// Press
|
|
if (new_btn_flag & BTN_FLAG_DEC)
|
|
{
|
|
event = EVENT_DEC_DOWN;
|
|
}
|
|
// Unpress
|
|
else
|
|
{
|
|
event = EVENT_DEC_UP;
|
|
}
|
|
|
|
// Actually put the event in the queue
|
|
if (event_count < MAX_EVENT_COUNT)
|
|
{
|
|
*event_write = event;
|
|
++event_count;
|
|
PTR_INC(event_write);
|
|
}
|
|
}
|
|
|
|
if ((new_btn_flag & BTN_FLAG_INC) != (btn_flag & BTN_FLAG_INC))
|
|
{
|
|
// Press
|
|
if (new_btn_flag & BTN_FLAG_INC)
|
|
{
|
|
event = EVENT_INC_DOWN;
|
|
}
|
|
// Unpress
|
|
else
|
|
{
|
|
event = EVENT_INC_UP;
|
|
}
|
|
|
|
// Actually put the event in the queue
|
|
if (event_count < MAX_EVENT_COUNT)
|
|
{
|
|
*event_write = event;
|
|
++event_count;
|
|
PTR_INC(event_write);
|
|
}
|
|
}
|
|
|
|
btn_flag = new_btn_flag;
|
|
sei();
|
|
}
|
|
|
|
void hard_init()
|
|
{
|
|
// Output Store, Data, Clock
|
|
DDRB = (1 << DDB0) | (1 << DDB1) | (1 << DDB2);
|
|
|
|
// Pull-up Buttons
|
|
PORTB = (1 << PORTB3) | (1 << PORTB4);
|
|
|
|
// Sleep mode - Power down
|
|
MCUCR = (1 << SM1);
|
|
|
|
// Disable power to Timer1, USI, ADC
|
|
PRR = (1 << PRTIM1) | (1 << PRUSI) | (1 << PRADC);
|
|
|
|
// Enable button interrupts
|
|
PCMSK = (1 << PCINT3) | (1 << PCINT4);
|
|
GIMSK = (1 << PCIE);
|
|
|
|
// CTC Mode
|
|
TCCR0A = (1 << WGM01);
|
|
|
|
// 64 Prescaler
|
|
TCCR0B = (1 << CS01) | (1 << CS00);
|
|
|
|
// Interrupt every 8 ms
|
|
OCR0A = 125;
|
|
|
|
// 1 000 000 / 64 = 15625 (Hz)
|
|
// 15625 / 125 = 125 (Hz)
|
|
// 1000 / 125 = 8 (ms)
|
|
|
|
// Enable Timer interrupt
|
|
TIMSK = (1 << OCIE0A);
|
|
|
|
sei();
|
|
}
|
|
|
|
void display_send_bit(uint8_t bit)
|
|
{
|
|
if (0 != bit)
|
|
{
|
|
PORTB |= (1 << PORTB1);
|
|
}
|
|
else
|
|
{
|
|
PORTB &= ~(1 << PORTB1);
|
|
}
|
|
|
|
PORTB |= (1 << PORTB2);
|
|
while(!(PINB & (1 << PINB2)));
|
|
PORTB &= ~(1 << PORTB2);
|
|
while(PINB & (1 << PINB2));
|
|
}
|
|
|
|
void display_send_buffer()
|
|
{
|
|
uint8_t i;
|
|
uint8_t b;
|
|
uint8_t custom_buffer[DISPLAY_SIZE];
|
|
|
|
for (b = 0; b < DISPLAY_SIZE / 2; ++b)
|
|
{
|
|
custom_buffer[b] = ((display_buffer[b * 2] & 0xF0) | ((display_buffer[b * 2 + 1] & 0xF0) >> 4));
|
|
}
|
|
|
|
for (b = 0; b < DISPLAY_SIZE / 2; ++b)
|
|
{
|
|
custom_buffer[b + DISPLAY_SIZE / 2] = (((display_buffer[DISPLAY_SIZE - 1 - b * 2] & 0x0F) << 4) | (display_buffer[DISPLAY_SIZE - 1 - b * 2 - 1] & 0x0F));
|
|
}
|
|
|
|
for (b = 0; b < DISPLAY_SIZE; ++b)
|
|
{
|
|
for (i = 0; i < 8; ++i)
|
|
{
|
|
display_send_bit(((custom_buffer[b]) >> (7 - i)) & 0x01);
|
|
}
|
|
}
|
|
}
|
|
|
|
void display_show()
|
|
{
|
|
PORTB |= (1 << PORTB0);
|
|
while(!(PINB & (1 << PINB0)));
|
|
PORTB &= ~(1 << PORTB0);
|
|
while(PINB & (1 << PINB0));
|
|
}
|
|
|
|
void display_show_value(uint8_t dots)
|
|
{
|
|
uint8_t i;
|
|
uint32_t div_val = 1;
|
|
uint32_t counter_copy;
|
|
uint8_t digit;
|
|
uint8_t txt;
|
|
|
|
for (i = 0; i < DISPLAY_SIZE; ++i)
|
|
{
|
|
display_buffer[i] = 0;
|
|
div_val *= 10;
|
|
}
|
|
|
|
div_val /= 10;
|
|
|
|
if (counter_value < 0)
|
|
{
|
|
counter_copy = -counter_value;
|
|
display_buffer[0] = seg_table[10];
|
|
}
|
|
else
|
|
{
|
|
counter_copy = counter_value;
|
|
}
|
|
|
|
txt = 0;
|
|
for (i = 0; i < DISPLAY_SIZE; ++i)
|
|
{
|
|
digit = counter_copy / div_val;
|
|
if ((0 != txt) || (0 != digit) || (DISPLAY_SIZE - 1 == i))
|
|
{
|
|
txt = 1;
|
|
display_buffer[i] = seg_table[digit];
|
|
}
|
|
counter_copy %= div_val;
|
|
div_val /= 10;
|
|
}
|
|
|
|
if (0 != dots)
|
|
{
|
|
display_buffer[DISPLAY_SIZE - 1] |= DP;
|
|
}
|
|
|
|
display_send_buffer();
|
|
display_show();
|
|
}
|
|
|
|
void mem_load()
|
|
{
|
|
int32_t value = 0;
|
|
uint8_t curr_byte;
|
|
uint8_t i;
|
|
|
|
value = 0;
|
|
for (i = 0; i < 4; ++i)
|
|
{
|
|
curr_byte = eeprom_read_byte((uint8_t *) i);
|
|
value <<= 8;
|
|
value |= curr_byte;
|
|
}
|
|
counter_value = value;
|
|
}
|
|
|
|
void mem_save()
|
|
{
|
|
int32_t value = counter_value;
|
|
uint8_t curr_byte;
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < 4; ++i)
|
|
{
|
|
curr_byte = (value >> (24 - 8 * i)) & 0xFF;
|
|
eeprom_update_byte((uint8_t *) i, curr_byte);
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
uint32_t ms_sleep = DISPLAY_DELAY;
|
|
uint32_t dec_ms_long = 0;
|
|
uint32_t inc_ms_long = 0;
|
|
uint8_t event;
|
|
|
|
hard_init();
|
|
display_show_value(0);
|
|
|
|
while(1)
|
|
{
|
|
while (event_count > 0)
|
|
{
|
|
// Consume Event
|
|
cli();
|
|
event = *event_read;
|
|
PTR_INC(event_read);
|
|
--event_count;
|
|
sei();
|
|
|
|
if (event != EVENT_NONE)
|
|
{
|
|
ms_sleep = ms + DISPLAY_DELAY;
|
|
}
|
|
|
|
// Process Event
|
|
switch (event)
|
|
{
|
|
case EVENT_DEC_DOWN:
|
|
dec_ms_long = ms + LONG_PRESS;
|
|
break;
|
|
|
|
case EVENT_DEC_UP:
|
|
if (0 != dec_ms_long)
|
|
{
|
|
if (counter_value > VALUE_MIN)
|
|
{
|
|
--counter_value;
|
|
}
|
|
dec_ms_long = 0;
|
|
}
|
|
display_show_value(0);
|
|
break;
|
|
|
|
case EVENT_INC_DOWN:
|
|
inc_ms_long = ms + LONG_PRESS;
|
|
break;
|
|
|
|
case EVENT_INC_UP:
|
|
if (0 != inc_ms_long)
|
|
{
|
|
if (counter_value < VALUE_MAX)
|
|
{
|
|
++counter_value;
|
|
}
|
|
inc_ms_long = 0;
|
|
}
|
|
display_show_value(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((0 != dec_ms_long) && (ms > dec_ms_long))
|
|
{
|
|
mem_load();
|
|
dec_ms_long = 0;
|
|
display_show_value(1);
|
|
}
|
|
|
|
if ((0 != inc_ms_long) && (ms > inc_ms_long))
|
|
{
|
|
mem_save();
|
|
inc_ms_long = 0;
|
|
display_show_value(1);
|
|
}
|
|
|
|
if (ms >= ms_sleep)
|
|
{
|
|
// Enable sleep
|
|
MCUCR |= (1 << SE);
|
|
|
|
// Go to sleep
|
|
sleep_cpu();
|
|
|
|
cli();
|
|
ms = 0;
|
|
sei();
|
|
|
|
// Disable sleep
|
|
MCUCR &= ~(1 << SE);
|
|
|
|
ms_sleep = ms + DISPLAY_DELAY;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|