Implement switches and rotary encoder

This commit is contained in:
Markus Koch 2020-07-27 21:39:29 +02:00
parent f71c65106f
commit 09bc6979e9
6 changed files with 243 additions and 14 deletions

View File

@ -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

98
firmware/input.c Normal file
View File

@ -0,0 +1,98 @@
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#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();
}

19
firmware/input.h Normal file
View File

@ -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

View File

@ -1,22 +1,37 @@
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/delay.h>
#include <util/delay.h>
#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;
}

66
firmware/systick.c Normal file
View File

@ -0,0 +1,66 @@
#include <avr/io.h>
#include "systick.h"
#include <stdlib.h>
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;
}

19
firmware/systick.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef SYSTICK_H
#define SYSTICK_H
#include <avr/io.h>
#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