#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 & (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; }