diff --git a/firmware/Makefile b/firmware/Makefile index a3b3aa6..efa55c1 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -1,10 +1,9 @@ MCU=atmega88 -CFLAGS=-g -Wall -mcall-prologues -mmcu=$(MCU) -Os -DF_CPU=8000000 -IAVR-SSD1306/Files/ +CFLAGS=-g -Wall -Wno-multichar -Wno-overflow -mcall-prologues -mmcu=$(MCU) -O2 -DF_CPU=8000000 -IAVR-SSD1306/Files/ LDFLAGS=-Wl,-gc-sections -Wl,-relax CC=avr-gcc TARGET=main -_OBJECT_FILES=main.o AVR-SSD1306/Files/SSD1306.c AVR-SSD1306/Files/TWI.c -OBJECT_FILES=main.o oled-display/font.o oled-display/i2c.o oled-display/lcd.o +OBJECT_FILES=main.o input.o systick.o oled-display/font.o oled-display/i2c.o oled-display/lcd.o all: $(TARGET).hex diff --git a/firmware/input.c b/firmware/input.c new file mode 100644 index 0000000..01b0cdb --- /dev/null +++ b/firmware/input.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include "input.h" +#include "systick.h" + +static volatile int8_t rotary_delta = 0; +static volatile uint8_t sw_event = 0; + +void input_init() +{ + PORT_ROTARY(DDR) &= ~(PIN_ROTARY_A | PIN_ROTARY_B); // Configure as input + PORT_ROTARY(PORT) &= ~(PIN_ROTARY_A | PIN_ROTARY_B); // Set pullups + + PORT_KEYPAD(DDR) &= ~(PIN_KEYPAD_MASK); // Key inputs + PORT_KEYPAD(PORT) |= PIN_KEYPAD_MASK; // Key pullups +} + +int8_t get_rotary_delta() +{ + int8_t temp = rotary_delta; + //rotary_delta = 0; + return temp; +} + +static inline void input_proc_rotary() +{ + static int8_t last_a; + int8_t a; + int8_t b; + int8_t p; + + p = PORT_ROTARY(PIN); + a = (p & PIN_ROTARY_A); + b = (p & PIN_ROTARY_B); + a = !a; + b = !b; + + if (a == 1 && last_a == 0) { + _delay_ms(1); // TODO + rotary_delta += !!(a ^ b); + } + if (a == 0 && last_a == 1) { + _delay_ms(1); // TODO + rotary_delta -= !(a ^ b); + } + + last_a = a; +} + +// TODO: Pressing two keys at the same time can lead to unfortunate behavior. +void input_proc_switches() +{ + uint8_t sw; + uint8_t ev; + uint8_t i; + static uint8_t sw_last = 0xFF; + uint8_t delta; + + sw = PORT_KEYPAD(PIN) & PIN_KEYPAD_MASK; + ev = sw ^ sw_last; + + if (ev) { + if (timer_expired(TIMID_SW_DEBOUNCE)) { // If we read a value, wait for the debounce time before we process any other state + for (i=0; i < 8; ++i) { + if (ev & (1 << i)) { + if (sw & (1 << i)) { // Key release + if (timer_expired(TIMID_SW_LONGPRESS)) + i |= (1 << 7) | (1 << 6); + else + i |= (1 << 7); + sw_event = i; + } else { // Key push + sw_event = 0; // To be honest, I don't have a f*cking clue why I need this... + timer_set(TIMID_SW_LONGPRESS, MS_TO_TICKS(1000)); + } + timer_set(TIMID_SW_DEBOUNCE, MS_TO_TICKS(250)); + break; // We can only process one event at a time anyway, so let's quit it here + } + } + } + sw_last = sw; + } +} + +uint8_t get_switch_event() +{ + uint8_t temp = sw_event; + sw_event = 0; + return temp; +} + +// Need to call timer_proc() before calling this function! +void input_proc() +{ + input_proc_rotary(); + input_proc_switches(); +} diff --git a/firmware/input.h b/firmware/input.h new file mode 100644 index 0000000..c450e55 --- /dev/null +++ b/firmware/input.h @@ -0,0 +1,19 @@ +#ifndef INPUT_H +#define INPUT_H + +#define PORT_KEYPAD(t) t##D // Keys connected to: 0,1,2(INT0->POWER),5,6,7 +#define PIN_KEYPAD_MASK 0b11100111 +#define PORT_KEYAUX(t) t##B +#define PIN_ROTARY_PUSH (1 << 0) + +#define PORT_ROTARY(t) t##D +#define PIN_ROTARY_A (1 << 3) // INT1 +#define PIN_ROTARY_B (1 << 4) + +void input_init(); +void input_proc(); +int8_t get_rotary_delta(); +uint8_t get_switch_event(); +extern uint8_t ctime; + +#endif // INPUT_H diff --git a/firmware/main.c b/firmware/main.c index e56d013..6f38fe3 100644 --- a/firmware/main.c +++ b/firmware/main.c @@ -1,22 +1,37 @@ #include +#include #include -#include +#include #include "oled-display/lcd.h" +#include "input.h" +#include "systick.h" #define PORT_SPI(t) t##B #define PIN_SPI_CS (1 << 2) -void spi_proc() +int dbg = 0; + +void spi_init() +{ + SPCR = (1 << SPE); + SPSR = (1 << SPI2X); + DDRB |= (1 << 4); +} + +uint8_t spi_proc() { static uint8_t new_data = 0; static uint8_t x = 0; static uint8_t y = DISPLAY_HEIGHT / 8 - 1; uint8_t spi_cs; - + spi_cs = !(PORT_SPI(PIN) & PIN_SPI_CS); - + if (spi_cs) { if (SPSR & (1 << SPIF)) { + //SPDR = get_rotary_delta(); + SPDR = get_switch_event(); + //SPDR = dbg; lcd_set_buffer(x, y, SPDR); if (y == 0) { y = DISPLAY_HEIGHT / 8 - 1; @@ -37,10 +52,12 @@ void spi_proc() x = 0; y = DISPLAY_HEIGHT / 8 - 1; } + + return spi_cs; } -int main() -{ +void lcd_startup() +{ lcd_init(LCD_DISP_ON); lcd_gotoxy(0, 0); lcd_puts_p(PSTR("== Internet Radio ==")); @@ -50,13 +67,24 @@ int main() lcd_gotoxy(0, 3); lcd_puts_p(PSTR("Please wait...")); lcd_display(); - - SPCR = (1 << SPE); - +} + +int main() +{ + cli(); + + lcd_startup(); + spi_init(); + systick_init(); + input_init(); + _delay_ms(10); - + while (1) { - spi_proc(); + if (!spi_proc()) { + timer_proc(); + input_proc(); + } } return 0; } diff --git a/firmware/systick.c b/firmware/systick.c new file mode 100644 index 0000000..677c148 --- /dev/null +++ b/firmware/systick.c @@ -0,0 +1,66 @@ +#include +#include "systick.h" +#include + +uint8_t ctime; + +static uint8_t tims[NTIM]; + +void systick_init() +{ + int i; + + TCCR1A = 0; + TCCR1B = (1 << CS10) | (1 << CS12); // f_tim = f_clk / 256 -> TCNT1H counts with 8ms / tick + TCCR1C = 0; + OCR1A = 0x7FFF; + + for (i=0; i < NTIM; ++i) { + tims[i] = 0; + } +} + +void timer_proc() +{ + uint8_t now; + uint8_t delta; + uint8_t i; + + now = TCNT1L; + now = TCNT1H; + + if (now == ctime) + return; + delta = now - ctime; + if (delta > 127) + delta -= 128; + + for (i=0; i < NTIM; ++i) { + if (tims[i] > delta) { + tims[i] -= delta; + } else { + tims[i] = 0; + } + } + + ctime = now; +} + +uint8_t timer_expired(uint8_t tim_id) +{ + return !tims[tim_id]; +} + +void timer_set(uint8_t tim_id, uint8_t ticks) +{ + tims[tim_id] = ticks; +} + +uint8_t block_for(uint8_t tim_id, uint8_t ticks) +{ + if (timer_expired(tim_id)) { + timer_set(tim_id, ticks); + return 1; + } + return 0; +} diff --git a/firmware/systick.h b/firmware/systick.h new file mode 100644 index 0000000..7b081d3 --- /dev/null +++ b/firmware/systick.h @@ -0,0 +1,19 @@ +#ifndef SYSTICK_H +#define SYSTICK_H +#include + +#define TIMID_SW_DEBOUNCE 0 +#define TIMID_SW_LONGPRESS 1 +#define NTIM 2 + +#define MS_TO_TICKS(ms) (((ms/1000 * F_CPU/1024) / 256)) + +#define SYSTICK_VAL TCNT1H + +void timer_proc(); +void systick_init(); +uint8_t timer_expired(uint8_t tim_id); +void timer_set(uint8_t tim_id, uint8_t ticks); +uint8_t block_for(uint8_t tim_id, uint8_t ticks); + +#endif // SYSTICK_H