/* * pit.c -- Periodic Interrupt Timer interface * * Copyright (C) 2017 Tomasz Kramkowski * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "pit.h" #include "usb/endpt1.h" #include "usb/keycodes.h" enum { T0_CYCLES = 5000, }; struct gpio { uint32_t port; uint32_t gpio; uint32_t bit; unsigned char pin; }; #define GPIO_PIN(port, pin) { \ PORT##port##_BASE, \ GPIO##port##_BASE, \ BV((pin)), \ (pin), \ } static const struct gpio cols[] = { GPIO_PIN(B, 2), GPIO_PIN(B, 3), GPIO_PIN(B, 18), GPIO_PIN(B, 19), GPIO_PIN(C, 0), GPIO_PIN(C, 8), GPIO_PIN(C, 9), GPIO_PIN(C, 10), GPIO_PIN(C, 11), }; static const struct gpio rows[] = { GPIO_PIN(D, 0), GPIO_PIN(D, 1), GPIO_PIN(D, 4), GPIO_PIN(D, 5), GPIO_PIN(D, 6), GPIO_PIN(D, 7), GPIO_PIN(C, 1), GPIO_PIN(C, 2), }; /* COL AND ROW SWAPPED!!! */ static enum keycode keymap[2][ARRLEN(rows)][ARRLEN(cols)] = { { { /* S01 */ KEY_ESCAPE, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, }, { /* S10 */ KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_NONE, KEY_BACKSPACE, KEY_GRAVE, KEY_TAB, KEY_Q, }, { /* S19 */ KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, }, { /* S28 */ KEY_LSQUARE,KEY_RSQUARE, KEY_NONE, KEY_DELETE, KEY_CAPSLK, KEY_A, KEY_S, KEY_D, KEY_F, }, { /* S37 */ KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, KEY_QUOTE, KEY_BACKSLASH, KEY_ENTER, }, { /* S46 */ KEY_PGUP, KEY_LSHIFT, KEY_NU_BACKSLASH,KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, }, { /* S55 */ KEY_M, KEY_COMMA, KEY_PERIOD, KEY_SLASH, KEY_RSHIFT, KEY_UP, KEY_PGDN, KEY_LCTRL, KEY_LGUI, }, { /* S64 */ KEY_LALT, KEY_SPACE, KEY_RALT, KEY_LAYER1, KEY_RCTRL, KEY_LEFT, KEY_DOWN, KEY_RIGHT, KEY_NONE, }, }, { { /* S01 */ KEY_ESCAPE, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, }, { /* S10 */ KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_NONE, KEY_BACKSPACE, KEY_GRAVE, KEY_TAB, KEY_Q, }, { /* S19 */ KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, }, { /* S28 */ KEY_LSQUARE, KEY_RSQUARE, KEY_NONE, KEY_INSERT, KEY_CAPSLK, KEY_A, KEY_S, KEY_D, KEY_F, }, { /* S37 */ KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, KEY_QUOTE, KEY_BACKSLASH, KEY_ENTER, }, { /* S46 */ KEY_HOME, KEY_LSHIFT, KEY_NU_BACKSLASH,KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, }, { /* S55 */ KEY_M, KEY_COMMA, KEY_PERIOD, KEY_SLASH, KEY_RSHIFT, KEY_UP, KEY_END, KEY_LCTRL, KEY_LGUI, }, { /* S64 */ KEY_LALT, KEY_SPACE, KEY_APPLICATION,KEY_NONE, KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, KEY_NONE, }, } }; static enum keycode keydown[ARRLEN(cols)][ARRLEN(rows)]; static unsigned char prevlayer; static unsigned char layer; void pit_setup(void) { /* Set up ports (could be done with GPC registers */ for (size_t i = 0; i < ARRLEN(cols); i++) { SET_BIT(GPIO_PDDR(cols[i].gpio), cols[i].pin); GPIO_PCOR(cols[i].gpio) = cols[i].bit; PORT_PCR(cols[i].port, cols[i].pin) = BV(PCR_SRE) | BV(PCR_DSE) | 1 << PCR_MUX; } for (size_t i = 0; i < ARRLEN(rows); i++) { UNSET_BIT(GPIO_PDDR(rows[i].gpio), rows[i].pin); PORT_PCR(rows[i].port, rows[i].pin) = BV(PCR_PFE) | BV(PCR_PE) | 1 << PCR_MUX; } /* Enable clock */ SET_BIT(SIM_SCGC6, SCGC6_PIT); /* NVIC Enable interrupt */ ISR_CLRPEND(68); ISR_SETENA(68); INTPRI(68) = 0x10; /* First column strobe */ GPIO_PSOR(cols[0].gpio) = cols[0].bit; /* Set up PIT0 */ PIT_MCR = 0; PIT_LDVAL(0) = T0_CYCLES - 1; PIT_TFLG(0) = BV(TFLG_TIF); PIT_TCTRL(0) = BV(TCTRL_TIE) | BV(TCTRL_TEN); } static uint8_t kstates[ARRLEN(cols)][ARRLEN(rows)] = { 0 }; static void key_state(int col, int row, bool state) { uint8_t kstate; enum keycode keycode; kstate = kstates[col][row] = kstates[col][row] << 1 | state; if (kstate == 0x7f) { keycode = keymap[layer][row][col]; keydown[col][row] = keycode; if (keycode < KEY_LAYER0) { usb_endpt1_setkey(keycode, true); return; } prevlayer = layer; layer = keycode - KEY_LAYER0; } else if (kstate == 0x80) { keycode = keydown[col][row]; if (keycode < KEY_LAYER0) { usb_endpt1_setkey(keycode, false); return; } /* * TODO: This will actually break with more than 2 layers * needs a layer stack and also some special handling for letting go of * previous layer keys mid-press. */ layer = prevlayer; } } void pit0_isr(void) { static unsigned char col = 0; for (size_t row = 0; row < ARRLEN(rows); row++) key_state(col, row, !!(GPIO_PDIR(rows[row].gpio) & rows[row].bit)); GPIO_PCOR(cols[col].gpio) = cols[col].bit; col = (col + 1) % ARRLEN(cols); GPIO_PSOR(cols[col].gpio) = cols[col].bit; if (col == 0) usb_endpt1_send(); PIT_TFLG(0) = BV(TFLG_TIF); }