commit c04af37359efd3fb5f23f7bb75f63f3e7d58e7fd Author: nedko Date: Mon Mar 6 21:12:55 2023 +0200 Initial code commit diff --git a/code/Makefile b/code/Makefile new file mode 100644 index 0000000..22a4220 --- /dev/null +++ b/code/Makefile @@ -0,0 +1,8 @@ +all: + make build + make program +build: + avr-gcc main.c -o counter -mmcu=attiny85 -Wall -Wextra + +program: + avrdude -p attiny85 -c usbtiny -U flash:w:counter diff --git a/code/main.c b/code/main.c new file mode 100644 index 0000000..5ded395 --- /dev/null +++ b/code/main.c @@ -0,0 +1,407 @@ +#define F_CPU 1000000 +#include +#include +#include +#include + +#include + +#include + +#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 & PIN_BTN_DEC)) + { + new_btn_flag |= BTN_FLAG_DEC; + } + + if (0 == (PINB & 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, Timer0, USI, ADC + //PRR = (1 << PRTIM1) | (1 << PRTIM0) | (1 << PRUSI) | (1 << PRADC); + 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 << OUT_DATA); + } + else + { + PORTB &= ~(1 << OUT_DATA); + } + + _delay_ms(1); + PORTB |= (1 << OUT_CLOCK); + _delay_ms(1); + PORTB &= (1 << OUT_CLOCK); +} + +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) << 4) | (display_buffer[b * 2 + 1] & 0xF0)); + } + + 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() +{ + _delay_ms(1); + PORTB |= (1 << OUT_STORE); + _delay_ms(1); + PORTB &= (1 << OUT_STORE); +} + +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)) + { + txt = 1; + display_buffer[i] = seg_table[digit]; + } + counter_copy %= div_val; + div_val /= 10; + } + + if (0 != dots) + { + display_buffer[6] |= 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; +}