├── Setup.hs ├── README.md ├── app └── Main.hs ├── .gitignore ├── stack.yaml ├── print.h ├── Makefile ├── LICENSE ├── cadet.cabal ├── print.c ├── src └── Lib.hs ├── usb.h ├── main.c └── usb.c /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | cadet 2 | ===== 3 | 4 | Keyboard controller written in [Ivory](http://ivorylang.org), an eDSL for safe systems programming. 5 | -------------------------------------------------------------------------------- /app/Main.hs: -------------------------------------------------------------------------------- 1 | import Lib 2 | import qualified Ivory.Compile.C.CmdlineFrontend as C (compile) 3 | 4 | main :: IO () 5 | main = C.compile [cadet] [] 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.c 2 | *.elf 3 | *.hex 4 | *.o 5 | *.swp 6 | .DS_Store 7 | .stack-work 8 | cadet.c 9 | cadet.h 10 | ivory.h 11 | ivory_asserts.h 12 | ivory_templates.h 13 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - '.' 3 | extra-deps: 4 | - ivory-0.1.0.5 5 | - ivory-artifact-0.1.0.5 6 | - ivory-backend-c-0.1.0.5 7 | - ivory-opts-0.1.0.5 8 | resolver: lts-8.3 9 | -------------------------------------------------------------------------------- /print.h: -------------------------------------------------------------------------------- 1 | #ifndef print_h__ 2 | #define print_h__ 3 | 4 | #include 5 | #include "usb.h" 6 | 7 | // this macro allows you to write print("some text") and 8 | // the string is automatically placed into flash memory :) 9 | #define print(s) print_P(PSTR(s)) 10 | #define pchar(c) usb_debug_putchar(c) 11 | 12 | void print_P(const char *s); 13 | void phex(unsigned char c); 14 | void phex16(unsigned int i); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = avr-gcc 2 | DFU = dfu-programmer 3 | MCU = atmega32u4 4 | OBJCOPY = avr-objcopy 5 | STACK = stack 6 | 7 | CFLAGS += -DF_CPU=16000000UL 8 | CFLAGS += -Os 9 | CFLAGS += -mmcu=$(MCU) 10 | CFLAGS += -std=c11 11 | 12 | haskell = cadet.cabal stack.yaml 13 | ivory-gen = cadet.c cadet.h ivory.h ivory_asserts.h ivory_templates.h 14 | ivory-src = app/Main.hs src/Lib.hs 15 | objects = cadet.o main.o print.o usb.o 16 | 17 | cadet.hex: cadet.elf 18 | $(OBJCOPY) -O ihex $< $@ 19 | 20 | cadet.elf: $(objects) 21 | $(CC) $(CFLAGS) -o $@ $(objects) 22 | 23 | cadet.o: $(ivory-gen) 24 | main.o: main.c print.h usb.h cadet.h 25 | print.o: print.c print.h usb.h 26 | usb.o: usb.c usb.h cadet.h 27 | 28 | cadet.c: $(ivory-src) $(haskell) 29 | $(STACK) setup 30 | $(STACK) build 31 | $(STACK) exec cadet -- --src-dir=. 32 | 33 | clean: 34 | $(RM) $(objects) $(ivory-gen) cadet.hex cadet.elf 35 | 36 | erase: 37 | $(DFU) $(MCU) erase 38 | 39 | flash: cadet.hex 40 | $(DFU) $(MCU) flash $< 41 | 42 | launch: 43 | $(DFU) $(MCU) launch --no-reset 44 | 45 | all: cadet.hex 46 | 47 | .PHONY: all clean erase flash launch 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Jihyeok Seo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /cadet.cabal: -------------------------------------------------------------------------------- 1 | name: cadet 2 | version: 0.1.0 3 | synopsis: Keyboard controller 4 | description: Written in Ivory, an eDSL for safe systems programming. 5 | homepage: https://github.com/limeburst/cadet#readme 6 | license: MIT 7 | license-file: LICENSE 8 | author: Jihyeok Seo 9 | maintainer: me@limeburst.net 10 | copyright: Copyright (c) 2016 Jihyeok Seo 11 | category: Embedded 12 | build-type: Simple 13 | cabal-version: >=1.10 14 | 15 | library 16 | hs-source-dirs: src 17 | exposed-modules: Lib 18 | build-depends: base >= 4.7 && < 5 19 | , ivory 20 | , ivory-backend-c 21 | default-language: Haskell2010 22 | 23 | executable cadet 24 | hs-source-dirs: app 25 | main-is: Main.hs 26 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 27 | build-depends: base 28 | , cadet 29 | , ivory 30 | , ivory-backend-c 31 | default-language: Haskell2010 32 | 33 | source-repository head 34 | type: git 35 | location: https://github.com/limeburst/cadet 36 | -------------------------------------------------------------------------------- /print.c: -------------------------------------------------------------------------------- 1 | /* Very basic print functions, intended to be used with usb_debug_only.c 2 | * http://www.pjrc.com/teensy/ 3 | * Copyright (c) 2008 PJRC.COM, LLC 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | // Version 1.0: Initial Release 25 | 26 | #include 27 | #include 28 | 29 | #include "print.h" 30 | 31 | void print_P(const char *s) 32 | { 33 | char c; 34 | 35 | while (1) { 36 | c = pgm_read_byte(s++); 37 | if (!c) break; 38 | if (c == '\n') usb_debug_putchar('\r'); 39 | usb_debug_putchar(c); 40 | } 41 | } 42 | 43 | void phex1(unsigned char c) 44 | { 45 | usb_debug_putchar(c + ((c < 10) ? '0' : 'A' - 10)); 46 | } 47 | 48 | void phex(unsigned char c) 49 | { 50 | phex1(c >> 4); 51 | phex1(c & 15); 52 | } 53 | 54 | void phex16(unsigned int i) 55 | { 56 | phex(i >> 8); 57 | phex(i); 58 | } 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/Lib.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE FlexibleInstances #-} 3 | {-# LANGUAGE QuasiQuotes #-} 4 | {-# LANGUAGE TypeOperators #-} 5 | 6 | module Lib where 7 | 8 | import Ivory.Language 9 | import qualified Ivory.Compile.C.CmdlineFrontend as C (compile) 10 | 11 | type Keymap = Array 5 (Array 15 (Stored Uint8)) 12 | type Matrix = Array 5 (Stored Uint16) 13 | 14 | key_none = 0x00 15 | key_fn = 0xC0 16 | key_left_ctrl = 0xE0 17 | key_left_shift = 0xE1 18 | key_left_alt = 0xE2 19 | key_left_gui = 0xE3 20 | key_right_ctrl = 0xE4 21 | key_right_shift = 0xE5 22 | key_right_alt = 0xE6 23 | key_right_gui = 0xE7 24 | 25 | [ivory| 26 | struct KeyboardInputReport 27 | { modifier :: Stored Uint8 28 | ; reserved :: Stored Uint8 29 | ; keycode :: Array 6 (Stored Uint8) 30 | } 31 | |] 32 | 33 | report :: MemArea ('Struct "KeyboardInputReport") 34 | report = area "report" $ Just (istruct []) 35 | 36 | is_modifier :: Def ('[Uint8] :-> IBool) 37 | is_modifier = proc "is_modifier" $ \ key -> body $ do 38 | ret (key >=? key_left_ctrl .&& key <=? key_right_gui) 39 | 40 | matrix_changed :: Def ('[Ref s Matrix, Ref s Matrix] :-> IBool) 41 | matrix_changed = proc "matrix_changed" $ \ a b -> body $ do 42 | n <- local (ival (0 :: Uint8)) 43 | arrayMap $ \ ix -> do 44 | aE <- deref (a ! ix) 45 | bE <- deref (b ! ix) 46 | ifte_ (aE /=? bE) 47 | (do 48 | n' <- deref n 49 | store n (n' + 1)) 50 | (do 51 | n' <- deref n 52 | store n n') 53 | n' <- deref n 54 | ifte_ (n' ==? 0) 55 | (ret false) 56 | (ret true) 57 | 58 | fn_pressed :: Def ('[Ref s Matrix, Ref s Keymap] :-> IBool) 59 | fn_pressed = proc "fn_pressed" $ \ matrix keymap -> body $ do 60 | n <- local (ival (0 :: Uint8)) 61 | arrayMap $ \ ix -> do 62 | arrayMap $ \ iy -> do 63 | m <- deref (matrix ! ix) 64 | k <- deref (keymap ! ix ! iy) 65 | ifte_ ((((m `iShiftR` (safeCast iy)) .& 1) ==? 1) .&& k ==? key_fn) 66 | (do 67 | n' <- deref n 68 | store n (n' + 1)) 69 | (do 70 | n' <- deref n 71 | store n n') 72 | n' <- deref n 73 | ifte_ (n' ==? 0) 74 | (ret false) 75 | (ret true) 76 | 77 | get_fn_key :: Def ('[Ref s (Array 2 Keymap), Ix 5, Ix 15] :-> Uint8) 78 | get_fn_key = proc "get_fn_key" $ \ keymaps row col -> body $ do 79 | fn_layer_key <- deref (keymaps ! 1 ! row ! col) 80 | default_layer_key <- deref (keymaps ! 0 ! row ! col) 81 | ifte_ (fn_layer_key ==? 0) 82 | (ret default_layer_key) 83 | (ret fn_layer_key) 84 | 85 | update_report :: Def ('[Ref s Matrix, Ref s (Array 2 Keymap)] :-> ()) 86 | update_report = proc "update_report" $ \ matrix keymaps -> body $ do 87 | store ((addrOf report) ~> modifier) 0 88 | arrayMap $ \ ix -> do 89 | store (((addrOf report) ~> keycode) ! (ix :: Ix 6)) 0 90 | keys <- local (izero :: Init ('Stored Uint8)) 91 | fp <- call fn_pressed matrix (keymaps ! 0) 92 | arrayMap $ \ ix -> do 93 | arrayMap $ \ iy -> do 94 | m <- deref (matrix ! ix) 95 | ifte_ ((m `iShiftR` (safeCast iy) .& 1) ==? 1) 96 | (do 97 | key_ref <- local (izero :: Init ('Stored Uint8)) 98 | fn_key <- call get_fn_key keymaps ix iy 99 | normal_key <- deref (keymaps ! 0 ! ix ! iy) 100 | ifte_ (fp) 101 | (store key_ref fn_key) 102 | (store key_ref normal_key) 103 | key <- deref key_ref 104 | current_modifier <- deref ((addrOf report) ~> modifier) 105 | is_modifier_key <- call is_modifier key 106 | ifte_ (is_modifier_key) 107 | (store 108 | ((addrOf report) ~> modifier) 109 | (current_modifier .| (1 `iShiftL` (key - 0xE0))) 110 | ) 111 | (do 112 | keys_val <- deref keys 113 | ifte_ (keys_val ==? 0) 114 | (store (((addrOf report) ~> keycode) ! 0 ) key) 115 | (store keys keys_val) 116 | ifte_ (keys_val ==? 1) 117 | (store (((addrOf report) ~> keycode) ! 1 ) key) 118 | (store keys keys_val) 119 | ifte_ (keys_val ==? 2) 120 | (store (((addrOf report) ~> keycode) ! 2 ) key) 121 | (store keys keys_val) 122 | ifte_ (keys_val ==? 3) 123 | (store (((addrOf report) ~> keycode) ! 3 ) key) 124 | (store keys keys_val) 125 | ifte_ (keys_val ==? 4) 126 | (store (((addrOf report) ~> keycode) ! 4 ) key) 127 | (store keys keys_val) 128 | ifte_ (keys_val ==? 5) 129 | (store (((addrOf report) ~> keycode) ! 5 ) key) 130 | (store keys keys_val) 131 | store keys (keys_val + 1) 132 | ) 133 | ) 134 | (do 135 | keys_val <- deref keys 136 | store keys keys_val) 137 | 138 | cadet :: Module 139 | cadet = package "cadet" $ do 140 | defMemArea report 141 | incl fn_pressed 142 | incl is_modifier 143 | incl update_report 144 | incl matrix_changed 145 | incl get_fn_key 146 | defStruct (Proxy :: Proxy "KeyboardInputReport") 147 | -------------------------------------------------------------------------------- /usb.h: -------------------------------------------------------------------------------- 1 | #ifndef usb_serial_h__ 2 | #define usb_serial_h__ 3 | 4 | #include 5 | 6 | void usb_init(void); // initialize everything 7 | uint8_t usb_configured(void); // is the USB port configured 8 | 9 | int8_t usb_keyboard_press(uint8_t key, uint8_t modifier); 10 | int8_t usb_keyboard_send(void); 11 | extern uint8_t keyboard_modifier_keys; 12 | extern uint8_t keyboard_keys[6]; 13 | extern volatile uint8_t keyboard_leds; 14 | 15 | int8_t usb_debug_putchar(uint8_t c); // transmit a character 16 | void usb_debug_flush_output(void); // immediately transmit any buffered output 17 | #define USB_DEBUG_HID 18 | 19 | #define KEY_A 4 20 | #define KEY_B 5 21 | #define KEY_C 6 22 | #define KEY_D 7 23 | #define KEY_E 8 24 | #define KEY_F 9 25 | #define KEY_G 10 26 | #define KEY_H 11 27 | #define KEY_I 12 28 | #define KEY_J 13 29 | #define KEY_K 14 30 | #define KEY_L 15 31 | #define KEY_M 16 32 | #define KEY_N 17 33 | #define KEY_O 18 34 | #define KEY_P 19 35 | #define KEY_Q 20 36 | #define KEY_R 21 37 | #define KEY_S 22 38 | #define KEY_T 23 39 | #define KEY_U 24 40 | #define KEY_V 25 41 | #define KEY_W 26 42 | #define KEY_X 27 43 | #define KEY_Y 28 44 | #define KEY_Z 29 45 | #define KEY_1 30 46 | #define KEY_2 31 47 | #define KEY_3 32 48 | #define KEY_4 33 49 | #define KEY_5 34 50 | #define KEY_6 35 51 | #define KEY_7 36 52 | #define KEY_8 37 53 | #define KEY_9 38 54 | #define KEY_0 39 55 | #define KEY_ENTER 40 56 | #define KEY_ESC 41 57 | #define KEY_BACKSPACE 42 58 | #define KEY_TAB 43 59 | #define KEY_SPACE 44 60 | #define KEY_MINUS 45 61 | #define KEY_EQUAL 46 62 | #define KEY_LEFT_BRACE 47 63 | #define KEY_RIGHT_BRACE 48 64 | #define KEY_BACKSLASH 49 65 | #define KEY_NUMBER 50 66 | #define KEY_SEMICOLON 51 67 | #define KEY_QUOTE 52 68 | #define KEY_TILDE 53 69 | #define KEY_COMMA 54 70 | #define KEY_PERIOD 55 71 | #define KEY_SLASH 56 72 | #define KEY_CAPS_LOCK 57 73 | #define KEY_F1 58 74 | #define KEY_F2 59 75 | #define KEY_F3 60 76 | #define KEY_F4 61 77 | #define KEY_F5 62 78 | #define KEY_F6 63 79 | #define KEY_F7 64 80 | #define KEY_F8 65 81 | #define KEY_F9 66 82 | #define KEY_F10 67 83 | #define KEY_F11 68 84 | #define KEY_F12 69 85 | #define KEY_PRINTSCREEN 70 86 | #define KEY_SCROLL_LOCK 71 87 | #define KEY_PAUSE 72 88 | #define KEY_INSERT 73 89 | #define KEY_HOME 74 90 | #define KEY_PAGE_UP 75 91 | #define KEY_DELETE 76 92 | #define KEY_END 77 93 | #define KEY_PAGE_DOWN 78 94 | #define KEY_RIGHT 79 95 | #define KEY_LEFT 80 96 | #define KEY_DOWN 81 97 | #define KEY_UP 82 98 | #define KEY_NUM_LOCK 83 99 | #define KEYPAD_SLASH 84 100 | #define KEYPAD_ASTERIX 85 101 | #define KEYPAD_MINUS 86 102 | #define KEYPAD_PLUS 87 103 | #define KEYPAD_ENTER 88 104 | #define KEYPAD_1 89 105 | #define KEYPAD_2 90 106 | #define KEYPAD_3 91 107 | #define KEYPAD_4 92 108 | #define KEYPAD_5 93 109 | #define KEYPAD_6 94 110 | #define KEYPAD_7 95 111 | #define KEYPAD_8 96 112 | #define KEYPAD_9 97 113 | #define KEYPAD_0 98 114 | #define KEYPAD_PERIOD 99 115 | 116 | #define KEY_MUTE 0x7F 117 | #define KEY_VOLUME_UP 0x80 118 | #define KEY_VOLUME_DOWN 0x81 119 | 120 | #define KEY_APP 101 121 | 122 | #define KEY_FN 0xC0 123 | 124 | #define KEY_LEFT_CTRL 0xE0 125 | #define KEY_LEFT_SHIFT 0xE1 126 | #define KEY_LEFT_ALT 0xE2 127 | #define KEY_LEFT_GUI 0xE3 128 | #define KEY_RIGHT_CTRL 0xE4 129 | #define KEY_RIGHT_SHIFT 0xE5 130 | #define KEY_RIGHT_ALT 0xE6 131 | #define KEY_RIGHT_GUI 0xE7 132 | 133 | 134 | 135 | 136 | // Everything below this point is only intended for usb_serial.c 137 | #ifdef USB_SERIAL_PRIVATE_INCLUDE 138 | #include 139 | #include 140 | #include 141 | 142 | #define EP_TYPE_CONTROL 0x00 143 | #define EP_TYPE_BULK_IN 0x81 144 | #define EP_TYPE_BULK_OUT 0x80 145 | #define EP_TYPE_INTERRUPT_IN 0xC1 146 | #define EP_TYPE_INTERRUPT_OUT 0xC0 147 | #define EP_TYPE_ISOCHRONOUS_IN 0x41 148 | #define EP_TYPE_ISOCHRONOUS_OUT 0x40 149 | 150 | #define EP_SINGLE_BUFFER 0x02 151 | #define EP_DOUBLE_BUFFER 0x06 152 | 153 | #define EP_SIZE(s) ((s) == 64 ? 0x30 : \ 154 | ((s) == 32 ? 0x20 : \ 155 | ((s) == 16 ? 0x10 : \ 156 | 0x00))) 157 | 158 | #define MAX_ENDPOINT 4 159 | 160 | #define LSB(n) (n & 255) 161 | #define MSB(n) ((n >> 8) & 255) 162 | 163 | #if defined(__AVR_AT90USB162__) 164 | #define HW_CONFIG() 165 | #define PLL_CONFIG() (PLLCSR = ((1< 2 | #include 3 | #include 4 | #include 5 | 6 | #include "cadet.h" 7 | #include "usb.h" 8 | #include "print.h" 9 | 10 | int main(void); 11 | uint16_t read_column(void); 12 | void deselect_row(uint8_t row); 13 | void select_row(uint8_t row); 14 | 15 | void select_row(uint8_t row) { 16 | if (row == 0) { 17 | DDRB |= 1 << 7; 18 | } else if (row == 1) { 19 | DDRB |= 1 << 3; 20 | } else if (row == 2) { 21 | DDRB |= 1 << 2; 22 | } else if (row == 3) { 23 | DDRB |= 1 << 1; 24 | } else if (row == 4) { 25 | DDRB |= 1 << 0; 26 | } 27 | } 28 | 29 | void deselect_row(uint8_t row) { 30 | if (row == 0) { 31 | DDRB &= ~(1 << 7); 32 | } else if (row == 1) { 33 | DDRB &= ~(1 << 3); 34 | } else if (row == 2) { 35 | DDRB &= ~(1 << 2); 36 | } else if (row == 3) { 37 | DDRB &= ~(1 << 1); 38 | } else if (row == 4) { 39 | DDRB &= ~(1 << 0); 40 | } 41 | } 42 | 43 | uint16_t read_column(void) { 44 | return (PIND & (1 << 0) ? 0 : (1 << 0)) | 45 | (PIND & (1 << 1) ? 0 : (1 << 1)) | 46 | (PIND & (1 << 2) ? 0 : (1 << 2)) | 47 | (PIND & (1 << 3) ? 0 : (1 << 3)) | 48 | (PIND & (1 << 5) ? 0 : (1 << 4)) | 49 | (PIND & (1 << 4) ? 0 : (1 << 5)) | 50 | (PIND & (1 << 6) ? 0 : (1 << 6)) | 51 | (PIND & (1 << 7) ? 0 : (1 << 7)) | 52 | (PINB & (1 << 4) ? 0 : (1 << 8)) | 53 | (PINB & (1 << 5) ? 0 : (1 << 9)) | 54 | (PINB & (1 << 6) ? 0 : (1 << 10)) | 55 | (PINC & (1 << 6) ? 0 : (1 << 11)) | 56 | (PINC & (1 << 7) ? 0 : (1 << 12)) | 57 | (PINE & (1 << 6) ? 0 : (1 << 13)) | 58 | (PINF & (1 << 1) ? 0 : (1 << 14)); 59 | } 60 | 61 | void print_matrix(uint16_t matrix[]) { 62 | for (uint8_t i = 0; i < 5; i++) { 63 | for (uint8_t j = 0; j < 15; j++) { 64 | if ((matrix[i] >> j) & 1) { 65 | print("1"); 66 | } else { 67 | print("0"); 68 | } 69 | } 70 | print("\n"); 71 | } 72 | } 73 | 74 | uint8_t keymap[2][5][15] = { 75 | { 76 | {KEY_ESC, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSLASH, KEY_TILDE}, 77 | {KEY_TAB, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_LEFT_BRACE, KEY_RIGHT_BRACE, KEY_BACKSPACE, 0}, 78 | {KEY_LEFT_CTRL, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, KEY_QUOTE, 0, KEY_ENTER, 0}, 79 | {KEY_LEFT_SHIFT, 0, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_COMMA, KEY_PERIOD, KEY_SLASH, 0, KEY_RIGHT_SHIFT, KEY_FN}, 80 | {KEY_CAPS_LOCK, KEY_LEFT_GUI, KEY_LEFT_ALT, 0, 0, 0, KEY_SPACE, 0, 0, 0, KEY_RIGHT_ALT, KEY_RIGHT_GUI, KEY_APP, KEY_RIGHT_CTRL, 0} 81 | }, 82 | { 83 | {0, 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_INSERT, KEY_DELETE}, 84 | {0, 0, 0, 0, 0, 0, 0, 0, KEY_PRINTSCREEN, KEY_SCROLL_LOCK, KEY_PAUSE, KEY_UP, 0, 0, 0}, 85 | {0, KEY_VOLUME_DOWN, KEY_VOLUME_UP, KEY_MUTE, 0, 0, KEYPAD_ASTERIX, KEYPAD_SLASH, KEY_HOME, KEY_PAGE_UP, KEY_LEFT, KEY_RIGHT, 0, 0, 0}, 86 | {0, 0, 0, 0, 0, 0, 0, KEYPAD_PLUS, KEYPAD_MINUS, KEY_END, KEY_PAGE_DOWN, KEY_DOWN, 0, 0, 0}, 87 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 88 | }, 89 | }; 90 | 91 | int main(void) { 92 | // set for 16 MHz clock 93 | CLKPR = 0x80; 94 | CLKPR = 0; 95 | 96 | // Initialize the USB, and then wait for the host to set configuration. 97 | // If the Teensy is powered without a PC connected to the USB port, 98 | // this will wait forever. 99 | usb_init(); 100 | while (!usb_configured()) /* wait */ ; 101 | 102 | // Wait an extra second for the PC's operating system to load drivers 103 | // and do whatever it does to actually be ready for input 104 | _delay_ms(1000); 105 | 106 | // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 107 | // D0 D1 D2 D3 D5 D4 D6 D7 B4 B5 B6 C6 C7 E6 F1 108 | // 0 B7 109 | // 1 B3 110 | // 2 B2 111 | // 3 B1 112 | // 4 B0 113 | 114 | // set pin mode 115 | PORTB = 0b01110000; 116 | PORTC = 0b11111111; 117 | PORTD = 0b11111111; 118 | PORTE = 0b11111111; 119 | PORTF = 0b11111111; 120 | 121 | uint16_t matrix_c[5] = {0, 0, 0, 0, 0}; 122 | uint16_t matrix_p[5] = {0, 0, 0, 0, 0}; 123 | uint16_t matrix_d[5] = {0, 0, 0, 0, 0}; 124 | 125 | uint8_t debouncing = 5; 126 | 127 | print("Ivory Cadet online.\n"); 128 | for (;;) { 129 | // read and debounce matrix 130 | for (uint8_t i = 0; i < 5; i++) { 131 | select_row(i); 132 | _delay_us(50); 133 | uint16_t column = read_column(); 134 | if (matrix_d[i] != column) { 135 | matrix_d[i] = column; 136 | if (debouncing) { 137 | print("bounce!: "); phex(debouncing); print("\n"); 138 | } 139 | debouncing = 5; 140 | } 141 | deselect_row(i); 142 | } 143 | if (debouncing) { 144 | debouncing--; 145 | if (debouncing) { 146 | _delay_ms(1); 147 | } else { 148 | for (uint8_t i = 0; i < 5; i++) { 149 | matrix_c[i] = matrix_d[i]; 150 | } 151 | } 152 | } 153 | // compare current and previous matrix 154 | if (matrix_changed(matrix_p, matrix_c) == true) { 155 | update_report(matrix_c, keymap); 156 | keyboard_modifier_keys = report.modifier; 157 | memcpy(&keyboard_keys, &report.keycode, sizeof keyboard_keys); 158 | usb_keyboard_send(); 159 | } 160 | // set previous matrix 161 | for (uint8_t i = 0; i < 5; i++) { 162 | matrix_p[i] = matrix_c[i]; 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /usb.c: -------------------------------------------------------------------------------- 1 | /* USB Keyboard Plus Debug Channel Example for Teensy USB Development Board 2 | * http://www.pjrc.com/teensy/usb_keyboard.html 3 | * Copyright (c) 2009 PJRC.COM, LLC 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | // Version 1.0: Initial Release 25 | // Version 1.1: Add support for Teensy 2.0 26 | 27 | #define USB_SERIAL_PRIVATE_INCLUDE 28 | #include "cadet.h" 29 | #include "usb.h" 30 | 31 | /************************************************************************** 32 | * 33 | * Configurable Options 34 | * 35 | **************************************************************************/ 36 | 37 | // You can change these to give your code its own name. 38 | #define STR_MANUFACTURER L"Jihyeok Seo" 39 | #define STR_PRODUCT L"Ivory Cadet" 40 | 41 | 42 | // Mac OS-X and Linux automatically load the correct drivers. On 43 | // Windows, even though the driver is supplied by Microsoft, an 44 | // INF file is needed to load the driver. These numbers need to 45 | // match the INF file. 46 | #define VENDOR_ID 0x16C0 47 | #define PRODUCT_ID 0x047D 48 | 49 | 50 | // USB devices are supposed to implment a halt feature, which is 51 | // rarely (if ever) used. If you comment this line out, the halt 52 | // code will be removed, saving 102 bytes of space (gcc 4.3.0). 53 | // This is not strictly USB compliant, but works with all major 54 | // operating systems. 55 | #define SUPPORT_ENDPOINT_HALT 56 | 57 | 58 | 59 | /************************************************************************** 60 | * 61 | * Endpoint Buffer Configuration 62 | * 63 | **************************************************************************/ 64 | 65 | #define ENDPOINT0_SIZE 32 66 | 67 | #define KEYBOARD_INTERFACE 0 68 | #define KEYBOARD_ENDPOINT 3 69 | #define KEYBOARD_SIZE 8 70 | #define KEYBOARD_BUFFER EP_DOUBLE_BUFFER 71 | 72 | #define DEBUG_INTERFACE 1 73 | #define DEBUG_TX_ENDPOINT 4 74 | #define DEBUG_TX_SIZE 32 75 | #define DEBUG_TX_BUFFER EP_DOUBLE_BUFFER 76 | 77 | const uint8_t PROGMEM endpoint_config_table[] = { 78 | 0, 79 | 0, 80 | 1, EP_TYPE_INTERRUPT_IN, EP_SIZE(KEYBOARD_SIZE) | KEYBOARD_BUFFER, 81 | 1, EP_TYPE_INTERRUPT_IN, EP_SIZE(DEBUG_TX_SIZE) | DEBUG_TX_BUFFER 82 | }; 83 | 84 | 85 | /************************************************************************** 86 | * 87 | * Descriptor Data 88 | * 89 | **************************************************************************/ 90 | 91 | // Descriptors are the data that your computer reads when it auto-detects 92 | // this USB device (called "enumeration" in USB lingo). The most commonly 93 | // changed items are editable at the top of this file. Changing things 94 | // in here should only be done by those who've read chapter 9 of the USB 95 | // spec and relevant portions of any USB class specifications! 96 | 97 | 98 | const uint8_t PROGMEM device_descriptor[] = { 99 | 18, // bLength 100 | 1, // bDescriptorType 101 | 0x00, 0x02, // bcdUSB 102 | 0, // bDeviceClass 103 | 0, // bDeviceSubClass 104 | 0, // bDeviceProtocol 105 | ENDPOINT0_SIZE, // bMaxPacketSize0 106 | LSB(VENDOR_ID), MSB(VENDOR_ID), // idVendor 107 | LSB(PRODUCT_ID), MSB(PRODUCT_ID), // idProduct 108 | 0x00, 0x01, // bcdDevice 109 | 1, // iManufacturer 110 | 2, // iProduct 111 | 0, // iSerialNumber 112 | 1 // bNumConfigurations 113 | }; 114 | 115 | // Keyboard Protocol 1, HID 1.11 spec, Appendix B, page 59-60 116 | const uint8_t PROGMEM keyboard_hid_report_desc[] = { 117 | 0x05, 0x01, // Usage Page (Generic Desktop), 118 | 0x09, 0x06, // Usage (Keyboard), 119 | 0xA1, 0x01, // Collection (Application), 120 | 0x75, 0x01, // Report Size (1), 121 | 0x95, 0x08, // Report Count (8), 122 | 0x05, 0x07, // Usage Page (Key Codes), 123 | 0x19, 0xE0, // Usage Minimum (224), 124 | 0x29, 0xE7, // Usage Maximum (231), 125 | 0x15, 0x00, // Logical Minimum (0), 126 | 0x25, 0x01, // Logical Maximum (1), 127 | 0x81, 0x02, // Input (Data, Variable, Absolute), ;Modifier byte 128 | 0x95, 0x01, // Report Count (1), 129 | 0x75, 0x08, // Report Size (8), 130 | 0x81, 0x03, // Input (Constant), ;Reserved byte 131 | 0x95, 0x05, // Report Count (5), 132 | 0x75, 0x01, // Report Size (1), 133 | 0x05, 0x08, // Usage Page (LEDs), 134 | 0x19, 0x01, // Usage Minimum (1), 135 | 0x29, 0x05, // Usage Maximum (5), 136 | 0x91, 0x02, // Output (Data, Variable, Absolute), ;LED report 137 | 0x95, 0x01, // Report Count (1), 138 | 0x75, 0x03, // Report Size (3), 139 | 0x91, 0x03, // Output (Constant), ;LED report padding 140 | 0x95, 0x06, // Report Count (6), 141 | 0x75, 0x08, // Report Size (8), 142 | 0x15, 0x00, // Logical Minimum (0), 143 | 0x25, 0x68, // Logical Maximum(104), 144 | 0x05, 0x07, // Usage Page (Key Codes), 145 | 0x19, 0x00, // Usage Minimum (0), 146 | 0x29, 0x68, // Usage Maximum (104), 147 | 0x81, 0x00, // Input (Data, Array), 148 | 0xc0 // End Collection 149 | }; 150 | 151 | const uint8_t PROGMEM debug_hid_report_desc[] = { 152 | 0x06, 0x31, 0xFF, // Usage Page 0xFF31 (vendor defined) 153 | 0x09, 0x74, // Usage 0x74 154 | 0xA1, 0x53, // Collection 0x53 155 | 0x75, 0x08, // report size = 8 bits 156 | 0x15, 0x00, // logical minimum = 0 157 | 0x26, 0xFF, 0x00, // logical maximum = 255 158 | 0x95, DEBUG_TX_SIZE, // report count 159 | 0x09, 0x75, // usage 160 | 0x81, 0x02, // Input (array) 161 | 0xC0 // end collection 162 | }; 163 | 164 | #define CONFIG1_DESC_SIZE (9+9+9+7+9+9+7) 165 | #define KEYBOARD_HID_DESC_OFFSET (9+9) 166 | #define DEBUG_HID_DESC_OFFSET (9+9+9+7+9) 167 | const uint8_t PROGMEM config1_descriptor[CONFIG1_DESC_SIZE] = { 168 | // configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10 169 | 9, // bLength; 170 | 2, // bDescriptorType; 171 | LSB(CONFIG1_DESC_SIZE), // wTotalLength 172 | MSB(CONFIG1_DESC_SIZE), 173 | 2, // bNumInterfaces 174 | 1, // bConfigurationValue 175 | 0, // iConfiguration 176 | 0xC0, // bmAttributes 177 | 50, // bMaxPower 178 | // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 179 | 9, // bLength 180 | 4, // bDescriptorType 181 | KEYBOARD_INTERFACE, // bInterfaceNumber 182 | 0, // bAlternateSetting 183 | 1, // bNumEndpoints 184 | 0x03, // bInterfaceClass (0x03 = HID) 185 | 0x01, // bInterfaceSubClass (0x01 = Boot) 186 | 0x01, // bInterfaceProtocol (0x01 = Keyboard) 187 | 0, // iInterface 188 | // HID interface descriptor, HID 1.11 spec, section 6.2.1 189 | 9, // bLength 190 | 0x21, // bDescriptorType 191 | 0x11, 0x01, // bcdHID 192 | 0, // bCountryCode 193 | 1, // bNumDescriptors 194 | 0x22, // bDescriptorType 195 | sizeof(keyboard_hid_report_desc), // wDescriptorLength 196 | 0, 197 | // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 198 | 7, // bLength 199 | 5, // bDescriptorType 200 | KEYBOARD_ENDPOINT | 0x80, // bEndpointAddress 201 | 0x03, // bmAttributes (0x03=intr) 202 | KEYBOARD_SIZE, 0, // wMaxPacketSize 203 | 1, // bInterval 204 | // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 205 | 9, // bLength 206 | 4, // bDescriptorType 207 | DEBUG_INTERFACE, // bInterfaceNumber 208 | 0, // bAlternateSetting 209 | 1, // bNumEndpoints 210 | 0x03, // bInterfaceClass (0x03 = HID) 211 | 0x00, // bInterfaceSubClass 212 | 0x00, // bInterfaceProtocol 213 | 0, // iInterface 214 | // HID interface descriptor, HID 1.11 spec, section 6.2.1 215 | 9, // bLength 216 | 0x21, // bDescriptorType 217 | 0x11, 0x01, // bcdHID 218 | 0, // bCountryCode 219 | 1, // bNumDescriptors 220 | 0x22, // bDescriptorType 221 | sizeof(debug_hid_report_desc), // wDescriptorLength 222 | 0, 223 | // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 224 | 7, // bLength 225 | 5, // bDescriptorType 226 | DEBUG_TX_ENDPOINT | 0x80, // bEndpointAddress 227 | 0x03, // bmAttributes (0x03=intr) 228 | DEBUG_TX_SIZE, 0, // wMaxPacketSize 229 | 1 // bInterval 230 | }; 231 | 232 | // If you're desperate for a little extra code memory, these strings 233 | // can be completely removed if iManufacturer, iProduct, iSerialNumber 234 | // in the device desciptor are changed to zeros. 235 | struct usb_string_descriptor_struct { 236 | uint8_t bLength; 237 | uint8_t bDescriptorType; 238 | int16_t wString[]; 239 | }; 240 | const struct usb_string_descriptor_struct PROGMEM string0 = { 241 | 4, 242 | 3, 243 | {0x0409} 244 | }; 245 | const struct usb_string_descriptor_struct PROGMEM string1 = { 246 | sizeof(STR_MANUFACTURER), 247 | 3, 248 | STR_MANUFACTURER 249 | }; 250 | const struct usb_string_descriptor_struct PROGMEM string2 = { 251 | sizeof(STR_PRODUCT), 252 | 3, 253 | STR_PRODUCT 254 | }; 255 | 256 | // This table defines which descriptor data is sent for each specific 257 | // request from the host (in wValue and wIndex). 258 | const struct descriptor_list_struct { 259 | uint16_t wValue; 260 | uint16_t wIndex; 261 | const uint8_t *addr; 262 | uint8_t length; 263 | } PROGMEM descriptor_list[] = { 264 | {0x0100, 0x0000, device_descriptor, sizeof(device_descriptor)}, 265 | {0x0200, 0x0000, config1_descriptor, sizeof(config1_descriptor)}, 266 | {0x2200, KEYBOARD_INTERFACE, keyboard_hid_report_desc, sizeof(keyboard_hid_report_desc)}, 267 | {0x2100, KEYBOARD_INTERFACE, config1_descriptor+KEYBOARD_HID_DESC_OFFSET, 9}, 268 | {0x2200, DEBUG_INTERFACE, debug_hid_report_desc, sizeof(debug_hid_report_desc)}, 269 | {0x2100, DEBUG_INTERFACE, config1_descriptor+DEBUG_HID_DESC_OFFSET, 9}, 270 | {0x0300, 0x0000, (const uint8_t *)&string0, 4}, 271 | {0x0301, 0x0409, (const uint8_t *)&string1, sizeof(STR_MANUFACTURER)}, 272 | {0x0302, 0x0409, (const uint8_t *)&string2, sizeof(STR_PRODUCT)} 273 | }; 274 | #define NUM_DESC_LIST (sizeof(descriptor_list)/sizeof(struct descriptor_list_struct)) 275 | 276 | 277 | /************************************************************************** 278 | * 279 | * Variables - these are the only non-stack RAM usage 280 | * 281 | **************************************************************************/ 282 | 283 | // zero when we are not configured, non-zero when enumerated 284 | static volatile uint8_t usb_configuration=0; 285 | 286 | // the time remaining before we transmit any partially full 287 | // packet, or send a zero length packet. 288 | static volatile uint8_t debug_flush_timer=0; 289 | 290 | // which modifier keys are currently pressed 291 | // 1=left ctrl, 2=left shift, 4=left alt, 8=left gui 292 | // 16=right ctrl, 32=right shift, 64=right alt, 128=right gui 293 | uint8_t keyboard_modifier_keys=0; 294 | 295 | // which keys are currently pressed, up to 6 keys may be down at once 296 | uint8_t keyboard_keys[6]={0,0,0,0,0,0}; 297 | 298 | // protocol setting from the host. We use exactly the same report 299 | // either way, so this variable only stores the setting since we 300 | // are required to be able to report which setting is in use. 301 | static uint8_t keyboard_protocol=1; 302 | 303 | // the idle configuration, how often we send the report to the 304 | // host (ms * 4) even when it hasn't changed 305 | static uint8_t keyboard_idle_config=125; 306 | 307 | // count until idle timeout 308 | static uint8_t keyboard_idle_count=0; 309 | 310 | // 1=num lock, 2=caps lock, 4=scroll lock, 8=compose, 16=kana 311 | volatile uint8_t keyboard_leds=0; 312 | 313 | 314 | /************************************************************************** 315 | * 316 | * Public Functions - these are the API intended for the user 317 | * 318 | **************************************************************************/ 319 | 320 | 321 | // initialize USB 322 | void usb_init(void) 323 | { 324 | HW_CONFIG(); 325 | USB_FREEZE(); // enable USB 326 | PLL_CONFIG(); // config PLL 327 | while (!(PLLCSR & (1<= NUM_DESC_LIST) { 578 | UECONX = (1< desc_length) len = desc_length; 600 | do { 601 | // wait for host ready for IN packet 602 | do { 603 | i = UEINTX; 604 | } while (!(i & ((1<= 1 && i <= MAX_ENDPOINT) { 666 | usb_send_in(); 667 | UENUM = i; 668 | if (bRequest == SET_FEATURE) { 669 | UECONX = (1<> 8); 714 | keyboard_idle_count = 0; 715 | //usb_wait_in_ready(); 716 | usb_send_in(); 717 | return; 718 | } 719 | if (bRequest == HID_SET_PROTOCOL) { 720 | keyboard_protocol = wValue; 721 | //usb_wait_in_ready(); 722 | usb_send_in(); 723 | return; 724 | } 725 | } 726 | } 727 | if (wIndex == DEBUG_INTERFACE) { 728 | if (bRequest == HID_GET_REPORT && bmRequestType == 0xA1) { 729 | len = wLength; 730 | do { 731 | // wait for host ready for IN packet 732 | do { 733 | i = UEINTX; 734 | } while (!(i & ((1<