Commit d4444390 authored by Robey Pointer's avatar Robey Pointer
Browse files

use the PWM timer to drive a 32KHz tick counter and interrupt, so we can go...

use the PWM timer to drive a 32KHz tick counter and interrupt, so we can go idle between 1ms keyboard scans. also separate the keyboard scan (slow) from the collecting/reporting (fast, done often).
parent fbdc2cce
......@@ -32,6 +32,11 @@
this software.
*/
// just for vscode
#ifndef __AVR_ATmega32U4__
#define __AVR_ATmega32U4__
#endif
#include <stdlib.h>
#include <avr/io.h>
#include <avr/sleep.h>
......@@ -79,7 +84,7 @@ USB_ClassInfo_HID_Device_t Keyboard_HID_Interface = {
#define MATRIX_ROWS 6
#define MATRIX_COLUMNS 15
#define MATRIX_BITS ((MATRIX_ROWS * MATRIX_COLUMNS + 7) >> 3)
#define MATRIX_BYTES ((MATRIX_ROWS * MATRIX_COLUMNS + 7) >> 3)
uint8_t matrix[MATRIX_ROWS * MATRIX_COLUMNS] = {
KEY_ESCAPE, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_CIRCLE, KEY_CIRCLE,
......@@ -95,6 +100,12 @@ uint8_t matrix[MATRIX_ROWS * MATRIX_COLUMNS] = {
HID_KEYBOARD_SC_RIGHT_GUI, HID_KEYBOARD_SC_LEFT_GUI, HID_KEYBOARD_SC_RIGHT_CONTROL, KEY_SPACE, HID_KEYBOARD_SC_LEFT_ALT, HID_KEYBOARD_SC_RIGHT_ALT, KEY_SPACE, HID_KEYBOARD_SC_PAGE_UP, HID_KEYBOARD_SC_PAGE_DOWN, HID_KEYBOARD_SC_LEFT_ARROW, HID_KEYBOARD_SC_DOWN_ARROW, HID_KEYBOARD_SC_RIGHT_ARROW, 0,0,0
};
#define CIRCLE_BYTE 0x01
#define CIRCLE_BIT (1 << 5)
static uint8_t state[MATRIX_BYTES] = { 0, };
// f8 = sleep
// 49 = mute
// 84 = scroll lock
......@@ -111,17 +122,43 @@ static void remote_check_for_low_battery(void) {
}
volatile static uint32_t timer_tick = 0;
ISR(TIMER0_OVF_vect) {
timer_tick++;
}
uint32_t timer_get_ticks(void) {
return timer_tick;
}
// rough estimate!
// don't use for timekeeping, only for human-perceivable delays.
uint32_t timer_diff_msec(uint32_t start_ticks, uint32_t end_ticks) {
return (end_ticks - start_ticks) >> 5;
}
uint8_t pwmval = 8;
void kbd_brightness_init(void) {
// initial brightness
OCR0A = pwmval;
// clear/set, WGM1:0 set (Phase correct PWM)
TCCR0A = (1 << 7) | (0 << 6) | (0 << 1) | 1;
// configure timer 0:
// - WGM0 = 001: phase-correct PWM
// - COM0A = 10: clear when counting up, set when counting down
// - CS0 = 001: no prescaling, clock is 16MHz
// - TOIE0 = 1: trigger interrupt at 0
// interrupts happen after every complete phase (256 up, 256 down), so
// each interrupt is 16MHz / (256 * 2) = 32KHz, which we track in a 32-bit
// counter, which will roll over about every 1.5 days.
// 3=WGM02, (cs02 2:0 -> clock/1 = 001)
TCCR0B = /*(1 << 3) |*/ (0 << 2) | (0 << 1) | 1;
// 7:6 = COM0A, 1:0 = WGM0 1:0 (WGM0 is split across 2 registers)
TCCR0A = (1 << 7) | (0 << 6) | (0 << 1) | 1;
// 3 = WGM0 2, 2:0 = CS0
TCCR0B = (0 << 2) | (0 << 1) | 1;
// 0 = TOIE0
TIMSK0 = 1;
}
void kbd_brightness_inc(void) {
......@@ -173,15 +210,14 @@ const menu_item_t circle_menu[] = {
};
#endif
// scan the keyboard matrix and fill in a list of (up to MAX_CONCURRENT_KEYS)
// keys that are currently pressed. returns the count of pressed keys.
static uint8_t scan_keyboard_raw(uint8_t keys[]) {
// scan the keyboard matrix and record which keys are currently pressed,
// after debouncing. this takes over half a millisecond.
static void scan_keyboard_matrix(void) {
static uint8_t debounce_history[MATRIX_COLUMNS * MATRIX_ROWS] = { 0, };
static uint8_t state[MATRIX_BITS] = { 0, };
uint8_t count = 0;
// pull ROWs low one after the other
for (int y = 0; y < 6; y++) {
for (int y = 0; y < MATRIX_ROWS; y++) {
switch (y) {
case 0: output_low(PORTB, 6); break;
case 1: output_low(PORTB, 5); break;
......@@ -191,12 +227,8 @@ static uint8_t scan_keyboard_raw(uint8_t keys[]) {
case 5: output_low(PORTD, 4); break;
}
// wait for signal to stabilize
// TODO maybe not necessary
_delay_us(10);
// check input COLs
for (int x = 0; x < 14; x++) {
for (int x = 0; x < MATRIX_COLUMNS; x++) {
uint8_t pressed = 0;
// column pins are all over the place
......@@ -220,23 +252,17 @@ static uint8_t scan_keyboard_raw(uint8_t keys[]) {
// shift new state as bit into debounce "register"
uint8_t loc = y * 15 + x;
uint8_t pressed_byte = loc >> 3;
uint8_t pressed_bit = loc & 7;
bool now_pressed = state[pressed_byte] & (1 << pressed_bit);
uint8_t pressed_bit = 1 << (loc & 7);
bool now_pressed = state[pressed_byte] & pressed_bit;
debounce_history[loc] = (debounce_history[loc] << 1) | pressed;
// transition to "pressed" only on the first down event after a
// run (7) of unpressed scans. transition to "unpressed" only
// after a run (8) of unpressed scans.
if (debounce_history[loc] == 0x00 && now_pressed) {
state[pressed_byte] &= ~(1 << pressed_bit);
now_pressed = 0;
state[pressed_byte] &= ~pressed_bit;
} else if (debounce_history[loc] == 0x01 && !now_pressed) {
state[pressed_byte] |= (1 << pressed_bit);
now_pressed = 1;
}
if (now_pressed && count < MAX_CONCURRENT_KEYS) {
keys[count++] = matrix[loc];
state[pressed_byte] |= pressed_bit;
}
}
......@@ -249,25 +275,25 @@ static uint8_t scan_keyboard_raw(uint8_t keys[]) {
case 5: output_high(PORTD, 4); break;
}
}
return count;
}
// do a tight loop of scanning, to allow the debounce logic to settle.
// report the result of the latest scan_keyboard_matrix, and fill in a list
// of (up to MAX_CONCURRENT_KEYS) keys that are currently pressed. also
// handles the circle menu. returns the count of pressed keys.
static uint8_t scan_keyboard(uint8_t keys[]) {
uint8_t count = 0;
for (int i = 0; i < 8; i++) {
count = scan_keyboard_raw(keys);
}
return count;
}
// scan_keyboard, and handle the circle-menu
static uint8_t scan_keyboard_enhanced(uint8_t keys[]) {
static bool in_circle_menu = false;
static uint8_t previous_key = 0;
uint8_t count = scan_keyboard(keys);
uint8_t count = 0;
for (int i = 0; i < MATRIX_BYTES; i++) {
if (!state[i]) continue;
for (int bit = 0; bit < 8; bit++) {
if ((state[i] & (1 << bit)) && count < MAX_CONCURRENT_KEYS) {
keys[count++] = matrix[(i << 3) | bit];
}
}
}
if (count == 1 && keys[0] == KEY_CIRCLE && !in_circle_menu && !previous_key) {
menu_start(circle_menu);
in_circle_menu = true;
......@@ -315,6 +341,7 @@ int main(void) {
SetupHardware();
GlobalInterruptEnable();
set_sleep_mode(SLEEP_MODE_IDLE);
// check for safe boot (holding down the circle key for 500msec)...
// this is to cover a case where the firmware is messed up enough that
......@@ -323,7 +350,8 @@ int main(void) {
// allow you to reflash. :)
uint8_t keys[MAX_CONCURRENT_KEYS];
uint8_t safe_count = 0;
while (scan_keyboard(keys) == 1 && keys[0] == KEY_CIRCLE && safe_count < 50) {
scan_keyboard_matrix();
while ((state[CIRCLE_BYTE] & CIRCLE_BIT) != 0 && safe_count < 50) {
safe_count++;
if (safe_count >= 50) {
display_clear();
......@@ -334,17 +362,23 @@ int main(void) {
break;
}
Delay_MS(10);
scan_keyboard_matrix();
}
int counter = 0;
while (true) {
// do our own (non-usb) scan, too, so we can handle the circle menu
// when the laptop is off :)
scan_keyboard_enhanced(keys);
scan_keyboard_matrix();
scan_keyboard(keys);
HID_Device_USBTask(&Keyboard_HID_Interface);
USB_USBTask();
counter++;
// this turns out to spend < 50% of its time sleeping, even if we
// wake up only every 1ms, so it might not really be worth doing.
sleep_mode();
while ((timer_tick & 31) != 0) sleep_mode();
#ifndef KBD_VARIANT_STANDALONE
#if 0
if (counter >= 30000) {
......@@ -382,8 +416,8 @@ void SetupHardware(void) {
PORTF = 0b11111111;
// disable JTAG
MCUCR |=(1<<JTD);
MCUCR |=(1<<JTD);
MCUCR |= (1 << JTD);
MCUCR |= (1 << JTD);
kbd_brightness_init();
oled_init();
......@@ -454,7 +488,7 @@ bool CALLBACK_HID_Device_CreateHIDReport(
) {
USB_KeyboardReport_Data_t* KeyboardReport = (USB_KeyboardReport_Data_t*)ReportData;
uint8_t count = scan_keyboard_enhanced(KeyboardReport->KeyCode);
uint8_t count = scan_keyboard(KeyboardReport->KeyCode);
if (count == 0) memset(KeyboardReport->KeyCode, 0, MAX_CONCURRENT_KEYS);
*ReportSize = sizeof(USB_KeyboardReport_Data_t);
......
......@@ -70,6 +70,9 @@ void SetupHardware(void);
void kbd_brightness_init(void);
void kbd_brightness_set(uint8_t brite);
uint32_t timer_get_ticks(void);
uint32_t timer_diff_msec(uint32_t start_ticks, uint32_t end_ticks);
void EVENT_USB_Device_Connect(void);
void EVENT_USB_Device_Disconnect(void);
void EVENT_USB_Device_ConfigurationChanged(void);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment