From be9be90483ce083490c11b1ae4695ec5149bc783 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Mon, 7 Sep 2015 21:33:06 +0100 Subject: [PATCH 01/56] Modularity and gcc warnings fixes. --- tmk_core/common.mk | 2 +- tmk_core/common/action_tapping.c | 2 +- tmk_core/common/bootmagic.c | 2 +- tmk_core/common/command.c | 15 ++++++++++++--- tmk_core/common/keymap.c | 6 ++++++ tmk_core/common/progmem.h | 4 ++-- 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/tmk_core/common.mk b/tmk_core/common.mk index cb030b33..f5a464d0 100644 --- a/tmk_core/common.mk +++ b/tmk_core/common.mk @@ -1,4 +1,4 @@ -COMMON_DIR = common +COMMON_DIR = $(TMK_DIR)/common SRC += $(COMMON_DIR)/host.c \ $(COMMON_DIR)/keyboard.c \ $(COMMON_DIR)/action.c \ diff --git a/tmk_core/common/action_tapping.c b/tmk_core/common/action_tapping.c index 826c2330..487c62f1 100644 --- a/tmk_core/common/action_tapping.c +++ b/tmk_core/common/action_tapping.c @@ -257,7 +257,7 @@ bool process_tapping(keyrecord_t *keyp) return true; } } else { - if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n"); + if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n") {}; process_action(keyp); return true; } diff --git a/tmk_core/common/bootmagic.c b/tmk_core/common/bootmagic.c index b002a585..d6684b2d 100644 --- a/tmk_core/common/bootmagic.c +++ b/tmk_core/common/bootmagic.c @@ -19,7 +19,7 @@ void bootmagic(void) } /* do scans in case of bounce */ - print("boogmagic scan: ... "); + print("bootmagic scan: ... "); uint8_t scan = 100; while (scan--) { matrix_scan(); _delay_ms(10); } print("done.\n"); diff --git a/tmk_core/common/command.c b/tmk_core/common/command.c index 1b6808be..b017f2e2 100644 --- a/tmk_core/common/command.c +++ b/tmk_core/common/command.c @@ -16,7 +16,7 @@ along with this program. If not, see . */ #include #include -#include +#include "wait.h" #include "keycode.h" #include "host.h" #include "keymap.h" @@ -97,12 +97,14 @@ bool command_proc(uint8_t code) bool command_extra(uint8_t code) __attribute__ ((weak)); bool command_extra(uint8_t code) { + (void)code; return false; } bool command_console_extra(uint8_t code) __attribute__ ((weak)); bool command_console_extra(uint8_t code) { + (void)code; return false; } @@ -178,7 +180,9 @@ static void print_eeconfig(void) static bool command_common(uint8_t code) { +#ifdef KEYBOARD_LOCK_ENABLE static host_driver_t *host_driver = 0; +#endif switch (code) { #ifdef SLEEP_LED_ENABLE case KC_Z: @@ -223,7 +227,7 @@ static bool command_common(uint8_t code) case KC_PAUSE: clear_keyboard(); print("\n\nbootloader... "); - _delay_ms(1000); + wait_ms(1000); bootloader_jump(); // not return break; case KC_D: @@ -307,15 +311,20 @@ static bool command_common(uint8_t code) " " STR(BOOTLOADER_SIZE) "\n"); print("GCC: " STR(__GNUC__) "." STR(__GNUC_MINOR__) "." STR(__GNUC_PATCHLEVEL__) +#if defined(__AVR__) " AVR-LIBC: " __AVR_LIBC_VERSION_STRING__ " AVR_ARCH: avr" STR(__AVR_ARCH__) "\n"); +#elif defined(__arm__) + // TODO + ); +#endif break; case KC_S: print("\n\t- Status -\n"); print_val_hex8(host_keyboard_leds()); print_val_hex8(keyboard_protocol); print_val_hex8(keyboard_idle); - print_val_hex32(timer_count); + print_val_hex32(timer_read32()); #ifdef PROTOCOL_PJRC print_val_hex8(UDCON); diff --git a/tmk_core/common/keymap.c b/tmk_core/common/keymap.c index 9f4fab52..be00076c 100644 --- a/tmk_core/common/keymap.c +++ b/tmk_core/common/keymap.c @@ -108,6 +108,9 @@ action_t action_for_key(uint8_t layer, keypos_t key) __attribute__ ((weak)) const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { + (void)record; + (void)id; + (void)opt; return MACRO_NONE; } @@ -115,6 +118,9 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) __attribute__ ((weak)) void action_function(keyrecord_t *record, uint8_t id, uint8_t opt) { + (void)record; + (void)id; + (void)opt; } diff --git a/tmk_core/common/progmem.h b/tmk_core/common/progmem.h index 199b1bed..5b276562 100644 --- a/tmk_core/common/progmem.h +++ b/tmk_core/common/progmem.h @@ -5,8 +5,8 @@ # include #elif defined(__arm__) # define PROGMEM -# define pgm_read_byte(p) *(p) -# define pgm_read_word(p) *(p) +# define pgm_read_byte(p) *((unsigned char*)p) +# define pgm_read_word(p) *((uint16_t*)p) #endif #endif From 27dec2db7bb33cc45b04b89b6855bac6b98de2e5 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Mon, 7 Sep 2015 21:57:04 +0100 Subject: [PATCH 02/56] Add ChibiOS support (USB stack + support files). --- tmk_core/common/chibios/bootloader.c | 7 + tmk_core/common/chibios/printf.c | 240 +++++ tmk_core/common/chibios/printf.h | 111 +++ tmk_core/common/chibios/suspend.c | 8 + tmk_core/common/chibios/timer.c | 27 + tmk_core/common/command.c | 3 + tmk_core/common/print.c | 8 +- tmk_core/common/print.h | 10 +- tmk_core/common/wait.h | 8 +- tmk_core/protocol/chibios/.gitignore | 1 + tmk_core/protocol/chibios/README.md | 25 + tmk_core/protocol/chibios/chibios.mk | 225 +++++ tmk_core/protocol/chibios/main.c | 91 ++ tmk_core/protocol/chibios/usb_main.c | 1251 ++++++++++++++++++++++++++ tmk_core/protocol/chibios/usb_main.h | 149 +++ tmk_core/tool/chibios/common.mk | 86 ++ 16 files changed, 2245 insertions(+), 5 deletions(-) create mode 100644 tmk_core/common/chibios/bootloader.c create mode 100644 tmk_core/common/chibios/printf.c create mode 100644 tmk_core/common/chibios/printf.h create mode 100644 tmk_core/common/chibios/suspend.c create mode 100644 tmk_core/common/chibios/timer.c create mode 100644 tmk_core/protocol/chibios/.gitignore create mode 100644 tmk_core/protocol/chibios/README.md create mode 100644 tmk_core/protocol/chibios/chibios.mk create mode 100644 tmk_core/protocol/chibios/main.c create mode 100644 tmk_core/protocol/chibios/usb_main.c create mode 100644 tmk_core/protocol/chibios/usb_main.h create mode 100644 tmk_core/tool/chibios/common.mk diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c new file mode 100644 index 00000000..6c34e2e0 --- /dev/null +++ b/tmk_core/common/chibios/bootloader.c @@ -0,0 +1,7 @@ +/* TODO */ +/* ... chip dependent ... */ + +#include "bootloader.h" + + +void bootloader_jump(void) {} diff --git a/tmk_core/common/chibios/printf.c b/tmk_core/common/chibios/printf.c new file mode 100644 index 00000000..72e3d4f8 --- /dev/null +++ b/tmk_core/common/chibios/printf.c @@ -0,0 +1,240 @@ +/* + * found at: http://www.sparetimelabs.com/tinyprintf/tinyprintf.php + * and: http://www.sparetimelabs.com/printfrevisited/printfrevisited.php + */ + +/* +File: printf.c + +Copyright (C) 2004 Kustaa Nyholm + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "printf.h" + +typedef void (*putcf) (void*,char); +static putcf stdout_putf; +static void* stdout_putp; + +// this adds cca 400 bytes +#define PRINTF_LONG_SUPPORT + +#ifdef PRINTF_LONG_SUPPORT + +static void uli2a(unsigned long int num, unsigned int base, int uc,char * bf) + { + int n=0; + unsigned int d=1; + while (num/d >= base) + d*=base; + while (d!=0) { + int dgt = num / d; + num%=d; + d/=base; + if (n || dgt>0|| d==0) { + *bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10); + ++n; + } + } + *bf=0; + } + +static void li2a (long num, char * bf) + { + if (num<0) { + num=-num; + *bf++ = '-'; + } + uli2a(num,10,0,bf); + } + +#endif + +static void ui2a(unsigned int num, unsigned int base, int uc,char * bf) + { + int n=0; + unsigned int d=1; + while (num/d >= base) + d*=base; + while (d!=0) { + int dgt = num / d; + num%= d; + d/=base; + if (n || dgt>0 || d==0) { + *bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10); + ++n; + } + } + *bf=0; + } + +static void i2a (int num, char * bf) + { + if (num<0) { + num=-num; + *bf++ = '-'; + } + ui2a(num,10,0,bf); + } + +static int a2d(char ch) + { + if (ch>='0' && ch<='9') + return ch-'0'; + else if (ch>='a' && ch<='f') + return ch-'a'+10; + else if (ch>='A' && ch<='F') + return ch-'A'+10; + else return -1; + } + +static char a2i(char ch, char** src,int base,int* nump) + { + char* p= *src; + int num=0; + int digit; + while ((digit=a2d(ch))>=0) { + if (digit>base) break; + num=num*base+digit; + ch=*p++; + } + *src=p; + *nump=num; + return ch; + } + +static void putchw(void* putp,putcf putf,int n, char z, char* bf) + { + char fc=z? '0' : ' '; + char ch; + char* p=bf; + while (*p++ && n > 0) + n--; + while (n-- > 0) + putf(putp,fc); + while ((ch= *bf++)) + putf(putp,ch); + } + +void tfp_format(void* putp,putcf putf,char *fmt, va_list va) + { + char bf[12]; + + char ch; + + + while ((ch=*(fmt++))) { + if (ch!='%') + putf(putp,ch); + else { + char lz=0; +#ifdef PRINTF_LONG_SUPPORT + char lng=0; +#endif + int w=0; + ch=*(fmt++); + if (ch=='0') { + ch=*(fmt++); + lz=1; + } + if (ch>='0' && ch<='9') { + ch=a2i(ch,&fmt,10,&w); + } +#ifdef PRINTF_LONG_SUPPORT + if (ch=='l') { + ch=*(fmt++); + lng=1; + } +#endif + switch (ch) { + case 0: + goto abort; + case 'u' : { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int),10,0,bf); + else +#endif + ui2a(va_arg(va, unsigned int),10,0,bf); + putchw(putp,putf,w,lz,bf); + break; + } + case 'd' : { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + li2a(va_arg(va, unsigned long int),bf); + else +#endif + i2a(va_arg(va, int),bf); + putchw(putp,putf,w,lz,bf); + break; + } + case 'x': case 'X' : +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int),16,(ch=='X'),bf); + else +#endif + ui2a(va_arg(va, unsigned int),16,(ch=='X'),bf); + putchw(putp,putf,w,lz,bf); + break; + case 'c' : + putf(putp,(char)(va_arg(va, int))); + break; + case 's' : + putchw(putp,putf,w,0,va_arg(va, char*)); + break; + case '%' : + putf(putp,ch); + default: + break; + } + } + } + abort:; + } + + +void init_printf(void* putp,void (*putf) (void*,char)) + { + stdout_putf=putf; + stdout_putp=putp; + } + +void tfp_printf(char *fmt, ...) + { + va_list va; + va_start(va,fmt); + tfp_format(stdout_putp,stdout_putf,fmt,va); + va_end(va); + } + +static void putcp(void* p,char c) + { + *(*((char**)p))++ = c; + } + + + +void tfp_sprintf(char* s,char *fmt, ...) + { + va_list va; + va_start(va,fmt); + tfp_format(&s,putcp,fmt,va); + putcp(&s,0); + va_end(va); + } diff --git a/tmk_core/common/chibios/printf.h b/tmk_core/common/chibios/printf.h new file mode 100644 index 00000000..00813198 --- /dev/null +++ b/tmk_core/common/chibios/printf.h @@ -0,0 +1,111 @@ +/* + * found at: http://www.sparetimelabs.com/tinyprintf/tinyprintf.php + * and: http://www.sparetimelabs.com/printfrevisited/printfrevisited.php + */ + +/* +File: printf.h + +Copyright (C) 2004 Kustaa Nyholm + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +This library is realy just two files: 'printf.h' and 'printf.c'. + +They provide a simple and small (+200 loc) printf functionality to +be used in embedded systems. + +I've found them so usefull in debugging that I do not bother with a +debugger at all. + +They are distributed in source form, so to use them, just compile them +into your project. + +Two printf variants are provided: printf and sprintf. + +The formats supported by this implementation are: 'd' 'u' 'c' 's' 'x' 'X'. + +Zero padding and field width are also supported. + +If the library is compiled with 'PRINTF_SUPPORT_LONG' defined then the +long specifier is also +supported. Note that this will pull in some long math routines (pun intended!) +and thus make your executable noticably longer. + +The memory foot print of course depends on the target cpu, compiler and +compiler options, but a rough guestimate (based on a H8S target) is about +1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space. +Not too bad. Your milage may vary. By hacking the source code you can +get rid of some hunred bytes, I'm sure, but personally I feel the balance of +functionality and flexibility versus code size is close to optimal for +many embedded systems. + +To use the printf you need to supply your own character output function, +something like : + + void putc ( void* p, char c) + { + while (!SERIAL_PORT_EMPTY) ; + SERIAL_PORT_TX_REGISTER = c; + } + +Before you can call printf you need to initialize it to use your +character output function with something like: + + init_printf(NULL,putc); + +Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc', +the NULL (or any pointer) you pass into the 'init_printf' will eventually be +passed to your 'putc' routine. This allows you to pass some storage space (or +anything realy) to the character output function, if necessary. +This is not often needed but it was implemented like that because it made +implementing the sprintf function so neat (look at the source code). + +The code is re-entrant, except for the 'init_printf' function, so it +is safe to call it from interupts too, although this may result in mixed output. +If you rely on re-entrancy, take care that your 'putc' function is re-entrant! + +The printf and sprintf functions are actually macros that translate to +'tfp_printf' and 'tfp_sprintf'. This makes it possible +to use them along with 'stdio.h' printf's in a single source file. +You just need to undef the names before you include the 'stdio.h'. +Note that these are not function like macros, so if you have variables +or struct members with these names, things will explode in your face. +Without variadic macros this is the best we can do to wrap these +fucnction. If it is a problem just give up the macros and use the +functions directly or rename them. + +For further details see source code. + +regs Kusti, 23.10.2004 +*/ + + +#ifndef __TFP_PRINTF__ +#define __TFP_PRINTF__ + +#include + +void init_printf(void* putp,void (*putf) (void*,char)); + +void tfp_printf(char *fmt, ...); +void tfp_sprintf(char* s,char *fmt, ...); + +void tfp_format(void* putp,void (*putf) (void*,char),char *fmt, va_list va); + +#define printf tfp_printf +#define sprintf tfp_sprintf + +#endif diff --git a/tmk_core/common/chibios/suspend.c b/tmk_core/common/chibios/suspend.c new file mode 100644 index 00000000..49ecad43 --- /dev/null +++ b/tmk_core/common/chibios/suspend.c @@ -0,0 +1,8 @@ +/* TODO */ + +#include + + +void suspend_power_down(void) {} +bool suspend_wakeup_condition(void) { return true; } +void suspend_wakeup_init(void) {} diff --git a/tmk_core/common/chibios/timer.c b/tmk_core/common/chibios/timer.c new file mode 100644 index 00000000..c812994b --- /dev/null +++ b/tmk_core/common/chibios/timer.c @@ -0,0 +1,27 @@ +#include "ch.h" + +#include "timer.h" + +void timer_init(void) {} + +void timer_clear(void) {} + +uint16_t timer_read(void) +{ + return (uint16_t)ST2MS(chVTGetSystemTime()); +} + +uint32_t timer_read32(void) +{ + return ST2MS(chVTGetSystemTime()); +} + +uint16_t timer_elapsed(uint16_t last) +{ + return (uint16_t)(ST2MS(chVTTimeElapsedSinceX(MS2ST(last)))); +} + +uint32_t timer_elapsed32(uint32_t last) +{ + return ST2MS(chVTTimeElapsedSinceX(MS2ST(last))); +} diff --git a/tmk_core/common/command.c b/tmk_core/common/command.c index b017f2e2..8fb6db56 100644 --- a/tmk_core/common/command.c +++ b/tmk_core/common/command.c @@ -287,6 +287,9 @@ static bool command_common(uint8_t code) #ifdef PROTOCOL_VUSB " VUSB" #endif +#ifdef PROTOCOL_CHIBIOS + " CHIBIOS" +#endif #ifdef BOOTMAGIC_ENABLE " BOOTMAGIC" #endif diff --git a/tmk_core/common/print.c b/tmk_core/common/print.c index ca94e1e5..00489557 100644 --- a/tmk_core/common/print.c +++ b/tmk_core/common/print.c @@ -38,11 +38,15 @@ void print_set_sendchar(int8_t (*sendchar_func)(uint8_t)) xdev_out(sendchar_func); } -#elif defined(__arm__) +#elif defined(PROTOCOL_CHIBIOS) /* __AVR__ */ + +// don't need anything extra + +#elif defined(__arm__) /* __AVR__ */ // TODO //void print_set_sendchar(int8_t (*sendchar_func)(uint8_t)) { } -#endif +#endif /* __AVR__ */ #endif diff --git a/tmk_core/common/print.h b/tmk_core/common/print.h index c0e9e143..1e1b9b40 100644 --- a/tmk_core/common/print.h +++ b/tmk_core/common/print.h @@ -47,7 +47,15 @@ extern "C" /* function pointer of sendchar to be used by print utility */ void print_set_sendchar(int8_t (*print_sendchar_func)(uint8_t)); -#elif defined(__arm__) +#elif defined(PROTOCOL_CHIBIOS) /* __AVR__ */ + +#include "chibios/printf.h" + +#define print(s) printf(s) +#define println(s) printf(s "\r\n") +#define xprintf printf + +#elif defined(__arm__) /* __AVR__ */ #include "mbed/xprintf.h" diff --git a/tmk_core/common/wait.h b/tmk_core/common/wait.h index 40d00b0c..82727be0 100644 --- a/tmk_core/common/wait.h +++ b/tmk_core/common/wait.h @@ -9,9 +9,13 @@ extern "C" { # include # define wait_ms(ms) _delay_ms(ms) # define wait_us(us) _delay_us(us) -#elif defined(__arm__) +#elif defined(PROTOCOL_CHIBIOS) /* __AVR__ */ +# include "ch.h" +# define wait_ms(ms) chThdSleepMilliseconds(ms) +# define wait_us(us) chThdSleepMicroseconds(us) +#elif defined(__arm__) /* __AVR__ */ # include "wait_api.h" -#endif +#endif /* __AVR__ */ #ifdef __cplusplus } diff --git a/tmk_core/protocol/chibios/.gitignore b/tmk_core/protocol/chibios/.gitignore new file mode 100644 index 00000000..3118dbef --- /dev/null +++ b/tmk_core/protocol/chibios/.gitignore @@ -0,0 +1 @@ +chibios diff --git a/tmk_core/protocol/chibios/README.md b/tmk_core/protocol/chibios/README.md new file mode 100644 index 00000000..38fd495e --- /dev/null +++ b/tmk_core/protocol/chibios/README.md @@ -0,0 +1,25 @@ +## USB stack implementation using ChibiOS + +### Notes + +- To use, unpack or symlink ChibiOS here, to `chibios`. +- For gcc options, inspect `chibios.mk`. For instance, I enabled `-Wno-missing-field-initializers`, because TMK common bits generated a lot of hits on that. +Also pay attention to `-O0` (enabled for debugging); for deployment use `-O2`. +- USB string descriptors are a mess. I did not find a way to cleanly generate the right structures from actual strings, so the definitions in individual keyboards' `config.h` are ugly as heck. +- There are some random constants left so far, e.g. 5ms sleep between calling `keyboard_task` in `main.c`. There should be no such in `usb_main.c`. Everything is based on timers/interrupts/kernel scheduling (well except `keyboard_task`), so no periodically called things (again, except `keyboard_task`, which is just how TMK is designed). +- It is easy to add some code for testing (e.g. blink LED, do stuff on button press, etc...) - just create another thread in `main.c`, it will run independently of the keyboard business. +- The USB stack works pretty completely; however there are bits of other TMK stuff that are not done yet: + +### Immediate todo + +- suspend / sleep led + +### Missing / not working (TMK vs ChibiOS bits) + +- eeprom / bootmagic (will be chip dependent) +- bootloader jump (chip dependent) + +### Tried with + +- ChibiOS 3.0.1 and ST F072RB DISCOVERY board. +- Need to test on other STM32 chips (F3, F4) to make it as much chip-independent as possible. diff --git a/tmk_core/protocol/chibios/chibios.mk b/tmk_core/protocol/chibios/chibios.mk new file mode 100644 index 00000000..d667f60e --- /dev/null +++ b/tmk_core/protocol/chibios/chibios.mk @@ -0,0 +1,225 @@ +############################################################################## +# Build global options +# NOTE: Can be overridden externally. +# + +# Compiler options here. +ifeq ($(USE_OPT),) + USE_OPT = -O0 -ggdb -fomit-frame-pointer -falign-functions=16 -std=gnu99 -DPROTOCOL_CHIBIOS +endif + +# C specific options here (added to USE_OPT). +ifeq ($(USE_COPT),) + USE_COPT = +endif + +# include specific config.h? +ifdef CONFIG_H + USE_COPT += -include $(CONFIG_H) +endif + +# C++ specific options here (added to USE_OPT). +ifeq ($(USE_CPPOPT),) + USE_CPPOPT = -fno-rtti +endif + +# Enable this if you want the linker to remove unused code and data +ifeq ($(USE_LINK_GC),) + USE_LINK_GC = yes +endif + +# Linker extra options here. +ifeq ($(USE_LDOPT),) + USE_LDOPT = +endif + +# Enable this if you want link time optimizations (LTO) +ifeq ($(USE_LTO),) + USE_LTO = yes +endif + +# If enabled, this option allows to compile the application in THUMB mode. +ifeq ($(USE_THUMB),) + USE_THUMB = yes +endif + +# Enable this if you want to see the full log while compiling. +ifeq ($(USE_VERBOSE_COMPILE),) + USE_VERBOSE_COMPILE = no +endif + +# If enabled, this option makes the build process faster by not compiling +# modules not used in the current configuration. +ifeq ($(USE_SMART_BUILD),) + USE_SMART_BUILD = yes +endif + +# +# Build global options +############################################################################## + +############################################################################## +# Architecture or project specific options +# + +# Stack size to be allocated to the Cortex-M process stack. This stack is +# the stack used by the main() thread. +ifeq ($(USE_PROCESS_STACKSIZE),) + USE_PROCESS_STACKSIZE = 0x200 +endif + +# Stack size to the allocated to the Cortex-M main/exceptions stack. This +# stack is used for processing interrupts and exceptions. +ifeq ($(USE_EXCEPTIONS_STACKSIZE),) + USE_EXCEPTIONS_STACKSIZE = 0x400 +endif + +# +# Architecture or project specific options +############################################################################## + +############################################################################## +# Project, sources and paths +# + +# Imported source files and paths +CHIBIOS = $(TMK_DIR)/protocol/chibios/chibios +# Startup files. +include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/startup_$(shell echo $(MCU_SERIES) | tr '[:upper:]' '[:lower:]').mk +# HAL-OSAL files (optional). +include $(CHIBIOS)/os/hal/hal.mk +include $(CHIBIOS)/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)/platform.mk +ifneq ("$(wildcard $(TARGET_DIR)/boards/$(BOARD))","") + include $(TARGET_DIR)/boards/$(BOARD)/board.mk +else + include $(CHIBIOS)/os/hal/boards/$(BOARD)/board.mk +endif +include $(CHIBIOS)/os/hal/osal/rt/osal.mk +# RTOS files (optional). +include $(CHIBIOS)/os/rt/rt.mk +include $(CHIBIOS)/os/rt/ports/ARMCMx/compilers/GCC/mk/port_v6m.mk +# Other files (optional). + +# Define linker script file here +ifneq ("$(wildcard $(TARGET_DIR)/ld/$(MCU_MODEL_FAMILY).ld)","") +LDSCRIPT = $(TARGET_DIR)/ld/$(MCU_MODEL_FAMILY).ld +else +LDSCRIPT = $(STARTUPLD)/$(MCU_MODEL_FAMILY).ld +endif + +# C sources that can be compiled in ARM or THUMB mode depending on the global +# setting. +CSRC = $(STARTUPSRC) \ + $(KERNSRC) \ + $(PORTSRC) \ + $(OSALSRC) \ + $(HALSRC) \ + $(PLATFORMSRC) \ + $(BOARDSRC) \ + $(CHIBIOS)/os/hal/lib/streams/chprintf.c \ + $(TMK_DIR)/protocol/chibios/usb_main.c \ + $(TMK_DIR)/protocol/chibios/main.c \ + $(SRC) + +# C++ sources that can be compiled in ARM or THUMB mode depending on the global +# setting. +CPPSRC = + +# C sources to be compiled in ARM mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +ACSRC = + +# C++ sources to be compiled in ARM mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +ACPPSRC = + +# C sources to be compiled in THUMB mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +TCSRC = + +# C sources to be compiled in THUMB mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +TCPPSRC = + +# List ASM source files here +ASMSRC = $(STARTUPASM) $(PORTASM) $(OSALASM) + +INCDIR = $(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \ + $(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \ + $(CHIBIOS)/os/hal/lib/streams $(CHIBIOS)/os/various \ + $(TMK_DIR) $(COMMON_DIR) $(TMK_DIR)/protocol/chibios \ + $(TARGET_DIR) + +# +# Project, sources and paths +############################################################################## + +############################################################################## +# Compiler settings +# + +MCU = cortex-m0 + +#TRGT = arm-elf- +TRGT = arm-none-eabi- +CC = $(TRGT)gcc +CPPC = $(TRGT)g++ +# Enable loading with g++ only if you need C++ runtime support. +# NOTE: You can use C++ even without C++ support if you are careful. C++ +# runtime support makes code size explode. +LD = $(TRGT)gcc +#LD = $(TRGT)g++ +CP = $(TRGT)objcopy +AS = $(TRGT)gcc -x assembler-with-cpp +AR = $(TRGT)ar +OD = $(TRGT)objdump +SZ = $(TRGT)size +HEX = $(CP) -O ihex +BIN = $(CP) -O binary + +# ARM-specific options here +AOPT = + +# THUMB-specific options here +TOPT = -mthumb -DTHUMB + +# Define C warning options here +CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes -Wno-missing-field-initializers + +# Define C++ warning options here +CPPWARN = -Wall -Wextra -Wundef + +# +# Compiler settings +############################################################################## + +############################################################################## +# Start of user section +# + +# List all user C define here, like -D_DEBUG=1 +## Select which interfaces to include here! +UDEFS = $(OPT_DEFS) + +# Define ASM defines here +UADEFS = + +# List all user directories here +UINCDIR = + +# List the user directory to look for the libraries here +ULIBDIR = + +# List all user libraries here +ULIBS = + +# +# End of user defines +############################################################################## + +RULESPATH = $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC +include $(RULESPATH)/rules.mk diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c new file mode 100644 index 00000000..ce7cd8e3 --- /dev/null +++ b/tmk_core/protocol/chibios/main.c @@ -0,0 +1,91 @@ +/* + * (c) 2015 flabberast + * + * Based on the following work: + * - Guillaume Duc's raw hid example (MIT License) + * https://github.com/guiduc/usb-hid-chibios-example + * - PJRC Teensy examples (MIT License) + * https://www.pjrc.com/teensy/usb_keyboard.html + * - hasu's TMK keyboard code (GPL v2 and some code Modified BSD) + * https://github.com/tmk/tmk_keyboard/ + * - ChibiOS demo code (Apache 2.0 License) + * http://www.chibios.org + * + * Since some GPL'd code is used, this work is licensed under + * GPL v2 or later. + */ + +#include "ch.h" +#include "hal.h" + +#include "usb_main.h" + +/* TMK includes */ +#include "report.h" +#include "host.h" +#include "host_driver.h" +#include "keyboard.h" +#include "action.h" +#include "led.h" +#include "sendchar.h" +#include "debug.h" +#ifdef SLEEP_LED_ENABLE +#include "sleep_led.h" +#endif +#include "suspend.h" + + +/* ------------------------- + * TMK host driver defs + * ------------------------- + */ + +host_driver_t chibios_driver = { + keyboard_leds, + send_keyboard, + send_mouse, + send_system, + send_consumer +}; + +/* Main thread + */ +int main(void) { + /* ChibiOS/RT init */ + halInit(); + chSysInit(); + + palSetPad(GPIOC, GPIOC_LED_BLUE); + chThdSleepMilliseconds(400); + palClearPad(GPIOC, GPIOC_LED_BLUE); + + /* Init USB */ + init_usb_driver(); + + /* init printf */ + init_printf(NULL,sendchar_pf); + + /* Wait until the USB is active */ + while(USB_DRIVER.state != USB_ACTIVE) + chThdSleepMilliseconds(50); + + print("USB configured.\n"); + + /* init TMK modules */ + keyboard_init(); + host_set_driver(&chibios_driver); + +#ifdef SLEEP_LED_ENABLE + sleep_led_init(); +#endif + + print("Keyboard start.\n"); + + /* Main loop */ + while(true) { + /* TODO: check for suspended event */ + + keyboard_task(); + chThdSleepMilliseconds(5); + } +} diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c new file mode 100644 index 00000000..875f47dc --- /dev/null +++ b/tmk_core/protocol/chibios/usb_main.c @@ -0,0 +1,1251 @@ +/* + * (c) 2015 flabberast + * + * Based on the following work: + * - Guillaume Duc's raw hid example (MIT License) + * https://github.com/guiduc/usb-hid-chibios-example + * - PJRC Teensy examples (MIT License) + * https://www.pjrc.com/teensy/usb_keyboard.html + * - hasu's TMK keyboard code (GPL v2 and some code Modified BSD) + * https://github.com/tmk/tmk_keyboard/ + * - ChibiOS demo code (Apache 2.0 License) + * http://www.chibios.org + * + * Since some GPL'd code is used, this work is licensed under + * GPL v2 or later. + */ + +#include "ch.h" +#include "hal.h" + +#include "usb_main.h" + +/* --------------------------------------------------------- + * Global interface variables and declarations + * --------------------------------------------------------- + */ + +uint8_t keyboard_idle = 0; +uint8_t keyboard_protocol = 1; +uint16_t keyboard_led_stats = 0; +volatile uint16_t keyboard_idle_count = 0; +static virtual_timer_t keyboard_idle_timer; +static void keyboard_idle_timer_cb(void *arg); +#ifdef NKRO_ENABLE +bool keyboard_nkro = true; +#endif /* NKRO_ENABLE */ + +report_keyboard_t keyboard_report_sent = {{0}}; + +#ifdef CONSOLE_ENABLE +/* The emission queue */ +output_queue_t console_queue; +static uint8_t console_queue_buffer[CONSOLE_QUEUE_BUFFER_SIZE]; +static virtual_timer_t console_flush_timer; +void console_queue_onotify(io_queue_t *qp); +static void console_flush_cb(void *arg); +#endif /* CONSOLE_ENABLE */ + +/* --------------------------------------------------------- + * Descriptors and USB driver objects + * --------------------------------------------------------- + */ + +/* HID specific constants */ +#define USB_DESCRIPTOR_HID 0x21 +#define USB_DESCRIPTOR_HID_REPORT 0x22 +#define HID_GET_REPORT 0x01 +#define HID_GET_IDLE 0x02 +#define HID_GET_PROTOCOL 0x03 +#define HID_SET_REPORT 0x09 +#define HID_SET_IDLE 0x0A +#define HID_SET_PROTOCOL 0x0B + +/* USB Device Descriptor */ +static const uint8_t usb_device_descriptor_data[] = { + USB_DESC_DEVICE(0x0200, // bcdUSB (1.1) + 0, // bDeviceClass (defined in later in interface) + 0, // bDeviceSubClass + 0, // bDeviceProtocol + 64, // bMaxPacketSize (64 bytes) (the driver didn't work with 32) + VENDOR_ID, // idVendor + PRODUCT_ID, // idProduct + DEVICE_VER, // bcdDevice + 1, // iManufacturer + 2, // iProduct + 3, // iSerialNumber + 1) // bNumConfigurations +}; + +/* Device Descriptor wrapper */ +static const USBDescriptor usb_device_descriptor = { + sizeof usb_device_descriptor_data, + usb_device_descriptor_data +}; + +/* + * HID Report Descriptor + * + * See "Device Class Definition for Human Interface Devices (HID)" + * (http://www.usb.org/developers/hidpage/HID1_11.pdf) for the + * detailed descrition of all the fields + */ + +/* Keyboard Protocol 1, HID 1.11 spec, Appendix B, page 59-60 */ +static const uint8_t keyboard_hid_report_desc_data[] = { + 0x05, 0x01, // Usage Page (Generic Desktop), + 0x09, 0x06, // Usage (Keyboard), + 0xA1, 0x01, // Collection (Application), + 0x75, 0x01, // Report Size (1), + 0x95, 0x08, // Report Count (8), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0xE0, // Usage Minimum (224), + 0x29, 0xE7, // Usage Maximum (231), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum (1), + 0x81, 0x02, // Input (Data, Variable, Absolute), ;Modifier byte + 0x95, 0x01, // Report Count (1), + 0x75, 0x08, // Report Size (8), + 0x81, 0x03, // Input (Constant), ;Reserved byte + 0x95, 0x05, // Report Count (5), + 0x75, 0x01, // Report Size (1), + 0x05, 0x08, // Usage Page (LEDs), + 0x19, 0x01, // Usage Minimum (1), + 0x29, 0x05, // Usage Maximum (5), + 0x91, 0x02, // Output (Data, Variable, Absolute), ;LED report + 0x95, 0x01, // Report Count (1), + 0x75, 0x03, // Report Size (3), + 0x91, 0x03, // Output (Constant), ;LED report padding + 0x95, KBD_REPORT_KEYS, // Report Count (), + 0x75, 0x08, // Report Size (8), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0xFF, // Logical Maximum(255), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0x00, // Usage Minimum (0), + 0x29, 0xFF, // Usage Maximum (255), + 0x81, 0x00, // Input (Data, Array), + 0xc0 // End Collection +}; +/* wrapper */ +static const USBDescriptor keyboard_hid_report_descriptor = { + sizeof keyboard_hid_report_desc_data, + keyboard_hid_report_desc_data +}; + +#ifdef NKRO_ENABLE +static const uint8_t nkro_hid_report_desc_data[] = { + 0x05, 0x01, // Usage Page (Generic Desktop), + 0x09, 0x06, // Usage (Keyboard), + 0xA1, 0x01, // Collection (Application), + // bitmap of modifiers + 0x75, 0x01, // Report Size (1), + 0x95, 0x08, // Report Count (8), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0xE0, // Usage Minimum (224), + 0x29, 0xE7, // Usage Maximum (231), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum (1), + 0x81, 0x02, // Input (Data, Variable, Absolute), ;Modifier byte + // LED output report + 0x95, 0x05, // Report Count (5), + 0x75, 0x01, // Report Size (1), + 0x05, 0x08, // Usage Page (LEDs), + 0x19, 0x01, // Usage Minimum (1), + 0x29, 0x05, // Usage Maximum (5), + 0x91, 0x02, // Output (Data, Variable, Absolute), + 0x95, 0x01, // Report Count (1), + 0x75, 0x03, // Report Size (3), + 0x91, 0x03, // Output (Constant), + // bitmap of keys + 0x95, NKRO_REPORT_KEYS * 8, // Report Count (), + 0x75, 0x01, // Report Size (1), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum(1), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0x00, // Usage Minimum (0), + 0x29, NKRO_REPORT_KEYS * 8 - 1, // Usage Maximum (), + 0x81, 0x02, // Input (Data, Variable, Absolute), + 0xc0 // End Collection +}; +/* wrapper */ +static const USBDescriptor nkro_hid_report_descriptor = { + sizeof nkro_hid_report_desc_data, + nkro_hid_report_desc_data +}; +#endif /* NKRO_ENABLE */ + +#ifdef MOUSE_ENABLE +/* Mouse Protocol 1, HID 1.11 spec, Appendix B, page 59-60, with wheel extension + * http://www.microchip.com/forums/tm.aspx?high=&m=391435&mpage=1#391521 + * http://www.keil.com/forum/15671/ + * http://www.microsoft.com/whdc/device/input/wheel.mspx */ +static const uint8_t mouse_hid_report_desc_data[] = { + /* mouse */ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x02, // USAGE (Mouse) + 0xa1, 0x01, // COLLECTION (Application) + //0x85, REPORT_ID_MOUSE, // REPORT_ID (1) + 0x09, 0x01, // USAGE (Pointer) + 0xa1, 0x00, // COLLECTION (Physical) + // ---------------------------- Buttons + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM (Button 1) + 0x29, 0x05, // USAGE_MAXIMUM (Button 5) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x05, // REPORT_COUNT (5) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x75, 0x03, // REPORT_SIZE (3) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) + // ---------------------------- X,Y position + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x02, // REPORT_COUNT (2) + 0x81, 0x06, // INPUT (Data,Var,Rel) + // ---------------------------- Vertical wheel + 0x09, 0x38, // USAGE (Wheel) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x35, 0x00, // PHYSICAL_MINIMUM (0) - reset physical + 0x45, 0x00, // PHYSICAL_MAXIMUM (0) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x06, // INPUT (Data,Var,Rel) + // ---------------------------- Horizontal wheel + 0x05, 0x0c, // USAGE_PAGE (Consumer Devices) + 0x0a, 0x38, 0x02, // USAGE (AC Pan) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x06, // INPUT (Data,Var,Rel) + 0xc0, // END_COLLECTION + 0xc0, // END_COLLECTION +}; +/* wrapper */ +static const USBDescriptor mouse_hid_report_descriptor = { + sizeof mouse_hid_report_desc_data, + mouse_hid_report_desc_data +}; +#endif /* MOUSE_ENABLE */ + +#ifdef CONSOLE_ENABLE +static const uint8_t console_hid_report_desc_data[] = { + 0x06, 0x31, 0xFF, // Usage Page 0xFF31 (vendor defined) + 0x09, 0x74, // Usage 0x74 + 0xA1, 0x53, // Collection 0x53 + 0x75, 0x08, // report size = 8 bits + 0x15, 0x00, // logical minimum = 0 + 0x26, 0xFF, 0x00, // logical maximum = 255 + 0x95, CONSOLE_SIZE, // report count + 0x09, 0x75, // usage + 0x81, 0x02, // Input (array) + 0xC0 // end collection +}; +/* wrapper */ +static const USBDescriptor console_hid_report_descriptor = { + sizeof console_hid_report_desc_data, + console_hid_report_desc_data +}; +#endif /* CONSOLE_ENABLE */ + +#ifdef EXTRAKEY_ENABLE +/* audio controls & system controls + * http://www.microsoft.com/whdc/archive/w2kbd.mspx */ +static const uint8_t extra_hid_report_desc_data[] = { + /* system control */ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x80, // USAGE (System Control) + 0xa1, 0x01, // COLLECTION (Application) + 0x85, REPORT_ID_SYSTEM, // REPORT_ID (2) + 0x15, 0x01, // LOGICAL_MINIMUM (0x1) + 0x25, 0xb7, // LOGICAL_MAXIMUM (0xb7) + 0x19, 0x01, // USAGE_MINIMUM (0x1) + 0x29, 0xb7, // USAGE_MAXIMUM (0xb7) + 0x75, 0x10, // REPORT_SIZE (16) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x00, // INPUT (Data,Array,Abs) + 0xc0, // END_COLLECTION + /* consumer */ + 0x05, 0x0c, // USAGE_PAGE (Consumer Devices) + 0x09, 0x01, // USAGE (Consumer Control) + 0xa1, 0x01, // COLLECTION (Application) + 0x85, REPORT_ID_CONSUMER, // REPORT_ID (3) + 0x15, 0x01, // LOGICAL_MINIMUM (0x1) + 0x26, 0x9c, 0x02, // LOGICAL_MAXIMUM (0x29c) + 0x19, 0x01, // USAGE_MINIMUM (0x1) + 0x2a, 0x9c, 0x02, // USAGE_MAXIMUM (0x29c) + 0x75, 0x10, // REPORT_SIZE (16) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x00, // INPUT (Data,Array,Abs) + 0xc0, // END_COLLECTION +}; +/* wrapper */ +static const USBDescriptor extra_hid_report_descriptor = { + sizeof extra_hid_report_desc_data, + extra_hid_report_desc_data +}; +#endif /* EXTRAKEY_ENABLE */ + + +/* + * Configuration Descriptor tree for a HID device + * + * The HID Specifications version 1.11 require the following order: + * - Configuration Descriptor + * - Interface Descriptor + * - HID Descriptor + * - Endpoints Descriptors + */ +#define KBD_HID_DESC_NUM 0 +#define KBD_HID_DESC_OFFSET (9 + (9 + 9 + 7) * KBD_HID_DESC_NUM + 9) + +#ifdef MOUSE_ENABLE +# define MOUSE_HID_DESC_NUM (KBD_HID_DESC_NUM + 1) +# define MOUSE_HID_DESC_OFFSET (9 + (9 + 9 + 7) * MOUSE_HID_DESC_NUM + 9) +#else /* MOUSE_ENABLE */ +# define MOUSE_HID_DESC_NUM (KBD_HID_DESC_NUM + 0) +#endif /* MOUSE_ENABLE */ + +#ifdef CONSOLE_ENABLE +#define CONSOLE_HID_DESC_NUM (MOUSE_HID_DESC_NUM + 1) +#define CONSOLE_HID_DESC_OFFSET (9 + (9 + 9 + 7) * CONSOLE_HID_DESC_NUM + 9) +#else /* CONSOLE_ENABLE */ +# define CONSOLE_HID_DESC_NUM (MOUSE_HID_DESC_NUM + 0) +#endif /* CONSOLE_ENABLE */ + +#ifdef EXTRAKEY_ENABLE +# define EXTRA_HID_DESC_NUM (CONSOLE_HID_DESC_NUM + 1) +# define EXTRA_HID_DESC_OFFSET (9 + (9 + 9 + 7) * EXTRA_HID_DESC_NUM + 9) +#else /* EXTRAKEY_ENABLE */ +# define EXTRA_HID_DESC_NUM (CONSOLE_HID_DESC_NUM + 0) +#endif /* EXTRAKEY_ENABLE */ + +#ifdef NKRO_ENABLE +# define NKRO_HID_DESC_NUM (EXTRA_HID_DESC_NUM + 1) +# define NKRO_HID_DESC_OFFSET (9 + (9 + 9 + 7) * EXTRA_HID_DESC_NUM + 9) +#else /* NKRO_ENABLE */ +# define NKRO_HID_DESC_NUM (EXTRA_HID_DESC_NUM + 0) +#endif /* NKRO_ENABLE */ + +#define NUM_INTERFACES (NKRO_HID_DESC_NUM + 1) +#define CONFIG1_DESC_SIZE (9 + (9 + 9 + 7) * NUM_INTERFACES) + +static const uint8_t hid_configuration_descriptor_data[] = { + /* Configuration Descriptor (9 bytes) USB spec 9.6.3, page 264-266, Table 9-10 */ + USB_DESC_CONFIGURATION(CONFIG1_DESC_SIZE, // wTotalLength + NUM_INTERFACES, // bNumInterfaces + 1, // bConfigurationValue + 0, // iConfiguration + 0xA0, // bmAttributes + 50), // bMaxPower (100mA) + + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(KBD_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass: HID + 0x01, // bInterfaceSubClass: Boot + 0x01, // bInterfaceProtocol: Keyboard + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(keyboard_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(KBD_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + KBD_SIZE, // wMaxPacketSize + 10), // bInterval + + #ifdef MOUSE_ENABLE + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(MOUSE_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + // ThinkPad T23 BIOS doesn't work with boot mouse. + 0x00, // bInterfaceSubClass (0x01 = Boot) + 0x00, // bInterfaceProtocol (0x02 = Mouse) + /* + 0x01, // bInterfaceSubClass (0x01 = Boot) + 0x02, // bInterfaceProtocol (0x02 = Mouse) + */ + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(mouse_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(MOUSE_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + MOUSE_SIZE, // wMaxPacketSize + 1), // bInterval + #endif /* MOUSE_ENABLE */ + + #ifdef CONSOLE_ENABLE + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(CONSOLE_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass: HID + 0x00, // bInterfaceSubClass: None + 0x00, // bInterfaceProtocol: None + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(console_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(CONSOLE_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + CONSOLE_SIZE, // wMaxPacketSize + 1), // bInterval + #endif /* CONSOLE_ENABLE */ + + #ifdef EXTRAKEY_ENABLE + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(EXTRA_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass: HID + 0x00, // bInterfaceSubClass: None + 0x00, // bInterfaceProtocol: None + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(extra_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(EXTRA_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + EXTRA_SIZE, // wMaxPacketSize + 10), // bInterval + #endif /* EXTRAKEY_ENABLE */ + + #ifdef NKRO_ENABLE + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(NKRO_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass: HID + 0x00, // bInterfaceSubClass: None + 0x00, // bInterfaceProtocol: None + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(nkro_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(NKRO_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + NKRO_SIZE, // wMaxPacketSize + 1), // bInterval + #endif /* NKRO_ENABLE */ +}; + +/* Configuration Descriptor wrapper */ +static const USBDescriptor hid_configuration_descriptor = { + sizeof hid_configuration_descriptor_data, + hid_configuration_descriptor_data +}; + +/* wrappers */ +#define HID_DESCRIPTOR_SIZE 9 +static const USBDescriptor keyboard_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[KBD_HID_DESC_OFFSET] +}; +#ifdef MOUSE_ENABLE +static const USBDescriptor mouse_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[MOUSE_HID_DESC_OFFSET] +}; +#endif /* MOUSE_ENABLE */ +#ifdef CONSOLE_ENABLE +static const USBDescriptor console_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[CONSOLE_HID_DESC_OFFSET] +}; +#endif /* CONSOLE_ENABLE */ +#ifdef EXTRAKEY_ENABLE +static const USBDescriptor extra_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[EXTRA_HID_DESC_OFFSET] +}; +#endif /* EXTRAKEY_ENABLE */ +#ifdef NKRO_ENABLE +static const USBDescriptor nkro_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[NKRO_HID_DESC_OFFSET] +}; +#endif /* NKRO_ENABLE */ + + +/* U.S. English language identifier */ +static const uint8_t usb_string_langid[] = { + USB_DESC_BYTE(4), // bLength + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType + USB_DESC_WORD(0x0409) // wLANGID (U.S. English) +}; + +/* ugly ugly hack */ +#define PP_NARG(...) \ + PP_NARG_(__VA_ARGS__,PP_RSEQ_N()) +#define PP_NARG_(...) \ + PP_ARG_N(__VA_ARGS__) +#define PP_ARG_N( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ + _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ + _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ + _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ + _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,N,...) N +#define PP_RSEQ_N() \ + 63,62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9,8,7,6,5,4,3,2,1,0 + +/* Vendor string = manufacturer */ +static const uint8_t usb_string_vendor[] = { + USB_DESC_BYTE(PP_NARG(USBSTR_MANUFACTURER)+2), // bLength + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType + USBSTR_MANUFACTURER +}; + +/* Device Description string = product */ +static const uint8_t usb_string_description[] = { + USB_DESC_BYTE(PP_NARG(USBSTR_PRODUCT)+2), // bLength + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType + USBSTR_PRODUCT +}; + +/* Serial Number string (will be filled by the function init_usb_serial_string) */ +static uint8_t usb_string_serial[] = { + USB_DESC_BYTE(22), // bLength + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType + '0', 0, 'x', 0, 'D', 0, 'E', 0, 'A', 0, 'D', 0, 'B', 0, 'E', 0, 'E', 0, 'F', 0 +}; + +/* Strings wrappers array */ +static const USBDescriptor usb_strings[] = { + { sizeof usb_string_langid, usb_string_langid } + , + { sizeof usb_string_vendor, usb_string_vendor } + , + { sizeof usb_string_description, usb_string_description } + , + { sizeof usb_string_serial, usb_string_serial } +}; + +/* + * Handles the GET_DESCRIPTOR callback + * + * Returns the proper descriptor + */ +static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t lang) { + (void)usbp; + (void)lang; + switch(dtype) { + /* Generic descriptors */ + case USB_DESCRIPTOR_DEVICE: /* Device Descriptor */ + return &usb_device_descriptor; + + case USB_DESCRIPTOR_CONFIGURATION: /* Configuration Descriptor */ + return &hid_configuration_descriptor; + + case USB_DESCRIPTOR_STRING: /* Strings */ + if(dindex < 4) + return &usb_strings[dindex]; + break; + + /* HID specific descriptors */ + case USB_DESCRIPTOR_HID: /* HID Descriptors */ + switch(lang) { /* yea, poor label, it's actually wIndex from the setup packet */ + case KBD_INTERFACE: + return &keyboard_hid_descriptor; + +#ifdef MOUSE_ENABLE + case MOUSE_INTERFACE: + return &mouse_hid_descriptor; +#endif /* MOUSE_ENABLE */ +#ifdef CONSOLE_ENABLE + case CONSOLE_INTERFACE: + return &console_hid_descriptor; +#endif /* CONSOLE_ENABLE */ +#ifdef EXTRAKEY_ENABLE + case EXTRA_INTERFACE: + return &extra_hid_descriptor; +#endif /* EXTRAKEY_ENABLE */ +#ifdef NKRO_ENABLE + case NKRO_INTERFACE: + return &nkro_hid_descriptor; +#endif /* NKRO_ENABLE */ + } + + case USB_DESCRIPTOR_HID_REPORT: /* HID Report Descriptor */ + switch(lang) { + case KBD_INTERFACE: + return &keyboard_hid_report_descriptor; + +#ifdef MOUSE_ENABLE + case MOUSE_INTERFACE: + return &mouse_hid_report_descriptor; +#endif /* MOUSE_ENABLE */ +#ifdef CONSOLE_ENABLE + case CONSOLE_INTERFACE: + return &console_hid_report_descriptor; +#endif /* CONSOLE_ENABLE */ +#ifdef EXTRAKEY_ENABLE + case EXTRA_INTERFACE: + return &extra_hid_report_descriptor; +#endif /* EXTRAKEY_ENABLE */ +#ifdef NKRO_ENABLE + case NKRO_INTERFACE: + return &nkro_hid_report_descriptor; +#endif /* NKRO_ENABLE */ + } + } + return NULL; +} + +/* keyboard endpoint state structure */ +static USBInEndpointState kbd_ep_state; +/* keyboard endpoint initialization structure (IN) */ +static const USBEndpointConfig kbd_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + kbd_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + KBD_SIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &kbd_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; + +#ifdef MOUSE_ENABLE +/* mouse endpoint state structure */ +static USBInEndpointState mouse_ep_state; + +/* mouse endpoint initialization structure (IN) */ +static const USBEndpointConfig mouse_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + mouse_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + MOUSE_SIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &mouse_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif /* MOUSE_ENABLE */ + +#ifdef CONSOLE_ENABLE +/* console endpoint state structure */ +static USBInEndpointState console_ep_state; + +/* console endpoint initialization structure (IN) */ +static const USBEndpointConfig console_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + console_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + CONSOLE_SIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &console_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif /* CONSOLE_ENABLE */ + +#ifdef EXTRAKEY_ENABLE +/* extrakey endpoint state structure */ +static USBInEndpointState extra_ep_state; + +/* extrakey endpoint initialization structure (IN) */ +static const USBEndpointConfig extra_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + extra_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + EXTRA_SIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &extra_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif /* EXTRAKEY_ENABLE */ + +#ifdef NKRO_ENABLE +/* nkro endpoint state structure */ +static USBInEndpointState nkro_ep_state; + +/* nkro endpoint initialization structure (IN) */ +static const USBEndpointConfig nkro_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + nkro_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + NKRO_SIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &nkro_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif /* NKRO_ENABLE */ + +/* --------------------------------------------------------- + * USB driver functions + * --------------------------------------------------------- + */ + +/* Handles the USB driver global events + * TODO: maybe disable some things when connection is lost? */ +static void usb_event_cb(USBDriver *usbp, usbevent_t event) { + switch(event) { + case USB_EVENT_RESET: + return; + + case USB_EVENT_ADDRESS: + return; + + case USB_EVENT_CONFIGURED: + osalSysLockFromISR(); + /* Enable the endpoints specified into the configuration. */ + usbInitEndpointI(usbp, KBD_ENDPOINT, &kbd_ep_config); +#ifdef MOUSE_ENABLE + usbInitEndpointI(usbp, MOUSE_ENDPOINT, &mouse_ep_config); +#endif /* MOUSE_ENABLE */ +#ifdef CONSOLE_ENABLE + usbInitEndpointI(usbp, CONSOLE_ENDPOINT, &console_ep_config); + /* don't need to start the flush timer, it starts from console_in_cb automatically */ +#endif /* CONSOLE_ENABLE */ +#ifdef EXTRAKEY_ENABLE + usbInitEndpointI(usbp, EXTRA_ENDPOINT, &extra_ep_config); +#endif /* EXTRAKEY_ENABLE */ +#ifdef NKRO_ENABLE + usbInitEndpointI(usbp, NKRO_ENDPOINT, &nkro_ep_config); +#endif /* NKRO_ENABLE */ + osalSysUnlockFromISR(); + return; + + case USB_EVENT_SUSPEND: + return; + + case USB_EVENT_WAKEUP: + return; + + case USB_EVENT_STALLED: + return; + } +} + +/* Function used locally in os/hal/src/usb.c for getting descriptors + * need it here for HID descriptor */ +static uint16_t get_hword(uint8_t *p) { + uint16_t hw; + + hw = (uint16_t)*p++; + hw |= (uint16_t)*p << 8U; + return hw; +} + +/* + * Appendix G: HID Request Support Requirements + * + * The following table enumerates the requests that need to be supported by various types of HID class devices. + * Device type GetReport SetReport GetIdle SetIdle GetProtocol SetProtocol + * ------------------------------------------------------------------------------------------ + * Boot Mouse Required Optional Optional Optional Required Required + * Non-Boot Mouse Required Optional Optional Optional Optional Optional + * Boot Keyboard Required Optional Required Required Required Required + * Non-Boot Keybrd Required Optional Required Required Optional Optional + * Other Device Required Optional Optional Optional Optional Optional + */ + +/* Callback for SETUP request on the endpoint 0 (control) */ +static bool usb_request_hook_cb(USBDriver *usbp) { + const USBDescriptor *dp; + + /* usbp->setup fields: + * 0: bmRequestType (bitmask) + * 1: bRequest + * 2,3: (LSB,MSB) wValue + * 4,5: (LSB,MSB) wIndex + * 6,7: (LSB,MSB) wLength (number of bytes to transfer if there is a data phase) */ + + /* Handle HID class specific requests */ + if(((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) && + ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) { + switch(usbp->setup[0] & USB_RTYPE_DIR_MASK) { + case USB_RTYPE_DIR_DEV2HOST: + switch(usbp->setup[1]) { /* bRequest */ + case HID_GET_REPORT: + switch(usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */ + case KBD_INTERFACE: +#ifdef NKRO_ENABLE + case NKRO_INTERFACE: +#endif /* NKRO_ENABLE */ + usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent), NULL); + return TRUE; + break; + + default: + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + } + break; + + case HID_GET_PROTOCOL: + if((usbp->setup[4] == KBD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ + usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL); + return TRUE; + } + break; + + case HID_GET_IDLE: + usbSetupTransfer(usbp, &keyboard_idle, 1, NULL); + return TRUE; + break; + } + break; + + case USB_RTYPE_DIR_HOST2DEV: + switch(usbp->setup[1]) { /* bRequest */ + case HID_SET_REPORT: + switch(usbp->setup[4]) { /* LSB(wIndex) (check MSB==0 and wLength==1?) */ + case KBD_INTERFACE: +#ifdef NKRO_ENABLE + case NKRO_INTERFACE: +#endif /* NKRO_ENABLE */ + /* keyboard_led_stats = + * keyboard_led_stats needs be word (or dword), otherwise we get an exception on F0 */ + usbSetupTransfer(usbp, (uint8_t *)&keyboard_led_stats, 1, NULL); + return TRUE; + break; + } + break; + + case HID_SET_PROTOCOL: + if((usbp->setup[4] == KBD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ + keyboard_protocol = ((usbp->setup[2]) != 0x00); /* LSB(wValue) */ +#ifdef NKRO_ENABLE + keyboard_nkro = !!keyboard_protocol; + if(!keyboard_nkro && keyboard_idle) { +#else /* NKRO_ENABLE */ + if(keyboard_idle) { +#endif /* NKRO_ENABLE */ + /* arm the idle timer if boot protocol & idle */ + osalSysLockFromISR(); + chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, NULL); + osalSysUnlockFromISR(); + } + } + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + + case HID_SET_IDLE: + keyboard_idle = usbp->setup[3]; /* MSB(wValue) */ + /* arm the timer */ +#ifdef NKRO_ENABLE + if(!keyboard_nkro && keyboard_idle) { +#else /* NKRO_ENABLE */ + if(keyboard_idle) { +#endif /* NKRO_ENABLE */ + osalSysLockFromISR(); + chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, NULL); + osalSysUnlockFromISR(); + } + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + } + break; + } + } + + /* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */ + if((usbp->setup[0] == 0x81) && (usbp->setup[1] == USB_REQ_GET_DESCRIPTOR)) { + dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], get_hword(&usbp->setup[4])); + if(dp == NULL) + return FALSE; + usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL); + return TRUE; + } + + return FALSE; +} + +/* Start-of-frame callback */ +static void usb_sof_cb(USBDriver *usbp) { + kbd_sof_cb(usbp); +} + + +/* USB driver configuration */ +static const USBConfig usbcfg = { + usb_event_cb, /* USB events callback */ + usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */ + usb_request_hook_cb, /* Requests hook callback */ + usb_sof_cb /* Start Of Frame callback */ +}; + +/* + * Initialize the USB driver + */ +void init_usb_driver(void) { + /* + * Activates the USB driver and then the USB bus pull-up on D+. + * Note, a delay is inserted in order to not have to disconnect the cable + * after a reset. + */ + usbDisconnectBus(&USB_DRIVER); + chThdSleepMilliseconds(1500); + usbStart(&USB_DRIVER, &usbcfg); + usbConnectBus(&USB_DRIVER); + + chVTObjectInit(&keyboard_idle_timer); +#ifdef CONSOLE_ENABLE + oqObjectInit(&console_queue, console_queue_buffer, sizeof(console_queue_buffer), console_queue_onotify, NULL); + chVTObjectInit(&console_flush_timer); +#endif +} + +/* --------------------------------------------------------- + * Keyboard functions + * --------------------------------------------------------- + */ + +/* keyboard IN callback hander (a kbd report has made it IN) */ +void kbd_in_cb(USBDriver *usbp, usbep_t ep) { + /* STUB */ + (void)usbp; + (void)ep; +} + +#ifdef NKRO_ENABLE +/* nkro IN callback hander (a nkro report has made it IN) */ +void nkro_in_cb(USBDriver *usbp, usbep_t ep) { + /* STUB */ + (void)usbp; + (void)ep; +} +#endif /* NKRO_ENABLE */ + +/* start-of-frame handler + * TODO: i guess it would be better to re-implement using timers, + * so that this is not going to have to be checked every 1ms */ +void kbd_sof_cb(USBDriver *usbp) { + (void)usbp; +} + +/* Idle requests timer code + * callback (called from ISR, unlocked state) */ +static void keyboard_idle_timer_cb(void *arg) { + (void)arg; + + osalSysLockFromISR(); + + /* check that the states of things are as they're supposed to */ + if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + /* do not rearm the timer, should be enabled on IDLE request */ + osalSysUnlockFromISR(); + return; + } + +#ifdef NKRO_ENABLE + if(!keyboard_nkro && keyboard_idle) { +#else /* NKRO_ENABLE */ + if(keyboard_idle) { +#endif /* NKRO_ENABLE */ + /* TODO: are we sure we want the KBD_ENDPOINT? */ + osalSysUnlockFromISR(); + usbPrepareTransmit(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent)); + osalSysLockFromISR(); + usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT); + /* rearm the timer */ + chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, NULL); + } + + /* do not rearm the timer if the condition above fails + * it should be enabled again on either IDLE or SET_PROTOCOL requests */ + osalSysUnlockFromISR(); +} + +/* LED status */ +uint8_t keyboard_leds(void) { + return (uint8_t)(keyboard_led_stats & 0xFF); +} + +/* prepare and start sending a report IN + * not callable from ISR or locked state */ +void send_keyboard(report_keyboard_t *report) { + osalSysLock(); + if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + osalSysUnlock(); + +#ifdef NKRO_ENABLE + if(keyboard_nkro) { /* NKRO protocol */ + usbPrepareTransmit(&USB_DRIVER, NKRO_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t)); + osalSysLock(); + usbStartTransmitI(&USB_DRIVER, NKRO_ENDPOINT); + osalSysUnlock(); + } else +#endif /* NKRO_ENABLE */ + { /* boot protocol */ + usbPrepareTransmit(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t)); + osalSysLock(); + usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT); + osalSysUnlock(); + } + keyboard_report_sent = *report; +} + +/* --------------------------------------------------------- + * Mouse functions + * --------------------------------------------------------- + */ + +#ifdef MOUSE_ENABLE + +/* mouse IN callback hander (a mouse report has made it IN) */ +void mouse_in_cb(USBDriver *usbp, usbep_t ep) { + (void)usbp; + (void)ep; +} + +void send_mouse(report_mouse_t *report) { + osalSysLock(); + if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + osalSysUnlock(); + + /* TODO: LUFA manually waits for the endpoint to become ready + * for about 10ms for mouse, kbd, system; 1ms for nkro + * is this really needed? + */ + + usbPrepareTransmit(&USB_DRIVER, MOUSE_ENDPOINT, (uint8_t *)report, sizeof(report_mouse_t)); + osalSysLock(); + usbStartTransmitI(&USB_DRIVER, MOUSE_ENDPOINT); + osalSysUnlock(); +} + +#else /* MOUSE_ENABLE */ +void send_mouse(report_mouse_t *report) { + (void)report; +} +#endif /* MOUSE_ENABLE */ + +/* --------------------------------------------------------- + * Extrakey functions + * --------------------------------------------------------- + */ + +#ifdef EXTRAKEY_ENABLE + +/* extrakey IN callback hander */ +void extra_in_cb(USBDriver *usbp, usbep_t ep) { + /* STUB */ + (void)usbp; + (void)ep; +} + +static void send_extra_report(uint8_t report_id, uint16_t data) { + osalSysLock(); + if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + + report_extra_t report = { + .report_id = report_id, + .usage = data + }; + + osalSysUnlock(); + usbPrepareTransmit(&USB_DRIVER, EXTRA_ENDPOINT, (uint8_t *)&report, sizeof(report_extra_t)); + osalSysLock(); + usbStartTransmitI(&USB_DRIVER, EXTRA_ENDPOINT); + osalSysUnlock(); +} + +void send_system(uint16_t data) { + send_extra_report(REPORT_ID_SYSTEM, data); +} + +void send_consumer(uint16_t data) { + send_extra_report(REPORT_ID_CONSUMER, data); +} + +#else /* EXTRAKEY_ENABLE */ +void send_system(uint16_t data) { + (void)data; +} +void send_consumer(uint16_t data) { + (void)data; +} +#endif /* EXTRAKEY_ENABLE */ + +/* --------------------------------------------------------- + * Console functions + * --------------------------------------------------------- + */ + +#ifdef CONSOLE_ENABLE + +/* debug IN callback hander */ +void console_in_cb(USBDriver *usbp, usbep_t ep) { + (void)ep; + osalSysLockFromISR(); + + /* rearm the timer */ + chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, NULL); + + /* Check if there is data to send left in the output queue */ + if(chOQGetFullI(&console_queue) >= CONSOLE_SIZE) { + osalSysUnlockFromISR(); + usbPrepareQueuedTransmit(usbp, CONSOLE_ENDPOINT, &console_queue, CONSOLE_SIZE); + osalSysLockFromISR(); + usbStartTransmitI(usbp, CONSOLE_ENDPOINT); + } + + osalSysUnlockFromISR(); +} + +/* Callback when data is inserted into the output queue + * Called from a locked state */ +void console_queue_onotify(io_queue_t *qp) { + (void)qp; + + if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) + return; + + if(!usbGetTransmitStatusI(&USB_DRIVER, CONSOLE_ENDPOINT) + && (chOQGetFullI(&console_queue) >= CONSOLE_SIZE)) { + osalSysUnlock(); + usbPrepareQueuedTransmit(&USB_DRIVER, CONSOLE_ENDPOINT, &console_queue, CONSOLE_SIZE); + osalSysLock(); + usbStartTransmitI(&USB_DRIVER, CONSOLE_ENDPOINT); + } +} + +/* Flush timer code + * callback (called from ISR, unlocked state) */ +static void console_flush_cb(void *arg) { + (void)arg; + size_t i, n; + uint8_t buf[CONSOLE_SIZE]; /* TODO: a solution without extra buffer? */ + osalSysLockFromISR(); + + /* check that the states of things are as they're supposed to */ + if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + /* rearm the timer */ + chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, NULL); + osalSysUnlockFromISR(); + return; + } + + /* don't do anything if the queue is empty or has enough stuff in it */ + if(((n = oqGetFullI(&console_queue)) == 0) || (n >= CONSOLE_SIZE)) { + /* rearm the timer */ + chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, NULL); + osalSysUnlockFromISR(); + return; + } + + /* there's stuff hanging in the queue - so dequeue and send */ + for(i = 0; i < n; i++) + buf[i] = (uint8_t)oqGetI(&console_queue); + for(i = n; i < CONSOLE_SIZE; i++) + buf[i] = 0; + osalSysUnlockFromISR(); + usbPrepareTransmit(&USB_DRIVER, CONSOLE_ENDPOINT, buf, CONSOLE_SIZE); + osalSysLockFromISR(); + (void)usbStartTransmitI(&USB_DRIVER, CONSOLE_ENDPOINT); + + /* rearm the timer */ + chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, NULL); + osalSysUnlockFromISR(); +} + + +int8_t sendchar(uint8_t c) { + osalSysLock(); + if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return 0; + } + osalSysUnlock(); + /* should get suspended and wait if the queue is full + * but it's not blocking even if noone is listening, + * because the USB packets are sent anyway */ + return(chOQPut(&console_queue, c)); +} + +#else /* CONSOLE_ENABLE */ +int8_t sendchar(uint8_t c) { + (void)c; + return 0; +} +#endif /* CONSOLE_ENABLE */ + +void sendchar_pf(void *p, char c) { + (void)p; + sendchar((uint8_t)c); +} diff --git a/tmk_core/protocol/chibios/usb_main.h b/tmk_core/protocol/chibios/usb_main.h new file mode 100644 index 00000000..b7696643 --- /dev/null +++ b/tmk_core/protocol/chibios/usb_main.h @@ -0,0 +1,149 @@ +/* + * (c) 2015 flabberast + * + * Based on the following work: + * - Guillaume Duc's raw hid example (MIT License) + * https://github.com/guiduc/usb-hid-chibios-example + * - PJRC Teensy examples (MIT License) + * https://www.pjrc.com/teensy/usb_keyboard.html + * - hasu's TMK keyboard code (GPL v2 and some code Modified BSD) + * https://github.com/tmk/tmk_keyboard/ + * - ChibiOS demo code (Apache 2.0 License) + * http://www.chibios.org + * + * Since some GPL'd code is used, this work is licensed under + * GPL v2 or later. + */ + + +#ifndef _USB_MAIN_H_ +#define _USB_MAIN_H_ + +#include "ch.h" +#include "hal.h" + +/* ------------------------- + * General USB driver header + * ------------------------- + */ + +/* The USB driver to use */ +#define USB_DRIVER USBD1 + +/* Initialize the USB driver and bus */ +void init_usb_driver(void); + +/* --------------- + * Keyboard header + * --------------- + */ + +/* main keyboard (6kro) */ +#define KBD_INTERFACE 0 +#define KBD_ENDPOINT 1 +#define KBD_SIZE 8 +#define KBD_REPORT_KEYS (KBD_SIZE - 2) + +/* secondary keyboard */ +#ifdef NKRO_ENABLE +#define NKRO_INTERFACE 4 +#define NKRO_ENDPOINT 5 +#define NKRO_SIZE 16 +#define NKRO_REPORT_KEYS (NKRO_SIZE - 1) +#endif + +/* this defines report_keyboard_t and computes REPORT_SIZE defines */ +#include "report.h" + +/* extern report_keyboard_t keyboard_report_sent; */ + +/* keyboard IN request callback handler */ +void kbd_in_cb(USBDriver *usbp, usbep_t ep); + +/* start-of-frame handler */ +void kbd_sof_cb(USBDriver *usbp); + +#ifdef NKRO_ENABLE +/* nkro IN callback hander */ +void nkro_in_cb(USBDriver *usbp, usbep_t ep); +#endif /* NKRO_ENABLE */ + +/* ------------ + * Mouse header + * ------------ + */ + +#ifdef MOUSE_ENABLE + +#define MOUSE_INTERFACE 1 +#define MOUSE_ENDPOINT 2 +#define MOUSE_SIZE 8 + +/* mouse IN request callback handler */ +void mouse_in_cb(USBDriver *usbp, usbep_t ep); +#endif /* MOUSE_ENABLE */ + +/* --------------- + * Extrakey header + * --------------- + */ + +#ifdef EXTRAKEY_ENABLE + +#define EXTRA_INTERFACE 3 +#define EXTRA_ENDPOINT 4 +#define EXTRA_SIZE 8 + +/* extrakey IN request callback handler */ +void extra_in_cb(USBDriver *usbp, usbep_t ep); + +/* extra report structure */ +typedef struct { + uint8_t report_id; + uint16_t usage; +} __attribute__ ((packed)) report_extra_t; +#endif /* EXTRAKEY_ENABLE */ + +/* -------------- + * Console header + * -------------- + */ + +#ifdef CONSOLE_ENABLE + +#define CONSOLE_INTERFACE 2 +#define CONSOLE_ENDPOINT 3 +#define CONSOLE_SIZE 16 + +/* Number of IN reports that can be stored inside the output queue */ +#define CONSOLE_QUEUE_CAPACITY 2 +#define CONSOLE_QUEUE_BUFFER_SIZE (CONSOLE_QUEUE_CAPACITY * CONSOLE_SIZE) + +/* Console flush time */ +#define CONSOLE_FLUSH_MS 50 + +/* Putchar over the USB console */ +int8_t sendchar(uint8_t c); +/* wrapper for printf lib */ + +/* Flush output (send everything immediately) */ +void console_flush_output(void); + +/* console IN request callback handler */ +void console_in_cb(USBDriver *usbp, usbep_t ep); +#endif /* CONSOLE_ENABLE */ + +void sendchar_pf(void *p, char c); + +/* --------------------------- + * Host driver functions (TMK) + * --------------------------- + */ + +uint8_t keyboard_leds(void); +void send_keyboard(report_keyboard_t *report); +void send_mouse(report_mouse_t *report); +void send_system(uint16_t data); +void send_consumer(uint16_t data); + +#endif /* _USB_MAIN_H_ */ diff --git a/tmk_core/tool/chibios/common.mk b/tmk_core/tool/chibios/common.mk new file mode 100644 index 00000000..952ec493 --- /dev/null +++ b/tmk_core/tool/chibios/common.mk @@ -0,0 +1,86 @@ +COMMON_DIR = $(TMK_DIR)/common +SRC += $(COMMON_DIR)/host.c \ + $(COMMON_DIR)/keyboard.c \ + $(COMMON_DIR)/action.c \ + $(COMMON_DIR)/action_tapping.c \ + $(COMMON_DIR)/action_macro.c \ + $(COMMON_DIR)/action_layer.c \ + $(COMMON_DIR)/action_util.c \ + $(COMMON_DIR)/keymap.c \ + $(COMMON_DIR)/print.c \ + $(COMMON_DIR)/debug.c \ + $(COMMON_DIR)/util.c \ + $(COMMON_DIR)/chibios/suspend.c \ + $(COMMON_DIR)/chibios/printf.c \ + $(COMMON_DIR)/chibios/timer.c \ + $(COMMON_DIR)/chibios/bootloader.c + + +# Option modules +ifdef BOOTMAGIC_ENABLE + $(error Bootmagic Not Supported) + SRC += $(COMMON_DIR)/bootmagic.c + SRC += $(COMMON_DIR)/chibios/eeconfig.c + OPT_DEFS += -DBOOTMAGIC_ENABLE +endif + +ifdef MOUSEKEY_ENABLE + SRC += $(COMMON_DIR)/mousekey.c + OPT_DEFS += -DMOUSEKEY_ENABLE + OPT_DEFS += -DMOUSE_ENABLE +endif + +ifdef EXTRAKEY_ENABLE + OPT_DEFS += -DEXTRAKEY_ENABLE +endif + +ifdef CONSOLE_ENABLE + OPT_DEFS += -DCONSOLE_ENABLE +else + OPT_DEFS += -DNO_PRINT + OPT_DEFS += -DNO_DEBUG +endif + +ifdef COMMAND_ENABLE + SRC += $(COMMON_DIR)/command.c + OPT_DEFS += -DCOMMAND_ENABLE +endif + +ifdef NKRO_ENABLE + OPT_DEFS += -DNKRO_ENABLE +endif + +ifdef USB_6KRO_ENABLE + OPT_DEFS += -DUSB_6KRO_ENABLE +endif + +ifdef SLEEP_LED_ENABLE + $(error Sleep LED Not Supported) + SRC += $(COMMON_DIR)/sleep_led.c + OPT_DEFS += -DSLEEP_LED_ENABLE + OPT_DEFS += -DNO_SUSPEND_POWER_DOWN +endif + +ifdef BACKLIGHT_ENABLE + SRC += $(COMMON_DIR)/backlight.c + OPT_DEFS += -DBACKLIGHT_ENABLE +endif + +ifdef KEYMAP_SECTION_ENABLE + OPT_DEFS += -DKEYMAP_SECTION_ENABLE + + ifeq ($(strip $(MCU)),atmega32u2) + EXTRALDFLAGS = -Wl,-L$(TMK_DIR),-Tldscript_keymap_avr35.x + else ifeq ($(strip $(MCU)),atmega32u4) + EXTRALDFLAGS = -Wl,-L$(TMK_DIR),-Tldscript_keymap_avr5.x + else + EXTRALDFLAGS = $(error no ldscript for keymap section) + endif +endif + +# Version string +OPT_DEFS += -DVERSION=$(shell (git describe --always --dirty || echo 'unknown') 2> /dev/null) + + +# Search Path +VPATH += $(TMK_DIR)/common From 1d115301d4134f59e8043a34400f08aac094704e Mon Sep 17 00:00:00 2001 From: flabbergast Date: Tue, 8 Sep 2015 15:35:02 +0100 Subject: [PATCH 03/56] Make usb_main more USB_DRIVER #define independent. --- tmk_core/protocol/chibios/chibios.mk | 2 +- tmk_core/protocol/chibios/main.c | 2 +- tmk_core/protocol/chibios/usb_main.c | 50 ++++++++++++++-------------- tmk_core/protocol/chibios/usb_main.h | 2 +- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/tmk_core/protocol/chibios/chibios.mk b/tmk_core/protocol/chibios/chibios.mk index d667f60e..2ae1e5f9 100644 --- a/tmk_core/protocol/chibios/chibios.mk +++ b/tmk_core/protocol/chibios/chibios.mk @@ -97,7 +97,7 @@ endif include $(CHIBIOS)/os/hal/osal/rt/osal.mk # RTOS files (optional). include $(CHIBIOS)/os/rt/rt.mk -include $(CHIBIOS)/os/rt/ports/ARMCMx/compilers/GCC/mk/port_v6m.mk +include $(CHIBIOS)/os/rt/ports/ARMCMx/compilers/GCC/mk/port_v$(ARMV)m.mk # Other files (optional). # Define linker script file here diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c index ce7cd8e3..0d79a91f 100644 --- a/tmk_core/protocol/chibios/main.c +++ b/tmk_core/protocol/chibios/main.c @@ -60,7 +60,7 @@ int main(void) { palClearPad(GPIOC, GPIOC_LED_BLUE); /* Init USB */ - init_usb_driver(); + init_usb_driver(&USB_DRIVER); /* init printf */ init_printf(NULL,sendchar_pf); diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 875f47dc..b10e5daa 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -886,7 +886,7 @@ static bool usb_request_hook_cb(USBDriver *usbp) { #endif /* NKRO_ENABLE */ /* arm the idle timer if boot protocol & idle */ osalSysLockFromISR(); - chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, NULL); + chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); osalSysUnlockFromISR(); } } @@ -903,7 +903,7 @@ static bool usb_request_hook_cb(USBDriver *usbp) { if(keyboard_idle) { #endif /* NKRO_ENABLE */ osalSysLockFromISR(); - chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, NULL); + chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); osalSysUnlockFromISR(); } usbSetupTransfer(usbp, NULL, 0, NULL); @@ -943,20 +943,20 @@ static const USBConfig usbcfg = { /* * Initialize the USB driver */ -void init_usb_driver(void) { +void init_usb_driver(USBDriver *usbp) { /* * Activates the USB driver and then the USB bus pull-up on D+. * Note, a delay is inserted in order to not have to disconnect the cable * after a reset. */ - usbDisconnectBus(&USB_DRIVER); + usbDisconnectBus(usbp); chThdSleepMilliseconds(1500); - usbStart(&USB_DRIVER, &usbcfg); - usbConnectBus(&USB_DRIVER); + usbStart(usbp, &usbcfg); + usbConnectBus(usbp); chVTObjectInit(&keyboard_idle_timer); #ifdef CONSOLE_ENABLE - oqObjectInit(&console_queue, console_queue_buffer, sizeof(console_queue_buffer), console_queue_onotify, NULL); + oqObjectInit(&console_queue, console_queue_buffer, sizeof(console_queue_buffer), console_queue_onotify, (void *)usbp); chVTObjectInit(&console_flush_timer); #endif } @@ -992,12 +992,12 @@ void kbd_sof_cb(USBDriver *usbp) { /* Idle requests timer code * callback (called from ISR, unlocked state) */ static void keyboard_idle_timer_cb(void *arg) { - (void)arg; + USBDriver *usbp = (USBDriver *)arg; osalSysLockFromISR(); /* check that the states of things are as they're supposed to */ - if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + if(usbGetDriverStateI(usbp) != USB_ACTIVE) { /* do not rearm the timer, should be enabled on IDLE request */ osalSysUnlockFromISR(); return; @@ -1010,11 +1010,11 @@ static void keyboard_idle_timer_cb(void *arg) { #endif /* NKRO_ENABLE */ /* TODO: are we sure we want the KBD_ENDPOINT? */ osalSysUnlockFromISR(); - usbPrepareTransmit(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent)); + usbPrepareTransmit(usbp, KBD_ENDPOINT, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent)); osalSysLockFromISR(); - usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT); + usbStartTransmitI(usbp, KBD_ENDPOINT); /* rearm the timer */ - chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, NULL); + chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); } /* do not rearm the timer if the condition above fails @@ -1155,7 +1155,7 @@ void console_in_cb(USBDriver *usbp, usbep_t ep) { osalSysLockFromISR(); /* rearm the timer */ - chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, NULL); + chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); /* Check if there is data to send left in the output queue */ if(chOQGetFullI(&console_queue) >= CONSOLE_SIZE) { @@ -1171,32 +1171,32 @@ void console_in_cb(USBDriver *usbp, usbep_t ep) { /* Callback when data is inserted into the output queue * Called from a locked state */ void console_queue_onotify(io_queue_t *qp) { - (void)qp; + USBDriver *usbp = qGetLink(qp); - if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) + if(usbGetDriverStateI(usbp) != USB_ACTIVE) return; - if(!usbGetTransmitStatusI(&USB_DRIVER, CONSOLE_ENDPOINT) + if(!usbGetTransmitStatusI(usbp, CONSOLE_ENDPOINT) && (chOQGetFullI(&console_queue) >= CONSOLE_SIZE)) { osalSysUnlock(); - usbPrepareQueuedTransmit(&USB_DRIVER, CONSOLE_ENDPOINT, &console_queue, CONSOLE_SIZE); + usbPrepareQueuedTransmit(usbp, CONSOLE_ENDPOINT, &console_queue, CONSOLE_SIZE); osalSysLock(); - usbStartTransmitI(&USB_DRIVER, CONSOLE_ENDPOINT); + usbStartTransmitI(usbp, CONSOLE_ENDPOINT); } } /* Flush timer code * callback (called from ISR, unlocked state) */ static void console_flush_cb(void *arg) { - (void)arg; + USBDriver *usbp = (USBDriver *)arg; size_t i, n; uint8_t buf[CONSOLE_SIZE]; /* TODO: a solution without extra buffer? */ osalSysLockFromISR(); /* check that the states of things are as they're supposed to */ - if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + if(usbGetDriverStateI(usbp) != USB_ACTIVE) { /* rearm the timer */ - chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, NULL); + chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); osalSysUnlockFromISR(); return; } @@ -1204,7 +1204,7 @@ static void console_flush_cb(void *arg) { /* don't do anything if the queue is empty or has enough stuff in it */ if(((n = oqGetFullI(&console_queue)) == 0) || (n >= CONSOLE_SIZE)) { /* rearm the timer */ - chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, NULL); + chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); osalSysUnlockFromISR(); return; } @@ -1215,12 +1215,12 @@ static void console_flush_cb(void *arg) { for(i = n; i < CONSOLE_SIZE; i++) buf[i] = 0; osalSysUnlockFromISR(); - usbPrepareTransmit(&USB_DRIVER, CONSOLE_ENDPOINT, buf, CONSOLE_SIZE); + usbPrepareTransmit(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_SIZE); osalSysLockFromISR(); - (void)usbStartTransmitI(&USB_DRIVER, CONSOLE_ENDPOINT); + (void)usbStartTransmitI(usbp, CONSOLE_ENDPOINT); /* rearm the timer */ - chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, NULL); + chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); osalSysUnlockFromISR(); } diff --git a/tmk_core/protocol/chibios/usb_main.h b/tmk_core/protocol/chibios/usb_main.h index b7696643..baeae80a 100644 --- a/tmk_core/protocol/chibios/usb_main.h +++ b/tmk_core/protocol/chibios/usb_main.h @@ -31,7 +31,7 @@ #define USB_DRIVER USBD1 /* Initialize the USB driver and bus */ -void init_usb_driver(void); +void init_usb_driver(USBDriver *usbp); /* --------------- * Keyboard header From 8152d279a4a4aa01f7d3f0a0a7b09e2081aa0d93 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Tue, 8 Sep 2015 16:06:33 +0100 Subject: [PATCH 04/56] Move chibios to tool. --- tmk_core/{protocol => tool}/chibios/.gitignore | 0 tmk_core/{protocol => tool}/chibios/chibios.mk | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename tmk_core/{protocol => tool}/chibios/.gitignore (100%) rename tmk_core/{protocol => tool}/chibios/chibios.mk (99%) diff --git a/tmk_core/protocol/chibios/.gitignore b/tmk_core/tool/chibios/.gitignore similarity index 100% rename from tmk_core/protocol/chibios/.gitignore rename to tmk_core/tool/chibios/.gitignore diff --git a/tmk_core/protocol/chibios/chibios.mk b/tmk_core/tool/chibios/chibios.mk similarity index 99% rename from tmk_core/protocol/chibios/chibios.mk rename to tmk_core/tool/chibios/chibios.mk index 2ae1e5f9..ee52082e 100644 --- a/tmk_core/protocol/chibios/chibios.mk +++ b/tmk_core/tool/chibios/chibios.mk @@ -83,7 +83,7 @@ endif # # Imported source files and paths -CHIBIOS = $(TMK_DIR)/protocol/chibios/chibios +CHIBIOS = $(TMK_DIR)/tool/chibios/chibios # Startup files. include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/startup_$(shell echo $(MCU_SERIES) | tr '[:upper:]' '[:lower:]').mk # HAL-OSAL files (optional). From d057e5157ed22508ce87f8fef113be52674ba209 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Tue, 8 Sep 2015 16:07:34 +0100 Subject: [PATCH 05/56] Implement jump-to-bootloader. --- tmk_core/common/chibios/bootloader.c | 15 ++- .../tool/chibios/ch-bootloader-jump.patch | 115 ++++++++++++++++++ tmk_core/tool/chibios/common.mk | 4 + 3 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 tmk_core/tool/chibios/ch-bootloader-jump.patch diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c index 6c34e2e0..93f2e378 100644 --- a/tmk_core/common/chibios/bootloader.c +++ b/tmk_core/common/chibios/bootloader.c @@ -1,7 +1,16 @@ -/* TODO */ -/* ... chip dependent ... */ - #include "bootloader.h" +#include "ch.h" +#ifdef BOOTLOADER_ADDRESS +#define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) +extern uint32_t __ram0_end__; + +void bootloader_jump(void) { + *((unsigned long *)(SYMVAL(__ram0_end__) - 4)) = 0xDEADBEEF; // set magic flag => reset handler will jump into boot loader + NVIC_SystemReset(); +} +#else /* BOOTLOADER_ADDRESS */ void bootloader_jump(void) {} +#endif /* BOOTLOADER_ADDRESS */ + diff --git a/tmk_core/tool/chibios/ch-bootloader-jump.patch b/tmk_core/tool/chibios/ch-bootloader-jump.patch new file mode 100644 index 00000000..7f33e8ac --- /dev/null +++ b/tmk_core/tool/chibios/ch-bootloader-jump.patch @@ -0,0 +1,115 @@ +diff --git a/os/common/ports/ARMCMx/compilers/GCC/crt0_v6m.s b/os/common/ports/ARMCMx/compilers/GCC/crt0_v6m.s +index 38b4513..12a3f39 100644 +--- a/os/common/ports/ARMCMx/compilers/GCC/crt0_v6m.s ++++ b/os/common/ports/ARMCMx/compilers/GCC/crt0_v6m.s +@@ -98,6 +98,13 @@ + #define CRT0_CALL_DESTRUCTORS TRUE + #endif + ++/** ++ * @brief Magic number for jumping to bootloader. ++ */ ++#if !defined(MAGIC_BOOTLOADER_NUMBER) || defined(__DOXYGEN__) ++#define MAGIC_BOOTLOADER_NUMBER 0xDEADBEEF ++#endif ++ + /*===========================================================================*/ + /* Code section. */ + /*===========================================================================*/ +@@ -117,6 +124,17 @@ + .thumb_func + .global Reset_Handler + Reset_Handler: ++ ++#ifdef BOOTLOADER_ADDRESS ++ /* jump to bootloader code */ ++ ldr r0, =__ram0_end__-4 ++ ldr r1, =MAGIC_BOOTLOADER_NUMBER ++ ldr r2, [r0, #0] ++ str r0, [r0, #0] /* erase stored magic */ ++ cmp r2, r1 ++ beq Bootloader_Jump ++#endif /* BOOTLOADER_ADDRESS */ ++ + /* Interrupts are globally masked initially.*/ + cpsid i + +@@ -230,6 +248,21 @@ endfiniloop: + ldr r1, =__default_exit + bx r1 + ++#ifdef BOOTLOADER_ADDRESS ++/* ++ * Jump-to-bootloader function. ++ */ ++ ++ .align 2 ++ .thumb_func ++Bootloader_Jump: ++ ldr r0, =BOOTLOADER_ADDRESS ++ ldr r1, [r0, #0] ++ mov sp, r1 ++ ldr r0, [r0, #4] ++ bx r0 ++#endif /* BOOTLOADER_ADDRESS */ ++ + #endif + + /** @} */ +diff --git a/os/common/ports/ARMCMx/compilers/GCC/crt0_v7m.s b/os/common/ports/ARMCMx/compilers/GCC/crt0_v7m.s +index fcfa4de..2d560da 100644 +--- a/os/common/ports/ARMCMx/compilers/GCC/crt0_v7m.s ++++ b/os/common/ports/ARMCMx/compilers/GCC/crt0_v7m.s +@@ -133,6 +133,13 @@ + #define CRT0_CPACR_INIT 0x00F00000 + #endif + ++/** ++ * @brief Magic number for jumping to bootloader. ++ */ ++#if !defined(MAGIC_BOOTLOADER_NUMBER) || defined(__DOXYGEN__) ++#define MAGIC_BOOTLOADER_NUMBER 0xDEADBEEF ++#endif ++ + /*===========================================================================*/ + /* Code section. */ + /*===========================================================================*/ +@@ -157,6 +164,16 @@ + .thumb_func + .global Reset_Handler + Reset_Handler: ++#ifdef BOOTLOADER_ADDRESS ++ /* jump to bootloader code */ ++ ldr r0, =__ram0_end__-4 ++ ldr r1, =MAGIC_BOOTLOADER_NUMBER ++ ldr r2, [r0, #0] ++ str r0, [r0, #0] /* erase stored magic */ ++ cmp r2, r1 ++ beq Bootloader_Jump ++#endif /* BOOTLOADER_ADDRESS */ ++ + /* Interrupts are globally masked initially.*/ + cpsid i + +@@ -289,6 +306,21 @@ endfiniloop: + /* Branching to the defined exit handler.*/ + b __default_exit + ++#ifdef BOOTLOADER_ADDRESS ++/* ++ * Jump-to-bootloader function. ++ */ ++ ++ .align 2 ++ .thumb_func ++Bootloader_Jump: ++ ldr r0, =BOOTLOADER_ADDRESS ++ ldr r1, [r0, #0] ++ mov sp, r1 ++ ldr r0, [r0, #4] ++ bx r0 ++#endif /* BOOTLOADER_ADDRESS */ ++ + #endif /* !defined(__DOXYGEN__) */ + + /** @} */ diff --git a/tmk_core/tool/chibios/common.mk b/tmk_core/tool/chibios/common.mk index 952ec493..0e9935dc 100644 --- a/tmk_core/tool/chibios/common.mk +++ b/tmk_core/tool/chibios/common.mk @@ -81,6 +81,10 @@ endif # Version string OPT_DEFS += -DVERSION=$(shell (git describe --always --dirty || echo 'unknown') 2> /dev/null) +# Bootloader address +ifdef BOOTLOADER_ADDRESS + OPT_DEFS += -DBOOTLOADER_ADDRESS=$(BOOTLOADER_ADDRESS) +endif # Search Path VPATH += $(TMK_DIR)/common From 9cc281b4efdf2dcd817189bb8e633578d638bc45 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Tue, 8 Sep 2015 19:22:00 +0100 Subject: [PATCH 06/56] Small updates. --- tmk_core/protocol/chibios/README.md | 5 +++-- tmk_core/protocol/chibios/main.c | 2 +- tmk_core/protocol/chibios/usb_main.c | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/tmk_core/protocol/chibios/README.md b/tmk_core/protocol/chibios/README.md index 38fd495e..6fbc7c6e 100644 --- a/tmk_core/protocol/chibios/README.md +++ b/tmk_core/protocol/chibios/README.md @@ -8,16 +8,17 @@ Also pay attention to `-O0` (enabled for debugging); for deployment use `-O2`. - USB string descriptors are a mess. I did not find a way to cleanly generate the right structures from actual strings, so the definitions in individual keyboards' `config.h` are ugly as heck. - There are some random constants left so far, e.g. 5ms sleep between calling `keyboard_task` in `main.c`. There should be no such in `usb_main.c`. Everything is based on timers/interrupts/kernel scheduling (well except `keyboard_task`), so no periodically called things (again, except `keyboard_task`, which is just how TMK is designed). - It is easy to add some code for testing (e.g. blink LED, do stuff on button press, etc...) - just create another thread in `main.c`, it will run independently of the keyboard business. +- Jumping to bootloader works, but it is not entirely pleasant, since it is very much MCU dependent. So, one needs to dig out the right address to jump to, and pass it to the compiler in the `Makefile`. Also, a patch to upstream ChibiOS is needed (supplied), because it `ResetHandler` needs adjusting. - The USB stack works pretty completely; however there are bits of other TMK stuff that are not done yet: ### Immediate todo -- suspend / sleep led +- suspend +- sleep led ### Missing / not working (TMK vs ChibiOS bits) - eeprom / bootmagic (will be chip dependent) -- bootloader jump (chip dependent) ### Tried with diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c index 0d79a91f..6b4ec350 100644 --- a/tmk_core/protocol/chibios/main.c +++ b/tmk_core/protocol/chibios/main.c @@ -76,7 +76,7 @@ int main(void) { host_set_driver(&chibios_driver); #ifdef SLEEP_LED_ENABLE - sleep_led_init(); + sleep_led_init(); #endif print("Keyboard start.\n"); diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index b10e5daa..f8a31593 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -20,6 +20,11 @@ #include "usb_main.h" +#include "debug.h" +#ifdef SLEEP_LED_ENABLE +#include "sleep_led.h" +#endif + /* --------------------------------------------------------- * Global interface variables and declarations * --------------------------------------------------------- @@ -752,6 +757,7 @@ static const USBEndpointConfig nkro_ep_config = { static void usb_event_cb(USBDriver *usbp, usbevent_t event) { switch(event) { case USB_EVENT_RESET: + //TODO: from ISR! print("[R]"); return; case USB_EVENT_ADDRESS: @@ -778,9 +784,21 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) { return; case USB_EVENT_SUSPEND: + //TODO: from ISR! print("[S]"); + //TODO: signal suspend? +#ifdef SLEEP_LED_ENABLE + sleep_led_enable(); +#endif /* SLEEP_LED_ENABLE */ return; case USB_EVENT_WAKEUP: + //TODO: from ISR! print("[W]"); + //TODO: suspend_wakeup_init(); +#ifdef SLEEP_LED_ENABLE + sleep_led_disable(); + // NOTE: converters may not accept this + led_set(host_keyboard_leds()); +#endif /* SLEEP_LED_ENABLE */ return; case USB_EVENT_STALLED: From 5248511209cb765c2548c7037801a8102c75f5a5 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Tue, 8 Sep 2015 20:15:05 +0100 Subject: [PATCH 07/56] Fix bootloader-jump compiling. --- tmk_core/tool/chibios/chibios.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmk_core/tool/chibios/chibios.mk b/tmk_core/tool/chibios/chibios.mk index ee52082e..ed5fd5b8 100644 --- a/tmk_core/tool/chibios/chibios.mk +++ b/tmk_core/tool/chibios/chibios.mk @@ -206,7 +206,7 @@ CPPWARN = -Wall -Wextra -Wundef UDEFS = $(OPT_DEFS) # Define ASM defines here -UADEFS = +UADEFS = $(OPT_DEFS) # List all user directories here UINCDIR = From 7d4f3dd5a148f922007f321a0bb61a571d5bbbc9 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Thu, 10 Sep 2015 10:30:49 +0100 Subject: [PATCH 08/56] Move AVR specific sleep_led.c into avr. --- tmk_core/common.mk | 2 +- tmk_core/common/{ => avr}/sleep_led.c | 0 tmk_core/tool/chibios/common.mk | 3 +-- tmk_core/tool/mbed/common.mk | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) rename tmk_core/common/{ => avr}/sleep_led.c (100%) diff --git a/tmk_core/common.mk b/tmk_core/common.mk index f5a464d0..159183a2 100644 --- a/tmk_core/common.mk +++ b/tmk_core/common.mk @@ -54,7 +54,7 @@ ifdef USB_6KRO_ENABLE endif ifdef SLEEP_LED_ENABLE - SRC += $(COMMON_DIR)/sleep_led.c + SRC += $(COMMON_DIR)/avr/sleep_led.c OPT_DEFS += -DSLEEP_LED_ENABLE OPT_DEFS += -DNO_SUSPEND_POWER_DOWN endif diff --git a/tmk_core/common/sleep_led.c b/tmk_core/common/avr/sleep_led.c similarity index 100% rename from tmk_core/common/sleep_led.c rename to tmk_core/common/avr/sleep_led.c diff --git a/tmk_core/tool/chibios/common.mk b/tmk_core/tool/chibios/common.mk index 0e9935dc..f0d39f31 100644 --- a/tmk_core/tool/chibios/common.mk +++ b/tmk_core/tool/chibios/common.mk @@ -55,8 +55,7 @@ ifdef USB_6KRO_ENABLE endif ifdef SLEEP_LED_ENABLE - $(error Sleep LED Not Supported) - SRC += $(COMMON_DIR)/sleep_led.c + SRC += $(COMMON_DIR)/chibios/sleep_led.c OPT_DEFS += -DSLEEP_LED_ENABLE OPT_DEFS += -DNO_SUSPEND_POWER_DOWN endif diff --git a/tmk_core/tool/mbed/common.mk b/tmk_core/tool/mbed/common.mk index 77bf7c3e..a0aa8717 100644 --- a/tmk_core/tool/mbed/common.mk +++ b/tmk_core/tool/mbed/common.mk @@ -63,7 +63,7 @@ endif ifdef SLEEP_LED_ENABLE $(error Not Supported) - SRC += common/sleep_led.c + SRC += common/mbed/sleep_led.c OPT_DEFS += -DSLEEP_LED_ENABLE OPT_DEFS += -DNO_SUSPEND_POWER_DOWN endif From dc9fc9a7a457d44efc45f1dce51db3c5d4bc17c9 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Thu, 10 Sep 2015 10:31:19 +0100 Subject: [PATCH 09/56] Add basic sleep_led for chibios. --- tmk_core/common/chibios/printf.h | 12 ++++++------ tmk_core/common/chibios/sleep_led.c | 19 +++++++++++++++++++ tmk_core/common/chibios/timer.c | 2 +- tmk_core/protocol/chibios/README.md | 13 +++++++++---- tmk_core/protocol/chibios/main.c | 25 +++++++++++++++++++++++++ tmk_core/protocol/chibios/usb_main.c | 2 ++ tmk_core/protocol/chibios/usb_main.h | 3 +++ 7 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 tmk_core/common/chibios/sleep_led.c diff --git a/tmk_core/common/chibios/printf.h b/tmk_core/common/chibios/printf.h index 00813198..678a100c 100644 --- a/tmk_core/common/chibios/printf.h +++ b/tmk_core/common/chibios/printf.h @@ -55,16 +55,16 @@ many embedded systems. To use the printf you need to supply your own character output function, something like : - void putc ( void* p, char c) - { - while (!SERIAL_PORT_EMPTY) ; - SERIAL_PORT_TX_REGISTER = c; - } + void putc ( void* p, char c) + { + while (!SERIAL_PORT_EMPTY) ; + SERIAL_PORT_TX_REGISTER = c; + } Before you can call printf you need to initialize it to use your character output function with something like: - init_printf(NULL,putc); + init_printf(NULL,putc); Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc', the NULL (or any pointer) you pass into the 'init_printf' will eventually be diff --git a/tmk_core/common/chibios/sleep_led.c b/tmk_core/common/chibios/sleep_led.c new file mode 100644 index 00000000..b08c9c2a --- /dev/null +++ b/tmk_core/common/chibios/sleep_led.c @@ -0,0 +1,19 @@ +#include "ch.h" + +#include "led.h" +#include "sleep_led.h" + +void sleep_led_init(void) { +} + +void sleep_led_enable(void) { + led_set(1< Date: Thu, 10 Sep 2015 10:32:15 +0100 Subject: [PATCH 10/56] Add chibi_onekey example. --- keyboard/chibi_onekey/Makefile | 51 +++ keyboard/chibi_onekey/chconf.h | 499 +++++++++++++++++++++++++++ keyboard/chibi_onekey/config.h | 74 ++++ keyboard/chibi_onekey/halconf.h | 334 ++++++++++++++++++ keyboard/chibi_onekey/keymap_plain.c | 44 +++ keyboard/chibi_onekey/led.c | 34 ++ keyboard/chibi_onekey/matrix.c | 163 +++++++++ keyboard/chibi_onekey/mcuconf.h | 171 +++++++++ 8 files changed, 1370 insertions(+) create mode 100644 keyboard/chibi_onekey/Makefile create mode 100644 keyboard/chibi_onekey/chconf.h create mode 100644 keyboard/chibi_onekey/config.h create mode 100644 keyboard/chibi_onekey/halconf.h create mode 100644 keyboard/chibi_onekey/keymap_plain.c create mode 100644 keyboard/chibi_onekey/led.c create mode 100644 keyboard/chibi_onekey/matrix.c create mode 100644 keyboard/chibi_onekey/mcuconf.h diff --git a/keyboard/chibi_onekey/Makefile b/keyboard/chibi_onekey/Makefile new file mode 100644 index 00000000..3e8e715c --- /dev/null +++ b/keyboard/chibi_onekey/Makefile @@ -0,0 +1,51 @@ +# Target file name (without extension). +PROJECT = ch +TARGET = alps64 + +# Directory common source files exist +TMK_DIR = ../../tmk_core + +# Directory keyboard dependent files exist +TARGET_DIR = . + +# project specific files +SRC = matrix.c \ + led.c + +ifdef KEYMAP + SRC := keymap_$(KEYMAP).c $(SRC) +else + SRC := keymap_plain.c $(SRC) +endif + +CONFIG_H = config.h + +# chip/board +MCU_FAMILY = STM32 +MCU_SERIES = STM32F0xx +# linker script to use +MCU_MODEL_FAMILY = STM32F072xB +# the directory in board/ +BOARD = ST_STM32F072B_DISCOVERY +# ARM version, M0/M1 are 6, M3/M4/M7 are 7 +ARMV = 6 +# If you want to be able to jump to bootloader from firmware (on STM32 MCUs), +# set the correct BOOTLOADER_ADDRESS here. Otherwise leave commented out. +# It is chip dependent, the correct number can be looked up here (page 175): +# http://www.st.com/web/en/resource/technical/document/application_note/CD00167594.pdf +# This also requires a patch to chibios: /tmk_core/ +BOOTLOADER_ADDRESS = 0x1FFFC800 + +# Build Options +# comment out to disable the options. +# +#BOOTMAGIC_ENABLE = yes # Virtual DIP switch configuration +MOUSEKEY_ENABLE = yes # Mouse keys +EXTRAKEY_ENABLE = yes # Audio control and System control +CONSOLE_ENABLE = yes # Console for debug +COMMAND_ENABLE = yes # Commands for debug and configuration +SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +#NKRO_ENABLE = yes # USB Nkey Rollover + +include $(TMK_DIR)/tool/chibios/common.mk +include $(TMK_DIR)/tool/chibios/chibios.mk diff --git a/keyboard/chibi_onekey/chconf.h b/keyboard/chibi_onekey/chconf.h new file mode 100644 index 00000000..da4160b6 --- /dev/null +++ b/keyboard/chibi_onekey/chconf.h @@ -0,0 +1,499 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file templates/chconf.h + * @brief Configuration file template. + * @details A copy of this file must be placed in each project directory, it + * contains the application specific kernel settings. + * + * @addtogroup config + * @details Kernel related settings and hooks. + * @{ + */ + +#ifndef _CHCONF_H_ +#define _CHCONF_H_ + +/*===========================================================================*/ +/** + * @name System timers settings + * @{ + */ +/*===========================================================================*/ + +/** + * @brief System time counter resolution. + * @note Allowed values are 16 or 32 bits. + */ +#define CH_CFG_ST_RESOLUTION 32 + +/** + * @brief System tick frequency. + * @details Frequency of the system timer that drives the system ticks. This + * setting also defines the system tick time unit. + */ +#define CH_CFG_ST_FREQUENCY 10000 + +/** + * @brief Time delta constant for the tick-less mode. + * @note If this value is zero then the system uses the classic + * periodic tick. This value represents the minimum number + * of ticks that is safe to specify in a timeout directive. + * The value one is not valid, timeouts are rounded up to + * this value. + */ +#define CH_CFG_ST_TIMEDELTA 2 + +/** @} */ + +/*===========================================================================*/ +/** + * @name Kernel parameters and options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Round robin interval. + * @details This constant is the number of system ticks allowed for the + * threads before preemption occurs. Setting this value to zero + * disables the preemption for threads with equal priority and the + * round robin becomes cooperative. Note that higher priority + * threads can still preempt, the kernel is always preemptive. + * @note Disabling the round robin preemption makes the kernel more compact + * and generally faster. + * @note The round robin preemption is not supported in tickless mode and + * must be set to zero in that case. + */ +#define CH_CFG_TIME_QUANTUM 0 + +/** + * @brief Managed RAM size. + * @details Size of the RAM area to be managed by the OS. If set to zero + * then the whole available RAM is used. The core memory is made + * available to the heap allocator and/or can be used directly through + * the simplified core memory allocator. + * + * @note In order to let the OS manage the whole RAM the linker script must + * provide the @p __heap_base__ and @p __heap_end__ symbols. + * @note Requires @p CH_CFG_USE_MEMCORE. + */ +#define CH_CFG_MEMCORE_SIZE 0 + +/** + * @brief Idle thread automatic spawn suppression. + * @details When this option is activated the function @p chSysInit() + * does not spawn the idle thread. The application @p main() + * function becomes the idle thread and must implement an + * infinite loop. + */ +#define CH_CFG_NO_IDLE_THREAD FALSE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Performance options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief OS optimization. + * @details If enabled then time efficient rather than space efficient code + * is used when two possible implementations exist. + * + * @note This is not related to the compiler optimization options. + * @note The default is @p TRUE. + */ +#define CH_CFG_OPTIMIZE_SPEED FALSE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Subsystem options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Time Measurement APIs. + * @details If enabled then the time measurement APIs are included in + * the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_TM FALSE + +/** + * @brief Threads registry APIs. + * @details If enabled then the registry APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_REGISTRY TRUE + +/** + * @brief Threads synchronization APIs. + * @details If enabled then the @p chThdWait() function is included in + * the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_WAITEXIT TRUE + +/** + * @brief Semaphores APIs. + * @details If enabled then the Semaphores APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_SEMAPHORES TRUE + +/** + * @brief Semaphores queuing mode. + * @details If enabled then the threads are enqueued on semaphores by + * priority rather than in FIFO order. + * + * @note The default is @p FALSE. Enable this if you have special + * requirements. + * @note Requires @p CH_CFG_USE_SEMAPHORES. + */ +#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE + +/** + * @brief Mutexes APIs. + * @details If enabled then the mutexes APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MUTEXES TRUE + +/** + * @brief Enables recursive behavior on mutexes. + * @note Recursive mutexes are heavier and have an increased + * memory footprint. + * + * @note The default is @p FALSE. + * @note Requires @p CH_CFG_USE_MUTEXES. + */ +#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE + +/** + * @brief Conditional Variables APIs. + * @details If enabled then the conditional variables APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_MUTEXES. + */ +#define CH_CFG_USE_CONDVARS TRUE + +/** + * @brief Conditional Variables APIs with timeout. + * @details If enabled then the conditional variables APIs with timeout + * specification are included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_CONDVARS. + */ +#define CH_CFG_USE_CONDVARS_TIMEOUT FALSE + +/** + * @brief Events Flags APIs. + * @details If enabled then the event flags APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_EVENTS TRUE + +/** + * @brief Events Flags APIs with timeout. + * @details If enabled then the events APIs with timeout specification + * are included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_EVENTS. + */ +#define CH_CFG_USE_EVENTS_TIMEOUT TRUE + +/** + * @brief Synchronous Messages APIs. + * @details If enabled then the synchronous messages APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MESSAGES TRUE + +/** + * @brief Synchronous Messages queuing mode. + * @details If enabled then messages are served by priority rather than in + * FIFO order. + * + * @note The default is @p FALSE. Enable this if you have special + * requirements. + * @note Requires @p CH_CFG_USE_MESSAGES. + */ +#define CH_CFG_USE_MESSAGES_PRIORITY FALSE + +/** + * @brief Mailboxes APIs. + * @details If enabled then the asynchronous messages (mailboxes) APIs are + * included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_SEMAPHORES. + */ +#define CH_CFG_USE_MAILBOXES TRUE + +/** + * @brief I/O Queues APIs. + * @details If enabled then the I/O queues APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_QUEUES TRUE + +/** + * @brief Core Memory Manager APIs. + * @details If enabled then the core memory manager APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MEMCORE FALSE + +/** + * @brief Heap Allocator APIs. + * @details If enabled then the memory heap allocator APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or + * @p CH_CFG_USE_SEMAPHORES. + * @note Mutexes are recommended. + */ +#define CH_CFG_USE_HEAP FALSE + +/** + * @brief Memory Pools Allocator APIs. + * @details If enabled then the memory pools allocator APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MEMPOOLS FALSE + +/** + * @brief Dynamic Threads APIs. + * @details If enabled then the dynamic threads creation APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_WAITEXIT. + * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS. + */ +#define CH_CFG_USE_DYNAMIC FALSE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Debug options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Debug option, kernel statistics. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_STATISTICS FALSE + +/** + * @brief Debug option, system state check. + * @details If enabled the correct call protocol for system APIs is checked + * at runtime. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_SYSTEM_STATE_CHECK FALSE + +/** + * @brief Debug option, parameters checks. + * @details If enabled then the checks on the API functions input + * parameters are activated. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_ENABLE_CHECKS FALSE + +/** + * @brief Debug option, consistency checks. + * @details If enabled then all the assertions in the kernel code are + * activated. This includes consistency checks inside the kernel, + * runtime anomalies and port-defined checks. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_ENABLE_ASSERTS FALSE + +/** + * @brief Debug option, trace buffer. + * @details If enabled then the context switch circular trace buffer is + * activated. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_ENABLE_TRACE FALSE + +/** + * @brief Debug option, stack checks. + * @details If enabled then a runtime stack check is performed. + * + * @note The default is @p FALSE. + * @note The stack check is performed in a architecture/port dependent way. + * It may not be implemented or some ports. + * @note The default failure mode is to halt the system with the global + * @p panic_msg variable set to @p NULL. + */ +#define CH_DBG_ENABLE_STACK_CHECK FALSE + +/** + * @brief Debug option, stacks initialization. + * @details If enabled then the threads working area is filled with a byte + * value when a thread is created. This can be useful for the + * runtime measurement of the used stack. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_FILL_THREADS FALSE + +/** + * @brief Debug option, threads profiling. + * @details If enabled then a field is added to the @p thread_t structure that + * counts the system ticks occurred while executing the thread. + * + * @note The default is @p FALSE. + * @note This debug option is not currently compatible with the + * tickless mode. + */ +#define CH_DBG_THREADS_PROFILING FALSE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Kernel hooks + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Threads descriptor structure extension. + * @details User fields added to the end of the @p thread_t structure. + */ +#define CH_CFG_THREAD_EXTRA_FIELDS \ + /* Add threads custom fields here.*/ + +/** + * @brief Threads initialization hook. + * @details User initialization code added to the @p chThdInit() API. + * + * @note It is invoked from within @p chThdInit() and implicitly from all + * the threads creation APIs. + */ +#define CH_CFG_THREAD_INIT_HOOK(tp) { \ + /* Add threads initialization code here.*/ \ +} + +/** + * @brief Threads finalization hook. + * @details User finalization code added to the @p chThdExit() API. + * + * @note It is inserted into lock zone. + * @note It is also invoked when the threads simply return in order to + * terminate. + */ +#define CH_CFG_THREAD_EXIT_HOOK(tp) { \ + /* Add threads finalization code here.*/ \ +} + +/** + * @brief Context switch hook. + * @details This hook is invoked just before switching between threads. + */ +#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \ + /* Context switch code here.*/ \ +} + +/** + * @brief Idle thread enter hook. + * @note This hook is invoked within a critical zone, no OS functions + * should be invoked from here. + * @note This macro can be used to activate a power saving mode. + */ +#define CH_CFG_IDLE_ENTER_HOOK() { \ +} + +/** + * @brief Idle thread leave hook. + * @note This hook is invoked within a critical zone, no OS functions + * should be invoked from here. + * @note This macro can be used to deactivate a power saving mode. + */ +#define CH_CFG_IDLE_LEAVE_HOOK() { \ +} + +/** + * @brief Idle Loop hook. + * @details This hook is continuously invoked by the idle thread loop. + */ +#define CH_CFG_IDLE_LOOP_HOOK() { \ + /* Idle loop code here.*/ \ +} + +/** + * @brief System tick event hook. + * @details This hook is invoked in the system tick handler immediately + * after processing the virtual timers queue. + */ +#define CH_CFG_SYSTEM_TICK_HOOK() { \ + /* System tick event code here.*/ \ +} + +/** + * @brief System halt hook. + * @details This hook is invoked in case to a system halting error before + * the system is halted. + */ +#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \ + /* System halt code here.*/ \ +} + +/** @} */ + +/*===========================================================================*/ +/* Port-specific settings (override port settings defaulted in chcore.h). */ +/*===========================================================================*/ + +#endif /* _CHCONF_H_ */ + +/** @} */ diff --git a/keyboard/chibi_onekey/config.h b/keyboard/chibi_onekey/config.h new file mode 100644 index 00000000..8063c57e --- /dev/null +++ b/keyboard/chibi_onekey/config.h @@ -0,0 +1,74 @@ +/* +Copyright 2015 Jun Wako + +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 2 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 . +*/ + +#ifndef CONFIG_H +#define CONFIG_H + + +/* USB Device descriptor parameter */ +#define VENDOR_ID 0xFEED +#define PRODUCT_ID 0x6464 +#define DEVICE_VER 0x0001 +/* in python2: list(u"whatever".encode('utf-16-le')) */ +/* at most 32 characters or the ugly hack in usb_main.c borks */ +#define MANUFACTURER "TMK" +#define USBSTR_MANUFACTURER 'T', '\x00', 'M', '\x00', 'K', '\x00', ' ', '\x00', '\xc6', '\x00' +#define PRODUCT "ChibiOS TMK test" +#define USBSTR_PRODUCT 'C', '\x00', 'h', '\x00', 'i', '\x00', 'b', '\x00', 'i', '\x00', 'O', '\x00', 'S', '\x00', ' ', '\x00', 'T', '\x00', 'M', '\x00', 'K', '\x00', ' ', '\x00', 't', '\x00', 'e', '\x00', 's', '\x00', 't', '\x00' +#define DESCRIPTION "TMK keyboard firmware over ChibiOS" + +/* key matrix size */ +#define MATRIX_ROWS 1 +#define MATRIX_COLS 1 + +/* define if matrix has ghost */ +//#define MATRIX_HAS_GHOST + +/* Set 0 if debouncing isn't needed */ +#define DEBOUNCE 5 + +/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */ +#define LOCKING_SUPPORT_ENABLE +/* Locking resynchronize hack */ +#define LOCKING_RESYNC_ENABLE + +/* key combination for command */ +#define IS_COMMAND() ( \ + keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \ +) + + + +/* + * Feature disable options + * These options are also useful to firmware size reduction. + */ + +/* disable debug print */ +//#define NO_DEBUG + +/* disable print */ +//#define NO_PRINT + +/* disable action features */ +//#define NO_ACTION_LAYER +//#define NO_ACTION_TAPPING +//#define NO_ACTION_ONESHOT +//#define NO_ACTION_MACRO +//#define NO_ACTION_FUNCTION + +#endif diff --git a/keyboard/chibi_onekey/halconf.h b/keyboard/chibi_onekey/halconf.h new file mode 100644 index 00000000..d3415c55 --- /dev/null +++ b/keyboard/chibi_onekey/halconf.h @@ -0,0 +1,334 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file templates/halconf.h + * @brief HAL configuration header. + * @details HAL configuration file, this file allows to enable or disable the + * various device drivers from your application. You may also use + * this file in order to override the device drivers default settings. + * + * @addtogroup HAL_CONF + * @{ + */ + +#ifndef _HALCONF_H_ +#define _HALCONF_H_ + +#include "mcuconf.h" + +/** + * @brief Enables the PAL subsystem. + */ +#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__) +#define HAL_USE_PAL TRUE +#endif + +/** + * @brief Enables the ADC subsystem. + */ +#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__) +#define HAL_USE_ADC FALSE +#endif + +/** + * @brief Enables the CAN subsystem. + */ +#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__) +#define HAL_USE_CAN FALSE +#endif + +/** + * @brief Enables the DAC subsystem. + */ +#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__) +#define HAL_USE_DAC FALSE +#endif + +/** + * @brief Enables the EXT subsystem. + */ +#if !defined(HAL_USE_EXT) || defined(__DOXYGEN__) +#define HAL_USE_EXT FALSE +#endif + +/** + * @brief Enables the GPT subsystem. + */ +#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__) +#define HAL_USE_GPT FALSE +#endif + +/** + * @brief Enables the I2C subsystem. + */ +#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__) +#define HAL_USE_I2C FALSE +#endif + +/** + * @brief Enables the I2S subsystem. + */ +#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__) +#define HAL_USE_I2S FALSE +#endif + +/** + * @brief Enables the ICU subsystem. + */ +#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__) +#define HAL_USE_ICU FALSE +#endif + +/** + * @brief Enables the MAC subsystem. + */ +#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__) +#define HAL_USE_MAC FALSE +#endif + +/** + * @brief Enables the MMC_SPI subsystem. + */ +#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__) +#define HAL_USE_MMC_SPI FALSE +#endif + +/** + * @brief Enables the PWM subsystem. + */ +#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__) +#define HAL_USE_PWM FALSE +#endif + +/** + * @brief Enables the RTC subsystem. + */ +#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__) +#define HAL_USE_RTC FALSE +#endif + +/** + * @brief Enables the SDC subsystem. + */ +#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__) +#define HAL_USE_SDC FALSE +#endif + +/** + * @brief Enables the SERIAL subsystem. + */ +#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__) +#define HAL_USE_SERIAL FALSE +#endif + +/** + * @brief Enables the SERIAL over USB subsystem. + */ +#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__) +#define HAL_USE_SERIAL_USB FALSE +#endif + +/** + * @brief Enables the SPI subsystem. + */ +#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__) +#define HAL_USE_SPI FALSE +#endif + +/** + * @brief Enables the UART subsystem. + */ +#if !defined(HAL_USE_UART) || defined(__DOXYGEN__) +#define HAL_USE_UART FALSE +#endif + +/** + * @brief Enables the USB subsystem. + */ +#if !defined(HAL_USE_USB) || defined(__DOXYGEN__) +#define HAL_USE_USB TRUE +#endif + +/*===========================================================================*/ +/* ADC driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables synchronous APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__) +#define ADC_USE_WAIT TRUE +#endif + +/** + * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define ADC_USE_MUTUAL_EXCLUSION TRUE +#endif + +/*===========================================================================*/ +/* CAN driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Sleep mode related APIs inclusion switch. + */ +#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__) +#define CAN_USE_SLEEP_MODE TRUE +#endif + +/*===========================================================================*/ +/* I2C driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables the mutual exclusion APIs on the I2C bus. + */ +#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define I2C_USE_MUTUAL_EXCLUSION TRUE +#endif + +/*===========================================================================*/ +/* MAC driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables an event sources for incoming packets. + */ +#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__) +#define MAC_USE_ZERO_COPY FALSE +#endif + +/** + * @brief Enables an event sources for incoming packets. + */ +#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__) +#define MAC_USE_EVENTS TRUE +#endif + +/*===========================================================================*/ +/* MMC_SPI driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Delays insertions. + * @details If enabled this options inserts delays into the MMC waiting + * routines releasing some extra CPU time for the threads with + * lower priority, this may slow down the driver a bit however. + * This option is recommended also if the SPI driver does not + * use a DMA channel and heavily loads the CPU. + */ +#if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__) +#define MMC_NICE_WAITING TRUE +#endif + +/*===========================================================================*/ +/* SDC driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Number of initialization attempts before rejecting the card. + * @note Attempts are performed at 10mS intervals. + */ +#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__) +#define SDC_INIT_RETRY 100 +#endif + +/** + * @brief Include support for MMC cards. + * @note MMC support is not yet implemented so this option must be kept + * at @p FALSE. + */ +#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__) +#define SDC_MMC_SUPPORT FALSE +#endif + +/** + * @brief Delays insertions. + * @details If enabled this options inserts delays into the MMC waiting + * routines releasing some extra CPU time for the threads with + * lower priority, this may slow down the driver a bit however. + */ +#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__) +#define SDC_NICE_WAITING TRUE +#endif + +/*===========================================================================*/ +/* SERIAL driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Default bit rate. + * @details Configuration parameter, this is the baud rate selected for the + * default configuration. + */ +#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__) +#define SERIAL_DEFAULT_BITRATE 38400 +#endif + +/** + * @brief Serial buffers size. + * @details Configuration parameter, you can change the depth of the queue + * buffers depending on the requirements of your application. + * @note The default is 64 bytes for both the transmission and receive + * buffers. + */ +#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__) +#define SERIAL_BUFFERS_SIZE 16 +#endif + +/*===========================================================================*/ +/* SERIAL_USB driver related setting. */ +/*===========================================================================*/ + +/** + * @brief Serial over USB buffers size. + * @details Configuration parameter, the buffer size must be a multiple of + * the USB data endpoint maximum packet size. + * @note The default is 64 bytes for both the transmission and receive + * buffers. + */ +#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__) +#define SERIAL_USB_BUFFERS_SIZE 256 +#endif + +/*===========================================================================*/ +/* SPI driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables synchronous APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__) +#define SPI_USE_WAIT TRUE +#endif + +/** + * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define SPI_USE_MUTUAL_EXCLUSION TRUE +#endif + +#endif /* _HALCONF_H_ */ + +/** @} */ diff --git a/keyboard/chibi_onekey/keymap_plain.c b/keyboard/chibi_onekey/keymap_plain.c new file mode 100644 index 00000000..ea173935 --- /dev/null +++ b/keyboard/chibi_onekey/keymap_plain.c @@ -0,0 +1,44 @@ +/* +Copyright 2012,2013 Jun Wako + +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 2 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 "keycode.h" +#include "action.h" +#include "action_macro.h" +#include "report.h" +#include "host.h" +#include "print.h" +#include "debug.h" +#include "keymap.h" + +static const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + {{KC_A}}, +}; + +static const uint16_t fn_actions[] = { +}; + +/* translates key to keycode */ +uint8_t keymap_key_to_keycode(uint8_t layer, keypos_t key) +{ + return keymaps[(layer)][(key.row)][(key.col)]; +} + +/* translates Fn keycode to action */ +action_t keymap_fn_to_action(uint8_t keycode) +{ + return (action_t){ .code = fn_actions[FN_INDEX(keycode)] }; +} diff --git a/keyboard/chibi_onekey/led.c b/keyboard/chibi_onekey/led.c new file mode 100644 index 00000000..18edb8ba --- /dev/null +++ b/keyboard/chibi_onekey/led.c @@ -0,0 +1,34 @@ +/* +Copyright 2012 Jun Wako + +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 2 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 "hal.h" + +#include "led.h" + + +void led_set(uint8_t usb_led) +{ + (void)usb_led; + if (usb_led & (1< + +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 2 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 "ch.h" +#include "hal.h" + +/* + * scan matrix + */ +#include "print.h" +#include "debug.h" +#include "util.h" +#include "matrix.h" +#include "wait.h" + +#ifndef DEBOUNCE +# define DEBOUNCE 5 +#endif +static uint8_t debouncing = DEBOUNCE; + +/* matrix state(1:on, 0:off) */ +static matrix_row_t matrix[MATRIX_ROWS]; +static matrix_row_t matrix_debouncing[MATRIX_ROWS]; + +static matrix_row_t read_cols(void); +static void init_cols(void); +static void unselect_rows(void); +static void select_row(uint8_t row); + + +inline +uint8_t matrix_rows(void) +{ + return MATRIX_ROWS; +} + +inline +uint8_t matrix_cols(void) +{ + return MATRIX_COLS; +} + +#define LED_ON() do { palSetPad(GPIOC, GPIOC_LED_BLUE) ;} while (0) +#define LED_OFF() do { palClearPad(GPIOC, GPIOC_LED_BLUE); } while (0) +#define LED_TGL() do { palTogglePad(GPIOC, GPIOC_LED_BLUE); } while (0) + +void matrix_init(void) +{ + // initialize row and col + unselect_rows(); + init_cols(); + + // initialize matrix state: all keys off + for (uint8_t i=0; i < MATRIX_ROWS; i++) { + matrix[i] = 0; + matrix_debouncing[i] = 0; + } + + //debug + debug_matrix = true; + LED_ON(); + wait_ms(500); + LED_OFF(); +} + +uint8_t matrix_scan(void) +{ + for (uint8_t i = 0; i < MATRIX_ROWS; i++) { + select_row(i); + wait_us(30); // without this wait read unstable value. + matrix_row_t cols = read_cols(); + if (matrix_debouncing[i] != cols) { + matrix_debouncing[i] = cols; + if (debouncing) { + debug("bounce!: "); debug_hex(debouncing); debug("\n"); + } + debouncing = DEBOUNCE; + } + unselect_rows(); + } + + if (debouncing) { + if (--debouncing) { + wait_ms(1); + } else { + for (uint8_t i = 0; i < MATRIX_ROWS; i++) { + matrix[i] = matrix_debouncing[i]; + } + } + } + + return 1; +} + +inline +bool matrix_is_on(uint8_t row, uint8_t col) +{ + return (matrix[row] & ((matrix_row_t)1< Date: Thu, 10 Sep 2015 10:49:47 +0100 Subject: [PATCH 11/56] Update chibios README. --- tmk_core/protocol/chibios/README.md | 31 +++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/tmk_core/protocol/chibios/README.md b/tmk_core/protocol/chibios/README.md index 5df4c6e7..06569c51 100644 --- a/tmk_core/protocol/chibios/README.md +++ b/tmk_core/protocol/chibios/README.md @@ -1,12 +1,12 @@ -## USB stack implementation using ChibiOS +## TMK running on top of ChibiOS ### Notes -- To use, unpack or symlink ChibiOS here, to `chibios`. -- For gcc options, inspect `chibios.mk`. For instance, I enabled `-Wno-missing-field-initializers`, because TMK common bits generated a lot of hits on that. +- To use, unpack or symlink [ChibiOS] {currently 3.0.1} to `tmk_core/tool/chibios/chibios`. +- For gcc options, inspect `tmk_core/tool/chibios/chibios.mk`. For instance, I enabled `-Wno-missing-field-initializers`, because TMK common bits generated a lot of warnings on that. Also pay attention to `-O0` (enabled for debugging); for deployment use `-O2`. -- USB string descriptors are a mess. I did not find a way to cleanly generate the right structures from actual strings, so the definitions in individual keyboards' `config.h` are ugly as heck. -- There are some random constants left so far, e.g. 5ms sleep between calling `keyboard_task` in `main.c`. There should be no such in `usb_main.c`. Everything is based on timers/interrupts/kernel scheduling (well except `keyboard_task`), so no periodically called things (again, except `keyboard_task`, which is just how TMK is designed). +- USB string descriptors are messy. I did not find a way to cleanly generate the right structures from actual strings, so the definitions in individual keyboards' `config.h` are ugly as heck. +- There are some random constants left so far, e.g. 5ms sleep between calling `keyboard_task`, or 1.5sec wait for USB init, in `main.c`. There should be no such in `usb_main.c` (the main USB stack). Everything is based on timers/interrupts/kernel scheduling (well except `keyboard_task`), so no periodically called things (again, except `keyboard_task`, which is just how TMK is designed). - It is easy to add some code for testing (e.g. blink LED, do stuff on button press, etc...) - just create another thread in `main.c`, it will run independently of the keyboard business. - Jumping to bootloader works, but it is not entirely pleasant, since it is very much MCU dependent. The code is now geared towards STM32 chips and their built-in bootloaders. So, one needs to dig out the right address to jump to, and pass it to the compiler in the `Makefile`. Also, a patch to upstream ChibiOS is needed (supplied), because it `ResetHandler` needs adjusting. - Sleep LED works, but at the moment only on/off, i.e. no breathing. @@ -14,6 +14,7 @@ Also pay attention to `-O0` (enabled for debugging); for deployment use `-O2`. ### Immediate todo +- host-wakeup packet sending during suspend - power saving for suspend? - PWM for sleep led @@ -23,9 +24,27 @@ Also pay attention to `-O0` (enabled for debugging); for deployment use `-O2`. ### Missing / not working (TMK vs ChibiOS bits) -- eeprom / bootmagic (will be chip dependent; eeprom needs to be emulated in flash, which means less writes; wear-levelling?) +- eeprom / bootmagic (will be chip dependent; eeprom needs to be emulated in flash, which means less writes; wear-levelling?) There is a semi-official ST "driver" for eeprom, with wear-levelling, but I think it consumes a lot of RAM (like 2 pages, i.e. 1kB or so). ### Tried with - ChibiOS 3.0.1 and ST F072RB DISCOVERY board. - Need to test on other STM32 chips (F3, F4) to make it as much chip-independent as possible. + +## STM32-based keyboard design considerations + +- STM32F0x2 chips can do crystal-less USB, but they still need a 3.3V voltage regulator. +- The BOOT0 pin should be tied to GND. +- For a hardware way of accessing the in-built DFU bootloader, in addition to the reset button, put another button between the BOOT0 pin and 3V3. +- For breathing the caps lock LED during the suspended state ("sleep LED"), it is desirable to have that LED on a hardware PWM pin (there's usually plenty of those, look for TIMERs in the datasheet). + +## ChibiOS-supported MCUs (as of 3.0.1) + +- Pretty much all STM32 chips. +- There is also support for AVR8, but the USB stack is not implemented for them yet, and also the kernel itself takes about 1k of RAM. I think people managed to get ChibiOS running on atmega32[8p/u4] though. +- There is some support for K20 and KL25 Freescale chips (i.e. Teensy 3.0, mchck, FRDM-KL25Z, FRDM-K20D50M), but again, no USB stack yet. +- I've seen community support for Nordic NRF51822 (the chip in Adafruit's Bluefruit bluetooth-low-energy boards), but not sure about the extent. + + + +[ChibiOS]: http://chibios.org From a24ddf3403670724b798d18e1d39bc3f0751aecf Mon Sep 17 00:00:00 2001 From: flabbergast Date: Thu, 10 Sep 2015 12:48:44 +0100 Subject: [PATCH 12/56] NKRO fixes. --- tmk_core/common/report.h | 5 ++++ tmk_core/protocol/chibios/main.c | 23 ++++++++++----- tmk_core/protocol/chibios/usb_main.c | 42 ++++++++++++++-------------- tmk_core/protocol/chibios/usb_main.h | 31 +++++++------------- 4 files changed, 52 insertions(+), 49 deletions(-) diff --git a/tmk_core/common/report.h b/tmk_core/common/report.h index f6c0a315..0c799eca 100644 --- a/tmk_core/common/report.h +++ b/tmk_core/common/report.h @@ -84,6 +84,11 @@ along with this program. If not, see . # define KEYBOARD_REPORT_SIZE NKRO_EPSIZE # define KEYBOARD_REPORT_KEYS (NKRO_EPSIZE - 2) # define KEYBOARD_REPORT_BITS (NKRO_EPSIZE - 1) +#elif defined(PROTOCOL_CHIBIOS) && defined(NKRO_ENABLE) +# include "protocol/chibios/usb_main.h" +# define KEYBOARD_REPORT_SIZE NKRO_EPSIZE +# define KEYBOARD_REPORT_KEYS (NKRO_EPSIZE - 2) +# define KEYBOARD_REPORT_BITS (NKRO_EPSIZE - 1) #else # define KEYBOARD_REPORT_SIZE 8 diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c index 7aec7e3e..589675aa 100644 --- a/tmk_core/protocol/chibios/main.c +++ b/tmk_core/protocol/chibios/main.c @@ -40,6 +40,14 @@ * ------------------------- */ +/* declarations */ +uint8_t keyboard_leds(void); +void send_keyboard(report_keyboard_t *report); +void send_mouse(report_mouse_t *report); +void send_system(uint16_t data); +void send_consumer(uint16_t data); + +/* host struct */ host_driver_t chibios_driver = { keyboard_leds, send_keyboard, @@ -52,14 +60,15 @@ host_driver_t chibios_driver = { /* TESTING * Amber LED blinker thread, times are in milliseconds. */ -// uint8_t blinkLedState = 0; -// static THD_WORKING_AREA(waThread1, 128); -// static THD_FUNCTION(Thread1, arg) { +/* set this variable to non-zero anywhere to blink once */ +// uint8_t blinkLed = 0; +// static THD_WORKING_AREA(waBlinkerThread, 128); +// static THD_FUNCTION(blinkerThread, arg) { // (void)arg; -// chRegSetThreadName("blinker1"); +// chRegSetThreadName("blinkOrange"); // while(true) { -// if(blinkLedState) { -// blinkLedState = 0; +// if(blinkLed) { +// blinkLed = 0; // palSetPad(GPIOC, GPIOC_LED_ORANGE); // chThdSleepMilliseconds(100); // palClearPad(GPIOC, GPIOC_LED_ORANGE); @@ -82,7 +91,7 @@ int main(void) { palClearPad(GPIOC, GPIOC_LED_BLUE); // TESTING - // chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL); + // chThdCreateStatic(waBlinkerThread, sizeof(waBlinkerThread), NORMALPRIO, blinkerThread, NULL); /* Init USB */ init_usb_driver(&USB_DRIVER); diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index daefc490..f5f2dad7 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -20,11 +20,11 @@ #include "usb_main.h" +#include "host.h" #include "debug.h" #ifdef SLEEP_LED_ENABLE #include "sleep_led.h" #include "led.h" -#include "host.h" #endif /* --------------------------------------------------------- @@ -39,7 +39,7 @@ volatile uint16_t keyboard_idle_count = 0; static virtual_timer_t keyboard_idle_timer; static void keyboard_idle_timer_cb(void *arg); #ifdef NKRO_ENABLE -bool keyboard_nkro = true; +extern bool keyboard_nkro; #endif /* NKRO_ENABLE */ report_keyboard_t keyboard_report_sent = {{0}}; @@ -250,7 +250,7 @@ static const uint8_t console_hid_report_desc_data[] = { 0x75, 0x08, // report size = 8 bits 0x15, 0x00, // logical minimum = 0 0x26, 0xFF, 0x00, // logical maximum = 255 - 0x95, CONSOLE_SIZE, // report count + 0x95, CONSOLE_EPSIZE, // report count 0x09, 0x75, // usage 0x81, 0x02, // Input (array) 0xC0 // end collection @@ -374,7 +374,7 @@ static const uint8_t hid_configuration_descriptor_data[] = { /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ USB_DESC_ENDPOINT(KBD_ENDPOINT | 0x80, // bEndpointAddress 0x03, // bmAttributes (Interrupt) - KBD_SIZE, // wMaxPacketSize + KBD_EPSIZE,// wMaxPacketSize 10), // bInterval #ifdef MOUSE_ENABLE @@ -404,7 +404,7 @@ static const uint8_t hid_configuration_descriptor_data[] = { /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ USB_DESC_ENDPOINT(MOUSE_ENDPOINT | 0x80, // bEndpointAddress 0x03, // bmAttributes (Interrupt) - MOUSE_SIZE, // wMaxPacketSize + MOUSE_EPSIZE, // wMaxPacketSize 1), // bInterval #endif /* MOUSE_ENABLE */ @@ -430,7 +430,7 @@ static const uint8_t hid_configuration_descriptor_data[] = { /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ USB_DESC_ENDPOINT(CONSOLE_ENDPOINT | 0x80, // bEndpointAddress 0x03, // bmAttributes (Interrupt) - CONSOLE_SIZE, // wMaxPacketSize + CONSOLE_EPSIZE, // wMaxPacketSize 1), // bInterval #endif /* CONSOLE_ENABLE */ @@ -456,7 +456,7 @@ static const uint8_t hid_configuration_descriptor_data[] = { /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ USB_DESC_ENDPOINT(EXTRA_ENDPOINT | 0x80, // bEndpointAddress 0x03, // bmAttributes (Interrupt) - EXTRA_SIZE, // wMaxPacketSize + EXTRA_EPSIZE, // wMaxPacketSize 10), // bInterval #endif /* EXTRAKEY_ENABLE */ @@ -482,7 +482,7 @@ static const uint8_t hid_configuration_descriptor_data[] = { /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ USB_DESC_ENDPOINT(NKRO_ENDPOINT | 0x80, // bEndpointAddress 0x03, // bmAttributes (Interrupt) - NKRO_SIZE, // wMaxPacketSize + NKRO_EPSIZE, // wMaxPacketSize 1), // bInterval #endif /* NKRO_ENABLE */ }; @@ -665,7 +665,7 @@ static const USBEndpointConfig kbd_ep_config = { NULL, /* SETUP packet notification callback */ kbd_in_cb, /* IN notification callback */ NULL, /* OUT notification callback */ - KBD_SIZE, /* IN maximum packet size */ + KBD_EPSIZE, /* IN maximum packet size */ 0, /* OUT maximum packet size */ &kbd_ep_state, /* IN Endpoint state */ NULL, /* OUT endpoint state */ @@ -683,7 +683,7 @@ static const USBEndpointConfig mouse_ep_config = { NULL, /* SETUP packet notification callback */ mouse_in_cb, /* IN notification callback */ NULL, /* OUT notification callback */ - MOUSE_SIZE, /* IN maximum packet size */ + MOUSE_EPSIZE, /* IN maximum packet size */ 0, /* OUT maximum packet size */ &mouse_ep_state, /* IN Endpoint state */ NULL, /* OUT endpoint state */ @@ -702,7 +702,7 @@ static const USBEndpointConfig console_ep_config = { NULL, /* SETUP packet notification callback */ console_in_cb, /* IN notification callback */ NULL, /* OUT notification callback */ - CONSOLE_SIZE, /* IN maximum packet size */ + CONSOLE_EPSIZE, /* IN maximum packet size */ 0, /* OUT maximum packet size */ &console_ep_state, /* IN Endpoint state */ NULL, /* OUT endpoint state */ @@ -721,7 +721,7 @@ static const USBEndpointConfig extra_ep_config = { NULL, /* SETUP packet notification callback */ extra_in_cb, /* IN notification callback */ NULL, /* OUT notification callback */ - EXTRA_SIZE, /* IN maximum packet size */ + EXTRA_EPSIZE, /* IN maximum packet size */ 0, /* OUT maximum packet size */ &extra_ep_state, /* IN Endpoint state */ NULL, /* OUT endpoint state */ @@ -740,7 +740,7 @@ static const USBEndpointConfig nkro_ep_config = { NULL, /* SETUP packet notification callback */ nkro_in_cb, /* IN notification callback */ NULL, /* OUT notification callback */ - NKRO_SIZE, /* IN maximum packet size */ + NKRO_EPSIZE, /* IN maximum packet size */ 0, /* OUT maximum packet size */ &nkro_ep_state, /* IN Endpoint state */ NULL, /* OUT endpoint state */ @@ -1178,9 +1178,9 @@ void console_in_cb(USBDriver *usbp, usbep_t ep) { chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); /* Check if there is data to send left in the output queue */ - if(chOQGetFullI(&console_queue) >= CONSOLE_SIZE) { + if(chOQGetFullI(&console_queue) >= CONSOLE_EPSIZE) { osalSysUnlockFromISR(); - usbPrepareQueuedTransmit(usbp, CONSOLE_ENDPOINT, &console_queue, CONSOLE_SIZE); + usbPrepareQueuedTransmit(usbp, CONSOLE_ENDPOINT, &console_queue, CONSOLE_EPSIZE); osalSysLockFromISR(); usbStartTransmitI(usbp, CONSOLE_ENDPOINT); } @@ -1197,9 +1197,9 @@ void console_queue_onotify(io_queue_t *qp) { return; if(!usbGetTransmitStatusI(usbp, CONSOLE_ENDPOINT) - && (chOQGetFullI(&console_queue) >= CONSOLE_SIZE)) { + && (chOQGetFullI(&console_queue) >= CONSOLE_EPSIZE)) { osalSysUnlock(); - usbPrepareQueuedTransmit(usbp, CONSOLE_ENDPOINT, &console_queue, CONSOLE_SIZE); + usbPrepareQueuedTransmit(usbp, CONSOLE_ENDPOINT, &console_queue, CONSOLE_EPSIZE); osalSysLock(); usbStartTransmitI(usbp, CONSOLE_ENDPOINT); } @@ -1210,7 +1210,7 @@ void console_queue_onotify(io_queue_t *qp) { static void console_flush_cb(void *arg) { USBDriver *usbp = (USBDriver *)arg; size_t i, n; - uint8_t buf[CONSOLE_SIZE]; /* TODO: a solution without extra buffer? */ + uint8_t buf[CONSOLE_EPSIZE]; /* TODO: a solution without extra buffer? */ osalSysLockFromISR(); /* check that the states of things are as they're supposed to */ @@ -1222,7 +1222,7 @@ static void console_flush_cb(void *arg) { } /* don't do anything if the queue is empty or has enough stuff in it */ - if(((n = oqGetFullI(&console_queue)) == 0) || (n >= CONSOLE_SIZE)) { + if(((n = oqGetFullI(&console_queue)) == 0) || (n >= CONSOLE_EPSIZE)) { /* rearm the timer */ chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); osalSysUnlockFromISR(); @@ -1232,10 +1232,10 @@ static void console_flush_cb(void *arg) { /* there's stuff hanging in the queue - so dequeue and send */ for(i = 0; i < n; i++) buf[i] = (uint8_t)oqGetI(&console_queue); - for(i = n; i < CONSOLE_SIZE; i++) + for(i = n; i < CONSOLE_EPSIZE; i++) buf[i] = 0; osalSysUnlockFromISR(); - usbPrepareTransmit(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_SIZE); + usbPrepareTransmit(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE); osalSysLockFromISR(); (void)usbStartTransmitI(usbp, CONSOLE_ENDPOINT); diff --git a/tmk_core/protocol/chibios/usb_main.h b/tmk_core/protocol/chibios/usb_main.h index 5897375b..3a7e76fc 100644 --- a/tmk_core/protocol/chibios/usb_main.h +++ b/tmk_core/protocol/chibios/usb_main.h @@ -20,7 +20,7 @@ #define _USB_MAIN_H_ // TESTING -// extern uint8_t blinkLedState; +// extern uint8_t blinkLed; #include "ch.h" #include "hal.h" @@ -44,19 +44,19 @@ void init_usb_driver(USBDriver *usbp); /* main keyboard (6kro) */ #define KBD_INTERFACE 0 #define KBD_ENDPOINT 1 -#define KBD_SIZE 8 -#define KBD_REPORT_KEYS (KBD_SIZE - 2) +#define KBD_EPSIZE 8 +#define KBD_REPORT_KEYS (KBD_EPSIZE - 2) /* secondary keyboard */ #ifdef NKRO_ENABLE #define NKRO_INTERFACE 4 #define NKRO_ENDPOINT 5 -#define NKRO_SIZE 16 -#define NKRO_REPORT_KEYS (NKRO_SIZE - 1) +#define NKRO_EPSIZE 16 +#define NKRO_REPORT_KEYS (NKRO_EPSIZE - 1) #endif /* this defines report_keyboard_t and computes REPORT_SIZE defines */ -#include "report.h" +// #include "report.h" /* extern report_keyboard_t keyboard_report_sent; */ @@ -80,7 +80,7 @@ void nkro_in_cb(USBDriver *usbp, usbep_t ep); #define MOUSE_INTERFACE 1 #define MOUSE_ENDPOINT 2 -#define MOUSE_SIZE 8 +#define MOUSE_EPSIZE 8 /* mouse IN request callback handler */ void mouse_in_cb(USBDriver *usbp, usbep_t ep); @@ -95,7 +95,7 @@ void mouse_in_cb(USBDriver *usbp, usbep_t ep); #define EXTRA_INTERFACE 3 #define EXTRA_ENDPOINT 4 -#define EXTRA_SIZE 8 +#define EXTRA_EPSIZE 8 /* extrakey IN request callback handler */ void extra_in_cb(USBDriver *usbp, usbep_t ep); @@ -116,11 +116,11 @@ typedef struct { #define CONSOLE_INTERFACE 2 #define CONSOLE_ENDPOINT 3 -#define CONSOLE_SIZE 16 +#define CONSOLE_EPSIZE 16 /* Number of IN reports that can be stored inside the output queue */ #define CONSOLE_QUEUE_CAPACITY 2 -#define CONSOLE_QUEUE_BUFFER_SIZE (CONSOLE_QUEUE_CAPACITY * CONSOLE_SIZE) +#define CONSOLE_QUEUE_BUFFER_SIZE (CONSOLE_QUEUE_CAPACITY * CONSOLE_EPSIZE) /* Console flush time */ #define CONSOLE_FLUSH_MS 50 @@ -138,15 +138,4 @@ void console_in_cb(USBDriver *usbp, usbep_t ep); void sendchar_pf(void *p, char c); -/* --------------------------- - * Host driver functions (TMK) - * --------------------------- - */ - -uint8_t keyboard_leds(void); -void send_keyboard(report_keyboard_t *report); -void send_mouse(report_mouse_t *report); -void send_system(uint16_t data); -void send_consumer(uint16_t data); - #endif /* _USB_MAIN_H_ */ From 1bcf15a8ea08b1728bc6caf27117a5ade0659a15 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Thu, 10 Sep 2015 12:50:41 +0100 Subject: [PATCH 13/56] Add comments to chibi_onekey Makefile. --- keyboard/chibi_onekey/Makefile | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/keyboard/chibi_onekey/Makefile b/keyboard/chibi_onekey/Makefile index 3e8e715c..0a88f075 100644 --- a/keyboard/chibi_onekey/Makefile +++ b/keyboard/chibi_onekey/Makefile @@ -20,12 +20,17 @@ endif CONFIG_H = config.h -# chip/board +## chip/board settings +# the next two should match the directories in +# /os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES) MCU_FAMILY = STM32 MCU_SERIES = STM32F0xx # linker script to use +# it should exist either in /os/common/ports/ARMCMx/compilers/GCC/ld/ +# or /ld/ MCU_MODEL_FAMILY = STM32F072xB -# the directory in board/ +# it should exist either in /os/hal/boards/ +# or /boards BOARD = ST_STM32F072B_DISCOVERY # ARM version, M0/M1 are 6, M3/M4/M7 are 7 ARMV = 6 @@ -45,7 +50,7 @@ EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = yes # Console for debug COMMAND_ENABLE = yes # Commands for debug and configuration SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend -#NKRO_ENABLE = yes # USB Nkey Rollover +NKRO_ENABLE = yes # USB Nkey Rollover include $(TMK_DIR)/tool/chibios/common.mk include $(TMK_DIR)/tool/chibios/chibios.mk From 438559f11dba792630201d66a2b690760550c2e2 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Tue, 15 Sep 2015 08:52:03 +0100 Subject: [PATCH 14/56] Rename some Makefile defines. --- keyboard/chibi_onekey/Makefile | 7 +++++-- tmk_core/protocol/chibios/usb_main.h | 1 - tmk_core/tool/chibios/chibios.mk | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/keyboard/chibi_onekey/Makefile b/keyboard/chibi_onekey/Makefile index 0a88f075..2d5ff8cd 100644 --- a/keyboard/chibi_onekey/Makefile +++ b/keyboard/chibi_onekey/Makefile @@ -28,11 +28,14 @@ MCU_SERIES = STM32F0xx # linker script to use # it should exist either in /os/common/ports/ARMCMx/compilers/GCC/ld/ # or /ld/ -MCU_MODEL_FAMILY = STM32F072xB +MCU_LDSCRIPT = STM32F072xB +# startup code to use +# is should exist in /os/common/ports/ARMCMx/compilers/GCC/mk/ +MCU_STARTUP = stm32f0xx # it should exist either in /os/hal/boards/ # or /boards BOARD = ST_STM32F072B_DISCOVERY -# ARM version, M0/M1 are 6, M3/M4/M7 are 7 +# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 ARMV = 6 # If you want to be able to jump to bootloader from firmware (on STM32 MCUs), # set the correct BOOTLOADER_ADDRESS here. Otherwise leave commented out. diff --git a/tmk_core/protocol/chibios/usb_main.h b/tmk_core/protocol/chibios/usb_main.h index 3a7e76fc..85ea9153 100644 --- a/tmk_core/protocol/chibios/usb_main.h +++ b/tmk_core/protocol/chibios/usb_main.h @@ -127,7 +127,6 @@ typedef struct { /* Putchar over the USB console */ int8_t sendchar(uint8_t c); -/* wrapper for printf lib */ /* Flush output (send everything immediately) */ void console_flush_output(void); diff --git a/tmk_core/tool/chibios/chibios.mk b/tmk_core/tool/chibios/chibios.mk index ed5fd5b8..6ba783e3 100644 --- a/tmk_core/tool/chibios/chibios.mk +++ b/tmk_core/tool/chibios/chibios.mk @@ -85,7 +85,7 @@ endif # Imported source files and paths CHIBIOS = $(TMK_DIR)/tool/chibios/chibios # Startup files. -include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/startup_$(shell echo $(MCU_SERIES) | tr '[:upper:]' '[:lower:]').mk +include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/startup_$(MCU_STARTUP).mk # HAL-OSAL files (optional). include $(CHIBIOS)/os/hal/hal.mk include $(CHIBIOS)/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)/platform.mk @@ -101,10 +101,10 @@ include $(CHIBIOS)/os/rt/ports/ARMCMx/compilers/GCC/mk/port_v$(ARMV)m.mk # Other files (optional). # Define linker script file here -ifneq ("$(wildcard $(TARGET_DIR)/ld/$(MCU_MODEL_FAMILY).ld)","") -LDSCRIPT = $(TARGET_DIR)/ld/$(MCU_MODEL_FAMILY).ld +ifneq ("$(wildcard $(TARGET_DIR)/ld/$(MCU_LDSCRIPT).ld)","") +LDSCRIPT = $(TARGET_DIR)/ld/$(MCU_LDSCRIPT).ld else -LDSCRIPT = $(STARTUPLD)/$(MCU_MODEL_FAMILY).ld +LDSCRIPT = $(STARTUPLD)/$(MCU_LDSCRIPT).ld endif # C sources that can be compiled in ARM or THUMB mode depending on the global From 88c053f78e24d9906c0f469ab1862a468044a376 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Mon, 5 Oct 2015 10:23:31 +0100 Subject: [PATCH 15/56] Move STM32 bootloader address config to separate .h file. --- keyboard/chibi_onekey/Makefile | 12 ++++-- keyboard/chibi_onekey/bootloader_defs.h | 7 ++++ tmk_core/common/chibios/bootloader.c | 6 +-- tmk_core/protocol/chibios/README.md | 12 +++--- .../tool/chibios/ch-bootloader-jump.patch | 37 ++++++++++--------- tmk_core/tool/chibios/chibios.mk | 8 ++++ 6 files changed, 51 insertions(+), 31 deletions(-) create mode 100644 keyboard/chibi_onekey/bootloader_defs.h diff --git a/keyboard/chibi_onekey/Makefile b/keyboard/chibi_onekey/Makefile index 2d5ff8cd..2c5af44b 100644 --- a/keyboard/chibi_onekey/Makefile +++ b/keyboard/chibi_onekey/Makefile @@ -37,12 +37,16 @@ MCU_STARTUP = stm32f0xx BOARD = ST_STM32F072B_DISCOVERY # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 ARMV = 6 -# If you want to be able to jump to bootloader from firmware (on STM32 MCUs), -# set the correct BOOTLOADER_ADDRESS here. Otherwise leave commented out. +# If you want to be able to jump to bootloader from firmware on STM32 MCUs, +# set the correct BOOTLOADER_ADDRESS. Either set it here, or define it in +# ./bootloader_defs.h or in ./boards//bootloader_defs.h (if you have +# a custom board definition that you plan to reuse). +# If you're not setting it here, leave it commented out. # It is chip dependent, the correct number can be looked up here (page 175): # http://www.st.com/web/en/resource/technical/document/application_note/CD00167594.pdf -# This also requires a patch to chibios: /tmk_core/ -BOOTLOADER_ADDRESS = 0x1FFFC800 +# This also requires a patch to chibios: +# /tmk_core/tool/chibios/ch-bootloader-jump.patch +#STM32_BOOTLOADER_ADDRESS = 0x1FFFC800 # Build Options # comment out to disable the options. diff --git a/keyboard/chibi_onekey/bootloader_defs.h b/keyboard/chibi_onekey/bootloader_defs.h new file mode 100644 index 00000000..02c48c4e --- /dev/null +++ b/keyboard/chibi_onekey/bootloader_defs.h @@ -0,0 +1,7 @@ +/* Address for jumping to bootloader on STM32 chips. */ +/* It is chip dependent, the correct number can be looked up here (page 175): + * http://www.st.com/web/en/resource/technical/document/application_note/CD00167594.pdf + * This also requires a patch to chibios: + * /tmk_core/tool/chibios/ch-bootloader-jump.patch + */ +#define STM32_BOOTLOADER_ADDRESS 0x1FFFC800 diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c index 93f2e378..f36eced4 100644 --- a/tmk_core/common/chibios/bootloader.c +++ b/tmk_core/common/chibios/bootloader.c @@ -2,7 +2,7 @@ #include "ch.h" -#ifdef BOOTLOADER_ADDRESS +#ifdef STM32_BOOTLOADER_ADDRESS #define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) extern uint32_t __ram0_end__; @@ -10,7 +10,7 @@ void bootloader_jump(void) { *((unsigned long *)(SYMVAL(__ram0_end__) - 4)) = 0xDEADBEEF; // set magic flag => reset handler will jump into boot loader NVIC_SystemReset(); } -#else /* BOOTLOADER_ADDRESS */ +#else /* STM32_BOOTLOADER_ADDRESS */ void bootloader_jump(void) {} -#endif /* BOOTLOADER_ADDRESS */ +#endif /* STM32_BOOTLOADER_ADDRESS */ diff --git a/tmk_core/protocol/chibios/README.md b/tmk_core/protocol/chibios/README.md index 06569c51..676a5f4f 100644 --- a/tmk_core/protocol/chibios/README.md +++ b/tmk_core/protocol/chibios/README.md @@ -2,13 +2,13 @@ ### Notes -- To use, unpack or symlink [ChibiOS] {currently 3.0.1} to `tmk_core/tool/chibios/chibios`. +- To use, unpack or symlink [ChibiOS] {currently 3.0.2} to `tmk_core/tool/chibios/chibios`. - For gcc options, inspect `tmk_core/tool/chibios/chibios.mk`. For instance, I enabled `-Wno-missing-field-initializers`, because TMK common bits generated a lot of warnings on that. Also pay attention to `-O0` (enabled for debugging); for deployment use `-O2`. - USB string descriptors are messy. I did not find a way to cleanly generate the right structures from actual strings, so the definitions in individual keyboards' `config.h` are ugly as heck. - There are some random constants left so far, e.g. 5ms sleep between calling `keyboard_task`, or 1.5sec wait for USB init, in `main.c`. There should be no such in `usb_main.c` (the main USB stack). Everything is based on timers/interrupts/kernel scheduling (well except `keyboard_task`), so no periodically called things (again, except `keyboard_task`, which is just how TMK is designed). - It is easy to add some code for testing (e.g. blink LED, do stuff on button press, etc...) - just create another thread in `main.c`, it will run independently of the keyboard business. -- Jumping to bootloader works, but it is not entirely pleasant, since it is very much MCU dependent. The code is now geared towards STM32 chips and their built-in bootloaders. So, one needs to dig out the right address to jump to, and pass it to the compiler in the `Makefile`. Also, a patch to upstream ChibiOS is needed (supplied), because it `ResetHandler` needs adjusting. +- Jumping to bootloader works, but it is not entirely pleasant, since it is very much MCU dependent. The code is now geared towards STM32 chips and their built-in bootloaders. So, one needs to dig out the right address to jump to, and either pass it to the compiler in the `Makefile`, or better, define it in `/bootloader_defs.h`. Also, a patch to upstream ChibiOS is needed (supplied), because it `ResetHandler` needs adjusting. - Sleep LED works, but at the moment only on/off, i.e. no breathing. - The USB stack works pretty completely; however there are bits of other TMK stuff that are not done yet: @@ -28,7 +28,7 @@ Also pay attention to `-O0` (enabled for debugging); for deployment use `-O2`. ### Tried with -- ChibiOS 3.0.1 and ST F072RB DISCOVERY board. +- ChibiOS 3.0.1, 3.0.2 and ST F072RB DISCOVERY board. - Need to test on other STM32 chips (F3, F4) to make it as much chip-independent as possible. ## STM32-based keyboard design considerations @@ -36,13 +36,13 @@ Also pay attention to `-O0` (enabled for debugging); for deployment use `-O2`. - STM32F0x2 chips can do crystal-less USB, but they still need a 3.3V voltage regulator. - The BOOT0 pin should be tied to GND. - For a hardware way of accessing the in-built DFU bootloader, in addition to the reset button, put another button between the BOOT0 pin and 3V3. -- For breathing the caps lock LED during the suspended state ("sleep LED"), it is desirable to have that LED on a hardware PWM pin (there's usually plenty of those, look for TIMERs in the datasheet). +- For breathing the caps lock LED during the suspended state ("sleep LED"), it is desirable to have that LED on a hardware PWM pin (there's usually plenty of those, look for TIMERs in the datasheet). However this is not strictly necessary, because instead of direct output of a timer to a pin (better of course), it is easy to define timer callbacks in ChibiOS that turn on/off an arbitrary pin. -## ChibiOS-supported MCUs (as of 3.0.1) +## ChibiOS-supported MCUs (as of 3.0.2) - Pretty much all STM32 chips. - There is also support for AVR8, but the USB stack is not implemented for them yet, and also the kernel itself takes about 1k of RAM. I think people managed to get ChibiOS running on atmega32[8p/u4] though. -- There is some support for K20 and KL25 Freescale chips (i.e. Teensy 3.0, mchck, FRDM-KL25Z, FRDM-K20D50M), but again, no USB stack yet. +- There is some support for K20x and KL2x Freescale chips (i.e. Teensy 3.0, mchck, FRDM-KL25Z, FRDM-K20D50M), but again, no official USB stack yet. This is being worked on, see the `kinetis` branch of [my ChibiOS fork](https://github.com/flabbergast/ChibiOS). It supports also Teensy LC, 3.1 and FRDM-KL26Z. - I've seen community support for Nordic NRF51822 (the chip in Adafruit's Bluefruit bluetooth-low-energy boards), but not sure about the extent. diff --git a/tmk_core/tool/chibios/ch-bootloader-jump.patch b/tmk_core/tool/chibios/ch-bootloader-jump.patch index 7f33e8ac..d8865762 100644 --- a/tmk_core/tool/chibios/ch-bootloader-jump.patch +++ b/tmk_core/tool/chibios/ch-bootloader-jump.patch @@ -1,8 +1,8 @@ diff --git a/os/common/ports/ARMCMx/compilers/GCC/crt0_v6m.s b/os/common/ports/ARMCMx/compilers/GCC/crt0_v6m.s -index 38b4513..12a3f39 100644 +index 51a79bb..42d07bd 100644 --- a/os/common/ports/ARMCMx/compilers/GCC/crt0_v6m.s +++ b/os/common/ports/ARMCMx/compilers/GCC/crt0_v6m.s -@@ -98,6 +98,13 @@ +@@ -105,6 +105,13 @@ #define CRT0_CALL_DESTRUCTORS TRUE #endif @@ -16,12 +16,12 @@ index 38b4513..12a3f39 100644 /*===========================================================================*/ /* Code section. */ /*===========================================================================*/ -@@ -117,6 +124,17 @@ +@@ -124,6 +131,17 @@ .thumb_func .global Reset_Handler Reset_Handler: + -+#ifdef BOOTLOADER_ADDRESS ++#ifdef STM32_BOOTLOADER_ADDRESS + /* jump to bootloader code */ + ldr r0, =__ram0_end__-4 + ldr r1, =MAGIC_BOOTLOADER_NUMBER @@ -29,16 +29,16 @@ index 38b4513..12a3f39 100644 + str r0, [r0, #0] /* erase stored magic */ + cmp r2, r1 + beq Bootloader_Jump -+#endif /* BOOTLOADER_ADDRESS */ ++#endif /* STM32_BOOTLOADER_ADDRESS */ + /* Interrupts are globally masked initially.*/ cpsid i -@@ -230,6 +248,21 @@ endfiniloop: +@@ -242,6 +260,21 @@ endfiniloop: ldr r1, =__default_exit bx r1 -+#ifdef BOOTLOADER_ADDRESS ++#ifdef STM32_BOOTLOADER_ADDRESS +/* + * Jump-to-bootloader function. + */ @@ -46,21 +46,21 @@ index 38b4513..12a3f39 100644 + .align 2 + .thumb_func +Bootloader_Jump: -+ ldr r0, =BOOTLOADER_ADDRESS ++ ldr r0, =STM32_BOOTLOADER_ADDRESS + ldr r1, [r0, #0] + mov sp, r1 + ldr r0, [r0, #4] + bx r0 -+#endif /* BOOTLOADER_ADDRESS */ ++#endif /* STM32_BOOTLOADER_ADDRESS */ + #endif /** @} */ diff --git a/os/common/ports/ARMCMx/compilers/GCC/crt0_v7m.s b/os/common/ports/ARMCMx/compilers/GCC/crt0_v7m.s -index fcfa4de..2d560da 100644 +index 4812a29..dca9f88 100644 --- a/os/common/ports/ARMCMx/compilers/GCC/crt0_v7m.s +++ b/os/common/ports/ARMCMx/compilers/GCC/crt0_v7m.s -@@ -133,6 +133,13 @@ +@@ -140,6 +140,13 @@ #define CRT0_CPACR_INIT 0x00F00000 #endif @@ -74,11 +74,12 @@ index fcfa4de..2d560da 100644 /*===========================================================================*/ /* Code section. */ /*===========================================================================*/ -@@ -157,6 +164,16 @@ +@@ -164,6 +171,17 @@ .thumb_func .global Reset_Handler Reset_Handler: -+#ifdef BOOTLOADER_ADDRESS ++ ++#ifdef STM32_BOOTLOADER_ADDRESS + /* jump to bootloader code */ + ldr r0, =__ram0_end__-4 + ldr r1, =MAGIC_BOOTLOADER_NUMBER @@ -86,16 +87,16 @@ index fcfa4de..2d560da 100644 + str r0, [r0, #0] /* erase stored magic */ + cmp r2, r1 + beq Bootloader_Jump -+#endif /* BOOTLOADER_ADDRESS */ ++#endif /* STM32_BOOTLOADER_ADDRESS */ + /* Interrupts are globally masked initially.*/ cpsid i -@@ -289,6 +306,21 @@ endfiniloop: +@@ -305,6 +323,21 @@ endfiniloop: /* Branching to the defined exit handler.*/ b __default_exit -+#ifdef BOOTLOADER_ADDRESS ++#ifdef STM32_BOOTLOADER_ADDRESS +/* + * Jump-to-bootloader function. + */ @@ -103,12 +104,12 @@ index fcfa4de..2d560da 100644 + .align 2 + .thumb_func +Bootloader_Jump: -+ ldr r0, =BOOTLOADER_ADDRESS ++ ldr r0, =STM32_BOOTLOADER_ADDRESS + ldr r1, [r0, #0] + mov sp, r1 + ldr r0, [r0, #4] + bx r0 -+#endif /* BOOTLOADER_ADDRESS */ ++#endif /* STM32_BOOTLOADER_ADDRESS */ + #endif /* !defined(__DOXYGEN__) */ diff --git a/tmk_core/tool/chibios/chibios.mk b/tmk_core/tool/chibios/chibios.mk index 6ba783e3..bc8c61b3 100644 --- a/tmk_core/tool/chibios/chibios.mk +++ b/tmk_core/tool/chibios/chibios.mk @@ -207,6 +207,14 @@ UDEFS = $(OPT_DEFS) # Define ASM defines here UADEFS = $(OPT_DEFS) +# bootloader definitions may be used in the startup .s file +ifneq ("$(wildcard $(TARGET_DIR)/bootloader_defs.h)","") + UADEFS += -include $(TARGET_DIR)/bootloader_defs.h + UDEFS += -include $(TARGET_DIR)/bootloader_defs.h +else ifneq ("$(wildcard $(TARGET_DIR)/boards/$(BOARD)/bootloader_defs.h)","") + UADEFS += -include $(TARGET_DIR)/boards/$(BOARD)/bootloader_defs.h + UDEFS += -include $(TARGET_DIR)/boards/$(BOARD)/bootloader_defs.h +endif # List all user directories here UINCDIR = From 19bb28d102b8c9b226aae38e2d8d9e53e574d6ec Mon Sep 17 00:00:00 2001 From: flabbergast Date: Sat, 10 Oct 2015 15:20:55 +0100 Subject: [PATCH 16/56] Add ARM Teensies bootloader code. --- tmk_core/common/chibios/bootloader.c | 23 ++++++++++++++++++++--- tmk_core/protocol/chibios/main.c | 8 ++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c index f36eced4..cac52209 100644 --- a/tmk_core/common/chibios/bootloader.c +++ b/tmk_core/common/chibios/bootloader.c @@ -1,8 +1,13 @@ #include "bootloader.h" #include "ch.h" +#include "hal.h" #ifdef STM32_BOOTLOADER_ADDRESS +/* STM32 */ + +#if defined(STM32F0XX) +/* This code should be checked whether it runs correctly on platforms */ #define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) extern uint32_t __ram0_end__; @@ -10,7 +15,19 @@ void bootloader_jump(void) { *((unsigned long *)(SYMVAL(__ram0_end__) - 4)) = 0xDEADBEEF; // set magic flag => reset handler will jump into boot loader NVIC_SystemReset(); } -#else /* STM32_BOOTLOADER_ADDRESS */ -void bootloader_jump(void) {} -#endif /* STM32_BOOTLOADER_ADDRESS */ +#else /* defined(STM32F0XX) */ +#error Check that the bootloader code works on your platform and add it to bootloader.c! +#endif /* defined(STM32F0XX) */ + +#elif defined(KL2x) || defined(K20x) /* STM32_BOOTLOADER_ADDRESS */ +/* Kinetis */ + +void bootloader_jump(void) { + chThdSleepMilliseconds(100); + __BKPT(0); +} + +#else /* neither STM32 nor KINETIS */ +void bootloader_jump(void) {} +#endif \ No newline at end of file diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c index 589675aa..b2526d14 100644 --- a/tmk_core/protocol/chibios/main.c +++ b/tmk_core/protocol/chibios/main.c @@ -69,9 +69,9 @@ host_driver_t chibios_driver = { // while(true) { // if(blinkLed) { // blinkLed = 0; -// palSetPad(GPIOC, GPIOC_LED_ORANGE); +// palSetPad(TEENSY_PIN13_IOPORT, TEENSY_PIN13); // chThdSleepMilliseconds(100); -// palClearPad(GPIOC, GPIOC_LED_ORANGE); +// palClearPad(TEENSY_PIN13_IOPORT, TEENSY_PIN13); // } // chThdSleepMilliseconds(100); // } @@ -86,10 +86,6 @@ int main(void) { halInit(); chSysInit(); - palSetPad(GPIOC, GPIOC_LED_BLUE); - chThdSleepMilliseconds(400); - palClearPad(GPIOC, GPIOC_LED_BLUE); - // TESTING // chThdCreateStatic(waBlinkerThread, sizeof(waBlinkerThread), NORMALPRIO, blinkerThread, NULL); From 3c08375616beed21b9882231795b2fd05ff4f3ad Mon Sep 17 00:00:00 2001 From: flabbergast Date: Mon, 12 Oct 2015 06:52:26 +0100 Subject: [PATCH 17/56] Fix chibios/usb_main GET_REPORT handing. --- tmk_core/protocol/chibios/usb_main.c | 43 ++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index f5f2dad7..2625c425 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -43,6 +43,12 @@ extern bool keyboard_nkro; #endif /* NKRO_ENABLE */ report_keyboard_t keyboard_report_sent = {{0}}; +#ifdef MOUSE_ENABLE +report_mouse_t mouse_report_blank = {0}; +#endif /* MOUSE_ENABLE */ +#ifdef EXTRAKEY_ENABLE +uint8_t extra_report_blank[3] = {0}; +#endif /* EXTRAKEY_ENABLE */ #ifdef CONSOLE_ENABLE /* The emission queue */ @@ -858,6 +864,43 @@ static bool usb_request_hook_cb(USBDriver *usbp) { return TRUE; break; +#ifdef MOUSE_ENABLE + case MOUSE_INTERFACE: + usbSetupTransfer(usbp, (uint8_t *)&mouse_report_blank, sizeof(mouse_report_blank), NULL); + return TRUE; + break; +#endif /* MOUSE_ENABLE */ + +#ifdef CONSOLE_ENABLE + case CONSOLE_INTERFACE: + usbSetupTransfer(usbp, console_queue_buffer, CONSOLE_EPSIZE, NULL); + return TRUE; + break; +#endif /* CONSOLE_ENABLE */ + +#ifdef EXTRAKEY_ENABLE + case EXTRA_INTERFACE: + if(usbp->setup[3] == 1) { /* MSB(wValue) [Report Type] == 1 [Input Report] */ + switch(usbp->setup[2]) { /* LSB(wValue) [Report ID] */ + case REPORT_ID_SYSTEM: + extra_report_blank[0] = REPORT_ID_SYSTEM; + usbSetupTransfer(usbp, (uint8_t *)extra_report_blank, sizeof(extra_report_blank), NULL); + return TRUE; + break; + case REPORT_ID_CONSUMER: + extra_report_blank[0] = REPORT_ID_CONSUMER; + usbSetupTransfer(usbp, (uint8_t *)extra_report_blank, sizeof(extra_report_blank), NULL); + return TRUE; + break; + default: + return FALSE; + } + } else { + return FALSE; + } + break; +#endif /* EXTRAKEY_ENABLE */ + default: usbSetupTransfer(usbp, NULL, 0, NULL); return TRUE; From cd7f30299a26dd32d19ef8ddd587eb8e1a5d4958 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Mon, 12 Oct 2015 07:14:07 +0100 Subject: [PATCH 18/56] Add missing #include to keymap.c. --- tmk_core/common/keymap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tmk_core/common/keymap.c b/tmk_core/common/keymap.c index ece69fd5..d227add8 100644 --- a/tmk_core/common/keymap.c +++ b/tmk_core/common/keymap.c @@ -22,6 +22,7 @@ along with this program. If not, see . #include "action_macro.h" #include "wait.h" #include "debug.h" +#include "bootloader.h" static action_t keycode_to_action(uint8_t keycode); From d9652cdf992baaa066f75866689ac50d2b37cb9f Mon Sep 17 00:00:00 2001 From: flabbergast Date: Mon, 12 Oct 2015 09:40:06 +0100 Subject: [PATCH 19/56] Make bootmagic.c code portable (_delay_ms -> wait_ms). --- tmk_core/common/bootmagic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tmk_core/common/bootmagic.c b/tmk_core/common/bootmagic.c index d6684b2d..698693cb 100644 --- a/tmk_core/common/bootmagic.c +++ b/tmk_core/common/bootmagic.c @@ -1,6 +1,6 @@ #include #include -#include +#include "wait.h" #include "matrix.h" #include "bootloader.h" #include "debug.h" @@ -21,7 +21,7 @@ void bootmagic(void) /* do scans in case of bounce */ print("bootmagic scan: ... "); uint8_t scan = 100; - while (scan--) { matrix_scan(); _delay_ms(10); } + while (scan--) { matrix_scan(); wait_ms(10); } print("done.\n"); /* bootmagic skip */ From cb663eeca6f5f2924974b4c6798a64489a5cdacc Mon Sep 17 00:00:00 2001 From: flabbergast Date: Mon, 12 Oct 2015 09:46:48 +0100 Subject: [PATCH 20/56] Move declaration of keymap_config. Should really not declare variables in .h files - since it's included in different .c files, a proper linker then complains that the same variable is declared more than once (once for each .c file that the offending .h is included in). --- tmk_core/common/bootmagic.c | 1 + tmk_core/common/keymap.c | 3 +++ tmk_core/common/keymap.h | 1 - 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tmk_core/common/bootmagic.c b/tmk_core/common/bootmagic.c index 698693cb..08241f7d 100644 --- a/tmk_core/common/bootmagic.c +++ b/tmk_core/common/bootmagic.c @@ -10,6 +10,7 @@ #include "eeconfig.h" #include "bootmagic.h" +keymap_config_t keymap_config; void bootmagic(void) { diff --git a/tmk_core/common/keymap.c b/tmk_core/common/keymap.c index d227add8..876f5c82 100644 --- a/tmk_core/common/keymap.c +++ b/tmk_core/common/keymap.c @@ -24,6 +24,9 @@ along with this program. If not, see . #include "debug.h" #include "bootloader.h" +#ifdef BOOTMAGIC_ENABLE +extern keymap_config_t keymap_config; +#endif static action_t keycode_to_action(uint8_t keycode); diff --git a/tmk_core/common/keymap.h b/tmk_core/common/keymap.h index e1a6f992..659ea357 100644 --- a/tmk_core/common/keymap.h +++ b/tmk_core/common/keymap.h @@ -38,7 +38,6 @@ typedef union { bool nkro:1; }; } keymap_config_t; -keymap_config_t keymap_config; #endif From 2ac5cd730bf1578e9d593f83926a49b9053c4919 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Mon, 12 Oct 2015 09:47:27 +0100 Subject: [PATCH 21/56] Add eeprom support for chibios/kinetis. --- tmk_core/common/chibios/eeconfig.c | 551 +++++++++++++++++++++++++++++ tmk_core/tool/chibios/common.mk | 1 - 2 files changed, 551 insertions(+), 1 deletion(-) create mode 100644 tmk_core/common/chibios/eeconfig.c diff --git a/tmk_core/common/chibios/eeconfig.c b/tmk_core/common/chibios/eeconfig.c new file mode 100644 index 00000000..f9e2718a --- /dev/null +++ b/tmk_core/common/chibios/eeconfig.c @@ -0,0 +1,551 @@ +#include "ch.h" +#include "hal.h" + +#include "eeconfig.h" + +/*************************************/ +/* Hardware backend */ +/* */ +/* Code from PJRC/Teensyduino */ +/*************************************/ + +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2013 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#if defined(K20x) /* chip selection */ +/* Teensy 3.0, 3.1, 3.2; mchck; infinity keyboard */ + +// The EEPROM is really RAM with a hardware-based backup system to +// flash memory. Selecting a smaller size EEPROM allows more wear +// leveling, for higher write endurance. If you edit this file, +// set this to the smallest size your application can use. Also, +// due to Freescale's implementation, writing 16 or 32 bit words +// (aligned to 2 or 4 byte boundaries) has twice the endurance +// compared to writing 8 bit bytes. +// +#define EEPROM_SIZE 32 + +// Writing unaligned 16 or 32 bit data is handled automatically when +// this is defined, but at a cost of extra code size. Without this, +// any unaligned write will cause a hard fault exception! If you're +// absolutely sure all 16 and 32 bit writes will be aligned, you can +// remove the extra unnecessary code. +// +#define HANDLE_UNALIGNED_WRITES + +// Minimum EEPROM Endurance +// ------------------------ +#if (EEPROM_SIZE == 2048) // 35000 writes/byte or 70000 writes/word + #define EEESIZE 0x33 +#elif (EEPROM_SIZE == 1024) // 75000 writes/byte or 150000 writes/word + #define EEESIZE 0x34 +#elif (EEPROM_SIZE == 512) // 155000 writes/byte or 310000 writes/word + #define EEESIZE 0x35 +#elif (EEPROM_SIZE == 256) // 315000 writes/byte or 630000 writes/word + #define EEESIZE 0x36 +#elif (EEPROM_SIZE == 128) // 635000 writes/byte or 1270000 writes/word + #define EEESIZE 0x37 +#elif (EEPROM_SIZE == 64) // 1275000 writes/byte or 2550000 writes/word + #define EEESIZE 0x38 +#elif (EEPROM_SIZE == 32) // 2555000 writes/byte or 5110000 writes/word + #define EEESIZE 0x39 +#endif + +void eeprom_initialize(void) +{ + uint32_t count=0; + uint16_t do_flash_cmd[] = { + 0xf06f, 0x037f, 0x7003, 0x7803, + 0xf013, 0x0f80, 0xd0fb, 0x4770}; + uint8_t status; + + if (FTFL->FCNFG & FTFL_FCNFG_RAMRDY) { + // FlexRAM is configured as traditional RAM + // We need to reconfigure for EEPROM usage + FTFL->FCCOB0 = 0x80; // PGMPART = Program Partition Command + FTFL->FCCOB4 = EEESIZE; // EEPROM Size + FTFL->FCCOB5 = 0x03; // 0K for Dataflash, 32K for EEPROM backup + __disable_irq(); + // do_flash_cmd() must execute from RAM. Luckily the C syntax is simple... + (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFL->FSTAT)); + __enable_irq(); + status = FTFL->FSTAT; + if (status & (FTFL_FSTAT_RDCOLERR|FTFL_FSTAT_ACCERR|FTFL_FSTAT_FPVIOL)) { + FTFL->FSTAT = (status & (FTFL_FSTAT_RDCOLERR|FTFL_FSTAT_ACCERR|FTFL_FSTAT_FPVIOL)); + return; // error + } + } + // wait for eeprom to become ready (is this really necessary?) + while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { + if (++count > 20000) break; + } +} + +#define FlexRAM ((uint8_t *)0x14000000) + +uint8_t eeprom_read_byte(const uint8_t *addr) +{ + uint32_t offset = (uint32_t)addr; + if (offset >= EEPROM_SIZE) return 0; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + return FlexRAM[offset]; +} + +uint16_t eeprom_read_word(const uint16_t *addr) +{ + uint32_t offset = (uint32_t)addr; + if (offset >= EEPROM_SIZE-1) return 0; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + return *(uint16_t *)(&FlexRAM[offset]); +} + +uint32_t eeprom_read_dword(const uint32_t *addr) +{ + uint32_t offset = (uint32_t)addr; + if (offset >= EEPROM_SIZE-3) return 0; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + return *(uint32_t *)(&FlexRAM[offset]); +} + +void eeprom_read_block(void *buf, const void *addr, uint32_t len) +{ + uint32_t offset = (uint32_t)addr; + uint8_t *dest = (uint8_t *)buf; + uint32_t end = offset + len; + + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + if (end > EEPROM_SIZE) end = EEPROM_SIZE; + while (offset < end) { + *dest++ = FlexRAM[offset++]; + } +} + +int eeprom_is_ready(void) +{ + return (FTFL->FCNFG & FTFL_FCNFG_EEERDY) ? 1 : 0; +} + +static void flexram_wait(void) +{ + while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { + // TODO: timeout + } +} + +void eeprom_write_byte(uint8_t *addr, uint8_t value) +{ + uint32_t offset = (uint32_t)addr; + + if (offset >= EEPROM_SIZE) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + if (FlexRAM[offset] != value) { + FlexRAM[offset] = value; + flexram_wait(); + } +} + +void eeprom_write_word(uint16_t *addr, uint16_t value) +{ + uint32_t offset = (uint32_t)addr; + + if (offset >= EEPROM_SIZE-1) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); +#ifdef HANDLE_UNALIGNED_WRITES + if ((offset & 1) == 0) { +#endif + if (*(uint16_t *)(&FlexRAM[offset]) != value) { + *(uint16_t *)(&FlexRAM[offset]) = value; + flexram_wait(); + } +#ifdef HANDLE_UNALIGNED_WRITES + } else { + if (FlexRAM[offset] != value) { + FlexRAM[offset] = value; + flexram_wait(); + } + if (FlexRAM[offset + 1] != (value >> 8)) { + FlexRAM[offset + 1] = value >> 8; + flexram_wait(); + } + } +#endif +} + +void eeprom_write_dword(uint32_t *addr, uint32_t value) +{ + uint32_t offset = (uint32_t)addr; + + if (offset >= EEPROM_SIZE-3) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); +#ifdef HANDLE_UNALIGNED_WRITES + switch (offset & 3) { + case 0: +#endif + if (*(uint32_t *)(&FlexRAM[offset]) != value) { + *(uint32_t *)(&FlexRAM[offset]) = value; + flexram_wait(); + } + return; +#ifdef HANDLE_UNALIGNED_WRITES + case 2: + if (*(uint16_t *)(&FlexRAM[offset]) != value) { + *(uint16_t *)(&FlexRAM[offset]) = value; + flexram_wait(); + } + if (*(uint16_t *)(&FlexRAM[offset + 2]) != (value >> 16)) { + *(uint16_t *)(&FlexRAM[offset + 2]) = value >> 16; + flexram_wait(); + } + return; + default: + if (FlexRAM[offset] != value) { + FlexRAM[offset] = value; + flexram_wait(); + } + if (*(uint16_t *)(&FlexRAM[offset + 1]) != (value >> 8)) { + *(uint16_t *)(&FlexRAM[offset + 1]) = value >> 8; + flexram_wait(); + } + if (FlexRAM[offset + 3] != (value >> 24)) { + FlexRAM[offset + 3] = value >> 24; + flexram_wait(); + } + } +#endif +} + +void eeprom_write_block(const void *buf, void *addr, uint32_t len) +{ + uint32_t offset = (uint32_t)addr; + const uint8_t *src = (const uint8_t *)buf; + + if (offset >= EEPROM_SIZE) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + if (len >= EEPROM_SIZE) len = EEPROM_SIZE; + if (offset + len >= EEPROM_SIZE) len = EEPROM_SIZE - offset; + while (len > 0) { + uint32_t lsb = offset & 3; + if (lsb == 0 && len >= 4) { + // write aligned 32 bits + uint32_t val32; + val32 = *src++; + val32 |= (*src++ << 8); + val32 |= (*src++ << 16); + val32 |= (*src++ << 24); + if (*(uint32_t *)(&FlexRAM[offset]) != val32) { + *(uint32_t *)(&FlexRAM[offset]) = val32; + flexram_wait(); + } + offset += 4; + len -= 4; + } else if ((lsb == 0 || lsb == 2) && len >= 2) { + // write aligned 16 bits + uint16_t val16; + val16 = *src++; + val16 |= (*src++ << 8); + if (*(uint16_t *)(&FlexRAM[offset]) != val16) { + *(uint16_t *)(&FlexRAM[offset]) = val16; + flexram_wait(); + } + offset += 2; + len -= 2; + } else { + // write 8 bits + uint8_t val8 = *src++; + if (FlexRAM[offset] != val8) { + FlexRAM[offset] = val8; + flexram_wait(); + } + offset++; + len--; + } + } +} + +/* +void do_flash_cmd(volatile uint8_t *fstat) +{ + *fstat = 0x80; + while ((*fstat & 0x80) == 0) ; // wait +} +00000000 : + 0: f06f 037f mvn.w r3, #127 ; 0x7f + 4: 7003 strb r3, [r0, #0] + 6: 7803 ldrb r3, [r0, #0] + 8: f013 0f80 tst.w r3, #128 ; 0x80 + c: d0fb beq.n 6 + e: 4770 bx lr +*/ + +#elif defined(KL2x) /* chip selection */ +/* Teensy LC (emulated) */ + +#define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) + +extern uint32_t __eeprom_workarea_start__; +extern uint32_t __eeprom_workarea_end__; + +#define EEPROM_SIZE 128 + +static uint32_t flashend = 0; + +void eeprom_initialize(void) +{ + const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); + + do { + if (*p++ == 0xFFFF) { + flashend = (uint32_t)(p - 2); + return; + } + } while (p < (uint16_t *)SYMVAL(__eeprom_workarea_end__)); + flashend = (uint32_t)((uint16_t *)SYMVAL(__eeprom_workarea_end__) - 1); +} + +uint8_t eeprom_read_byte(const uint8_t *addr) +{ + uint32_t offset = (uint32_t)addr; + const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); + const uint16_t *end = (const uint16_t *)((uint32_t)flashend); + uint16_t val; + uint8_t data=0xFF; + + if (!end) { + eeprom_initialize(); + end = (const uint16_t *)((uint32_t)flashend); + } + if (offset < EEPROM_SIZE) { + while (p <= end) { + val = *p++; + if ((val & 255) == offset) data = val >> 8; + } + } + return data; +} + +static void flash_write(const uint16_t *code, uint32_t addr, uint32_t data) +{ + // with great power comes great responsibility.... + uint32_t stat; + *(uint32_t *)&(FTFA->FCCOB3) = 0x06000000 | (addr & 0x00FFFFFC); + *(uint32_t *)&(FTFA->FCCOB7) = data; + __disable_irq(); + (*((void (*)(volatile uint8_t *))((uint32_t)code | 1)))(&(FTFA->FSTAT)); + __enable_irq(); + stat = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR|FTFA_FSTAT_ACCERR|FTFA_FSTAT_FPVIOL); + if (stat) { + FTFA->FSTAT = stat; + } + MCM->PLACR |= MCM_PLACR_CFCC; +} + +void eeprom_write_byte(uint8_t *addr, uint8_t data) +{ + uint32_t offset = (uint32_t)addr; + const uint16_t *p, *end = (const uint16_t *)((uint32_t)flashend); + uint32_t i, val, flashaddr; + uint16_t do_flash_cmd[] = { + 0x2380, 0x7003, 0x7803, 0xb25b, 0x2b00, 0xdafb, 0x4770}; + uint8_t buf[EEPROM_SIZE]; + + if (offset >= EEPROM_SIZE) return; + if (!end) { + eeprom_initialize(); + end = (const uint16_t *)((uint32_t)flashend); + } + if (++end < (uint16_t *)SYMVAL(__eeprom_workarea_end__)) { + val = (data << 8) | offset; + flashaddr = (uint32_t)end; + flashend = flashaddr; + if ((flashaddr & 2) == 0) { + val |= 0xFFFF0000; + } else { + val <<= 16; + val |= 0x0000FFFF; + } + flash_write(do_flash_cmd, flashaddr, val); + } else { + for (i=0; i < EEPROM_SIZE; i++) { + buf[i] = 0xFF; + } + for (p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); p < (uint16_t *)SYMVAL(__eeprom_workarea_end__); p++) { + val = *p; + if ((val & 255) < EEPROM_SIZE) { + buf[val & 255] = val >> 8; + } + } + buf[offset] = data; + for (flashaddr=(uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); flashaddr < (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_end__); flashaddr += 1024) { + *(uint32_t *)&(FTFA->FCCOB3) = 0x09000000 | flashaddr; + __disable_irq(); + (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFA->FSTAT)); + __enable_irq(); + val = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR|FTFA_FSTAT_ACCERR|FTFA_FSTAT_FPVIOL);; + if (val) FTFA->FSTAT = val; + MCM->PLACR |= MCM_PLACR_CFCC; + } + flashaddr=(uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); + for (i=0; i < EEPROM_SIZE; i++) { + if (buf[i] == 0xFF) continue; + if ((flashaddr & 2) == 0) { + val = (buf[i] << 8) | i; + } else { + val = val | (buf[i] << 24) | (i << 16); + flash_write(do_flash_cmd, flashaddr, val); + } + flashaddr += 2; + } + flashend = flashaddr; + if ((flashaddr & 2)) { + val |= 0xFFFF0000; + flash_write(do_flash_cmd, flashaddr, val); + } + } +} + +/* +void do_flash_cmd(volatile uint8_t *fstat) +{ + *fstat = 0x80; + while ((*fstat & 0x80) == 0) ; // wait +} +00000000 : + 0: 2380 movs r3, #128 ; 0x80 + 2: 7003 strb r3, [r0, #0] + 4: 7803 ldrb r3, [r0, #0] + 6: b25b sxtb r3, r3 + 8: 2b00 cmp r3, #0 + a: dafb bge.n 4 + c: 4770 bx lr +*/ + + +uint16_t eeprom_read_word(const uint16_t *addr) +{ + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8); +} + +uint32_t eeprom_read_dword(const uint32_t *addr) +{ + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8) + | (eeprom_read_byte(p+2) << 16) | (eeprom_read_byte(p+3) << 24); +} + +void eeprom_read_block(void *buf, const void *addr, uint32_t len) +{ + const uint8_t *p = (const uint8_t *)addr; + uint8_t *dest = (uint8_t *)buf; + while (len--) { + *dest++ = eeprom_read_byte(p++); + } +} + +int eeprom_is_ready(void) +{ + return 1; +} + +void eeprom_write_word(uint16_t *addr, uint16_t value) +{ + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p, value >> 8); +} + +void eeprom_write_dword(uint32_t *addr, uint32_t value) +{ + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p++, value >> 8); + eeprom_write_byte(p++, value >> 16); + eeprom_write_byte(p, value >> 24); +} + +void eeprom_write_block(const void *buf, void *addr, uint32_t len) +{ + uint8_t *p = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + while (len--) { + eeprom_write_byte(p++, *src++); + } +} + +#else +#error EEPROM support not implemented for your chip +#endif /* chip selection */ + + +/*****************/ +/* TMK functions */ +/*****************/ + +void eeconfig_init(void) +{ + eeprom_write_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); + eeprom_write_byte(EECONFIG_DEBUG, 0); + eeprom_write_byte(EECONFIG_DEFAULT_LAYER, 0); + eeprom_write_byte(EECONFIG_KEYMAP, 0); + eeprom_write_byte(EECONFIG_MOUSEKEY_ACCEL, 0); +#ifdef BACKLIGHT_ENABLE + eeprom_write_byte(EECONFIG_BACKLIGHT, 0); +#endif +} + +void eeconfig_enable(void) +{ + eeprom_write_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); +} + +void eeconfig_disable(void) +{ + eeprom_write_word(EECONFIG_MAGIC, 0xFFFF); +} + +bool eeconfig_is_enabled(void) +{ + return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER); +} + +uint8_t eeconfig_read_debug(void) { return eeprom_read_byte(EECONFIG_DEBUG); } +void eeconfig_write_debug(uint8_t val) { eeprom_write_byte(EECONFIG_DEBUG, val); } + +uint8_t eeconfig_read_default_layer(void) { return eeprom_read_byte(EECONFIG_DEFAULT_LAYER); } +void eeconfig_write_default_layer(uint8_t val) { eeprom_write_byte(EECONFIG_DEFAULT_LAYER, val); } + +uint8_t eeconfig_read_keymap(void) { return eeprom_read_byte(EECONFIG_KEYMAP); } +void eeconfig_write_keymap(uint8_t val) { eeprom_write_byte(EECONFIG_KEYMAP, val); } + +#ifdef BACKLIGHT_ENABLE +uint8_t eeconfig_read_backlight(void) { return eeprom_read_byte(EECONFIG_BACKLIGHT); } +void eeconfig_write_backlight(uint8_t val) { eeprom_write_byte(EECONFIG_BACKLIGHT, val); } +#endif diff --git a/tmk_core/tool/chibios/common.mk b/tmk_core/tool/chibios/common.mk index f0d39f31..883054a4 100644 --- a/tmk_core/tool/chibios/common.mk +++ b/tmk_core/tool/chibios/common.mk @@ -18,7 +18,6 @@ SRC += $(COMMON_DIR)/host.c \ # Option modules ifdef BOOTMAGIC_ENABLE - $(error Bootmagic Not Supported) SRC += $(COMMON_DIR)/bootmagic.c SRC += $(COMMON_DIR)/chibios/eeconfig.c OPT_DEFS += -DBOOTMAGIC_ENABLE From 9178ea2d73511786182317fe3e7b8ffc02cc3dc4 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Mon, 12 Oct 2015 10:20:16 +0100 Subject: [PATCH 22/56] Rename chibios example keyboard. --- .../{chibi_onekey => stm32_onekey}/Makefile | 1 + .../bootloader_defs.h | 0 .../{chibi_onekey => stm32_onekey}/chconf.h | 0 .../{chibi_onekey => stm32_onekey}/config.h | 0 .../{chibi_onekey => stm32_onekey}/halconf.h | 0 .../keymap_plain.c | 0 keyboard/{chibi_onekey => stm32_onekey}/led.c | 0 .../{chibi_onekey => stm32_onekey}/matrix.c | 0 .../{chibi_onekey => stm32_onekey}/mcuconf.h | 0 tmk_core/protocol/chibios/README.md | 22 +++++++++---------- tmk_core/tool/chibios/chibios.mk | 2 +- 11 files changed, 13 insertions(+), 12 deletions(-) rename keyboard/{chibi_onekey => stm32_onekey}/Makefile (97%) rename keyboard/{chibi_onekey => stm32_onekey}/bootloader_defs.h (100%) rename keyboard/{chibi_onekey => stm32_onekey}/chconf.h (100%) rename keyboard/{chibi_onekey => stm32_onekey}/config.h (100%) rename keyboard/{chibi_onekey => stm32_onekey}/halconf.h (100%) rename keyboard/{chibi_onekey => stm32_onekey}/keymap_plain.c (100%) rename keyboard/{chibi_onekey => stm32_onekey}/led.c (100%) rename keyboard/{chibi_onekey => stm32_onekey}/matrix.c (100%) rename keyboard/{chibi_onekey => stm32_onekey}/mcuconf.h (100%) diff --git a/keyboard/chibi_onekey/Makefile b/keyboard/stm32_onekey/Makefile similarity index 97% rename from keyboard/chibi_onekey/Makefile rename to keyboard/stm32_onekey/Makefile index 2c5af44b..d84bfa4b 100644 --- a/keyboard/chibi_onekey/Makefile +++ b/keyboard/stm32_onekey/Makefile @@ -52,6 +52,7 @@ ARMV = 6 # comment out to disable the options. # #BOOTMAGIC_ENABLE = yes # Virtual DIP switch configuration +## BOOTMAGIC is not supported on STM32 chips yet. MOUSEKEY_ENABLE = yes # Mouse keys EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = yes # Console for debug diff --git a/keyboard/chibi_onekey/bootloader_defs.h b/keyboard/stm32_onekey/bootloader_defs.h similarity index 100% rename from keyboard/chibi_onekey/bootloader_defs.h rename to keyboard/stm32_onekey/bootloader_defs.h diff --git a/keyboard/chibi_onekey/chconf.h b/keyboard/stm32_onekey/chconf.h similarity index 100% rename from keyboard/chibi_onekey/chconf.h rename to keyboard/stm32_onekey/chconf.h diff --git a/keyboard/chibi_onekey/config.h b/keyboard/stm32_onekey/config.h similarity index 100% rename from keyboard/chibi_onekey/config.h rename to keyboard/stm32_onekey/config.h diff --git a/keyboard/chibi_onekey/halconf.h b/keyboard/stm32_onekey/halconf.h similarity index 100% rename from keyboard/chibi_onekey/halconf.h rename to keyboard/stm32_onekey/halconf.h diff --git a/keyboard/chibi_onekey/keymap_plain.c b/keyboard/stm32_onekey/keymap_plain.c similarity index 100% rename from keyboard/chibi_onekey/keymap_plain.c rename to keyboard/stm32_onekey/keymap_plain.c diff --git a/keyboard/chibi_onekey/led.c b/keyboard/stm32_onekey/led.c similarity index 100% rename from keyboard/chibi_onekey/led.c rename to keyboard/stm32_onekey/led.c diff --git a/keyboard/chibi_onekey/matrix.c b/keyboard/stm32_onekey/matrix.c similarity index 100% rename from keyboard/chibi_onekey/matrix.c rename to keyboard/stm32_onekey/matrix.c diff --git a/keyboard/chibi_onekey/mcuconf.h b/keyboard/stm32_onekey/mcuconf.h similarity index 100% rename from keyboard/chibi_onekey/mcuconf.h rename to keyboard/stm32_onekey/mcuconf.h diff --git a/tmk_core/protocol/chibios/README.md b/tmk_core/protocol/chibios/README.md index 676a5f4f..9796f8c7 100644 --- a/tmk_core/protocol/chibios/README.md +++ b/tmk_core/protocol/chibios/README.md @@ -2,15 +2,14 @@ ### Notes -- To use, unpack or symlink [ChibiOS] {currently 3.0.2} to `tmk_core/tool/chibios/chibios`. +- To use, unpack or symlink [ChibiOS] {currently 3.0.2} to `tmk_core/tool/chibios/chibios`. For Kinetis support, you'll need a fork which implements the USB driver, e.g. [this one](https://github.com/flabbergast/ChibiOS/tree/kinetis). - For gcc options, inspect `tmk_core/tool/chibios/chibios.mk`. For instance, I enabled `-Wno-missing-field-initializers`, because TMK common bits generated a lot of warnings on that. Also pay attention to `-O0` (enabled for debugging); for deployment use `-O2`. - USB string descriptors are messy. I did not find a way to cleanly generate the right structures from actual strings, so the definitions in individual keyboards' `config.h` are ugly as heck. - There are some random constants left so far, e.g. 5ms sleep between calling `keyboard_task`, or 1.5sec wait for USB init, in `main.c`. There should be no such in `usb_main.c` (the main USB stack). Everything is based on timers/interrupts/kernel scheduling (well except `keyboard_task`), so no periodically called things (again, except `keyboard_task`, which is just how TMK is designed). - It is easy to add some code for testing (e.g. blink LED, do stuff on button press, etc...) - just create another thread in `main.c`, it will run independently of the keyboard business. -- Jumping to bootloader works, but it is not entirely pleasant, since it is very much MCU dependent. The code is now geared towards STM32 chips and their built-in bootloaders. So, one needs to dig out the right address to jump to, and either pass it to the compiler in the `Makefile`, or better, define it in `/bootloader_defs.h`. Also, a patch to upstream ChibiOS is needed (supplied), because it `ResetHandler` needs adjusting. +- Jumping to (the built-in) bootloaders on STM32 works, but it is not entirely pleasant, since it is very much MCU dependent. So, one needs to dig out the right address to jump to, and either pass it to the compiler in the `Makefile`, or better, define it in `/bootloader_defs.h`. Also, a patch to upstream ChibiOS is needed (supplied), because it `ResetHandler` needs adjusting. - Sleep LED works, but at the moment only on/off, i.e. no breathing. -- The USB stack works pretty completely; however there are bits of other TMK stuff that are not done yet: ### Immediate todo @@ -24,12 +23,20 @@ Also pay attention to `-O0` (enabled for debugging); for deployment use `-O2`. ### Missing / not working (TMK vs ChibiOS bits) -- eeprom / bootmagic (will be chip dependent; eeprom needs to be emulated in flash, which means less writes; wear-levelling?) There is a semi-official ST "driver" for eeprom, with wear-levelling, but I think it consumes a lot of RAM (like 2 pages, i.e. 1kB or so). +- eeprom / bootmagic for STM32 (will be chip dependent; eeprom needs to be emulated in flash, which means less writes; wear-levelling?) There is a semi-official ST "driver" for eeprom, with wear-levelling, but I think it consumes a lot of RAM (like 2 pages, i.e. 1kB or so). ### Tried with - ChibiOS 3.0.1, 3.0.2 and ST F072RB DISCOVERY board. - Need to test on other STM32 chips (F3, F4) to make it as much chip-independent as possible. +- ChibiOS with Kinetis patches and Teensy LC and 3.0. + +## ChibiOS-supported MCUs (as of 3.0.2) + +- Pretty much all STM32 chips. +- There is some support for K20x and KL2x Freescale chips (i.e. Teensy 3.x/LC, mchck, FRDM-KL2{5,6}Z, FRDM-K20D50M), but again, no official USB stack yet. However the `kinetis` branch of [my ChibiOS fork](https://github.com/flabbergast/ChibiOS). With this fork, TMK work normally on all the ARM Teensies. +- There is also support for AVR8, but the USB stack is not implemented for them yet, and also the kernel itself takes about 1k of RAM. I think people managed to get ChibiOS running on atmega32[8p/u4] though. +- I've seen community support for Nordic NRF51822 (the chip in Adafruit's Bluefruit bluetooth-low-energy boards), but not sure about the extent. ## STM32-based keyboard design considerations @@ -38,13 +45,6 @@ Also pay attention to `-O0` (enabled for debugging); for deployment use `-O2`. - For a hardware way of accessing the in-built DFU bootloader, in addition to the reset button, put another button between the BOOT0 pin and 3V3. - For breathing the caps lock LED during the suspended state ("sleep LED"), it is desirable to have that LED on a hardware PWM pin (there's usually plenty of those, look for TIMERs in the datasheet). However this is not strictly necessary, because instead of direct output of a timer to a pin (better of course), it is easy to define timer callbacks in ChibiOS that turn on/off an arbitrary pin. -## ChibiOS-supported MCUs (as of 3.0.2) - -- Pretty much all STM32 chips. -- There is also support for AVR8, but the USB stack is not implemented for them yet, and also the kernel itself takes about 1k of RAM. I think people managed to get ChibiOS running on atmega32[8p/u4] though. -- There is some support for K20x and KL2x Freescale chips (i.e. Teensy 3.0, mchck, FRDM-KL25Z, FRDM-K20D50M), but again, no official USB stack yet. This is being worked on, see the `kinetis` branch of [my ChibiOS fork](https://github.com/flabbergast/ChibiOS). It supports also Teensy LC, 3.1 and FRDM-KL26Z. -- I've seen community support for Nordic NRF51822 (the chip in Adafruit's Bluefruit bluetooth-low-energy boards), but not sure about the extent. - [ChibiOS]: http://chibios.org diff --git a/tmk_core/tool/chibios/chibios.mk b/tmk_core/tool/chibios/chibios.mk index bc8c61b3..d1738c13 100644 --- a/tmk_core/tool/chibios/chibios.mk +++ b/tmk_core/tool/chibios/chibios.mk @@ -5,7 +5,7 @@ # Compiler options here. ifeq ($(USE_OPT),) - USE_OPT = -O0 -ggdb -fomit-frame-pointer -falign-functions=16 -std=gnu99 -DPROTOCOL_CHIBIOS + USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16 -std=gnu99 -DPROTOCOL_CHIBIOS endif # C specific options here (added to USE_OPT). From 2b4066bb93ffda75dde58342a21c28878b6f50e6 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Mon, 12 Oct 2015 10:38:47 +0100 Subject: [PATCH 23/56] Move chibios/cortex selection to local Makefiles. --- keyboard/stm32_onekey/Makefile | 3 +++ tmk_core/tool/chibios/chibios.mk | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/keyboard/stm32_onekey/Makefile b/keyboard/stm32_onekey/Makefile index d84bfa4b..3868ee3d 100644 --- a/keyboard/stm32_onekey/Makefile +++ b/keyboard/stm32_onekey/Makefile @@ -35,6 +35,9 @@ MCU_STARTUP = stm32f0xx # it should exist either in /os/hal/boards/ # or /boards BOARD = ST_STM32F072B_DISCOVERY +# Cortex version +# Teensy LC is cortex-m0; Teensy 3.x are cortex-m4 +MCU = cortex-m0 # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 ARMV = 6 # If you want to be able to jump to bootloader from firmware on STM32 MCUs, diff --git a/tmk_core/tool/chibios/chibios.mk b/tmk_core/tool/chibios/chibios.mk index d1738c13..0f6af53d 100644 --- a/tmk_core/tool/chibios/chibios.mk +++ b/tmk_core/tool/chibios/chibios.mk @@ -162,8 +162,6 @@ INCDIR = $(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \ # Compiler settings # -MCU = cortex-m0 - #TRGT = arm-elf- TRGT = arm-none-eabi- CC = $(TRGT)gcc From c61210cfff21f88c25ad102dcd3afe58ce251c96 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Mon, 12 Oct 2015 10:39:24 +0100 Subject: [PATCH 24/56] Add Teensy LC onekey example. --- keyboard/teensy_lc_onekey/Makefile | 77 ++++ keyboard/teensy_lc_onekey/Makefile.3.0 | 77 ++++ keyboard/teensy_lc_onekey/chconf.h | 499 ++++++++++++++++++++++ keyboard/teensy_lc_onekey/config.h | 74 ++++ keyboard/teensy_lc_onekey/halconf.h | 168 ++++++++ keyboard/teensy_lc_onekey/instructions.md | 62 +++ keyboard/teensy_lc_onekey/keymap_plain.c | 44 ++ keyboard/teensy_lc_onekey/ld/MKL26Z64.ld | 161 +++++++ keyboard/teensy_lc_onekey/led.c | 32 ++ keyboard/teensy_lc_onekey/matrix.c | 163 +++++++ keyboard/teensy_lc_onekey/mcuconf.h | 55 +++ 11 files changed, 1412 insertions(+) create mode 100644 keyboard/teensy_lc_onekey/Makefile create mode 100644 keyboard/teensy_lc_onekey/Makefile.3.0 create mode 100644 keyboard/teensy_lc_onekey/chconf.h create mode 100644 keyboard/teensy_lc_onekey/config.h create mode 100644 keyboard/teensy_lc_onekey/halconf.h create mode 100644 keyboard/teensy_lc_onekey/instructions.md create mode 100644 keyboard/teensy_lc_onekey/keymap_plain.c create mode 100644 keyboard/teensy_lc_onekey/ld/MKL26Z64.ld create mode 100644 keyboard/teensy_lc_onekey/led.c create mode 100644 keyboard/teensy_lc_onekey/matrix.c create mode 100644 keyboard/teensy_lc_onekey/mcuconf.h diff --git a/keyboard/teensy_lc_onekey/Makefile b/keyboard/teensy_lc_onekey/Makefile new file mode 100644 index 00000000..dc489cb7 --- /dev/null +++ b/keyboard/teensy_lc_onekey/Makefile @@ -0,0 +1,77 @@ +# Target file name (without extension). +PROJECT = ch + +# Directory common source files exist +TMK_DIR = ../../tmk_core + +# Directory keyboard dependent files exist +TARGET_DIR = . + +# project specific files +SRC = matrix.c \ + led.c + +ifdef KEYMAP + SRC := keymap_$(KEYMAP).c $(SRC) +else + SRC := keymap_plain.c $(SRC) +endif + +CONFIG_H = config.h + +## chip/board settings +# - the next two should match the directories in +# /os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES) +# - For Teensies, FAMILY = KINETIS and SERIES is either +# KL2x (LC) or K20x (3.0,3.1,3.2). +MCU_FAMILY = KINETIS +MCU_SERIES = KL2x + +# Linker script to use +# - it should exist either in /os/common/ports/ARMCMx/compilers/GCC/ld/ +# or /ld/ +# - NOTE: a custom ld script is needed for EEPROM on Teensy LC +# - LDSCRIPT = +# - MKL26Z64 for Teensy LC +# - MK20DX128 for Teensy 3.0 +# - MK20DX256 for Teensy 3.1 and 3.2 +MCU_LDSCRIPT = MKL26Z64 + +# Startup code to use +# - it should exist in /os/common/ports/ARMCMx/compilers/GCC/mk/ +# - STARTUP = +# - kl2x for Teensy LC +# - k20x5 for Teensy 3.0 +# - k20x7 for Teensy 3.1 and 3.2 +MCU_STARTUP = kl2x + +# Board: it should exist either in /os/hal/boards/ +# or /boards +# - BOARD = +# - PJRC_TEENSY_LC for Teensy LC +# - PJRC_TEENSY_3 for Teensy 3.0 +# - PJRC_TEENSY_3_1 for Teensy 3.1 or 3.2 +BOARD = PJRC_TEENSY_LC + +# Cortex version +# Teensy LC is cortex-m0; Teensy 3.x are cortex-m4 +MCU = cortex-m0 + +# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 +# I.e. 6 for Teensy LC; 7 for Teensy 3.x +ARMV = 6 + +# Build Options +# comment out to disable the options. +# +BOOTMAGIC_ENABLE = yes # Virtual DIP switch configuration +## (Note that for BOOTMAGIC on Teensy LC you have to use a custom .ld script.) +MOUSEKEY_ENABLE = yes # Mouse keys +EXTRAKEY_ENABLE = yes # Audio control and System control +CONSOLE_ENABLE = yes # Console for debug +COMMAND_ENABLE = yes # Commands for debug and configuration +SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +NKRO_ENABLE = yes # USB Nkey Rollover + +include $(TMK_DIR)/tool/chibios/common.mk +include $(TMK_DIR)/tool/chibios/chibios.mk diff --git a/keyboard/teensy_lc_onekey/Makefile.3.0 b/keyboard/teensy_lc_onekey/Makefile.3.0 new file mode 100644 index 00000000..a2f98c7e --- /dev/null +++ b/keyboard/teensy_lc_onekey/Makefile.3.0 @@ -0,0 +1,77 @@ +# Target file name (without extension). +PROJECT = ch + +# Directory common source files exist +TMK_DIR = ../../tmk_core + +# Directory keyboard dependent files exist +TARGET_DIR = . + +# project specific files +SRC = matrix.c \ + led.c + +ifdef KEYMAP + SRC := keymap_$(KEYMAP).c $(SRC) +else + SRC := keymap_plain.c $(SRC) +endif + +CONFIG_H = config.h + +## chip/board settings +# - the next two should match the directories in +# /os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES) +# - For Teensies, FAMILY = KINETIS and SERIES is either +# KL2x (LC) or K20x (3.0,3.1,3.2). +MCU_FAMILY = KINETIS +MCU_SERIES = K20x + +# Linker script to use +# - it should exist either in /os/common/ports/ARMCMx/compilers/GCC/ld/ +# or /ld/ +# - NOTE: a custom ld script is needed for EEPROM on Teensy LC +# - LDSCRIPT = +# - MKL26Z64 for Teensy LC +# - MK20DX128 for Teensy 3.0 +# - MK20DX256 for Teensy 3.1 and 3.2 +MCU_LDSCRIPT = MK20DX128 + +# Startup code to use +# - it should exist in /os/common/ports/ARMCMx/compilers/GCC/mk/ +# - STARTUP = +# - kl2x for Teensy LC +# - k20x5 for Teensy 3.0 +# - k20x7 for Teensy 3.1 and 3.2 +MCU_STARTUP = k20x5 + +# Board: it should exist either in /os/hal/boards/ +# or /boards +# - BOARD = +# - PJRC_TEENSY_LC for Teensy LC +# - PJRC_TEENSY_3 for Teensy 3.0 +# - PJRC_TEENSY_3_1 for Teensy 3.1 or 3.2 +BOARD = PJRC_TEENSY_3 + +# Cortex version +# Teensy LC is cortex-m0; Teensy 3.x are cortex-m4 +MCU = cortex-m0 + +# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 +# I.e. 6 for Teensy LC; 7 for Teensy 3.x +ARMV = 7 + +# Build Options +# comment out to disable the options. +# +BOOTMAGIC_ENABLE = yes # Virtual DIP switch configuration +## (Note that for BOOTMAGIC on Teensy LC you have to use a custom .ld script.) +MOUSEKEY_ENABLE = yes # Mouse keys +EXTRAKEY_ENABLE = yes # Audio control and System control +CONSOLE_ENABLE = yes # Console for debug +COMMAND_ENABLE = yes # Commands for debug and configuration +SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +NKRO_ENABLE = yes # USB Nkey Rollover + +include $(TMK_DIR)/tool/chibios/common.mk +include $(TMK_DIR)/tool/chibios/chibios.mk diff --git a/keyboard/teensy_lc_onekey/chconf.h b/keyboard/teensy_lc_onekey/chconf.h new file mode 100644 index 00000000..50cb1be4 --- /dev/null +++ b/keyboard/teensy_lc_onekey/chconf.h @@ -0,0 +1,499 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file templates/chconf.h + * @brief Configuration file template. + * @details A copy of this file must be placed in each project directory, it + * contains the application specific kernel settings. + * + * @addtogroup config + * @details Kernel related settings and hooks. + * @{ + */ + +#ifndef _CHCONF_H_ +#define _CHCONF_H_ + +/*===========================================================================*/ +/** + * @name System timers settings + * @{ + */ +/*===========================================================================*/ + +/** + * @brief System time counter resolution. + * @note Allowed values are 16 or 32 bits. + */ +#define CH_CFG_ST_RESOLUTION 32 + +/** + * @brief System tick frequency. + * @details Frequency of the system timer that drives the system ticks. This + * setting also defines the system tick time unit. + */ +#define CH_CFG_ST_FREQUENCY 1000 + +/** + * @brief Time delta constant for the tick-less mode. + * @note If this value is zero then the system uses the classic + * periodic tick. This value represents the minimum number + * of ticks that is safe to specify in a timeout directive. + * The value one is not valid, timeouts are rounded up to + * this value. + */ +#define CH_CFG_ST_TIMEDELTA 0 + +/** @} */ + +/*===========================================================================*/ +/** + * @name Kernel parameters and options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Round robin interval. + * @details This constant is the number of system ticks allowed for the + * threads before preemption occurs. Setting this value to zero + * disables the preemption for threads with equal priority and the + * round robin becomes cooperative. Note that higher priority + * threads can still preempt, the kernel is always preemptive. + * @note Disabling the round robin preemption makes the kernel more compact + * and generally faster. + * @note The round robin preemption is not supported in tickless mode and + * must be set to zero in that case. + */ +#define CH_CFG_TIME_QUANTUM 20 + +/** + * @brief Managed RAM size. + * @details Size of the RAM area to be managed by the OS. If set to zero + * then the whole available RAM is used. The core memory is made + * available to the heap allocator and/or can be used directly through + * the simplified core memory allocator. + * + * @note In order to let the OS manage the whole RAM the linker script must + * provide the @p __heap_base__ and @p __heap_end__ symbols. + * @note Requires @p CH_CFG_USE_MEMCORE. + */ +#define CH_CFG_MEMCORE_SIZE 0 + +/** + * @brief Idle thread automatic spawn suppression. + * @details When this option is activated the function @p chSysInit() + * does not spawn the idle thread. The application @p main() + * function becomes the idle thread and must implement an + * infinite loop. + */ +#define CH_CFG_NO_IDLE_THREAD FALSE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Performance options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief OS optimization. + * @details If enabled then time efficient rather than space efficient code + * is used when two possible implementations exist. + * + * @note This is not related to the compiler optimization options. + * @note The default is @p TRUE. + */ +#define CH_CFG_OPTIMIZE_SPEED TRUE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Subsystem options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Time Measurement APIs. + * @details If enabled then the time measurement APIs are included in + * the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_TM FALSE + +/** + * @brief Threads registry APIs. + * @details If enabled then the registry APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_REGISTRY TRUE + +/** + * @brief Threads synchronization APIs. + * @details If enabled then the @p chThdWait() function is included in + * the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_WAITEXIT TRUE + +/** + * @brief Semaphores APIs. + * @details If enabled then the Semaphores APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_SEMAPHORES TRUE + +/** + * @brief Semaphores queuing mode. + * @details If enabled then the threads are enqueued on semaphores by + * priority rather than in FIFO order. + * + * @note The default is @p FALSE. Enable this if you have special + * requirements. + * @note Requires @p CH_CFG_USE_SEMAPHORES. + */ +#define CH_CFG_USE_SEMAPHORES_PRIORITY TRUE + +/** + * @brief Mutexes APIs. + * @details If enabled then the mutexes APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MUTEXES TRUE + +/** + * @brief Enables recursive behavior on mutexes. + * @note Recursive mutexes are heavier and have an increased + * memory footprint. + * + * @note The default is @p FALSE. + * @note Requires @p CH_CFG_USE_MUTEXES. + */ +#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE + +/** + * @brief Conditional Variables APIs. + * @details If enabled then the conditional variables APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_MUTEXES. + */ +#define CH_CFG_USE_CONDVARS TRUE + +/** + * @brief Conditional Variables APIs with timeout. + * @details If enabled then the conditional variables APIs with timeout + * specification are included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_CONDVARS. + */ +#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE + +/** + * @brief Events Flags APIs. + * @details If enabled then the event flags APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_EVENTS TRUE + +/** + * @brief Events Flags APIs with timeout. + * @details If enabled then the events APIs with timeout specification + * are included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_EVENTS. + */ +#define CH_CFG_USE_EVENTS_TIMEOUT TRUE + +/** + * @brief Synchronous Messages APIs. + * @details If enabled then the synchronous messages APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MESSAGES TRUE + +/** + * @brief Synchronous Messages queuing mode. + * @details If enabled then messages are served by priority rather than in + * FIFO order. + * + * @note The default is @p FALSE. Enable this if you have special + * requirements. + * @note Requires @p CH_CFG_USE_MESSAGES. + */ +#define CH_CFG_USE_MESSAGES_PRIORITY FALSE + +/** + * @brief Mailboxes APIs. + * @details If enabled then the asynchronous messages (mailboxes) APIs are + * included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_SEMAPHORES. + */ +#define CH_CFG_USE_MAILBOXES TRUE + +/** + * @brief I/O Queues APIs. + * @details If enabled then the I/O queues APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_QUEUES TRUE + +/** + * @brief Core Memory Manager APIs. + * @details If enabled then the core memory manager APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MEMCORE TRUE + +/** + * @brief Heap Allocator APIs. + * @details If enabled then the memory heap allocator APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or + * @p CH_CFG_USE_SEMAPHORES. + * @note Mutexes are recommended. + */ +#define CH_CFG_USE_HEAP TRUE + +/** + * @brief Memory Pools Allocator APIs. + * @details If enabled then the memory pools allocator APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MEMPOOLS TRUE + +/** + * @brief Dynamic Threads APIs. + * @details If enabled then the dynamic threads creation APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_WAITEXIT. + * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS. + */ +#define CH_CFG_USE_DYNAMIC TRUE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Debug options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Debug option, kernel statistics. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_STATISTICS FALSE + +/** + * @brief Debug option, system state check. + * @details If enabled the correct call protocol for system APIs is checked + * at runtime. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_SYSTEM_STATE_CHECK TRUE + +/** + * @brief Debug option, parameters checks. + * @details If enabled then the checks on the API functions input + * parameters are activated. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_ENABLE_CHECKS TRUE + +/** + * @brief Debug option, consistency checks. + * @details If enabled then all the assertions in the kernel code are + * activated. This includes consistency checks inside the kernel, + * runtime anomalies and port-defined checks. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_ENABLE_ASSERTS TRUE + +/** + * @brief Debug option, trace buffer. + * @details If enabled then the context switch circular trace buffer is + * activated. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_ENABLE_TRACE TRUE + +/** + * @brief Debug option, stack checks. + * @details If enabled then a runtime stack check is performed. + * + * @note The default is @p FALSE. + * @note The stack check is performed in a architecture/port dependent way. + * It may not be implemented or some ports. + * @note The default failure mode is to halt the system with the global + * @p panic_msg variable set to @p NULL. + */ +#define CH_DBG_ENABLE_STACK_CHECK TRUE + +/** + * @brief Debug option, stacks initialization. + * @details If enabled then the threads working area is filled with a byte + * value when a thread is created. This can be useful for the + * runtime measurement of the used stack. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_FILL_THREADS TRUE + +/** + * @brief Debug option, threads profiling. + * @details If enabled then a field is added to the @p thread_t structure that + * counts the system ticks occurred while executing the thread. + * + * @note The default is @p FALSE. + * @note This debug option is not currently compatible with the + * tickless mode. + */ +#define CH_DBG_THREADS_PROFILING FALSE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Kernel hooks + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Threads descriptor structure extension. + * @details User fields added to the end of the @p thread_t structure. + */ +#define CH_CFG_THREAD_EXTRA_FIELDS \ + /* Add threads custom fields here.*/ + +/** + * @brief Threads initialization hook. + * @details User initialization code added to the @p chThdInit() API. + * + * @note It is invoked from within @p chThdInit() and implicitly from all + * the threads creation APIs. + */ +#define CH_CFG_THREAD_INIT_HOOK(tp) { \ + /* Add threads initialization code here.*/ \ +} + +/** + * @brief Threads finalization hook. + * @details User finalization code added to the @p chThdExit() API. + * + * @note It is inserted into lock zone. + * @note It is also invoked when the threads simply return in order to + * terminate. + */ +#define CH_CFG_THREAD_EXIT_HOOK(tp) { \ + /* Add threads finalization code here.*/ \ +} + +/** + * @brief Context switch hook. + * @details This hook is invoked just before switching between threads. + */ +#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \ + /* Context switch code here.*/ \ +} + +/** + * @brief Idle thread enter hook. + * @note This hook is invoked within a critical zone, no OS functions + * should be invoked from here. + * @note This macro can be used to activate a power saving mode. + */ +#define CH_CFG_IDLE_ENTER_HOOK() { \ +} + +/** + * @brief Idle thread leave hook. + * @note This hook is invoked within a critical zone, no OS functions + * should be invoked from here. + * @note This macro can be used to deactivate a power saving mode. + */ +#define CH_CFG_IDLE_LEAVE_HOOK() { \ +} + +/** + * @brief Idle Loop hook. + * @details This hook is continuously invoked by the idle thread loop. + */ +#define CH_CFG_IDLE_LOOP_HOOK() { \ + /* Idle loop code here.*/ \ +} + +/** + * @brief System tick event hook. + * @details This hook is invoked in the system tick handler immediately + * after processing the virtual timers queue. + */ +#define CH_CFG_SYSTEM_TICK_HOOK() { \ + /* System tick event code here.*/ \ +} + +/** + * @brief System halt hook. + * @details This hook is invoked in case to a system halting error before + * the system is halted. + */ +#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \ + /* System halt code here.*/ \ +} + +/** @} */ + +/*===========================================================================*/ +/* Port-specific settings (override port settings defaulted in chcore.h). */ +/*===========================================================================*/ + +#endif /* _CHCONF_H_ */ + +/** @} */ diff --git a/keyboard/teensy_lc_onekey/config.h b/keyboard/teensy_lc_onekey/config.h new file mode 100644 index 00000000..8063c57e --- /dev/null +++ b/keyboard/teensy_lc_onekey/config.h @@ -0,0 +1,74 @@ +/* +Copyright 2015 Jun Wako + +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 2 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 . +*/ + +#ifndef CONFIG_H +#define CONFIG_H + + +/* USB Device descriptor parameter */ +#define VENDOR_ID 0xFEED +#define PRODUCT_ID 0x6464 +#define DEVICE_VER 0x0001 +/* in python2: list(u"whatever".encode('utf-16-le')) */ +/* at most 32 characters or the ugly hack in usb_main.c borks */ +#define MANUFACTURER "TMK" +#define USBSTR_MANUFACTURER 'T', '\x00', 'M', '\x00', 'K', '\x00', ' ', '\x00', '\xc6', '\x00' +#define PRODUCT "ChibiOS TMK test" +#define USBSTR_PRODUCT 'C', '\x00', 'h', '\x00', 'i', '\x00', 'b', '\x00', 'i', '\x00', 'O', '\x00', 'S', '\x00', ' ', '\x00', 'T', '\x00', 'M', '\x00', 'K', '\x00', ' ', '\x00', 't', '\x00', 'e', '\x00', 's', '\x00', 't', '\x00' +#define DESCRIPTION "TMK keyboard firmware over ChibiOS" + +/* key matrix size */ +#define MATRIX_ROWS 1 +#define MATRIX_COLS 1 + +/* define if matrix has ghost */ +//#define MATRIX_HAS_GHOST + +/* Set 0 if debouncing isn't needed */ +#define DEBOUNCE 5 + +/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */ +#define LOCKING_SUPPORT_ENABLE +/* Locking resynchronize hack */ +#define LOCKING_RESYNC_ENABLE + +/* key combination for command */ +#define IS_COMMAND() ( \ + keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \ +) + + + +/* + * Feature disable options + * These options are also useful to firmware size reduction. + */ + +/* disable debug print */ +//#define NO_DEBUG + +/* disable print */ +//#define NO_PRINT + +/* disable action features */ +//#define NO_ACTION_LAYER +//#define NO_ACTION_TAPPING +//#define NO_ACTION_ONESHOT +//#define NO_ACTION_MACRO +//#define NO_ACTION_FUNCTION + +#endif diff --git a/keyboard/teensy_lc_onekey/halconf.h b/keyboard/teensy_lc_onekey/halconf.h new file mode 100644 index 00000000..0d4bc4d0 --- /dev/null +++ b/keyboard/teensy_lc_onekey/halconf.h @@ -0,0 +1,168 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file templates/halconf.h + * @brief HAL configuration header. + * @details HAL configuration file, this file allows to enable or disable the + * various device drivers from your application. You may also use + * this file in order to override the device drivers default settings. + * + * @addtogroup HAL_CONF + * @{ + */ + +#ifndef _HALCONF_H_ +#define _HALCONF_H_ + +#include "mcuconf.h" + +/** + * @brief Enables the PAL subsystem. + */ +#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__) +#define HAL_USE_PAL TRUE +#endif + +/** + * @brief Enables the ADC subsystem. + */ +#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__) +#define HAL_USE_ADC FALSE +#endif + +/** + * @brief Enables the CAN subsystem. + */ +#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__) +#define HAL_USE_CAN FALSE +#endif + +/** + * @brief Enables the DAC subsystem. + */ +#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__) +#define HAL_USE_DAC FALSE +#endif + +/** + * @brief Enables the EXT subsystem. + */ +#if !defined(HAL_USE_EXT) || defined(__DOXYGEN__) +#define HAL_USE_EXT FALSE +#endif + +/** + * @brief Enables the GPT subsystem. + */ +#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__) +#define HAL_USE_GPT FALSE +#endif + +/** + * @brief Enables the I2C subsystem. + */ +#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__) +#define HAL_USE_I2C FALSE +#endif + +/** + * @brief Enables the I2S subsystem. + */ +#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__) +#define HAL_USE_I2S FALSE +#endif + +/** + * @brief Enables the ICU subsystem. + */ +#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__) +#define HAL_USE_ICU FALSE +#endif + +/** + * @brief Enables the MAC subsystem. + */ +#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__) +#define HAL_USE_MAC FALSE +#endif + +/** + * @brief Enables the MMC_SPI subsystem. + */ +#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__) +#define HAL_USE_MMC_SPI FALSE +#endif + +/** + * @brief Enables the PWM subsystem. + */ +#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__) +#define HAL_USE_PWM FALSE +#endif + +/** + * @brief Enables the RTC subsystem. + */ +#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__) +#define HAL_USE_RTC FALSE +#endif + +/** + * @brief Enables the SDC subsystem. + */ +#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__) +#define HAL_USE_SDC FALSE +#endif + +/** + * @brief Enables the SERIAL subsystem. + */ +#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__) +#define HAL_USE_SERIAL FALSE +#endif + +/** + * @brief Enables the SERIAL over USB subsystem. + */ +#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__) +#define HAL_USE_SERIAL_USB FALSE +#endif + +/** + * @brief Enables the SPI subsystem. + */ +#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__) +#define HAL_USE_SPI FALSE +#endif + +/** + * @brief Enables the UART subsystem. + */ +#if !defined(HAL_USE_UART) || defined(__DOXYGEN__) +#define HAL_USE_UART FALSE +#endif + +/** + * @brief Enables the USB subsystem. + */ +#if !defined(HAL_USE_USB) || defined(__DOXYGEN__) +#define HAL_USE_USB TRUE +#endif + +#endif /* _HALCONF_H_ */ + +/** @} */ diff --git a/keyboard/teensy_lc_onekey/instructions.md b/keyboard/teensy_lc_onekey/instructions.md new file mode 100644 index 00000000..b19b8984 --- /dev/null +++ b/keyboard/teensy_lc_onekey/instructions.md @@ -0,0 +1,62 @@ +# Teensy LC, 3.0, 3.1, 3.2 support + +These ARM Teensies are now supported through [chibios](http://chibios.org). + +You'll need to install an ARM toolchain, for instance from [gcc ARM embedded](https://launchpad.net/gcc-arm-embedded) website, or using your favourite package manager. After installing, you should be able to run `arm-none-eabi-gcc -v` in the command prompt and get sensible output. This toolchain is used instead of `avr-gcc`, which is only for AVR chips. Naturally you'll also need the usual development tools (e.g. `make`), just as in the AVR setting. + +Next, you'll need ChibiOS. The current release (3.0.2) does not have sufficient Kinetis support, so you'll need to get a patched version from [my fork](https://github.com/flabbergast/ChibiOS/tree/kinetis): you can download a current tree zipped from [here](https://github.com/flabbergast/ChibiOS/archive/kinetis.zip). Unpack the zip, rename the newly created `ChibiOS-kinetis` to `chibios`, and move it to `tmk/tool/chibios/` (so that the ChibiOS files reside in `tmk/tool/chibios/chibios`). + +This should be it. Running `make` in `keyboard/teensy_lc_onekey` should create a working firmware in `build/`, called `ch.hex`. + +For more notes about the ChibiOS backend in TMK, see `tmk_core/protocol/chibios/README.md`. + +## About this onekey example + +It's set up for Teensy LC. To use 3.x, you'll need to edit the `Makefile` (and comment out one line in `mcuconf.h`). A sample makefile for Teensy 3.0 is provided as `Makefile.3.0`, can be used without renaming with `make -f Makefile.3.0`. + +## Credits + +The USB support for Kinetis MCUs is due to RedoX. His ChibiOS fork is also [on github](https://github.com/RedoXyde/ChibiOS); but it doesn't include Teensy LC definitions. + +## Features that are not implemented yet + +Currently only the more fancy suspend features are not there (i.e. "breathing" LED during suspend, power saving during suspend, sending a wakeup packet). The rest should work fine (reports either way are welcome). + +# Matrix programming notes + +The notes below explain what commands can be used to examine and set the status of Teensy pins. + +## ChibiOS pin manipulation basics + +### Pins + +Each pin sits on a "port", each of which comprises at most 32 individual pins. +So for instance "PTC5" from Kinetis manual/datasheet refers to port C (or GPIOA), pin 5. Most functions dealing with pins take 2 parameters which specify the pin -- the first being the port, the second being the pin number. + +Within ChibiOS, there are definitions which simplify this a bit for the Teensies. `TEENSY_PINn_IOPORT` represents the port of the MCU's pin connected Teensy's PIN `n`, and `TEENSY_PINn` represents its MCU's pin number. + +### Mode + +A MCU pin can be in several modes. The basic command to set a pin mode is + + palSetPadMode(TEENSY_PINn_IOPORT, TEENSY_PINn, PAL_MODE_INPUT_PULLUP); + +The last parameter is the mode. For keyboards, the usual ones that are used are `PAL_MODE_INPUT_PULLUP` (input with a pullup), `PAL_MODE_INPUT_PULLDOWN` (input with a pulldown), `PAL_MODE_INPUT` (input floating, a.k.a. Hi-Z), `PAL_MODE_OUTPUT_PUSHPULL` (output in the Arduino sense -- can be then set HIGH or LOW). + +### Setting + +Pins are set HIGH (after they've been put into `OUTPUT_PUSHPULL` mode) by + + palSetPad(TEENSY_PINn_IOPORT, TEENSY_PINn); + +or set LOW by + + palClearPad(TEENSY_PINn_IOPORT, TEENSY_PINn); + +### Reading + +Reading pin status is done with + + palReadPad(TEENSY_PINn_IOPORT, TEENSY_PINn); + +The function returns either `PAL_HIGH` (actually `1`) or `PAL_LOW` (actually `0`). diff --git a/keyboard/teensy_lc_onekey/keymap_plain.c b/keyboard/teensy_lc_onekey/keymap_plain.c new file mode 100644 index 00000000..a8357489 --- /dev/null +++ b/keyboard/teensy_lc_onekey/keymap_plain.c @@ -0,0 +1,44 @@ +/* +Copyright 2012,2013 Jun Wako + +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 2 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 "keycode.h" +#include "action.h" +#include "action_macro.h" +#include "report.h" +#include "host.h" +#include "print.h" +#include "debug.h" +#include "keymap.h" + +static const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + {{KC_BTLD}}, +}; // to test: KC_CAPS, KT_BTLD + +static const uint16_t fn_actions[] = { +}; + +/* translates key to keycode */ +uint8_t keymap_key_to_keycode(uint8_t layer, keypos_t key) +{ + return keymaps[(layer)][(key.row)][(key.col)]; +} + +/* translates Fn keycode to action */ +action_t keymap_fn_to_action(uint8_t keycode) +{ + return (action_t){ .code = fn_actions[FN_INDEX(keycode)] }; +} diff --git a/keyboard/teensy_lc_onekey/ld/MKL26Z64.ld b/keyboard/teensy_lc_onekey/ld/MKL26Z64.ld new file mode 100644 index 00000000..fd9102de --- /dev/null +++ b/keyboard/teensy_lc_onekey/ld/MKL26Z64.ld @@ -0,0 +1,161 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS 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. + + ChibiOS 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 . +*/ + +/* + * KL25Z64 memory setup. + */ + +ENTRY(Reset_Handler) + +MEMORY +{ + flash0 : org = 0x00000000, len = 0xc0 + flashcfg : org = 0x00000400, len = 0x10 + flash : org = 0x00000410, len = 62k - 0x410 + eeprom_emu : org = 0x0000F800, len = 2k + ram : org = 0x1FFFF800, len = 8k +} + +__ram_start__ = ORIGIN(ram); +__ram_size__ = LENGTH(ram); +__ram_end__ = __ram_start__ + __ram_size__; + +__eeprom_workarea_start__ = ORIGIN(eeprom_emu); +__eeprom_workarea_size__ = LENGTH(eeprom_emu); +__eeprom_workarea_end__ = __eeprom_workarea_start__ + __eeprom_workarea_size__; + +SECTIONS +{ + . = 0; + + .isr : ALIGN(4) SUBALIGN(4) + { + KEEP(*(.vectors)) + } > flash0 + + .cfmprotect : ALIGN(4) SUBALIGN(4) + { + KEEP(*(.cfmconfig)) + } > flashcfg + + _text = .; + + constructors : ALIGN(4) SUBALIGN(4) + { + PROVIDE(__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE(__init_array_end = .); + } > flash + + destructors : ALIGN(4) SUBALIGN(4) + { + PROVIDE(__fini_array_start = .); + KEEP(*(.fini_array)) + KEEP(*(SORT(.fini_array.*))) + PROVIDE(__fini_array_end = .); + } > flash + + .text : ALIGN(4) SUBALIGN(4) + { + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + *(.glue_7t) + *(.glue_7) + *(.gcc*) + } > flash + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > flash + + .ARM.exidx : { + PROVIDE(__exidx_start = .); + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + PROVIDE(__exidx_end = .); + } > flash + + .eh_frame_hdr : + { + *(.eh_frame_hdr) + } > flash + + .eh_frame : ONLY_IF_RO + { + *(.eh_frame) + } > flash + + .textalign : ONLY_IF_RO + { + . = ALIGN(8); + } > flash + + _etext = .; + _textdata = _etext; + + .stacks : + { + . = ALIGN(8); + __main_stack_base__ = .; + . += __main_stack_size__; + . = ALIGN(8); + __main_stack_end__ = .; + __process_stack_base__ = .; + __main_thread_stack_base__ = .; + . += __process_stack_size__; + . = ALIGN(8); + __process_stack_end__ = .; + __main_thread_stack_end__ = .; + } > ram + + .data : + { + . = ALIGN(4); + PROVIDE(_data = .); + *(.data) + . = ALIGN(4); + *(.data.*) + . = ALIGN(4); + *(.ramtext) + . = ALIGN(4); + PROVIDE(_edata = .); + } > ram AT > flash + + .bss : + { + . = ALIGN(4); + PROVIDE(_bss_start = .); + *(.bss) + . = ALIGN(4); + *(.bss.*) + . = ALIGN(4); + *(COMMON) + . = ALIGN(4); + PROVIDE(_bss_end = .); + } > ram +} + +PROVIDE(end = .); +_end = .; + +__heap_base__ = _end; +__heap_end__ = __ram_end__; diff --git a/keyboard/teensy_lc_onekey/led.c b/keyboard/teensy_lc_onekey/led.c new file mode 100644 index 00000000..dfa60c10 --- /dev/null +++ b/keyboard/teensy_lc_onekey/led.c @@ -0,0 +1,32 @@ +/* +Copyright 2012 Jun Wako + +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 2 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 "hal.h" + +#include "led.h" + + +void led_set(uint8_t usb_led) { + if (usb_led & (1< + +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 2 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 "ch.h" +#include "hal.h" + +/* + * scan matrix + */ +#include "print.h" +#include "debug.h" +#include "util.h" +#include "matrix.h" +#include "wait.h" + +#ifndef DEBOUNCE +# define DEBOUNCE 5 +#endif +static uint8_t debouncing = DEBOUNCE; + +/* matrix state(1:on, 0:off) */ +static matrix_row_t matrix[MATRIX_ROWS]; +static matrix_row_t matrix_debouncing[MATRIX_ROWS]; + +static matrix_row_t read_cols(void); +static void init_cols(void); +static void unselect_rows(void); +static void select_row(uint8_t row); + + +inline +uint8_t matrix_rows(void) +{ + return MATRIX_ROWS; +} + +inline +uint8_t matrix_cols(void) +{ + return MATRIX_COLS; +} + +#define LED_ON() do { palSetPad(TEENSY_PIN13_IOPORT, TEENSY_PIN13) ;} while (0) +#define LED_OFF() do { palClearPad(TEENSY_PIN13_IOPORT, TEENSY_PIN13); } while (0) +#define LED_TGL() do { palTogglePad(TEENSY_PIN13_IOPORT, TEENSY_PIN13); } while (0) + +void matrix_init(void) +{ + // initialize row and col + unselect_rows(); + init_cols(); + + // initialize matrix state: all keys off + for (uint8_t i=0; i < MATRIX_ROWS; i++) { + matrix[i] = 0; + matrix_debouncing[i] = 0; + } + + //debug + debug_matrix = true; + LED_ON(); + wait_ms(500); + LED_OFF(); +} + +uint8_t matrix_scan(void) +{ + for (uint8_t i = 0; i < MATRIX_ROWS; i++) { + select_row(i); + wait_us(30); // without this wait read unstable value. + matrix_row_t cols = read_cols(); + if (matrix_debouncing[i] != cols) { + matrix_debouncing[i] = cols; + if (debouncing) { + debug("bounce!: "); debug_hex(debouncing); debug("\n"); + } + debouncing = DEBOUNCE; + } + unselect_rows(); + } + + if (debouncing) { + if (--debouncing) { + wait_ms(1); + } else { + for (uint8_t i = 0; i < MATRIX_ROWS; i++) { + matrix[i] = matrix_debouncing[i]; + } + } + } + + return 1; +} + +inline +bool matrix_is_on(uint8_t row, uint8_t col) +{ + return (matrix[row] & ((matrix_row_t)1< Date: Wed, 14 Oct 2015 10:13:49 +0100 Subject: [PATCH 25/56] Chibios: use WFI in idle. WIP suspend stuff. --- keyboard/stm32_onekey/chconf.h | 4 ++ keyboard/stm32_onekey/keymap_plain.c | 2 +- keyboard/teensy_lc_onekey/chconf.h | 4 ++ keyboard/teensy_lc_onekey/instructions.md | 2 + keyboard/teensy_lc_onekey/keymap_plain.c | 4 +- tmk_core/common/chibios/sleep_led.c | 4 ++ tmk_core/common/chibios/suspend.c | 65 +++++++++++++++++++++-- tmk_core/protocol/chibios/main.c | 21 +++++++- tmk_core/protocol/chibios/usb_main.c | 8 +-- 9 files changed, 102 insertions(+), 12 deletions(-) diff --git a/keyboard/stm32_onekey/chconf.h b/keyboard/stm32_onekey/chconf.h index da4160b6..be362a95 100644 --- a/keyboard/stm32_onekey/chconf.h +++ b/keyboard/stm32_onekey/chconf.h @@ -103,6 +103,10 @@ */ #define CH_CFG_NO_IDLE_THREAD FALSE +/* Use __WFI in the idle thread for waiting. Does lower the power + * consumption. */ +#define CORTEX_ENABLE_WFI_IDLE TRUE + /** @} */ /*===========================================================================*/ diff --git a/keyboard/stm32_onekey/keymap_plain.c b/keyboard/stm32_onekey/keymap_plain.c index ea173935..8de1c027 100644 --- a/keyboard/stm32_onekey/keymap_plain.c +++ b/keyboard/stm32_onekey/keymap_plain.c @@ -25,7 +25,7 @@ along with this program. If not, see . #include "keymap.h" static const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS] = { - {{KC_A}}, + {{KC_CAPS}}, // test with KC_CAPS, KC_A, KC_BTLD }; static const uint16_t fn_actions[] = { diff --git a/keyboard/teensy_lc_onekey/chconf.h b/keyboard/teensy_lc_onekey/chconf.h index 50cb1be4..ee527d47 100644 --- a/keyboard/teensy_lc_onekey/chconf.h +++ b/keyboard/teensy_lc_onekey/chconf.h @@ -103,6 +103,10 @@ */ #define CH_CFG_NO_IDLE_THREAD FALSE +/* Use __WFI in the idle thread for waiting. Does lower the power + * consumption. */ +#define CORTEX_ENABLE_WFI_IDLE TRUE + /** @} */ /*===========================================================================*/ diff --git a/keyboard/teensy_lc_onekey/instructions.md b/keyboard/teensy_lc_onekey/instructions.md index b19b8984..9a7aeece 100644 --- a/keyboard/teensy_lc_onekey/instructions.md +++ b/keyboard/teensy_lc_onekey/instructions.md @@ -16,6 +16,8 @@ It's set up for Teensy LC. To use 3.x, you'll need to edit the `Makefile` (and c ## Credits +TMK itself is written by hasu, original sources [here](https://github.com/tmk/tmk_keyboard). + The USB support for Kinetis MCUs is due to RedoX. His ChibiOS fork is also [on github](https://github.com/RedoXyde/ChibiOS); but it doesn't include Teensy LC definitions. ## Features that are not implemented yet diff --git a/keyboard/teensy_lc_onekey/keymap_plain.c b/keyboard/teensy_lc_onekey/keymap_plain.c index a8357489..922fa537 100644 --- a/keyboard/teensy_lc_onekey/keymap_plain.c +++ b/keyboard/teensy_lc_onekey/keymap_plain.c @@ -25,8 +25,8 @@ along with this program. If not, see . #include "keymap.h" static const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS] = { - {{KC_BTLD}}, -}; // to test: KC_CAPS, KT_BTLD + {{KC_A}}, +}; // to test: KC_CAPS, KT_BTLD, KC_A static const uint16_t fn_actions[] = { }; diff --git a/tmk_core/common/chibios/sleep_led.c b/tmk_core/common/chibios/sleep_led.c index b08c9c2a..fad7ac04 100644 --- a/tmk_core/common/chibios/sleep_led.c +++ b/tmk_core/common/chibios/sleep_led.c @@ -4,6 +4,10 @@ #include "sleep_led.h" void sleep_led_init(void) { + // we could go the 'software way' -- just enable *some* timer + // and go with callbacks + // or we could go the 'hardware way' -- and use timer output to + // pins directly } void sleep_led_enable(void) { diff --git a/tmk_core/common/chibios/suspend.c b/tmk_core/common/chibios/suspend.c index 49ecad43..6ca16034 100644 --- a/tmk_core/common/chibios/suspend.c +++ b/tmk_core/common/chibios/suspend.c @@ -1,8 +1,65 @@ /* TODO */ -#include +#include "ch.h" +#include "hal.h" +#include "matrix.h" +#include "action.h" +#include "action_util.h" +#include "mousekey.h" +#include "host.h" +#include "backlight.h" +#include "suspend.h" -void suspend_power_down(void) {} -bool suspend_wakeup_condition(void) { return true; } -void suspend_wakeup_init(void) {} +void suspend_idle(uint8_t time) { + // TODO: this is not used anywhere - what units is 'time' in? + chThdSleepMilliseconds(time); +} + +void suspend_power_down(void) { + // TODO: figure out what to power down and how + // shouldn't power down TPM/FTM if we want a breathing LED + // also shouldn't power down USB + + // on AVR, this enables the watchdog for 15ms (max), and goes to + // SLEEP_MODE_PWR_DOWN + + chThdSleepMilliseconds(17); +} + +__attribute__ ((weak)) void matrix_power_up(void) {} +__attribute__ ((weak)) void matrix_power_down(void) {} +bool suspend_wakeup_condition(void) +{ + matrix_power_up(); + matrix_scan(); + matrix_power_down(); + for (uint8_t r = 0; r < MATRIX_ROWS; r++) { + if (matrix_get_row(r)) return true; + } + return false; +} + +// run immediately after wakeup +void suspend_wakeup_init(void) +{ + // clear keyboard state + // need to do it manually, because we're running from ISR + // and clear_keyboard() calls print + // so only clear the variables in memory + // the reports will be sent from main.c afterwards + // or if the PC asks for GET_REPORT + clear_mods(); + clear_weak_mods(); + clear_keys(); +#ifdef MOUSEKEY_ENABLE + mousekey_clear(); +#endif /* MOUSEKEY_ENABLE */ +#ifdef EXTRAKEY_ENABLE + host_system_send(0); + host_consumer_send(0); +#endif /* EXTRAKEY_ENABLE */ +#ifdef BACKLIGHT_ENABLE + backlight_init(); +#endif /* BACKLIGHT_ENABLE */ +} diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c index b2526d14..c4666ebd 100644 --- a/tmk_core/protocol/chibios/main.c +++ b/tmk_core/protocol/chibios/main.c @@ -26,6 +26,8 @@ #include "host_driver.h" #include "keyboard.h" #include "action.h" +#include "action_util.h" +#include "mousekey.h" #include "led.h" #include "sendchar.h" #include "debug.h" @@ -113,7 +115,24 @@ int main(void) { /* Main loop */ while(true) { - /* TODO: check for suspended event */ + + if(USB_DRIVER.state == USB_SUSPENDED) { + print("[s]"); + while(USB_DRIVER.state == USB_SUSPENDED) { + /* Do this in the suspended state */ + suspend_power_down(); // on AVR this deep sleeps for 15ms + // TODO: remote wakeup + // if(USB_Device_RemoteWakeupEnabled (USB_DRIVER.status & 2) && suspend_wakeup_condition()) { + // USB_Device_SendRemoteWakeup(); + // } + } + /* Woken up */ + // variables has been already cleared by the wakeup hook + send_keyboard_report(); +#ifdef MOUSEKEY_ENABLE + mousekey_send(); +#endif /* MOUSEKEY_ENABLE */ + } keyboard_task(); chThdSleepMilliseconds(5); diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 2625c425..872d1c65 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -22,6 +22,7 @@ #include "host.h" #include "debug.h" +#include "suspend.h" #ifdef SLEEP_LED_ENABLE #include "sleep_led.h" #include "led.h" @@ -356,8 +357,8 @@ static const uint8_t hid_configuration_descriptor_data[] = { NUM_INTERFACES, // bNumInterfaces 1, // bConfigurationValue 0, // iConfiguration - 0xA0, // bmAttributes - 50), // bMaxPower (100mA) + 0xA0, // bmAttributes (RESERVED|REMOTEWAKEUP) + 50), // bMaxPower (50mA) /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ USB_DESC_INTERFACE(KBD_INTERFACE, // bInterfaceNumber @@ -793,7 +794,6 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) { case USB_EVENT_SUSPEND: //TODO: from ISR! print("[S]"); - //TODO: signal suspend? #ifdef SLEEP_LED_ENABLE sleep_led_enable(); #endif /* SLEEP_LED_ENABLE */ @@ -801,7 +801,7 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) { case USB_EVENT_WAKEUP: //TODO: from ISR! print("[W]"); - //TODO: suspend_wakeup_init(); + suspend_wakeup_init(); #ifdef SLEEP_LED_ENABLE sleep_led_disable(); // NOTE: converters may not accept this From b25da7a51df91a73520df5f5120478438d84dad7 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Wed, 14 Oct 2015 10:35:45 +0100 Subject: [PATCH 26/56] Update chibi/teensy instructions. --- keyboard/teensy_lc_onekey/instructions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/keyboard/teensy_lc_onekey/instructions.md b/keyboard/teensy_lc_onekey/instructions.md index 9a7aeece..4d8e51df 100644 --- a/keyboard/teensy_lc_onekey/instructions.md +++ b/keyboard/teensy_lc_onekey/instructions.md @@ -4,7 +4,9 @@ These ARM Teensies are now supported through [chibios](http://chibios.org). You'll need to install an ARM toolchain, for instance from [gcc ARM embedded](https://launchpad.net/gcc-arm-embedded) website, or using your favourite package manager. After installing, you should be able to run `arm-none-eabi-gcc -v` in the command prompt and get sensible output. This toolchain is used instead of `avr-gcc`, which is only for AVR chips. Naturally you'll also need the usual development tools (e.g. `make`), just as in the AVR setting. -Next, you'll need ChibiOS. The current release (3.0.2) does not have sufficient Kinetis support, so you'll need to get a patched version from [my fork](https://github.com/flabbergast/ChibiOS/tree/kinetis): you can download a current tree zipped from [here](https://github.com/flabbergast/ChibiOS/archive/kinetis.zip). Unpack the zip, rename the newly created `ChibiOS-kinetis` to `chibios`, and move it to `tmk/tool/chibios/` (so that the ChibiOS files reside in `tmk/tool/chibios/chibios`). +You'll need this fork/branch of TMK. If you're reading this from your own hard drive, then you already have it ;) Anyway, you can get a zip from [here](https://https://github.com/flabbergast/tmk_keyboard/archive/chibios.zip) {or clone this repo from github and checkout the `chibios` branch}. + +Next, you'll need ChibiOS. The current release (3.0.2) does not have sufficient Kinetis support, so you'll need to get a patched version from [my fork](https://github.com/flabbergast/ChibiOS/tree/kinetis): you can download a current tree zipped from [here](https://github.com/flabbergast/ChibiOS/archive/kinetis.zip) {or clone that repo from github and checkout the `kinetis` branch}. Unpack the zip, rename the newly created `ChibiOS-kinetis` to `chibios`, and move it to `tmk/tool/chibios/` (so that the ChibiOS files reside in `tmk/tool/chibios/chibios`). This should be it. Running `make` in `keyboard/teensy_lc_onekey` should create a working firmware in `build/`, called `ch.hex`. From aa2c56823574b56735b81e5db08bd9c2ec501294 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Wed, 14 Oct 2015 18:04:25 +0100 Subject: [PATCH 27/56] Update chibios/Teensy instructions. --- keyboard/teensy_lc_onekey/instructions.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/keyboard/teensy_lc_onekey/instructions.md b/keyboard/teensy_lc_onekey/instructions.md index 4d8e51df..ed355e29 100644 --- a/keyboard/teensy_lc_onekey/instructions.md +++ b/keyboard/teensy_lc_onekey/instructions.md @@ -57,6 +57,16 @@ or set LOW by palClearPad(TEENSY_PINn_IOPORT, TEENSY_PINn); +Toggling can be done with + + palTogglePad(TEENSY_PINn_IOPORT, TEENSY_PINn); + +Alternatively, you can use + + palWritePad(TEENSY_PINn_IOPORT, TEENSY_PINn, bit); + +where `bit` is either `PAL_LOW` or `PAL_HIGH` (i.e. `0` or `1`). + ### Reading Reading pin status is done with @@ -64,3 +74,7 @@ Reading pin status is done with palReadPad(TEENSY_PINn_IOPORT, TEENSY_PINn); The function returns either `PAL_HIGH` (actually `1`) or `PAL_LOW` (actually `0`). + +### Further docs + +All the commands that are available for pin manipulation through ChibiOS HAL are documented in [ChibiOS PAL driver docs](http://chibios.sourceforge.net/docs3/hal/group___p_a_l.html). From 2e06ccf70a7f86e4abeaf5cec73886c909a2f7a9 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Wed, 14 Oct 2015 19:55:09 +0100 Subject: [PATCH 28/56] ChibiOS/kinetis: sending remote wakeup. --- tmk_core/protocol/chibios/main.c | 8 ++++---- tmk_core/protocol/chibios/usb_main.c | 17 +++++++++++++++++ tmk_core/protocol/chibios/usb_main.h | 3 +++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c index c4666ebd..9607f1a9 100644 --- a/tmk_core/protocol/chibios/main.c +++ b/tmk_core/protocol/chibios/main.c @@ -121,10 +121,10 @@ int main(void) { while(USB_DRIVER.state == USB_SUSPENDED) { /* Do this in the suspended state */ suspend_power_down(); // on AVR this deep sleeps for 15ms - // TODO: remote wakeup - // if(USB_Device_RemoteWakeupEnabled (USB_DRIVER.status & 2) && suspend_wakeup_condition()) { - // USB_Device_SendRemoteWakeup(); - // } + /* Remote wakeup */ + if((USB_DRIVER.status & 2) && suspend_wakeup_condition()) { + send_remote_wakeup(&USB_DRIVER); + } } /* Woken up */ // variables has been already cleared by the wakeup hook diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 872d1c65..09de7196 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -1024,6 +1024,23 @@ void init_usb_driver(USBDriver *usbp) { #endif } +/* + * Send remote wakeup packet + * Note: should not be called from ISR + */ +void send_remote_wakeup(USBDriver *usbp) { + (void)usbp; +#if defined(K20x) || defined(KL2x) +#if KINETIS_USB_USE_USB0 + USB0->CTL |= USBx_CTL_RESUME; + chThdSleepMilliseconds(15); + USB0->CTL &= ~USBx_CTL_RESUME; +#endif /* KINETIS_USB_USE_USB0 */ +#else /* K20x || KL2x */ +#warning Sending remote wakeup packet not implemented for your platform. +#endif /* K20x || KL2x */ +} + /* --------------------------------------------------------- * Keyboard functions * --------------------------------------------------------- diff --git a/tmk_core/protocol/chibios/usb_main.h b/tmk_core/protocol/chibios/usb_main.h index 85ea9153..d8f30e95 100644 --- a/tmk_core/protocol/chibios/usb_main.h +++ b/tmk_core/protocol/chibios/usb_main.h @@ -36,6 +36,9 @@ /* Initialize the USB driver and bus */ void init_usb_driver(USBDriver *usbp); +/* Send remote wakeup packet */ +void send_remote_wakeup(USBDriver *usbp); + /* --------------- * Keyboard header * --------------- From 6def1045305aaf9c539b5ebbe04232a4c83662ec Mon Sep 17 00:00:00 2001 From: flabbergast Date: Thu, 15 Oct 2015 09:34:51 +0100 Subject: [PATCH 29/56] ChibiOS/STM32: send remote wakeup. --- tmk_core/protocol/chibios/usb_main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 09de7196..382966b9 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -1036,7 +1036,11 @@ void send_remote_wakeup(USBDriver *usbp) { chThdSleepMilliseconds(15); USB0->CTL &= ~USBx_CTL_RESUME; #endif /* KINETIS_USB_USE_USB0 */ -#else /* K20x || KL2x */ +#elif defined(STM32F0XX) /* K20x || KL2x */ + STM32_USB->CNTR |= CNTR_RESUME; + chThdSleepMilliseconds(15); + STM32_USB->CNTR &= ~CNTR_RESUME; +#else /* STM32F0XX */ #warning Sending remote wakeup packet not implemented for your platform. #endif /* K20x || KL2x */ } From fd71b3f4eff1825d6527b1c657e88e2e3bd87483 Mon Sep 17 00:00:00 2001 From: Jun Wako Date: Fri, 16 Oct 2015 17:44:26 +0900 Subject: [PATCH 30/56] Add infinity_chibios --- keyboard/infinity_chibios/MEMO.txt | 385 +++++++++++++++++ keyboard/infinity_chibios/Makefile | 87 ++++ keyboard/infinity_chibios/chconf.h | 499 ++++++++++++++++++++++ keyboard/infinity_chibios/config.h | 76 ++++ keyboard/infinity_chibios/halconf.h | 334 +++++++++++++++ keyboard/infinity_chibios/keymap_common.c | 30 ++ keyboard/infinity_chibios/keymap_common.h | 80 ++++ keyboard/infinity_chibios/keymap_plain.c | 48 +++ keyboard/infinity_chibios/led.c | 24 ++ keyboard/infinity_chibios/matrix.c | 128 ++++++ keyboard/infinity_chibios/mcuconf.h | 55 +++ 11 files changed, 1746 insertions(+) create mode 100644 keyboard/infinity_chibios/MEMO.txt create mode 100644 keyboard/infinity_chibios/Makefile create mode 100644 keyboard/infinity_chibios/chconf.h create mode 100644 keyboard/infinity_chibios/config.h create mode 100644 keyboard/infinity_chibios/halconf.h create mode 100644 keyboard/infinity_chibios/keymap_common.c create mode 100644 keyboard/infinity_chibios/keymap_common.h create mode 100644 keyboard/infinity_chibios/keymap_plain.c create mode 100644 keyboard/infinity_chibios/led.c create mode 100644 keyboard/infinity_chibios/matrix.c create mode 100644 keyboard/infinity_chibios/mcuconf.h diff --git a/keyboard/infinity_chibios/MEMO.txt b/keyboard/infinity_chibios/MEMO.txt new file mode 100644 index 00000000..e2886aa0 --- /dev/null +++ b/keyboard/infinity_chibios/MEMO.txt @@ -0,0 +1,385 @@ +flabbergast's TMK/ChibiOS port +============================== +2015/10/16 + + +Build +----- +$ git clone -b chibios https://github.com/flabbergast/tmk_keyboard.git + +$ cd tmk_keyboard +$ git submodule add -f -b kinetis https://github.com/flabbergast/ChibiOS.git tmk_core/tool/chibios/chibios +or +$ cd tmk_keyboard/tmk_core/tool/chibios +$ git clone -b kinetis https://github.com/flabbergast/ChibiOS.git tmk_core/tool/chibios/chibios + +$ cd tmk_keyboard/keyboard/infinity_chibios +$ make + + + + +Chibios Configuration +--------------------- +halconf.h: for HAL configuration + placed in project directory + read in chibios/os/hal/hal.mk + included in chibios/os/hal/include/hal.h +mcuconf.h: for MCU configuration + placed in project directory + included in halconf.h + + +Chibios Term +------------ +PAL = Port Abstraction Layer + palWritePad + palReadPad + palSetPad + chibios/os/hal/include/pal.h + +LLD = Low Level Driver + + +Makefile +-------- + # /os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES) + MCU_FAMILY = KINETIS + MCU_SERIES = KL2x + + # - it should exist either in /os/common/ports/ARMCMx/compilers/GCC/ld/ + # or /ld/ + MCU_LDSCRIPT = MKL26Z64 + + # - it should exist in /os/common/ports/ARMCMx/compilers/GCC/mk/ + MCU_STARTUP = kl2x + + # Board: it should exist either in /os/hal/boards/ + # or /boards + BOARD = PJRC_TEENSY_LC + + MCU = cortex-m0 + + # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 + ARMV = 6 + + +halconf.h +--------- + + +mcuconf.h +--------- + + +chconf.h +-------- + + +ld script +--------- +--- ../../tmk_core/tool/chibios/chibios/os/common/ports/ARMCMx/compilers/GCC/ld/MKL26Z64.ld 2015-10-15 09:08:58.732904304 +0900 ++++ ld/MKL26Z64.ld 2015-10-15 08:48:06.430215496 +0900 +@@ -27,7 +27,8 @@ + { + flash0 : org = 0x00000000, len = 0xc0 + flashcfg : org = 0x00000400, len = 0x10 +- flash : org = 0x00000410, len = 64k - 0x410 ++ flash : org = 0x00000410, len = 62k - 0x410 ++ eeprom_emu : org = 0x0000F800, len = 2k + ram : org = 0x1FFFF800, len = 8k + } + +@@ -35,6 +36,10 @@ + __ram_size__ = LENGTH(ram); + __ram_end__ = __ram_start__ + __ram_size__; + ++__eeprom_workarea_start__ = ORIGIN(eeprom_emu); ++__eeprom_workarea_size__ = LENGTH(eeprom_emu); ++__eeprom_workarea_end__ = __eeprom_workarea_start__ + __eeprom_workarea_size__; ++ + SECTIONS + { + . = 0; + + + +Configuration/Startup for Infinity 60% +-------------------------------------- +Configuration: + + +Clock: +Inifinity + FEI(FLL Engaged Internal) mode with core clock:48MHz, bus clock:48MHz, flash clock:24MHz + Clock dividor: + SIM_CLKDIV1[OUTDIV1] = 0 divide-by-1 for core clock + SIM_CLKDIV1[OUTDIV2] = 0 divide-by-1 for bus clock + SIM_CLKDIV1[OUTDIV4] = 1 divide-by-2 for flash clock + Internal reference clock: + MCG_C1[IREFS] = 1 Internal Reference Select for clock source for FLL + MCG_C1[IRCLKEN] = 1 Internal Reference Clock Enable + FLL multipilication: + MCG_C4[DMX32] = 1 + MCG_C4[DRST_DRS] = 01 FLL factor 1464 * 32.768kHz = 48MHz + +chibios/os/hal/ports/KINETIS/K20x/hal_lld.c + k20x_clock_init(): called in __early_init() defined in board.c + disable watchdog and configure clock + + configurable macros: + KINETIS_NO_INIT: whether init or not + KINETIS_MCG_MODE: clock mode + KINETIS_MCG_MODE_FEI + KINETIS_MCG_MODE_PEE + hal/ports/KINETIS/K20x/hal_lld.h + + +chibios/os/hal/boards/FREESCALE_FREEDOM_K20D50M/board.h + PALConfig pal_default_config + boardInit() + __early_init() + macro definitions for board infos, freq and mcu type + +chibios/os/hal/boards/FREESCALE_FREEDOM_K20D50M/board.c + +USB + + +Startup +------- + common/ports/ARMCMx/GCC/crt0_v[67]m.s + Reset_Handler: startup code + common/ports/ARMCMx/GCC/crt1.c + __core_init(): weak + __early_init(): weak + __late_init(): weak + __default_exit(): weak + called from Reset_Handler of crt0 + common/ports/ARMCMx/GCC/vector.c + common/ports/ARMCMx/GCC/ld/*.ld + +chibios/os/common/ports/ARMCMx/compilers/GCC/ +├── crt0_v6m.s +├── crt0_v7m.s +├── crt1.c +├── ld +│   ├── MK20DX128BLDR3.ld +│   ├── MK20DX128BLDR4.ld +│   ├── MK20DX128.ld +│   ├── MK20DX256.ld +│   ├── MKL25Z128.ld +│   ├── MKL26Z128.ld +│   ├── MKL26Z64.ld +│   └── STM32L476xG.ld +├── mk +│   ├── startup_k20x5.mk +│   ├── startup_k20x7.mk +│   ├── startup_k20x.mk +│   ├── startup_kl2x.mk +│   └── startup_stm32l4xx.mk +├── rules.ld +├── rules.mk +└── vectors.c + +chibios/os/hal/ +├── boards +│   ├── FREESCALE_FREEDOM_K20D50M +│   │   ├── board.c +│   │   ├── board.h +│   │   └── board.mk +│   ├── MCHCK_K20 +│   │   ├── board.c +│   │   ├── board.h +│   │   └── board.mk +│   ├── PJRC_TEENSY_3 +│   │   ├── board.c +│   │   ├── board.h +│   │   └── board.mk +│   ├── PJRC_TEENSY_3_1 +│   │   ├── board.c +│   │   ├── board.h +│   │   └── board.mk +│   ├── PJRC_TEENSY_LC +│   │   ├── board.c +│   │   ├── board.h +│   │   └── board.mk +│   ├── readme.txt +│   ├── simulator +│   │   ├── board.c +│   │   ├── board.h +│   │   └── board.mk +│   ├── ST_NUCLEO_F030R8 +│   │   ├── board.c +│   │   ├── board.h +│   │   ├── board.mk +│   │   └── cfg +│   │   └── board.chcfg +├── hal.mk +├── include +│   ├── adc.h +│   ├── can.h +│   ├── dac.h +│   ├── ext.h +│   ├── gpt.h +│   ├── hal_channels.h +│   ├── hal_files.h +│   ├── hal.h +│   ├── hal_ioblock.h +│   ├── hal_mmcsd.h +│   ├── hal_queues.h +│   ├── hal_streams.h +│   ├── i2c.h +│   ├── i2s.h +│   ├── icu.h +│   ├── mac.h +│   ├── mii.h +│   ├── mmc_spi.h +│   ├── pal.h +│   ├── pwm.h +│   ├── rtc.h +│   ├── sdc.h +│   ├── serial.h +│   ├── serial_usb.h +│   ├── spi.h +│   ├── st.h +│   ├── uart.h +│   └── usb.h +├── lib +│   └── streams +│   ├── chprintf.c +│   ├── chprintf.h +│   ├── memstreams.c +│   ├── memstreams.h +│   ├── nullstreams.c +│   └── nullstreams.h +├── osal +│   ├── nil +│   │   ├── osal.c +│   │   ├── osal.h +│   │   └── osal.mk +│   ├── os-less +│   │   └── ARMCMx +│   │   ├── osal.c +│   │   ├── osal.h +│   │   └── osal.mk +│   └── rt +│   ├── osal.c +│   ├── osal.h +│   └── osal.mk +├── ports +│   ├── AVR +│   ├── common +│   │   └── ARMCMx +│   │   ├── mpu.h +│   │   ├── nvic.c +│   │   └── nvic.h +│   ├── KINETIS +│   │   ├── K20x +│   │   │   ├── hal_lld.c +│   │   │   ├── hal_lld.h +│   │   │   ├── kinetis_registry.h +│   │   │   ├── platform.dox +│   │   │   ├── platform.mk +│   │   │   ├── pwm_lld.c +│   │   │   ├── pwm_lld.h +│   │   │   ├── spi_lld.c +│   │   │   └── spi_lld.h +│   │   ├── KL2x +│   │   │   ├── hal_lld.c +│   │   │   ├── hal_lld.h +│   │   │   ├── kinetis_registry.h +│   │   │   ├── platform.mk +│   │   │   ├── pwm_lld.c +│   │   │   └── pwm_lld.h +│   │   ├── LLD +│   │   │   ├── adc_lld.c +│   │   │   ├── adc_lld.h +│   │   │   ├── ext_lld.c +│   │   │   ├── ext_lld.h +│   │   │   ├── gpt_lld.c +│   │   │   ├── gpt_lld.h +│   │   │   ├── i2c_lld.c +│   │   │   ├── i2c_lld.h +│   │   │   ├── pal_lld.c +│   │   │   ├── pal_lld.h +│   │   │   ├── serial_lld.c +│   │   │   ├── serial_lld.h +│   │   │   ├── st_lld.c +│   │   │   ├── st_lld.h +│   │   │   ├── usb_lld.c +│   │   │   └── usb_lld.h +│   │   └── README.md +│   ├── LPC +│   ├── simulator +│   └── STM32 +├── src +│   ├── adc.c +│   ├── can.c +│   ├── dac.c +│   ├── ext.c +│   ├── gpt.c +│   ├── hal.c +│   ├── hal_mmcsd.c +│   ├── hal_queues.c +│   ├── i2c.c +│   ├── i2s.c +│   ├── icu.c +│   ├── mac.c +│   ├── mmc_spi.c +│   ├── pal.c +│   ├── pwm.c +│   ├── rtc.c +│   ├── sdc.c +│   ├── serial.c +│   ├── serial_usb.c +│   ├── spi.c +│   ├── st.c +│   ├── uart.c +│   └── usb.c +└── templates + ├── adc_lld.c + ├── adc_lld.h + ├── can_lld.c + ├── can_lld.h + ├── dac_lld.c + ├── dac_lld.h + ├── ext_lld.c + ├── ext_lld.h + ├── gpt_lld.c + ├── gpt_lld.h + ├── halconf.h + ├── hal_lld.c + ├── hal_lld.h + ├── i2c_lld.c + ├── i2c_lld.h + ├── i2s_lld.c + ├── i2s_lld.h + ├── icu_lld.c + ├── icu_lld.h + ├── mac_lld.c + ├── mac_lld.h + ├── mcuconf.h + ├── osal + │   ├── osal.c + │   ├── osal.h + │   └── osal.mk + ├── pal_lld.c + ├── pal_lld.h + ├── platform.mk + ├── pwm_lld.c + ├── pwm_lld.h + ├── rtc_lld.c + ├── rtc_lld.h + ├── sdc_lld.c + ├── sdc_lld.h + ├── serial_lld.c + ├── serial_lld.h + ├── spi_lld.c + ├── spi_lld.h + ├── st_lld.c + ├── st_lld.h + ├── uart_lld.c + ├── uart_lld.h + ├── usb_lld.c + └── usb_lld.h diff --git a/keyboard/infinity_chibios/Makefile b/keyboard/infinity_chibios/Makefile new file mode 100644 index 00000000..6fca4890 --- /dev/null +++ b/keyboard/infinity_chibios/Makefile @@ -0,0 +1,87 @@ +# Target file name (without extension). +PROJECT = ch + +# Directory common source files exist +TMK_DIR = ../../tmk_core + +# Directory keyboard dependent files exist +TARGET_DIR = . + +# project specific files +SRC = matrix.c \ + keymap_common.c \ + led.c + +ifdef KEYMAP + SRC := keymap_$(KEYMAP).c $(SRC) +else + SRC := keymap_plain.c $(SRC) +endif + +CONFIG_H = config.h + +## chip/board settings +# - the next two should match the directories in +# /os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES) +# - For Teensies, FAMILY = KINETIS and SERIES is either +# KL2x (LC) or K20x (3.0,3.1,3.2). +MCU_FAMILY = KINETIS +MCU_SERIES = K20x + +# Linker script to use +# - it should exist either in /os/common/ports/ARMCMx/compilers/GCC/ld/ +# or /ld/ +# - NOTE: a custom ld script is needed for EEPROM on Teensy LC +# - LDSCRIPT = +# - MKL26Z64 for Teensy LC +# - MK20DX128 for Teensy 3.0 +# - MK20DX256 for Teensy 3.1 and 3.2 +#MCU_LDSCRIPT = MK20DX128 +MCU_LDSCRIPT = MK20DX128BLDR4 + +# Startup code to use +# - it should exist in /os/common/ports/ARMCMx/compilers/GCC/mk/ +# - STARTUP = +# - kl2x for Teensy LC +# - k20x5 for Teensy 3.0 +# - k20x7 for Teensy 3.1 and 3.2 +MCU_STARTUP = k20x5 + +# Board: it should exist either in /os/hal/boards/ +# or /boards +# - BOARD = +# - PJRC_TEENSY_LC for Teensy LC +# - PJRC_TEENSY_3 for Teensy 3.0 +# - PJRC_TEENSY_3_1 for Teensy 3.1 or 3.2 +BOARD = MCHCK_K20 + +# Cortex version +# Teensy LC is cortex-m0; Teensy 3.x are cortex-m4 +MCU = cortex-m4 + +# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 +# I.e. 6 for Teensy LC; 7 for Teensy 3.x +ARMV = 7 + +# Vector table for application +# 0x00000000-0x00001000 area is occupied by bootlaoder.*/ +OPT_DEFS = -DCORTEX_VTOR_INIT=0x00001000 + + +# Build Options +# comment out to disable the options. +# +BOOTMAGIC_ENABLE = yes # Virtual DIP switch configuration +## (Note that for BOOTMAGIC on Teensy LC you have to use a custom .ld script.) +MOUSEKEY_ENABLE = yes # Mouse keys +EXTRAKEY_ENABLE = yes # Audio control and System control +CONSOLE_ENABLE = yes # Console for debug +COMMAND_ENABLE = yes # Commands for debug and configuration +SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +NKRO_ENABLE = yes # USB Nkey Rollover + +include $(TMK_DIR)/tool/chibios/common.mk +include $(TMK_DIR)/tool/chibios/chibios.mk + +program: $(BUILDDIR)/$(PROJECT).bin + dfu-util -D $(BUILDDIR)/$(PROJECT).bin diff --git a/keyboard/infinity_chibios/chconf.h b/keyboard/infinity_chibios/chconf.h new file mode 100644 index 00000000..b7a340d6 --- /dev/null +++ b/keyboard/infinity_chibios/chconf.h @@ -0,0 +1,499 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file templates/chconf.h + * @brief Configuration file template. + * @details A copy of this file must be placed in each project directory, it + * contains the application specific kernel settings. + * + * @addtogroup config + * @details Kernel related settings and hooks. + * @{ + */ + +#ifndef _CHCONF_H_ +#define _CHCONF_H_ + +/*===========================================================================*/ +/** + * @name System timers settings + * @{ + */ +/*===========================================================================*/ + +/** + * @brief System time counter resolution. + * @note Allowed values are 16 or 32 bits. + */ +#define CH_CFG_ST_RESOLUTION 32 + +/** + * @brief System tick frequency. + * @details Frequency of the system timer that drives the system ticks. This + * setting also defines the system tick time unit. + */ +#define CH_CFG_ST_FREQUENCY 1000 + +/** + * @brief Time delta constant for the tick-less mode. + * @note If this value is zero then the system uses the classic + * periodic tick. This value represents the minimum number + * of ticks that is safe to specify in a timeout directive. + * The value one is not valid, timeouts are rounded up to + * this value. + */ +#define CH_CFG_ST_TIMEDELTA 0 + +/** @} */ + +/*===========================================================================*/ +/** + * @name Kernel parameters and options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Round robin interval. + * @details This constant is the number of system ticks allowed for the + * threads before preemption occurs. Setting this value to zero + * disables the preemption for threads with equal priority and the + * round robin becomes cooperative. Note that higher priority + * threads can still preempt, the kernel is always preemptive. + * @note Disabling the round robin preemption makes the kernel more compact + * and generally faster. + * @note The round robin preemption is not supported in tickless mode and + * must be set to zero in that case. + */ +#define CH_CFG_TIME_QUANTUM 20 + +/** + * @brief Managed RAM size. + * @details Size of the RAM area to be managed by the OS. If set to zero + * then the whole available RAM is used. The core memory is made + * available to the heap allocator and/or can be used directly through + * the simplified core memory allocator. + * + * @note In order to let the OS manage the whole RAM the linker script must + * provide the @p __heap_base__ and @p __heap_end__ symbols. + * @note Requires @p CH_CFG_USE_MEMCORE. + */ +#define CH_CFG_MEMCORE_SIZE 0 + +/** + * @brief Idle thread automatic spawn suppression. + * @details When this option is activated the function @p chSysInit() + * does not spawn the idle thread. The application @p main() + * function becomes the idle thread and must implement an + * infinite loop. + */ +#define CH_CFG_NO_IDLE_THREAD FALSE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Performance options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief OS optimization. + * @details If enabled then time efficient rather than space efficient code + * is used when two possible implementations exist. + * + * @note This is not related to the compiler optimization options. + * @note The default is @p TRUE. + */ +#define CH_CFG_OPTIMIZE_SPEED TRUE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Subsystem options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Time Measurement APIs. + * @details If enabled then the time measurement APIs are included in + * the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_TM FALSE + +/** + * @brief Threads registry APIs. + * @details If enabled then the registry APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_REGISTRY TRUE + +/** + * @brief Threads synchronization APIs. + * @details If enabled then the @p chThdWait() function is included in + * the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_WAITEXIT TRUE + +/** + * @brief Semaphores APIs. + * @details If enabled then the Semaphores APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_SEMAPHORES TRUE + +/** + * @brief Semaphores queuing mode. + * @details If enabled then the threads are enqueued on semaphores by + * priority rather than in FIFO order. + * + * @note The default is @p FALSE. Enable this if you have special + * requirements. + * @note Requires @p CH_CFG_USE_SEMAPHORES. + */ +#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE + +/** + * @brief Mutexes APIs. + * @details If enabled then the mutexes APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MUTEXES TRUE + +/** + * @brief Enables recursive behavior on mutexes. + * @note Recursive mutexes are heavier and have an increased + * memory footprint. + * + * @note The default is @p FALSE. + * @note Requires @p CH_CFG_USE_MUTEXES. + */ +#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE + +/** + * @brief Conditional Variables APIs. + * @details If enabled then the conditional variables APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_MUTEXES. + */ +#define CH_CFG_USE_CONDVARS TRUE + +/** + * @brief Conditional Variables APIs with timeout. + * @details If enabled then the conditional variables APIs with timeout + * specification are included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_CONDVARS. + */ +#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE + +/** + * @brief Events Flags APIs. + * @details If enabled then the event flags APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_EVENTS TRUE + +/** + * @brief Events Flags APIs with timeout. + * @details If enabled then the events APIs with timeout specification + * are included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_EVENTS. + */ +#define CH_CFG_USE_EVENTS_TIMEOUT TRUE + +/** + * @brief Synchronous Messages APIs. + * @details If enabled then the synchronous messages APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MESSAGES TRUE + +/** + * @brief Synchronous Messages queuing mode. + * @details If enabled then messages are served by priority rather than in + * FIFO order. + * + * @note The default is @p FALSE. Enable this if you have special + * requirements. + * @note Requires @p CH_CFG_USE_MESSAGES. + */ +#define CH_CFG_USE_MESSAGES_PRIORITY FALSE + +/** + * @brief Mailboxes APIs. + * @details If enabled then the asynchronous messages (mailboxes) APIs are + * included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_SEMAPHORES. + */ +#define CH_CFG_USE_MAILBOXES TRUE + +/** + * @brief I/O Queues APIs. + * @details If enabled then the I/O queues APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_QUEUES TRUE + +/** + * @brief Core Memory Manager APIs. + * @details If enabled then the core memory manager APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MEMCORE TRUE + +/** + * @brief Heap Allocator APIs. + * @details If enabled then the memory heap allocator APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or + * @p CH_CFG_USE_SEMAPHORES. + * @note Mutexes are recommended. + */ +#define CH_CFG_USE_HEAP TRUE + +/** + * @brief Memory Pools Allocator APIs. + * @details If enabled then the memory pools allocator APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MEMPOOLS TRUE + +/** + * @brief Dynamic Threads APIs. + * @details If enabled then the dynamic threads creation APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_WAITEXIT. + * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS. + */ +#define CH_CFG_USE_DYNAMIC TRUE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Debug options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Debug option, kernel statistics. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_STATISTICS FALSE + +/** + * @brief Debug option, system state check. + * @details If enabled the correct call protocol for system APIs is checked + * at runtime. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_SYSTEM_STATE_CHECK FALSE + +/** + * @brief Debug option, parameters checks. + * @details If enabled then the checks on the API functions input + * parameters are activated. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_ENABLE_CHECKS FALSE + +/** + * @brief Debug option, consistency checks. + * @details If enabled then all the assertions in the kernel code are + * activated. This includes consistency checks inside the kernel, + * runtime anomalies and port-defined checks. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_ENABLE_ASSERTS FALSE + +/** + * @brief Debug option, trace buffer. + * @details If enabled then the context switch circular trace buffer is + * activated. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_ENABLE_TRACE FALSE + +/** + * @brief Debug option, stack checks. + * @details If enabled then a runtime stack check is performed. + * + * @note The default is @p FALSE. + * @note The stack check is performed in a architecture/port dependent way. + * It may not be implemented or some ports. + * @note The default failure mode is to halt the system with the global + * @p panic_msg variable set to @p NULL. + */ +#define CH_DBG_ENABLE_STACK_CHECK FALSE + +/** + * @brief Debug option, stacks initialization. + * @details If enabled then the threads working area is filled with a byte + * value when a thread is created. This can be useful for the + * runtime measurement of the used stack. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_FILL_THREADS FALSE + +/** + * @brief Debug option, threads profiling. + * @details If enabled then a field is added to the @p thread_t structure that + * counts the system ticks occurred while executing the thread. + * + * @note The default is @p FALSE. + * @note This debug option is not currently compatible with the + * tickless mode. + */ +#define CH_DBG_THREADS_PROFILING FALSE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Kernel hooks + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Threads descriptor structure extension. + * @details User fields added to the end of the @p thread_t structure. + */ +#define CH_CFG_THREAD_EXTRA_FIELDS \ + /* Add threads custom fields here.*/ + +/** + * @brief Threads initialization hook. + * @details User initialization code added to the @p chThdInit() API. + * + * @note It is invoked from within @p chThdInit() and implicitly from all + * the threads creation APIs. + */ +#define CH_CFG_THREAD_INIT_HOOK(tp) { \ + /* Add threads initialization code here.*/ \ +} + +/** + * @brief Threads finalization hook. + * @details User finalization code added to the @p chThdExit() API. + * + * @note It is inserted into lock zone. + * @note It is also invoked when the threads simply return in order to + * terminate. + */ +#define CH_CFG_THREAD_EXIT_HOOK(tp) { \ + /* Add threads finalization code here.*/ \ +} + +/** + * @brief Context switch hook. + * @details This hook is invoked just before switching between threads. + */ +#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \ + /* Context switch code here.*/ \ +} + +/** + * @brief Idle thread enter hook. + * @note This hook is invoked within a critical zone, no OS functions + * should be invoked from here. + * @note This macro can be used to activate a power saving mode. + */ +#define CH_CFG_IDLE_ENTER_HOOK() { \ +} + +/** + * @brief Idle thread leave hook. + * @note This hook is invoked within a critical zone, no OS functions + * should be invoked from here. + * @note This macro can be used to deactivate a power saving mode. + */ +#define CH_CFG_IDLE_LEAVE_HOOK() { \ +} + +/** + * @brief Idle Loop hook. + * @details This hook is continuously invoked by the idle thread loop. + */ +#define CH_CFG_IDLE_LOOP_HOOK() { \ + /* Idle loop code here.*/ \ +} + +/** + * @brief System tick event hook. + * @details This hook is invoked in the system tick handler immediately + * after processing the virtual timers queue. + */ +#define CH_CFG_SYSTEM_TICK_HOOK() { \ + /* System tick event code here.*/ \ +} + +/** + * @brief System halt hook. + * @details This hook is invoked in case to a system halting error before + * the system is halted. + */ +#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \ + /* System halt code here.*/ \ +} + +/** @} */ + +/*===========================================================================*/ +/* Port-specific settings (override port settings defaulted in chcore.h). */ +/*===========================================================================*/ + +#endif /* _CHCONF_H_ */ + +/** @} */ diff --git a/keyboard/infinity_chibios/config.h b/keyboard/infinity_chibios/config.h new file mode 100644 index 00000000..b4b7fcd3 --- /dev/null +++ b/keyboard/infinity_chibios/config.h @@ -0,0 +1,76 @@ +/* +Copyright 2015 Jun Wako + +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 2 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 . +*/ + +#ifndef CONFIG_H +#define CONFIG_H + + +/* USB Device descriptor parameter */ +#define VENDOR_ID 0xFEED +#define PRODUCT_ID 0x6464 +#define DEVICE_VER 0x0001 +/* in python2: list(u"whatever".encode('utf-16-le')) */ +/* at most 32 characters or the ugly hack in usb_main.c borks */ +#define MANUFACTURER "TMK" +#define USBSTR_MANUFACTURER 'T', '\x00', 'M', '\x00', 'K', '\x00', ' ', '\x00', '\xc6', '\x00' +#define PRODUCT "ChibiOS TMK test" +#define USBSTR_PRODUCT 'C', '\x00', 'h', '\x00', 'i', '\x00', 'b', '\x00', 'i', '\x00', 'O', '\x00', 'S', '\x00', ' ', '\x00', 'T', '\x00', 'M', '\x00', 'K', '\x00', ' ', '\x00', 't', '\x00', 'e', '\x00', 's', '\x00', 't', '\x00' +#define DESCRIPTION "TMK keyboard firmware over ChibiOS" + +/* key matrix size */ +#define MATRIX_ROWS 9 +#define MATRIX_COLS 7 + +/* define if matrix has ghost */ +//#define MATRIX_HAS_GHOST + +/* Set 0 if debouncing isn't needed */ +#define DEBOUNCE 5 + +/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */ +//#define LOCKING_SUPPORT_ENABLE +/* Locking resynchronize hack */ +//#define LOCKING_RESYNC_ENABLE + +/* key combination for command */ +#define IS_COMMAND() ( \ + keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \ +) + +/* Keymap for Infiity prototype */ +#define INFINITY_PROTOTYPE + + +/* + * Feature disable options + * These options are also useful to firmware size reduction. + */ + +/* disable debug print */ +//#define NO_DEBUG + +/* disable print */ +//#define NO_PRINT + +/* disable action features */ +//#define NO_ACTION_LAYER +//#define NO_ACTION_TAPPING +//#define NO_ACTION_ONESHOT +//#define NO_ACTION_MACRO +//#define NO_ACTION_FUNCTION + +#endif diff --git a/keyboard/infinity_chibios/halconf.h b/keyboard/infinity_chibios/halconf.h new file mode 100644 index 00000000..2472ba7e --- /dev/null +++ b/keyboard/infinity_chibios/halconf.h @@ -0,0 +1,334 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file templates/halconf.h + * @brief HAL configuration header. + * @details HAL configuration file, this file allows to enable or disable the + * various device drivers from your application. You may also use + * this file in order to override the device drivers default settings. + * + * @addtogroup HAL_CONF + * @{ + */ + +#ifndef _HALCONF_H_ +#define _HALCONF_H_ + +#include "mcuconf.h" + +/** + * @brief Enables the PAL subsystem. + */ +#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__) +#define HAL_USE_PAL TRUE +#endif + +/** + * @brief Enables the ADC subsystem. + */ +#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__) +#define HAL_USE_ADC FALSE +#endif + +/** + * @brief Enables the CAN subsystem. + */ +#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__) +#define HAL_USE_CAN FALSE +#endif + +/** + * @brief Enables the DAC subsystem. + */ +#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__) +#define HAL_USE_DAC FALSE +#endif + +/** + * @brief Enables the EXT subsystem. + */ +#if !defined(HAL_USE_EXT) || defined(__DOXYGEN__) +#define HAL_USE_EXT FALSE +#endif + +/** + * @brief Enables the GPT subsystem. + */ +#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__) +#define HAL_USE_GPT FALSE +#endif + +/** + * @brief Enables the I2C subsystem. + */ +#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__) +#define HAL_USE_I2C FALSE +#endif + +/** + * @brief Enables the I2S subsystem. + */ +#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__) +#define HAL_USE_I2S FALSE +#endif + +/** + * @brief Enables the ICU subsystem. + */ +#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__) +#define HAL_USE_ICU FALSE +#endif + +/** + * @brief Enables the MAC subsystem. + */ +#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__) +#define HAL_USE_MAC FALSE +#endif + +/** + * @brief Enables the MMC_SPI subsystem. + */ +#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__) +#define HAL_USE_MMC_SPI FALSE +#endif + +/** + * @brief Enables the PWM subsystem. + */ +#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__) +#define HAL_USE_PWM FALSE +#endif + +/** + * @brief Enables the RTC subsystem. + */ +#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__) +#define HAL_USE_RTC FALSE +#endif + +/** + * @brief Enables the SDC subsystem. + */ +#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__) +#define HAL_USE_SDC FALSE +#endif + +/** + * @brief Enables the SERIAL subsystem. + */ +#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__) +#define HAL_USE_SERIAL FALSE +#endif + +/** + * @brief Enables the SERIAL over USB subsystem. + */ +#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__) +#define HAL_USE_SERIAL_USB TRUE +#endif + +/** + * @brief Enables the SPI subsystem. + */ +#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__) +#define HAL_USE_SPI FALSE +#endif + +/** + * @brief Enables the UART subsystem. + */ +#if !defined(HAL_USE_UART) || defined(__DOXYGEN__) +#define HAL_USE_UART FALSE +#endif + +/** + * @brief Enables the USB subsystem. + */ +#if !defined(HAL_USE_USB) || defined(__DOXYGEN__) +#define HAL_USE_USB TRUE +#endif + +/*===========================================================================*/ +/* ADC driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables synchronous APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__) +#define ADC_USE_WAIT TRUE +#endif + +/** + * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define ADC_USE_MUTUAL_EXCLUSION TRUE +#endif + +/*===========================================================================*/ +/* CAN driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Sleep mode related APIs inclusion switch. + */ +#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__) +#define CAN_USE_SLEEP_MODE TRUE +#endif + +/*===========================================================================*/ +/* I2C driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables the mutual exclusion APIs on the I2C bus. + */ +#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define I2C_USE_MUTUAL_EXCLUSION TRUE +#endif + +/*===========================================================================*/ +/* MAC driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables an event sources for incoming packets. + */ +#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__) +#define MAC_USE_ZERO_COPY FALSE +#endif + +/** + * @brief Enables an event sources for incoming packets. + */ +#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__) +#define MAC_USE_EVENTS TRUE +#endif + +/*===========================================================================*/ +/* MMC_SPI driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Delays insertions. + * @details If enabled this options inserts delays into the MMC waiting + * routines releasing some extra CPU time for the threads with + * lower priority, this may slow down the driver a bit however. + * This option is recommended also if the SPI driver does not + * use a DMA channel and heavily loads the CPU. + */ +#if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__) +#define MMC_NICE_WAITING TRUE +#endif + +/*===========================================================================*/ +/* SDC driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Number of initialization attempts before rejecting the card. + * @note Attempts are performed at 10mS intervals. + */ +#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__) +#define SDC_INIT_RETRY 100 +#endif + +/** + * @brief Include support for MMC cards. + * @note MMC support is not yet implemented so this option must be kept + * at @p FALSE. + */ +#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__) +#define SDC_MMC_SUPPORT FALSE +#endif + +/** + * @brief Delays insertions. + * @details If enabled this options inserts delays into the MMC waiting + * routines releasing some extra CPU time for the threads with + * lower priority, this may slow down the driver a bit however. + */ +#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__) +#define SDC_NICE_WAITING TRUE +#endif + +/*===========================================================================*/ +/* SERIAL driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Default bit rate. + * @details Configuration parameter, this is the baud rate selected for the + * default configuration. + */ +#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__) +#define SERIAL_DEFAULT_BITRATE 38400 +#endif + +/** + * @brief Serial buffers size. + * @details Configuration parameter, you can change the depth of the queue + * buffers depending on the requirements of your application. + * @note The default is 64 bytes for both the transmission and receive + * buffers. + */ +#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__) +#define SERIAL_BUFFERS_SIZE 16 +#endif + +/*===========================================================================*/ +/* SERIAL_USB driver related setting. */ +/*===========================================================================*/ + +/** + * @brief Serial over USB buffers size. + * @details Configuration parameter, the buffer size must be a multiple of + * the USB data endpoint maximum packet size. + * @note The default is 64 bytes for both the transmission and receive + * buffers. + */ +#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__) +#define SERIAL_USB_BUFFERS_SIZE 256 +#endif + +/*===========================================================================*/ +/* SPI driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables synchronous APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__) +#define SPI_USE_WAIT TRUE +#endif + +/** + * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define SPI_USE_MUTUAL_EXCLUSION TRUE +#endif + +#endif /* _HALCONF_H_ */ + +/** @} */ diff --git a/keyboard/infinity_chibios/keymap_common.c b/keyboard/infinity_chibios/keymap_common.c new file mode 100644 index 00000000..69b170ce --- /dev/null +++ b/keyboard/infinity_chibios/keymap_common.c @@ -0,0 +1,30 @@ +/* +Copyright 2012,2013 Jun Wako + +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 2 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 "keymap_common.h" + + +/* translates key to keycode */ +uint8_t keymap_key_to_keycode(uint8_t layer, keypos_t key) +{ + return keymaps[(layer)][(key.row)][(key.col)]; +} + +/* translates Fn keycode to action */ +action_t keymap_fn_to_action(uint8_t keycode) +{ + return (action_t){ .code = fn_actions[FN_INDEX(keycode)] }; +} diff --git a/keyboard/infinity_chibios/keymap_common.h b/keyboard/infinity_chibios/keymap_common.h new file mode 100644 index 00000000..b0f765bf --- /dev/null +++ b/keyboard/infinity_chibios/keymap_common.h @@ -0,0 +1,80 @@ +/* +Copyright 2014 Jun Wako + +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 2 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 . +*/ +#ifndef KEYMAP_COMMON_H +#define KEYMAP_COMMON_H + +#include +#include +#include "keycode.h" +#include "action.h" +#include "action_macro.h" +#include "report.h" +#include "host.h" +#include "print.h" +#include "debug.h" +#include "keymap.h" + + +extern const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; +extern const uint16_t fn_actions[]; + + +#ifdef INFINITY_PROTOTYPE + +/* Infinity prototype */ +#define KEYMAP( \ + K00, K10, K20, K30, K40, K50, K60, K70, K80, K01, K11, K21, K31, K41, K86, \ + K51, K61, K71, K81, K02, K12, K22, K32, K42, K52, K62, K72, K82, K03, \ + K13, K23, K33, K43, K53, K63, K73, K83, K04, K14, K24, K34, K44, \ + K54, K64, K74, K84, K05, K15, K25, K35, K45, K55, K65, K75, K85, \ + K06, K16, K26, K36, K46, K56, K66, K76 \ +) { \ + { KC_##K00, KC_##K01, KC_##K02, KC_##K03, KC_##K04, KC_##K05, KC_##K06 }, \ + { KC_##K10, KC_##K11, KC_##K12, KC_##K13, KC_##K14, KC_##K15, KC_##K16 }, \ + { KC_##K20, KC_##K21, KC_##K22, KC_##K23, KC_##K24, KC_##K25, KC_##K26 }, \ + { KC_##K30, KC_##K31, KC_##K32, KC_##K33, KC_##K34, KC_##K35, KC_##K36 }, \ + { KC_##K40, KC_##K41, KC_##K42, KC_##K43, KC_##K44, KC_##K45, KC_##K46 }, \ + { KC_##K50, KC_##K51, KC_##K52, KC_##K53, KC_##K54, KC_##K55, KC_##K56 }, \ + { KC_##K60, KC_##K61, KC_##K62, KC_##K63, KC_##K64, KC_##K65, KC_##K66 }, \ + { KC_##K70, KC_##K71, KC_##K72, KC_##K73, KC_##K74, KC_##K75, KC_##K76 }, \ + { KC_##K80, KC_##K81, KC_##K82, KC_##K83, KC_##K84, KC_##K85, KC_##K86 } \ +} + +#else + +/* Infinity production */ +#define KEYMAP( \ + K00, K10, K20, K30, K40, K50, K60, K70, K80, K01, K11, K21, K31, K41, K51, \ + K61, K71, K81, K02, K12, K22, K32, K42, K52, K62, K72, K82, K03, K13, \ + K23, K33, K43, K53, K63, K73, K83, K04, K14, K24, K34, K44, K54, \ + K64, K74, K84, K05, K15, K25, K35, K45, K55, K65, K75, K85, K06, \ + K16, K26, K36, K46, K56, K66, K76, K86 \ +) { \ + { KC_##K00, KC_##K01, KC_##K02, KC_##K03, KC_##K04, KC_##K05, KC_##K06 }, \ + { KC_##K10, KC_##K11, KC_##K12, KC_##K13, KC_##K14, KC_##K15, KC_##K16 }, \ + { KC_##K20, KC_##K21, KC_##K22, KC_##K23, KC_##K24, KC_##K25, KC_##K26 }, \ + { KC_##K30, KC_##K31, KC_##K32, KC_##K33, KC_##K34, KC_##K35, KC_##K36 }, \ + { KC_##K40, KC_##K41, KC_##K42, KC_##K43, KC_##K44, KC_##K45, KC_##K46 }, \ + { KC_##K50, KC_##K51, KC_##K52, KC_##K53, KC_##K54, KC_##K55, KC_##K56 }, \ + { KC_##K60, KC_##K61, KC_##K62, KC_##K63, KC_##K64, KC_##K65, KC_##K66 }, \ + { KC_##K70, KC_##K71, KC_##K72, KC_##K73, KC_##K74, KC_##K75, KC_##K76 }, \ + { KC_##K80, KC_##K81, KC_##K82, KC_##K83, KC_##K84, KC_##K85, KC_##K86 } \ +} + +#endif + +#endif diff --git a/keyboard/infinity_chibios/keymap_plain.c b/keyboard/infinity_chibios/keymap_plain.c new file mode 100644 index 00000000..2673d6aa --- /dev/null +++ b/keyboard/infinity_chibios/keymap_plain.c @@ -0,0 +1,48 @@ +#include "keymap_common.h" + +const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + /* Layer 0: Default Layer + * ,-----------------------------------------------------------. + * |Esc| 1| 2| 3| 4| 5| 6| 7| 8| 9| 0| -| =| `|BSp| + * |-----------------------------------------------------------| + * |Tab | Q| W| E| R| T| Y| U| I| O| P| [| ]| \| + * |-----------------------------------------------------------| + * |Contro| A| S| D| F| G| H| J| K| L| ;| '|Enter | + * |-----------------------------------------------------------| + * |Shift | Z| X| C| V| B| N| M| ,| .| /|Shift |Fn0| + * |-----------------------------------------------------------' + * | |Gui|Alt | Space |Alt |Gui| | | + * `-----------------------------------------------------------' + */ + [0] = + KEYMAP(ESC, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, BSLS, GRV, \ + TAB, Q, W, E, R, T, Y, U, I, O, P, LBRC,RBRC,BSPC, \ + LCTL,A, S, D, F, G, H, J, K, L, SCLN,QUOT,ENT, \ + LSFT,Z, X, C, V, B, N, M, COMM,DOT, SLSH,RSFT,FN0, \ + NO, LGUI,LALT, SPC, RALT,RGUI,NO, NO), + + /* Layer 1: HHKB mode (HHKB Fn) + * ,-----------------------------------------------------------. + * |Pwr| F1| F2| F3| F4| F5| F6| F7| F8| F9|F10|F11|F12|Ins|Del| + * |-----------------------------------------------------------| + * |Caps | | | | | | | |Psc|Slk|Pus|Up | |Backs| + * |-----------------------------------------------------------| + * | |VoD|VoU|Mut| | | *| /|Hom|PgU|Lef|Rig|Enter | + * |-----------------------------------------------------------| + * | | | | | | | +| -|End|PgD|Dow| | | + * `-----------------------------------------------------------' + * | |Gui|Alt | Space |Alt |Gui| | | + * `-----------------------------------------------------------' + */ + [1] = + KEYMAP(PWR, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, INS, DEL, \ + CAPS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,PSCR,SLCK,PAUS, UP, TRNS, BSPC, \ + TRNS,VOLD,VOLU,MUTE,TRNS,TRNS,PAST,PSLS,HOME,PGUP,LEFT,RGHT,PENT, \ + TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,PPLS,PMNS,END, PGDN,DOWN,TRNS,TRNS, \ + TRNS,TRNS,TRNS, TRNS, TRNS,TRNS,TRNS,TRNS), +}; + +const uint16_t fn_actions[] = { + [0] = ACTION_LAYER_MOMENTARY(1), +}; + diff --git a/keyboard/infinity_chibios/led.c b/keyboard/infinity_chibios/led.c new file mode 100644 index 00000000..aed66c7c --- /dev/null +++ b/keyboard/infinity_chibios/led.c @@ -0,0 +1,24 @@ +/* +Copyright 2012 Jun Wako + +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 2 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 "hal.h" + +#include "led.h" + + +void led_set(uint8_t usb_led) { +} diff --git a/keyboard/infinity_chibios/matrix.c b/keyboard/infinity_chibios/matrix.c new file mode 100644 index 00000000..2c68ea83 --- /dev/null +++ b/keyboard/infinity_chibios/matrix.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include "hal.h" +#include "timer.h" +#include "wait.h" +#include "print.h" +#include "matrix.h" + + +/* + * Infinity Pinusage: + * Column pins are input with internal pull-down. Row pins are output and strobe with high. + * Key is high or 1 when it turns on. + * + * col: { PTD1, PTD2, PTD3, PTD4, PTD5, PTD6, PTD7 } + * row: { PTB0, PTB1, PTB2, PTB3, PTB16, PTB17, PTC4, PTC5, PTD0 } + */ +/* matrix state(1:on, 0:off) */ +static matrix_row_t matrix[MATRIX_ROWS]; +static matrix_row_t matrix_debouncing[MATRIX_ROWS]; +static bool debouncing = false; +static uint16_t debouncing_time = 0; + + +void matrix_init(void) +{ + /* Column(sense) */ + palSetPadMode(GPIOD, 1, PAL_MODE_INPUT_PULLDOWN); + palSetPadMode(GPIOD, 2, PAL_MODE_INPUT_PULLDOWN); + palSetPadMode(GPIOD, 3, PAL_MODE_INPUT_PULLDOWN); + palSetPadMode(GPIOD, 4, PAL_MODE_INPUT_PULLDOWN); + palSetPadMode(GPIOD, 5, PAL_MODE_INPUT_PULLDOWN); + palSetPadMode(GPIOD, 6, PAL_MODE_INPUT_PULLDOWN); + palSetPadMode(GPIOD, 7, PAL_MODE_INPUT_PULLDOWN); + + /* Row(strobe) */ + palSetPadMode(GPIOB, 0, PAL_MODE_OUTPUT_PUSHPULL); + palSetPadMode(GPIOB, 1, PAL_MODE_OUTPUT_PUSHPULL); + palSetPadMode(GPIOB, 2, PAL_MODE_OUTPUT_PUSHPULL); + palSetPadMode(GPIOB, 3, PAL_MODE_OUTPUT_PUSHPULL); + palSetPadMode(GPIOB, 16, PAL_MODE_OUTPUT_PUSHPULL); + palSetPadMode(GPIOB, 17, PAL_MODE_OUTPUT_PUSHPULL); + palSetPadMode(GPIOC, 4, PAL_MODE_OUTPUT_PUSHPULL); + palSetPadMode(GPIOC, 5, PAL_MODE_OUTPUT_PUSHPULL); + palSetPadMode(GPIOD, 0, PAL_MODE_OUTPUT_PUSHPULL); + + memset(matrix, 0, MATRIX_ROWS); + memset(matrix_debouncing, 0, MATRIX_ROWS); +} + +uint8_t matrix_scan(void) +{ + for (int row = 0; row < MATRIX_ROWS; row++) { + matrix_row_t data = 0; + + // strobe row + switch (row) { + case 0: palSetPad(GPIOB, 0); break; + case 1: palSetPad(GPIOB, 1); break; + case 2: palSetPad(GPIOB, 2); break; + case 3: palSetPad(GPIOB, 3); break; + case 4: palSetPad(GPIOB, 16); break; + case 5: palSetPad(GPIOB, 17); break; + case 6: palSetPad(GPIOC, 4); break; + case 7: palSetPad(GPIOC, 5); break; + case 8: palSetPad(GPIOD, 0); break; + } + + wait_us(1); // need wait to settle pin state + + // read col data + data = (palReadPort(GPIOD)>>1); + + // un-strobe row + switch (row) { + case 0: palClearPad(GPIOB, 0); break; + case 1: palClearPad(GPIOB, 1); break; + case 2: palClearPad(GPIOB, 2); break; + case 3: palClearPad(GPIOB, 3); break; + case 4: palClearPad(GPIOB, 16); break; + case 5: palClearPad(GPIOB, 17); break; + case 6: palClearPad(GPIOC, 4); break; + case 7: palClearPad(GPIOC, 5); break; + case 8: palClearPad(GPIOD, 0); break; + } + + if (matrix_debouncing[row] != data) { + matrix_debouncing[row] = data; + debouncing = true; + debouncing_time = timer_read(); + } + } + + if (debouncing && timer_elapsed(debouncing_time) > DEBOUNCE) { + for (int row = 0; row < MATRIX_ROWS; row++) { + matrix[row] = matrix_debouncing[row]; + } + debouncing = false; + } + return 1; +} + +bool matrix_is_on(uint8_t row, uint8_t col) +{ + return (matrix[row] & (1< Date: Sat, 17 Oct 2015 01:59:55 +0900 Subject: [PATCH 31/56] Fix report size of boot protocol. --- tmk_core/protocol/chibios/usb_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 382966b9..619b798b 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -1130,7 +1130,7 @@ void send_keyboard(report_keyboard_t *report) { } else #endif /* NKRO_ENABLE */ { /* boot protocol */ - usbPrepareTransmit(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t)); + usbPrepareTransmit(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)report, KBD_EPSIZE); osalSysLock(); usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT); osalSysUnlock(); From dcac43fa510eb5087a3b80d5654ffaa98fa471d4 Mon Sep 17 00:00:00 2001 From: Jun Wako Date: Sat, 17 Oct 2015 02:52:44 +0900 Subject: [PATCH 32/56] Add keymap_hasu.c --- keyboard/infinity_chibios/keymap_common.h | 1 + keyboard/infinity_chibios/keymap_hasu.c | 199 ++++++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 keyboard/infinity_chibios/keymap_hasu.c diff --git a/keyboard/infinity_chibios/keymap_common.h b/keyboard/infinity_chibios/keymap_common.h index b0f765bf..260822fc 100644 --- a/keyboard/infinity_chibios/keymap_common.h +++ b/keyboard/infinity_chibios/keymap_common.h @@ -22,6 +22,7 @@ along with this program. If not, see . #include "keycode.h" #include "action.h" #include "action_macro.h" +#include "action_util.h" #include "report.h" #include "host.h" #include "print.h" diff --git a/keyboard/infinity_chibios/keymap_hasu.c b/keyboard/infinity_chibios/keymap_hasu.c new file mode 100644 index 00000000..2d0635f0 --- /dev/null +++ b/keyboard/infinity_chibios/keymap_hasu.c @@ -0,0 +1,199 @@ +#include "keymap_common.h" + +const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + /* Layer 0: Default Layer + * ,-----------------------------------------------------------. + * |Esc| 1| 2| 3| 4| 5| 6| 7| 8| 9| 0| -| =| `|BSp| + * |-----------------------------------------------------------| + * |Tab | Q| W| E| R| T| Y| U| I| O| P| [| ]| \| + * |-----------------------------------------------------------| + * |Contro| A| S| D| F| G| H| J| K| L| ;| '|Enter | + * |-----------------------------------------------------------| + * |Shift | Z| X| C| V| B| N| M| ,| .| /|Shift |Fn0| + * |-----------------------------------------------------------' + * | |Gui|Alt | Space |Alt |Gui| | | + * `-----------------------------------------------------------' + */ + [0] = + KEYMAP(ESC, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, BSLS,GRV, \ + TAB, Q, W, E, R, T, Y, U, I, O, P, LBRC,RBRC,BSPC, \ + LCTL,A, S, D, F, G, H, J, K, L, FN3, QUOT,FN6, \ + FN7, Z, X, C, V, B, N, M, COMM,DOT, FN2, RSFT,FN1, \ + NO, LGUI,LALT, FN4, FN5, RGUI,NO, NO), + + /* Layer 1: HHKB mode (HHKB Fn) + * ,-----------------------------------------------------------. + * |Pwr| F1| F2| F3| F4| F5| F6| F7| F8| F9|F10|F11|F12|Ins|Del| + * |-----------------------------------------------------------| + * |Caps | | | | | | | |Psc|Slk|Pus|Up | |Backs| + * |-----------------------------------------------------------| + * | |VoD|VoU|Mut| | | *| /|Hom|PgU|Lef|Rig|Enter | + * |-----------------------------------------------------------| + * | | | | | | | +| -|End|PgD|Dow| | | + * `-----------------------------------------------------------' + * | |Gui|Alt | Space |Alt |Gui| | | + * `-----------------------------------------------------------' + */ + [1] = + KEYMAP(PWR, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, INS, DEL, \ + CAPS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,PSCR,SLCK,PAUS, UP, TRNS,BSPC, \ + LCTL,VOLD,VOLU,MUTE,TRNS,TRNS,PAST,PSLS,HOME,PGUP,LEFT,RGHT,ENT, \ + LSFT,TRNS,TRNS,TRNS,TRNS,TRNS,PPLS,PMNS,END, PGDN,DOWN,RSFT,TRNS, \ + TRNS,LGUI,LALT, TRNS, RALT,RGUI,TRNS,TRNS), + + /* Layer 2: Vi mode[Slash] + * ,-----------------------------------------------------------. + * |Esc| F1| F2| F3| F4| F5| F6| F7| F8| F9|F10|F11|F12|Ins|Del| + * |-----------------------------------------------------------| + * |Tab |Hom|PgD|Up |PgU|End|Hom|PgD|PgUlEnd| | | |Backs| + * |-----------------------------------------------------------| + * |Contro| |Lef|Dow|Rig| |Lef|Dow|Up |Rig| | |Return | + * |-----------------------------------------------------------| + * |Shift | | | | | |Hom|PgD|PgUlEnd|Fn0|Shift | | + * `-----------------------------------------------------------' + * |Gui|Alt | Space |Alt |Gui| + * `-------------------------------------------' + */ + [2] = \ + KEYMAP(GRV, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, INS, DEL, \ + TAB, HOME,PGDN,UP, PGUP,END, HOME,PGDN,PGUP,END, NO, NO, NO, BSPC, \ + LCTL,NO, LEFT,DOWN,RGHT,NO, LEFT,DOWN,UP, RGHT,NO, NO, ENT, \ + LSFT,NO, NO, NO, NO, NO, HOME,PGDN,PGUP,END, FN2, RSFT,TRNS, \ + TRNS,LGUI,LALT, SPC, RALT,RGUI,TRNS,TRNS), + + /* Layer 3: Mouse mode(IJKL)[Semicolon] + * ,-----------------------------------------------------------. + * |Esc| F1| F2| F3| F4| F5| F6| F7| F8| F9|F10|F11|F12|Ins|Del| + * |-----------------------------------------------------------| + * |Tab | | | | | |MwL|MwD|McU|MwU|MwR|Wbk|Wfr|Alt-T| + * |-----------------------------------------------------------| + * |Contro| | | | | |Mb2|McL|McD|McR|Fn | |Return | + * |-----------------------------------------------------------| + * |Shift | | | | |Mb3|Mb2|Mb1|Mb4|Mb5| |Shift | | + * `-----------------------------------------------------------' + * |Gui |Alt | Mb1 |Fn |Fn | + * `--------------------------------------------' + * Mc: Mouse Cursor / Mb: Mouse Button / Mw: Mouse Wheel + */ + [3] = \ + KEYMAP(GRV, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, INS, DEL, \ + FN8, NO, NO, NO, NO, NO, WH_L,WH_D,MS_U,WH_U,WH_R,FN9, FN10,FN8, \ + LCTL,ACL0,ACL1,ACL2,ACL2,NO, NO, MS_L,MS_D,MS_R,FN3, NO, ENT, \ + LSFT,NO, NO, NO, NO, BTN3,BTN2,BTN1,FN9, FN10,NO, RSFT,TRNS, \ + TRNS,LGUI,LALT, BTN1, TRNS,TRNS,TRNS,TRNS), + + /* Layer 4: Mouse mode(IJKL)[Space] + * ,-----------------------------------------------------------. + * |Esc| F1| F2| F3| F4| F5| F6| F7| F8| F9|F10|F11|F12|Ins|Del| + * |-----------------------------------------------------------| + * |Tab | | | | | |MwL|MwD|McU|MwU|MwR|Wbk|Wfr|Alt-T| + * |-----------------------------------------------------------| + * |Contro| | | | | |Mb2|McL|McD|McR|Mb1| |Return | + * |-----------------------------------------------------------| + * |Shift | | | | |Mb3|Mb2|Mb1|Mb4|Mb5| |Shift | | + * `-----------------------------------------------------------' + * |Gui |Alt | Mb1 |Fn |Fn | + * `--------------------------------------------' + * Mc: Mouse Cursor / Mb: Mouse Button / Mw: Mouse Wheel + */ + [4] = \ + KEYMAP(GRV, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, INS, DEL, \ + FN8, NO, NO, NO, NO, NO, WH_L,WH_D,MS_U,WH_U,WH_R,BTN4,BTN5,FN8, \ + LCTL,VOLD,VOLU,MUTE,NO, NO, NO, MS_L,MS_D,MS_R,BTN1,NO, ENT, \ + LSFT,NO, NO, NO, NO, BTN3,BTN2,BTN1,FN9, FN10,NO, RSFT,TRNS, \ + TRNS,LGUI,LALT, TRNS, TRNS,TRNS,TRNS,TRNS), + +}; + +/* id for user defined functions */ +enum function_id { + LSHIFT_LPAREN, +}; + +enum macro_id { + HELLO, + VOLUP, + ALT_TAB, +}; + +/* + * Fn action definition + */ +const uint16_t fn_actions[] = { + [0] = ACTION_DEFAULT_LAYER_SET(0), // Default layer(not used) + [1] = ACTION_LAYER_TAP_TOGGLE(1), // HHKB layer(toggle with 5 taps) + [2] = ACTION_LAYER_TAP_KEY(2, KC_SLASH), // Cursor layer with Slash* + [3] = ACTION_LAYER_TAP_KEY(3, KC_SCLN), // Mousekey layer with Semicolon* + [4] = ACTION_LAYER_TAP_KEY(4, KC_SPC), // Mousekey layer with Space + [5] = ACTION_LAYER_MOMENTARY(4), // Mousekey layer(IJKL) + [6] = ACTION_MODS_TAP_KEY(MOD_RCTL, KC_ENT), // RControl with tap Enter + [7] = ACTION_MODS_ONESHOT(MOD_LSFT), // Oneshot Shift + [8] = ACTION_MACRO(ALT_TAB), // Application switching + [9] = ACTION_MODS_KEY(MOD_LALT, KC_LEFT), + [10] = ACTION_MODS_KEY(MOD_LALT, KC_RIGHT), +}; + +/* + * Macro definition + */ +const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) +{ + switch (id) { + case HELLO: + return (record->event.pressed ? + MACRO( I(0), T(H), T(E), T(L), T(L), W(255), T(O), END ) : + MACRO_NONE ); + case VOLUP: + return (record->event.pressed ? + MACRO( D(VOLU), U(VOLU), END ) : + MACRO_NONE ); + case ALT_TAB: + return (record->event.pressed ? + MACRO( D(LALT), D(TAB), END ) : + MACRO( U(TAB), END )); + } + return MACRO_NONE; +} + + + +/* + * user defined action function + */ +void action_function(keyrecord_t *record, uint8_t id, uint8_t opt) +{ + if (record->event.pressed) dprint("P"); else dprint("R"); + dprintf("%d", record->tap.count); + if (record->tap.interrupted) dprint("i"); + dprint("\n"); + + switch (id) { + case LSHIFT_LPAREN: + // Shift parentheses example: LShft + tap '(' + // http://stevelosh.com/blog/2012/10/a-modern-space-cadet/#shift-parentheses + // http://geekhack.org/index.php?topic=41989.msg1304899#msg1304899 + if (record->event.pressed) { + if (record->tap.count > 0 && !record->tap.interrupted) { + if (record->tap.interrupted) { + dprint("tap interrupted\n"); + register_mods(MOD_BIT(KC_LSHIFT)); + } + } else { + register_mods(MOD_BIT(KC_LSHIFT)); + } + } else { + if (record->tap.count > 0 && !(record->tap.interrupted)) { + add_weak_mods(MOD_BIT(KC_LSHIFT)); + send_keyboard_report(); + register_code(KC_9); + unregister_code(KC_9); + del_weak_mods(MOD_BIT(KC_LSHIFT)); + send_keyboard_report(); + record->tap.count = 0; // ad hoc: cancel tap + } else { + unregister_mods(MOD_BIT(KC_LSHIFT)); + } + } + break; + } +} From 8bcddd24f7320344aba9c9e757be2a7d516f4faa Mon Sep 17 00:00:00 2001 From: Jun Wako Date: Sat, 17 Oct 2015 15:28:21 +0900 Subject: [PATCH 33/56] Fix drop key stroke Keyboard report should be checked if its transfer finishs successfully. Otherwise key stroke can be missing when other key event occurs before the last report transfer is done. Boot protocol 10ms interval probably causes this problem in case it receives key events in a row within the period. NKRO protocol suffers less or nothing due to its interval 1ms. --- tmk_core/protocol/chibios/usb_main.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 619b798b..0fc2f6e3 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -1121,19 +1121,24 @@ void send_keyboard(report_keyboard_t *report) { } osalSysUnlock(); + bool ret; #ifdef NKRO_ENABLE if(keyboard_nkro) { /* NKRO protocol */ usbPrepareTransmit(&USB_DRIVER, NKRO_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t)); - osalSysLock(); - usbStartTransmitI(&USB_DRIVER, NKRO_ENDPOINT); - osalSysUnlock(); + do { + osalSysLock(); + ret = usbStartTransmitI(&USB_DRIVER, NKRO_ENDPOINT); + osalSysUnlock(); + } while (ret); } else #endif /* NKRO_ENABLE */ { /* boot protocol */ usbPrepareTransmit(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)report, KBD_EPSIZE); - osalSysLock(); - usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT); - osalSysUnlock(); + do { + osalSysLock(); + ret = usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT); + osalSysUnlock(); + } while (ret); } keyboard_report_sent = *report; } From 7c4a2fab6d78e2f59f477bf75690a12c233b0ea7 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Sat, 17 Oct 2015 18:18:47 +0100 Subject: [PATCH 34/56] Chibios/usb_main: rename a variable for clarity. --- tmk_core/protocol/chibios/usb_main.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 0fc2f6e3..1ce5b4ae 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -1121,24 +1121,26 @@ void send_keyboard(report_keyboard_t *report) { } osalSysUnlock(); - bool ret; + bool ep_not_ready; #ifdef NKRO_ENABLE if(keyboard_nkro) { /* NKRO protocol */ usbPrepareTransmit(&USB_DRIVER, NKRO_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t)); + /* need to wait until the previous packet has made it through */ do { osalSysLock(); - ret = usbStartTransmitI(&USB_DRIVER, NKRO_ENDPOINT); + ep_not_ready = usbStartTransmitI(&USB_DRIVER, NKRO_ENDPOINT); osalSysUnlock(); - } while (ret); + } while (ep_not_ready); } else #endif /* NKRO_ENABLE */ { /* boot protocol */ usbPrepareTransmit(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)report, KBD_EPSIZE); + /* need to wait until the previous packet has made it through */ do { osalSysLock(); - ret = usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT); + ep_not_ready = usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT); osalSysUnlock(); - } while (ret); + } while (ep_not_ready); } keyboard_report_sent = *report; } From 0914c583518b3d80b668d1bc6265c15c92642047 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Sat, 17 Oct 2015 18:20:12 +0100 Subject: [PATCH 35/56] Add correct chibios/bootloader_jump for infinity KB. --- tmk_core/common/chibios/bootloader.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c index cac52209..fa951128 100644 --- a/tmk_core/common/chibios/bootloader.c +++ b/tmk_core/common/chibios/bootloader.c @@ -23,10 +23,24 @@ void bootloader_jump(void) { #elif defined(KL2x) || defined(K20x) /* STM32_BOOTLOADER_ADDRESS */ /* Kinetis */ +#if defined(KIIBOHD_BOOTLOADER) +/* Kiibohd Bootloader (MCHCK and Infinity KB) */ +#define SCB_AIRCR (*(volatile uint32_t *)0xE000ED0C) // Application Interrupt and Reset Control +#define VBAT (*(volatile uint8_t *)0x4003E000) // VBAT register file (32 bytes) +const uint8_t sys_reset_to_loader_magic[] = "\xff\x00\x7fRESET TO LOADER\x7f\x00\xff"; +void bootloader_jump(void) { + __builtin_memcpy((void *)&VBAT, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic)); + // request reset + SCB_AIRCR = 0x05FA0004; +} + +#else /* defined(KIIBOHD_BOOTLOADER) */ +/* Default for Kinetis - expecting an ARM Teensy */ void bootloader_jump(void) { chThdSleepMilliseconds(100); __BKPT(0); } +#endif /* defined(KIIBOHD_BOOTLOADER) */ #else /* neither STM32 nor KINETIS */ void bootloader_jump(void) {} From 7524f5c9c334103510aeaecd312f955421e6c86a Mon Sep 17 00:00:00 2001 From: flabbergast Date: Sat, 17 Oct 2015 18:23:13 +0100 Subject: [PATCH 36/56] Infinity_chibios: select correct bootloader_jump. --- keyboard/infinity_chibios/bootloader_defs.h | 1 + 1 file changed, 1 insertion(+) create mode 100644 keyboard/infinity_chibios/bootloader_defs.h diff --git a/keyboard/infinity_chibios/bootloader_defs.h b/keyboard/infinity_chibios/bootloader_defs.h new file mode 100644 index 00000000..c67153be --- /dev/null +++ b/keyboard/infinity_chibios/bootloader_defs.h @@ -0,0 +1 @@ +#define KIIBOHD_BOOTLOADER From 44421f5bf4a15e0e0cfdcc18e9008fc242c5308a Mon Sep 17 00:00:00 2001 From: flabbergast Date: Sun, 18 Oct 2015 10:44:08 +0100 Subject: [PATCH 37/56] ChibiOS: make reset request more CMSISy. --- tmk_core/common/chibios/bootloader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c index fa951128..d19aab89 100644 --- a/tmk_core/common/chibios/bootloader.c +++ b/tmk_core/common/chibios/bootloader.c @@ -25,13 +25,13 @@ void bootloader_jump(void) { #if defined(KIIBOHD_BOOTLOADER) /* Kiibohd Bootloader (MCHCK and Infinity KB) */ -#define SCB_AIRCR (*(volatile uint32_t *)0xE000ED0C) // Application Interrupt and Reset Control +#define SCB_AIRCR_VECTKEY_WRITEMAGIC 0x05FA0000 #define VBAT (*(volatile uint8_t *)0x4003E000) // VBAT register file (32 bytes) const uint8_t sys_reset_to_loader_magic[] = "\xff\x00\x7fRESET TO LOADER\x7f\x00\xff"; void bootloader_jump(void) { __builtin_memcpy((void *)&VBAT, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic)); // request reset - SCB_AIRCR = 0x05FA0004; + SCB->AIRCR = SCB_AIRCR_VECTKEY_WRITEMAGIC | SCB_AIRCR_SYSRESETREQ_Msk; } #else /* defined(KIIBOHD_BOOTLOADER) */ From aef7a2973e223bbfc813c97b6244edc403051a55 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Sun, 18 Oct 2015 11:37:27 +0100 Subject: [PATCH 38/56] Chibios: Add breathing sleep LED on Kinetis MCUs. --- tmk_core/common/chibios/sleep_led.c | 143 ++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 7 deletions(-) diff --git a/tmk_core/common/chibios/sleep_led.c b/tmk_core/common/chibios/sleep_led.c index fad7ac04..d9cdeba1 100644 --- a/tmk_core/common/chibios/sleep_led.c +++ b/tmk_core/common/chibios/sleep_led.c @@ -1,23 +1,152 @@ #include "ch.h" +#include "hal.h" #include "led.h" #include "sleep_led.h" +#if defined(KL2x) || defined(K20x) /* platform selection: familiar Kinetis chips */ +/* All right, we go the "software" way: LP timer, toggle LED in interrupt. + * Based on hasu's code for AVRs. + */ + +/* Breathing Sleep LED brighness(PWM On period) table + * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle + * + * http://www.wolframalpha.com/input/?i=%28sin%28+x%2F64*pi%29**8+*+255%2C+x%3D0+to+63 + * (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i } + */ +static const uint8_t breathing_table[64] = { +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 10, +15, 23, 32, 44, 58, 74, 93, 113, 135, 157, 179, 199, 218, 233, 245, 252, +255, 252, 245, 233, 218, 199, 179, 157, 135, 113, 93, 74, 58, 44, 32, 23, +15, 10, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* LP Timer interrupt handler */ +OSAL_IRQ_HANDLER(KINETIS_LPTMR0_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + /* Software PWM + * timer:1111 1111 1111 1111 + * \_____/\/ \_______/____ count(0-255) + * \ \______________ duration of step(4) + * \__________________ index of step table(0-63) + */ + + // this works for cca 65536 irqs/sec + static union { + uint16_t row; + struct { + uint8_t count:8; + uint8_t duration:2; + uint8_t index:6; + } pwm; + } timer = { .row = 0 }; + + timer.row++; + + // LED on + if (timer.pwm.count == 0) { + led_set(1<CSR |= LPTMRx_CSR_TCF; + + OSAL_IRQ_EPILOGUE(); +} + +/* LPTMR clock options */ +#define LPTMR_CLOCK_MCGIRCLK 0 /* 4MHz clock */ +#define LPTMR_CLOCK_LPO 1 /* 1kHz clock */ +#define LPTMR_CLOCK_ERCLK32K 2 +#define LPTMR_CLOCK_OSCERCLK 3 /* output from OSC */ + +/* Work around inconsistencies in Freescale naming */ +#if !defined(SIM_SCGC5_LPTMR) +#define SIM_SCGC5_LPTMR SIM_SCGC5_LPTIMER +#endif + +/* Initialise the timer */ void sleep_led_init(void) { - // we could go the 'software way' -- just enable *some* timer - // and go with callbacks - // or we could go the 'hardware way' -- and use timer output to - // pins directly + /* Make sure the clock to the LPTMR is enabled */ + SIM->SCGC5 |= SIM_SCGC5_LPTMR; + /* Reset LPTMR settings */ + LPTMR0->CSR = 0; + /* Set the compare value */ + LPTMR0->CMR = 1; // trigger on counter value (i.e. every time) + /* Set up clock source and prescaler */ + /* Software PWM + * ______ ______ __ + * | ON |___OFF___| ON |___OFF___| .... + * |<-------------->|<-------------->|<- .... + * PWM period PWM period + * + * R interrupts/period[resolution] + * F periods/second[frequency] + * R * F interrupts/second + */ + /* === OPTION 1 === */ + // for 1kHz LPO + // No prescaler => 1024 irqs/sec + // LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_LPO)|LPTMRx_PSR_PBYP; + /* === OPTION 2 === */ + // for nMHz IRC (n=4 on KL25Z, KL26Z and K20x; n=2 or 8 on KL27Z) + MCG->C2 |= MCG_C2_IRCS; // fast (4MHz) internal ref clock + #if defined(KL27Z) // divide the 8MHz IRC by 2, to have the same MCGIRCLK speed as others + MCG->MC |= MCG_MC_LIRC_DIV2_DIV2; + #endif /* KL27Z */ + MCG->C1 |= MCG_C1_IRCLKEN; // enable internal ref clock + // to work in stop mode, also MCG_C1_IREFSTEN + // Divide 4MHz by 2^N (N=5) => 62500 irqs/sec => + // => approx F=61, R=256, duration = 4 + LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_MCGIRCLK)|LPTMRx_PSR_PRESCALE(5); + /* === OPTION 3 === */ + // for OSC output (external crystal), usually 8MHz or 16MHz + // OSC0->CR |= OSC_CR_ERCLKEN; // enable ext ref clock + // to work in stop mode, also OSC_CR_EREFSTEN + // Divide by 2^N + // LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_OSCERCLK)|LPTMRx_PSR_PRESCALE(7); + /* === END OPTIONS === */ + /* Interrupt on TCF set (compare flag) */ + nvicEnableVector(LPTMR0_IRQn, 2); // vector, priority + LPTMR0->CSR |= LPTMRx_CSR_TIE; } void sleep_led_enable(void) { - led_set(1<CSR |= LPTMRx_CSR_TEN; } void sleep_led_disable(void) { - led_set(0); + /* Disable the timer */ + LPTMR0->CSR &= ~LPTMRx_CSR_TEN; } void sleep_led_toggle(void) { - // not working yet, state not saved anywhere currently + /* Toggle the timer */ + LPTMR0->CSR ^= LPTMRx_CSR_TEN; } + +#else /* platform selection: not on familiar Kinetis chips */ + +void sleep_led_init(void) { +} + +void sleep_led_enable(void) { + led_set(1< Date: Sun, 18 Oct 2015 11:55:39 +0100 Subject: [PATCH 39/56] ChibiOS: Update infinity bootloader code to match updated ChibiOS. --- tmk_core/common/chibios/bootloader.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c index d19aab89..c8888f3c 100644 --- a/tmk_core/common/chibios/bootloader.c +++ b/tmk_core/common/chibios/bootloader.c @@ -26,10 +26,9 @@ void bootloader_jump(void) { #if defined(KIIBOHD_BOOTLOADER) /* Kiibohd Bootloader (MCHCK and Infinity KB) */ #define SCB_AIRCR_VECTKEY_WRITEMAGIC 0x05FA0000 -#define VBAT (*(volatile uint8_t *)0x4003E000) // VBAT register file (32 bytes) const uint8_t sys_reset_to_loader_magic[] = "\xff\x00\x7fRESET TO LOADER\x7f\x00\xff"; void bootloader_jump(void) { - __builtin_memcpy((void *)&VBAT, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic)); + __builtin_memcpy((void *)VBAT, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic)); // request reset SCB->AIRCR = SCB_AIRCR_VECTKEY_WRITEMAGIC | SCB_AIRCR_SYSRESETREQ_Msk; } From c60dd8791808642420eea828b2217ec62fef0429 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Sun, 18 Oct 2015 18:54:34 +0100 Subject: [PATCH 40/56] ChibiOS: prettify/document sleep_led code. --- tmk_core/common/chibios/sleep_led.c | 38 +++++++++++++++++++---------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/tmk_core/common/chibios/sleep_led.c b/tmk_core/common/chibios/sleep_led.c index d9cdeba1..50cf4eb7 100644 --- a/tmk_core/common/chibios/sleep_led.c +++ b/tmk_core/common/chibios/sleep_led.c @@ -22,7 +22,7 @@ static const uint8_t breathing_table[64] = { 15, 10, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -/* LP Timer interrupt handler */ +/* Low Power Timer interrupt handler */ OSAL_IRQ_HANDLER(KINETIS_LPTMR0_IRQ_VECTOR) { OSAL_IRQ_PROLOGUE(); @@ -63,7 +63,7 @@ OSAL_IRQ_HANDLER(KINETIS_LPTMR0_IRQ_VECTOR) { /* LPTMR clock options */ #define LPTMR_CLOCK_MCGIRCLK 0 /* 4MHz clock */ #define LPTMR_CLOCK_LPO 1 /* 1kHz clock */ -#define LPTMR_CLOCK_ERCLK32K 2 +#define LPTMR_CLOCK_ERCLK32K 2 /* external 32kHz crystal */ #define LPTMR_CLOCK_OSCERCLK 3 /* output from OSC */ /* Work around inconsistencies in Freescale naming */ @@ -78,7 +78,8 @@ void sleep_led_init(void) { /* Reset LPTMR settings */ LPTMR0->CSR = 0; /* Set the compare value */ - LPTMR0->CMR = 1; // trigger on counter value (i.e. every time) + LPTMR0->CMR = 0; // trigger on counter value (i.e. every time) + /* Set up clock source and prescaler */ /* Software PWM * ______ ______ __ @@ -90,28 +91,39 @@ void sleep_led_init(void) { * F periods/second[frequency] * R * F interrupts/second */ + /* === OPTION 1 === */ - // for 1kHz LPO + #if 0 + // 1kHz LPO // No prescaler => 1024 irqs/sec - // LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_LPO)|LPTMRx_PSR_PBYP; + // Note: this is too slow for a smooth breathe + LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_LPO)|LPTMRx_PSR_PBYP; + #endif /* OPTION 1 */ + /* === OPTION 2 === */ - // for nMHz IRC (n=4 on KL25Z, KL26Z and K20x; n=2 or 8 on KL27Z) + #if 1 + // nMHz IRC (n=4 on KL25Z, KL26Z and K20x; n=2 or 8 on KL27Z) MCG->C2 |= MCG_C2_IRCS; // fast (4MHz) internal ref clock - #if defined(KL27Z) // divide the 8MHz IRC by 2, to have the same MCGIRCLK speed as others + #if defined(KL27) // divide the 8MHz IRC by 2, to have the same MCGIRCLK speed as others MCG->MC |= MCG_MC_LIRC_DIV2_DIV2; - #endif /* KL27Z */ + #endif /* KL27 */ MCG->C1 |= MCG_C1_IRCLKEN; // enable internal ref clock // to work in stop mode, also MCG_C1_IREFSTEN - // Divide 4MHz by 2^N (N=5) => 62500 irqs/sec => + // Divide 4MHz by 2^N (N=6) => 62500 irqs/sec => // => approx F=61, R=256, duration = 4 - LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_MCGIRCLK)|LPTMRx_PSR_PRESCALE(5); + LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_MCGIRCLK)|LPTMRx_PSR_PRESCALE(6); + #endif /* OPTION 2 */ + /* === OPTION 3 === */ - // for OSC output (external crystal), usually 8MHz or 16MHz - // OSC0->CR |= OSC_CR_ERCLKEN; // enable ext ref clock + #if 0 + // OSC output (external crystal), usually 8MHz or 16MHz + OSC0->CR |= OSC_CR_ERCLKEN; // enable ext ref clock // to work in stop mode, also OSC_CR_EREFSTEN // Divide by 2^N - // LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_OSCERCLK)|LPTMRx_PSR_PRESCALE(7); + LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_OSCERCLK)|LPTMRx_PSR_PRESCALE(7); + #endif /* OPTION 3 */ /* === END OPTIONS === */ + /* Interrupt on TCF set (compare flag) */ nvicEnableVector(LPTMR0_IRQn, 2); // vector, priority LPTMR0->CSR |= LPTMRx_CSR_TIE; From df1b665a3cc5fdab60a0f6520a34d64da8f9f764 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Tue, 10 Nov 2015 20:54:01 +0000 Subject: [PATCH 41/56] Infinity_chibios: improve comments. --- keyboard/infinity_chibios/Makefile | 8 +++++--- keyboard/infinity_chibios/config.h | 5 ++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/keyboard/infinity_chibios/Makefile b/keyboard/infinity_chibios/Makefile index 6fca4890..9999451c 100644 --- a/keyboard/infinity_chibios/Makefile +++ b/keyboard/infinity_chibios/Makefile @@ -25,6 +25,7 @@ CONFIG_H = config.h # /os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES) # - For Teensies, FAMILY = KINETIS and SERIES is either # KL2x (LC) or K20x (3.0,3.1,3.2). +# - For Infinity KB, SERIES = K20x MCU_FAMILY = KINETIS MCU_SERIES = K20x @@ -36,14 +37,14 @@ MCU_SERIES = K20x # - MKL26Z64 for Teensy LC # - MK20DX128 for Teensy 3.0 # - MK20DX256 for Teensy 3.1 and 3.2 -#MCU_LDSCRIPT = MK20DX128 +# - MK20DX128BLDR4 for Infinity with Kiibohd bootloader MCU_LDSCRIPT = MK20DX128BLDR4 # Startup code to use # - it should exist in /os/common/ports/ARMCMx/compilers/GCC/mk/ # - STARTUP = # - kl2x for Teensy LC -# - k20x5 for Teensy 3.0 +# - k20x5 for Teensy 3.0 and Infinity KB # - k20x7 for Teensy 3.1 and 3.2 MCU_STARTUP = k20x5 @@ -53,6 +54,7 @@ MCU_STARTUP = k20x5 # - PJRC_TEENSY_LC for Teensy LC # - PJRC_TEENSY_3 for Teensy 3.0 # - PJRC_TEENSY_3_1 for Teensy 3.1 or 3.2 +# - MCHCK_K20 for Infinity KB BOARD = MCHCK_K20 # Cortex version @@ -65,9 +67,9 @@ ARMV = 7 # Vector table for application # 0x00000000-0x00001000 area is occupied by bootlaoder.*/ +# The CORTEX_VTOR... is needed only for MCHCK/Infinity KB OPT_DEFS = -DCORTEX_VTOR_INIT=0x00001000 - # Build Options # comment out to disable the options. # diff --git a/keyboard/infinity_chibios/config.h b/keyboard/infinity_chibios/config.h index b4b7fcd3..cb902220 100644 --- a/keyboard/infinity_chibios/config.h +++ b/keyboard/infinity_chibios/config.h @@ -27,9 +27,8 @@ along with this program. If not, see . /* at most 32 characters or the ugly hack in usb_main.c borks */ #define MANUFACTURER "TMK" #define USBSTR_MANUFACTURER 'T', '\x00', 'M', '\x00', 'K', '\x00', ' ', '\x00', '\xc6', '\x00' -#define PRODUCT "ChibiOS TMK test" -#define USBSTR_PRODUCT 'C', '\x00', 'h', '\x00', 'i', '\x00', 'b', '\x00', 'i', '\x00', 'O', '\x00', 'S', '\x00', ' ', '\x00', 'T', '\x00', 'M', '\x00', 'K', '\x00', ' ', '\x00', 't', '\x00', 'e', '\x00', 's', '\x00', 't', '\x00' -#define DESCRIPTION "TMK keyboard firmware over ChibiOS" +#define PRODUCT "Infinity keyboard/TMK" +#define USBSTR_PRODUCT 'I', '\x00', 'n', '\x00', 'f', '\x00', 'i', '\x00', 'n', '\x00', 'i', '\x00', 't', '\x00', 'y', '\x00', ' ', '\x00', 'k', '\x00', 'e', '\x00', 'y', '\x00', 'b', '\x00', 'o', '\x00', 'a', '\x00', 'r', '\x00', 'd', '\x00', '/', '\x00', 'T', '\x00', 'M', '\x00', 'K', '\x00' /* key matrix size */ #define MATRIX_ROWS 9 From 41f5be3b7a145b71fdb14f6a6912d7fec874e515 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Sat, 14 Nov 2015 09:17:01 +0000 Subject: [PATCH 42/56] Chibios: Remove the wait in the main loop. --- tmk_core/protocol/chibios/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c index 9607f1a9..b62cfa9c 100644 --- a/tmk_core/protocol/chibios/main.c +++ b/tmk_core/protocol/chibios/main.c @@ -135,6 +135,5 @@ int main(void) { } keyboard_task(); - chThdSleepMilliseconds(5); } } From 39c0c27587a76e94996f2f17fef94c8b90c73bf6 Mon Sep 17 00:00:00 2001 From: Aurelien Bondis Date: Mon, 30 Nov 2015 17:36:15 +0000 Subject: [PATCH 43/56] Add generic STM32F103C8T6 example. --- keyboard/stm32_f103_onekey/Makefile | 80 +++ .../boards/GENERIC_STM32_F103/board.c | 49 ++ .../boards/GENERIC_STM32_F103/board.h | 166 ++++++ .../boards/GENERIC_STM32_F103/board.mk | 5 + .../GENERIC_STM32_F103/mini_stm32_mapping.png | Bin 0 -> 162908 bytes keyboard/stm32_f103_onekey/bootloader_defs.h | 10 + keyboard/stm32_f103_onekey/chconf.h | 503 ++++++++++++++++++ keyboard/stm32_f103_onekey/config.h | 74 +++ keyboard/stm32_f103_onekey/flash.sh | 2 + keyboard/stm32_f103_onekey/halconf.h | 334 ++++++++++++ keyboard/stm32_f103_onekey/keymap_plain.c | 44 ++ keyboard/stm32_f103_onekey/ld/STM32F103x8.ld | 53 ++ .../ld/STM32F103x8_stm32duino_bootloader.ld | 53 ++ keyboard/stm32_f103_onekey/led.c | 32 ++ keyboard/stm32_f103_onekey/matrix.c | 163 ++++++ keyboard/stm32_f103_onekey/mcuconf.h | 172 ++++++ 16 files changed, 1740 insertions(+) create mode 100644 keyboard/stm32_f103_onekey/Makefile create mode 100644 keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.c create mode 100644 keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.h create mode 100644 keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.mk create mode 100644 keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/mini_stm32_mapping.png create mode 100644 keyboard/stm32_f103_onekey/bootloader_defs.h create mode 100644 keyboard/stm32_f103_onekey/chconf.h create mode 100644 keyboard/stm32_f103_onekey/config.h create mode 100755 keyboard/stm32_f103_onekey/flash.sh create mode 100644 keyboard/stm32_f103_onekey/halconf.h create mode 100644 keyboard/stm32_f103_onekey/keymap_plain.c create mode 100644 keyboard/stm32_f103_onekey/ld/STM32F103x8.ld create mode 100644 keyboard/stm32_f103_onekey/ld/STM32F103x8_stm32duino_bootloader.ld create mode 100644 keyboard/stm32_f103_onekey/led.c create mode 100644 keyboard/stm32_f103_onekey/matrix.c create mode 100644 keyboard/stm32_f103_onekey/mcuconf.h diff --git a/keyboard/stm32_f103_onekey/Makefile b/keyboard/stm32_f103_onekey/Makefile new file mode 100644 index 00000000..ea2ee08b --- /dev/null +++ b/keyboard/stm32_f103_onekey/Makefile @@ -0,0 +1,80 @@ +# Target file name (without extension). +PROJECT = ch +TARGET = alps64 + +# Directory common source files exist +TMK_DIR = ../../tmk_core + +# Directory keyboard dependent files exist +TARGET_DIR = . + +# project specific files +SRC = matrix.c \ + led.c + +ifdef KEYMAP + SRC := keymap_$(KEYMAP).c $(SRC) +else + SRC := keymap_plain.c $(SRC) +endif + +CONFIG_H = config.h + +# GENERIC STM32F103C8T6 board - stm32duino bootloader +# OPT_DEFS = -DCORTEX_VTOR_INIT=0x2000 +# MCU_LDSCRIPT = STM32F103x8_stm32duino_bootloader +# BOARD = GENERIC_STM32_F103 + +# GENERIC STM32F103C8T6 board - no bootloader (programmer over serial or SWD) +OPT_DEFS = +MCU_LDSCRIPT = STM32F103x8 +BOARD = GENERIC_STM32_F103 + +# MAPLE MINI +# OPT_DEFS = -DCORTEX_VTOR_INIT=0x5000 +# MCU_LDSCRIPT = STM32F103xE_maplemini_bootloader.ld +# BOARD = MAPLEMINI_STM32_F103 + +## chip/board settings +# the next two should match the directories in +# /os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES) +MCU_FAMILY = STM32 +MCU_SERIES = STM32F1xx +# linker script to use +# it should exist either in /os/common/ports/ARMCMx/compilers/GCC/ld/ +# or /ld/ +# startup code to use +# is should exist in /os/common/ports/ARMCMx/compilers/GCC/mk/ +MCU_STARTUP = stm32f1xx +# it should exist either in /os/hal/boards/ +# or /boards +# Cortex version +# Teensy LC is cortex-m0; Teensy 3.x are cortex-m4 +MCU = cortex-m3 +# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 +ARMV = 7 +# If you want to be able to jump to bootloader from firmware on STM32 MCUs, +# set the correct BOOTLOADER_ADDRESS. Either set it here, or define it in +# ./bootloader_defs.h or in ./boards//bootloader_defs.h (if you have +# a custom board definition that you plan to reuse). +# If you're not setting it here, leave it commented out. +# It is chip dependent, the correct number can be looked up here (page 175): +# http://www.st.com/web/en/resource/technical/document/application_note/CD00167594.pdf +# This also requires a patch to chibios: +# /tmk_core/tool/chibios/ch-bootloader-jump.patch +#STM32_BOOTLOADER_ADDRESS = 0x1FFFC800 + +# Build Options +# comment out to disable the options. +# +#BOOTMAGIC_ENABLE = yes # Virtual DIP switch configuration +## BOOTMAGIC is not supported on STM32 chips yet. +MOUSEKEY_ENABLE = yes # Mouse keys +EXTRAKEY_ENABLE = yes # Audio control and System control +CONSOLE_ENABLE = yes # Console for debug +COMMAND_ENABLE = yes # Commands for debug and configuration +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend +NKRO_ENABLE = yes # USB Nkey Rollover + +include $(TMK_DIR)/tool/chibios/common.mk +include $(TMK_DIR)/tool/chibios/chibios.mk diff --git a/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.c b/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.c new file mode 100644 index 00000000..31319ef5 --- /dev/null +++ b/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.c @@ -0,0 +1,49 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "hal.h" + +/** + * @brief PAL setup. + * @details Digital I/O ports static configuration as defined in @p board.h. + * This variable is used by the HAL when initializing the PAL driver. + */ +#if HAL_USE_PAL || defined(__DOXYGEN__) +const PALConfig pal_default_config = +{ + {VAL_GPIOAODR, VAL_GPIOACRL, VAL_GPIOACRH}, + {VAL_GPIOBODR, VAL_GPIOBCRL, VAL_GPIOBCRH}, + {VAL_GPIOCODR, VAL_GPIOCCRL, VAL_GPIOCCRH}, + {VAL_GPIODODR, VAL_GPIODCRL, VAL_GPIODCRH}, + {VAL_GPIOEODR, VAL_GPIOECRL, VAL_GPIOECRH}, +}; +#endif + +/* + * Early initialization code. + * This initialization must be performed just after stack setup and before + * any other initialization. + */ +void __early_init(void) { + + stm32_clock_init(); +} + +/* + * Board-specific initialization code. + */ +void boardInit(void) { +} diff --git a/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.h b/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.h new file mode 100644 index 00000000..ef7318ee --- /dev/null +++ b/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.h @@ -0,0 +1,166 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef _BOARD_H_ +#define _BOARD_H_ + +/* + * Setup for a Generic STM32F103 board. + */ + +/* + * Board identifier. + */ +#define BOARD_GENERIC_STM32_F103 +#define BOARD_NAME "Generic STM32F103x board" + +/* + * Board frequencies. + */ +#define STM32_LSECLK 32768 +#define STM32_HSECLK 8000000 + +/* + * MCU type, supported types are defined in ./os/hal/platforms/hal_lld.h. + */ +#define STM32F103xB + +/* + * IO pins assignments + */ + +/* on-board */ + +#define GPIOC_LED 13 +#define GPIOD_OSC_IN 0 +#define GPIOD_OSC_OUT 1 + +/* In case your board has a "USB enable" hardware + controlled by a pin, define it here. (It could be just + a 1.5k resistor connected to D+ line.) +*/ +/* +#define GPIOB_USB_DISC 10 +*/ + +/* + * I/O ports initial setup, this configuration is established soon after reset + * in the initialization code. + * + * The digits have the following meaning: + * 0 - Analog input. + * 1 - Push Pull output 10MHz. + * 2 - Push Pull output 2MHz. + * 3 - Push Pull output 50MHz. + * 4 - Digital input. + * 5 - Open Drain output 10MHz. + * 6 - Open Drain output 2MHz. + * 7 - Open Drain output 50MHz. + * 8 - Digital input with PullUp or PullDown resistor depending on ODR. + * 9 - Alternate Push Pull output 10MHz. + * A - Alternate Push Pull output 2MHz. + * B - Alternate Push Pull output 50MHz. + * C - Reserved. + * D - Alternate Open Drain output 10MHz. + * E - Alternate Open Drain output 2MHz. + * F - Alternate Open Drain output 50MHz. + * Please refer to the STM32 Reference Manual for details. + */ + +/* + * Port A setup. + * Everything input with pull-up except: + * PA2 - Alternate output (USART2 TX). + * PA3 - Normal input (USART2 RX). + * PA9 - Alternate output (USART1 TX). + * PA10 - Normal input (USART1 RX). + */ +#define VAL_GPIOACRL 0x88884B88 /* PA7...PA0 */ +#define VAL_GPIOACRH 0x888884B8 /* PA15...PA8 */ +#define VAL_GPIOAODR 0xFFFFFFFF + +/* + * Port B setup. + * Everything input with pull-up except: + * PB10 - Push Pull output (USB switch). + */ +#define VAL_GPIOBCRL 0x88888888 /* PB7...PB0 */ +#define VAL_GPIOBCRH 0x88888388 /* PB15...PB8 */ +#define VAL_GPIOBODR 0xFFFFFFFF + +/* + * Port C setup. + * Everything input with pull-up except: + * PC13 - Push Pull output (LED). + */ +#define VAL_GPIOCCRL 0x88888888 /* PC7...PC0 */ +#define VAL_GPIOCCRH 0x88388888 /* PC15...PC8 */ +#define VAL_GPIOCODR 0xFFFFFFFF + +/* + * Port D setup. + * Everything input with pull-up except: + * PD0 - Normal input (XTAL). + * PD1 - Normal input (XTAL). + */ +#define VAL_GPIODCRL 0x88888844 /* PD7...PD0 */ +#define VAL_GPIODCRH 0x88888888 /* PD15...PD8 */ +#define VAL_GPIODODR 0xFFFFFFFF + +/* + * Port E setup. + * Everything input with pull-up except: + */ +#define VAL_GPIOECRL 0x88888888 /* PE7...PE0 */ +#define VAL_GPIOECRH 0x88888888 /* PE15...PE8 */ +#define VAL_GPIOEODR 0xFFFFFFFF + +/* + * USB bus activation macro, required by the USB driver. + */ +/* The point is that most of the generic STM32F103* boards + have a 1.5k resistor connected on one end to the D+ line + and on the other end to some pin. Or even a slightly more + complicated "USB enable" circuit, controlled by a pin. + That should go here. + + However on some boards (e.g. one that I have), there's no + such hardware. In which case it's better to not do anything. +*/ +/* +#define usb_lld_connect_bus(usbp) palClearPad(GPIOB, GPIOB_USB_DISC) +*/ +#define usb_lld_connect_bus(usbp) + +/* + * USB bus de-activation macro, required by the USB driver. + */ +/* +#define usb_lld_disconnect_bus(usbp) palSetPad(GPIOB, GPIOB_USB_DISC) +*/ +#define usb_lld_disconnect_bus(usbp) + +#if !defined(_FROM_ASM_) +#ifdef __cplusplus +extern "C" { +#endif + void boardInit(void); +#ifdef __cplusplus +} +#endif +#endif /* _FROM_ASM_ */ + +#endif /* _BOARD_H_ */ diff --git a/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.mk b/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.mk new file mode 100644 index 00000000..a87d78b6 --- /dev/null +++ b/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.mk @@ -0,0 +1,5 @@ +# List of all the board related files. +BOARDSRC = ./boards/GENERIC_STM32_F103/board.c + +# Required include directories +BOARDINC = ./boards/GENERIC_STM32_F103 diff --git a/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/mini_stm32_mapping.png b/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/mini_stm32_mapping.png new file mode 100644 index 0000000000000000000000000000000000000000..c44a7d9ebcbf5f50ce5ad9a191bcf50cae2b2522 GIT binary patch literal 162908 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%0A*1RQ~AE_7CD2*!ePO3Ptc0b3ThU{N^mV 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 z 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/tmk_core/tool/chibios/ch-bootloader-jump.patch + */ + +// STM32F103* does NOT have an USB bootloader in ROM (only serial), +// so setting anything here does not make much sense +// #define STM32_BOOTLOADER_ADDRESS 0x1FFFC800 diff --git a/keyboard/stm32_f103_onekey/chconf.h b/keyboard/stm32_f103_onekey/chconf.h new file mode 100644 index 00000000..689d4b39 --- /dev/null +++ b/keyboard/stm32_f103_onekey/chconf.h @@ -0,0 +1,503 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file templates/chconf.h + * @brief Configuration file template. + * @details A copy of this file must be placed in each project directory, it + * contains the application specific kernel settings. + * + * @addtogroup config + * @details Kernel related settings and hooks. + * @{ + */ + +#ifndef _CHCONF_H_ +#define _CHCONF_H_ + +/*===========================================================================*/ +/** + * @name System timers settings + * @{ + */ +/*===========================================================================*/ + +/** + * @brief System time counter resolution. + * @note Allowed values are 16 or 32 bits. + */ +#define CH_CFG_ST_RESOLUTION 16 + +/** + * @brief System tick frequency. + * @details Frequency of the system timer that drives the system ticks. This + * setting also defines the system tick time unit. + */ +#define CH_CFG_ST_FREQUENCY 10000 + +/** + * @brief Time delta constant for the tick-less mode. + * @note If this value is zero then the system uses the classic + * periodic tick. This value represents the minimum number + * of ticks that is safe to specify in a timeout directive. + * The value one is not valid, timeouts are rounded up to + * this value. + */ +#define CH_CFG_ST_TIMEDELTA 2 + +/** @} */ + +/*===========================================================================*/ +/** + * @name Kernel parameters and options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Round robin interval. + * @details This constant is the number of system ticks allowed for the + * threads before preemption occurs. Setting this value to zero + * disables the preemption for threads with equal priority and the + * round robin becomes cooperative. Note that higher priority + * threads can still preempt, the kernel is always preemptive. + * @note Disabling the round robin preemption makes the kernel more compact + * and generally faster. + * @note The round robin preemption is not supported in tickless mode and + * must be set to zero in that case. + */ +#define CH_CFG_TIME_QUANTUM 0 + +/** + * @brief Managed RAM size. + * @details Size of the RAM area to be managed by the OS. If set to zero + * then the whole available RAM is used. The core memory is made + * available to the heap allocator and/or can be used directly through + * the simplified core memory allocator. + * + * @note In order to let the OS manage the whole RAM the linker script must + * provide the @p __heap_base__ and @p __heap_end__ symbols. + * @note Requires @p CH_CFG_USE_MEMCORE. + */ +#define CH_CFG_MEMCORE_SIZE 0 + +/** + * @brief Idle thread automatic spawn suppression. + * @details When this option is activated the function @p chSysInit() + * does not spawn the idle thread. The application @p main() + * function becomes the idle thread and must implement an + * infinite loop. + */ +#define CH_CFG_NO_IDLE_THREAD FALSE + +/* Use __WFI in the idle thread for waiting. Does lower the power + * consumption. */ +#define CORTEX_ENABLE_WFI_IDLE TRUE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Performance options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief OS optimization. + * @details If enabled then time efficient rather than space efficient code + * is used when two possible implementations exist. + * + * @note This is not related to the compiler optimization options. + * @note The default is @p TRUE. + */ +#define CH_CFG_OPTIMIZE_SPEED FALSE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Subsystem options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Time Measurement APIs. + * @details If enabled then the time measurement APIs are included in + * the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_TM FALSE + +/** + * @brief Threads registry APIs. + * @details If enabled then the registry APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_REGISTRY TRUE + +/** + * @brief Threads synchronization APIs. + * @details If enabled then the @p chThdWait() function is included in + * the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_WAITEXIT TRUE + +/** + * @brief Semaphores APIs. + * @details If enabled then the Semaphores APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_SEMAPHORES TRUE + +/** + * @brief Semaphores queuing mode. + * @details If enabled then the threads are enqueued on semaphores by + * priority rather than in FIFO order. + * + * @note The default is @p FALSE. Enable this if you have special + * requirements. + * @note Requires @p CH_CFG_USE_SEMAPHORES. + */ +#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE + +/** + * @brief Mutexes APIs. + * @details If enabled then the mutexes APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MUTEXES TRUE + +/** + * @brief Enables recursive behavior on mutexes. + * @note Recursive mutexes are heavier and have an increased + * memory footprint. + * + * @note The default is @p FALSE. + * @note Requires @p CH_CFG_USE_MUTEXES. + */ +#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE + +/** + * @brief Conditional Variables APIs. + * @details If enabled then the conditional variables APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_MUTEXES. + */ +#define CH_CFG_USE_CONDVARS TRUE + +/** + * @brief Conditional Variables APIs with timeout. + * @details If enabled then the conditional variables APIs with timeout + * specification are included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_CONDVARS. + */ +#define CH_CFG_USE_CONDVARS_TIMEOUT FALSE + +/** + * @brief Events Flags APIs. + * @details If enabled then the event flags APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_EVENTS TRUE + +/** + * @brief Events Flags APIs with timeout. + * @details If enabled then the events APIs with timeout specification + * are included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_EVENTS. + */ +#define CH_CFG_USE_EVENTS_TIMEOUT TRUE + +/** + * @brief Synchronous Messages APIs. + * @details If enabled then the synchronous messages APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MESSAGES TRUE + +/** + * @brief Synchronous Messages queuing mode. + * @details If enabled then messages are served by priority rather than in + * FIFO order. + * + * @note The default is @p FALSE. Enable this if you have special + * requirements. + * @note Requires @p CH_CFG_USE_MESSAGES. + */ +#define CH_CFG_USE_MESSAGES_PRIORITY FALSE + +/** + * @brief Mailboxes APIs. + * @details If enabled then the asynchronous messages (mailboxes) APIs are + * included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_SEMAPHORES. + */ +#define CH_CFG_USE_MAILBOXES TRUE + +/** + * @brief I/O Queues APIs. + * @details If enabled then the I/O queues APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_QUEUES TRUE + +/** + * @brief Core Memory Manager APIs. + * @details If enabled then the core memory manager APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MEMCORE FALSE + +/** + * @brief Heap Allocator APIs. + * @details If enabled then the memory heap allocator APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or + * @p CH_CFG_USE_SEMAPHORES. + * @note Mutexes are recommended. + */ +#define CH_CFG_USE_HEAP FALSE + +/** + * @brief Memory Pools Allocator APIs. + * @details If enabled then the memory pools allocator APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#define CH_CFG_USE_MEMPOOLS FALSE + +/** + * @brief Dynamic Threads APIs. + * @details If enabled then the dynamic threads creation APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_CFG_USE_WAITEXIT. + * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS. + */ +#define CH_CFG_USE_DYNAMIC FALSE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Debug options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Debug option, kernel statistics. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_STATISTICS FALSE + +/** + * @brief Debug option, system state check. + * @details If enabled the correct call protocol for system APIs is checked + * at runtime. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_SYSTEM_STATE_CHECK FALSE + +/** + * @brief Debug option, parameters checks. + * @details If enabled then the checks on the API functions input + * parameters are activated. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_ENABLE_CHECKS FALSE + +/** + * @brief Debug option, consistency checks. + * @details If enabled then all the assertions in the kernel code are + * activated. This includes consistency checks inside the kernel, + * runtime anomalies and port-defined checks. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_ENABLE_ASSERTS FALSE + +/** + * @brief Debug option, trace buffer. + * @details If enabled then the context switch circular trace buffer is + * activated. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_ENABLE_TRACE FALSE + +/** + * @brief Debug option, stack checks. + * @details If enabled then a runtime stack check is performed. + * + * @note The default is @p FALSE. + * @note The stack check is performed in a architecture/port dependent way. + * It may not be implemented or some ports. + * @note The default failure mode is to halt the system with the global + * @p panic_msg variable set to @p NULL. + */ +#define CH_DBG_ENABLE_STACK_CHECK FALSE + +/** + * @brief Debug option, stacks initialization. + * @details If enabled then the threads working area is filled with a byte + * value when a thread is created. This can be useful for the + * runtime measurement of the used stack. + * + * @note The default is @p FALSE. + */ +#define CH_DBG_FILL_THREADS FALSE + +/** + * @brief Debug option, threads profiling. + * @details If enabled then a field is added to the @p thread_t structure that + * counts the system ticks occurred while executing the thread. + * + * @note The default is @p FALSE. + * @note This debug option is not currently compatible with the + * tickless mode. + */ +#define CH_DBG_THREADS_PROFILING FALSE + +/** @} */ + +/*===========================================================================*/ +/** + * @name Kernel hooks + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Threads descriptor structure extension. + * @details User fields added to the end of the @p thread_t structure. + */ +#define CH_CFG_THREAD_EXTRA_FIELDS \ + /* Add threads custom fields here.*/ + +/** + * @brief Threads initialization hook. + * @details User initialization code added to the @p chThdInit() API. + * + * @note It is invoked from within @p chThdInit() and implicitly from all + * the threads creation APIs. + */ +#define CH_CFG_THREAD_INIT_HOOK(tp) { \ + /* Add threads initialization code here.*/ \ +} + +/** + * @brief Threads finalization hook. + * @details User finalization code added to the @p chThdExit() API. + * + * @note It is inserted into lock zone. + * @note It is also invoked when the threads simply return in order to + * terminate. + */ +#define CH_CFG_THREAD_EXIT_HOOK(tp) { \ + /* Add threads finalization code here.*/ \ +} + +/** + * @brief Context switch hook. + * @details This hook is invoked just before switching between threads. + */ +#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \ + /* Context switch code here.*/ \ +} + +/** + * @brief Idle thread enter hook. + * @note This hook is invoked within a critical zone, no OS functions + * should be invoked from here. + * @note This macro can be used to activate a power saving mode. + */ +#define CH_CFG_IDLE_ENTER_HOOK() { \ +} + +/** + * @brief Idle thread leave hook. + * @note This hook is invoked within a critical zone, no OS functions + * should be invoked from here. + * @note This macro can be used to deactivate a power saving mode. + */ +#define CH_CFG_IDLE_LEAVE_HOOK() { \ +} + +/** + * @brief Idle Loop hook. + * @details This hook is continuously invoked by the idle thread loop. + */ +#define CH_CFG_IDLE_LOOP_HOOK() { \ + /* Idle loop code here.*/ \ +} + +/** + * @brief System tick event hook. + * @details This hook is invoked in the system tick handler immediately + * after processing the virtual timers queue. + */ +#define CH_CFG_SYSTEM_TICK_HOOK() { \ + /* System tick event code here.*/ \ +} + +/** + * @brief System halt hook. + * @details This hook is invoked in case to a system halting error before + * the system is halted. + */ +#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \ + /* System halt code here.*/ \ +} + +/** @} */ + +/*===========================================================================*/ +/* Port-specific settings (override port settings defaulted in chcore.h). */ +/*===========================================================================*/ + +#endif /* _CHCONF_H_ */ + +/** @} */ diff --git a/keyboard/stm32_f103_onekey/config.h b/keyboard/stm32_f103_onekey/config.h new file mode 100644 index 00000000..8063c57e --- /dev/null +++ b/keyboard/stm32_f103_onekey/config.h @@ -0,0 +1,74 @@ +/* +Copyright 2015 Jun Wako + +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 2 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 . +*/ + +#ifndef CONFIG_H +#define CONFIG_H + + +/* USB Device descriptor parameter */ +#define VENDOR_ID 0xFEED +#define PRODUCT_ID 0x6464 +#define DEVICE_VER 0x0001 +/* in python2: list(u"whatever".encode('utf-16-le')) */ +/* at most 32 characters or the ugly hack in usb_main.c borks */ +#define MANUFACTURER "TMK" +#define USBSTR_MANUFACTURER 'T', '\x00', 'M', '\x00', 'K', '\x00', ' ', '\x00', '\xc6', '\x00' +#define PRODUCT "ChibiOS TMK test" +#define USBSTR_PRODUCT 'C', '\x00', 'h', '\x00', 'i', '\x00', 'b', '\x00', 'i', '\x00', 'O', '\x00', 'S', '\x00', ' ', '\x00', 'T', '\x00', 'M', '\x00', 'K', '\x00', ' ', '\x00', 't', '\x00', 'e', '\x00', 's', '\x00', 't', '\x00' +#define DESCRIPTION "TMK keyboard firmware over ChibiOS" + +/* key matrix size */ +#define MATRIX_ROWS 1 +#define MATRIX_COLS 1 + +/* define if matrix has ghost */ +//#define MATRIX_HAS_GHOST + +/* Set 0 if debouncing isn't needed */ +#define DEBOUNCE 5 + +/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */ +#define LOCKING_SUPPORT_ENABLE +/* Locking resynchronize hack */ +#define LOCKING_RESYNC_ENABLE + +/* key combination for command */ +#define IS_COMMAND() ( \ + keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \ +) + + + +/* + * Feature disable options + * These options are also useful to firmware size reduction. + */ + +/* disable debug print */ +//#define NO_DEBUG + +/* disable print */ +//#define NO_PRINT + +/* disable action features */ +//#define NO_ACTION_LAYER +//#define NO_ACTION_TAPPING +//#define NO_ACTION_ONESHOT +//#define NO_ACTION_MACRO +//#define NO_ACTION_FUNCTION + +#endif diff --git a/keyboard/stm32_f103_onekey/flash.sh b/keyboard/stm32_f103_onekey/flash.sh new file mode 100755 index 00000000..15501dfa --- /dev/null +++ b/keyboard/stm32_f103_onekey/flash.sh @@ -0,0 +1,2 @@ +#!/bin/bash +Arduino_STM32_usb_hid/tools/linux/maple_upload ttyACM0 2 1EAF:0003 build/ch.bin diff --git a/keyboard/stm32_f103_onekey/halconf.h b/keyboard/stm32_f103_onekey/halconf.h new file mode 100644 index 00000000..d3415c55 --- /dev/null +++ b/keyboard/stm32_f103_onekey/halconf.h @@ -0,0 +1,334 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file templates/halconf.h + * @brief HAL configuration header. + * @details HAL configuration file, this file allows to enable or disable the + * various device drivers from your application. You may also use + * this file in order to override the device drivers default settings. + * + * @addtogroup HAL_CONF + * @{ + */ + +#ifndef _HALCONF_H_ +#define _HALCONF_H_ + +#include "mcuconf.h" + +/** + * @brief Enables the PAL subsystem. + */ +#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__) +#define HAL_USE_PAL TRUE +#endif + +/** + * @brief Enables the ADC subsystem. + */ +#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__) +#define HAL_USE_ADC FALSE +#endif + +/** + * @brief Enables the CAN subsystem. + */ +#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__) +#define HAL_USE_CAN FALSE +#endif + +/** + * @brief Enables the DAC subsystem. + */ +#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__) +#define HAL_USE_DAC FALSE +#endif + +/** + * @brief Enables the EXT subsystem. + */ +#if !defined(HAL_USE_EXT) || defined(__DOXYGEN__) +#define HAL_USE_EXT FALSE +#endif + +/** + * @brief Enables the GPT subsystem. + */ +#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__) +#define HAL_USE_GPT FALSE +#endif + +/** + * @brief Enables the I2C subsystem. + */ +#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__) +#define HAL_USE_I2C FALSE +#endif + +/** + * @brief Enables the I2S subsystem. + */ +#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__) +#define HAL_USE_I2S FALSE +#endif + +/** + * @brief Enables the ICU subsystem. + */ +#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__) +#define HAL_USE_ICU FALSE +#endif + +/** + * @brief Enables the MAC subsystem. + */ +#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__) +#define HAL_USE_MAC FALSE +#endif + +/** + * @brief Enables the MMC_SPI subsystem. + */ +#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__) +#define HAL_USE_MMC_SPI FALSE +#endif + +/** + * @brief Enables the PWM subsystem. + */ +#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__) +#define HAL_USE_PWM FALSE +#endif + +/** + * @brief Enables the RTC subsystem. + */ +#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__) +#define HAL_USE_RTC FALSE +#endif + +/** + * @brief Enables the SDC subsystem. + */ +#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__) +#define HAL_USE_SDC FALSE +#endif + +/** + * @brief Enables the SERIAL subsystem. + */ +#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__) +#define HAL_USE_SERIAL FALSE +#endif + +/** + * @brief Enables the SERIAL over USB subsystem. + */ +#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__) +#define HAL_USE_SERIAL_USB FALSE +#endif + +/** + * @brief Enables the SPI subsystem. + */ +#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__) +#define HAL_USE_SPI FALSE +#endif + +/** + * @brief Enables the UART subsystem. + */ +#if !defined(HAL_USE_UART) || defined(__DOXYGEN__) +#define HAL_USE_UART FALSE +#endif + +/** + * @brief Enables the USB subsystem. + */ +#if !defined(HAL_USE_USB) || defined(__DOXYGEN__) +#define HAL_USE_USB TRUE +#endif + +/*===========================================================================*/ +/* ADC driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables synchronous APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__) +#define ADC_USE_WAIT TRUE +#endif + +/** + * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define ADC_USE_MUTUAL_EXCLUSION TRUE +#endif + +/*===========================================================================*/ +/* CAN driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Sleep mode related APIs inclusion switch. + */ +#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__) +#define CAN_USE_SLEEP_MODE TRUE +#endif + +/*===========================================================================*/ +/* I2C driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables the mutual exclusion APIs on the I2C bus. + */ +#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define I2C_USE_MUTUAL_EXCLUSION TRUE +#endif + +/*===========================================================================*/ +/* MAC driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables an event sources for incoming packets. + */ +#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__) +#define MAC_USE_ZERO_COPY FALSE +#endif + +/** + * @brief Enables an event sources for incoming packets. + */ +#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__) +#define MAC_USE_EVENTS TRUE +#endif + +/*===========================================================================*/ +/* MMC_SPI driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Delays insertions. + * @details If enabled this options inserts delays into the MMC waiting + * routines releasing some extra CPU time for the threads with + * lower priority, this may slow down the driver a bit however. + * This option is recommended also if the SPI driver does not + * use a DMA channel and heavily loads the CPU. + */ +#if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__) +#define MMC_NICE_WAITING TRUE +#endif + +/*===========================================================================*/ +/* SDC driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Number of initialization attempts before rejecting the card. + * @note Attempts are performed at 10mS intervals. + */ +#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__) +#define SDC_INIT_RETRY 100 +#endif + +/** + * @brief Include support for MMC cards. + * @note MMC support is not yet implemented so this option must be kept + * at @p FALSE. + */ +#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__) +#define SDC_MMC_SUPPORT FALSE +#endif + +/** + * @brief Delays insertions. + * @details If enabled this options inserts delays into the MMC waiting + * routines releasing some extra CPU time for the threads with + * lower priority, this may slow down the driver a bit however. + */ +#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__) +#define SDC_NICE_WAITING TRUE +#endif + +/*===========================================================================*/ +/* SERIAL driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Default bit rate. + * @details Configuration parameter, this is the baud rate selected for the + * default configuration. + */ +#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__) +#define SERIAL_DEFAULT_BITRATE 38400 +#endif + +/** + * @brief Serial buffers size. + * @details Configuration parameter, you can change the depth of the queue + * buffers depending on the requirements of your application. + * @note The default is 64 bytes for both the transmission and receive + * buffers. + */ +#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__) +#define SERIAL_BUFFERS_SIZE 16 +#endif + +/*===========================================================================*/ +/* SERIAL_USB driver related setting. */ +/*===========================================================================*/ + +/** + * @brief Serial over USB buffers size. + * @details Configuration parameter, the buffer size must be a multiple of + * the USB data endpoint maximum packet size. + * @note The default is 64 bytes for both the transmission and receive + * buffers. + */ +#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__) +#define SERIAL_USB_BUFFERS_SIZE 256 +#endif + +/*===========================================================================*/ +/* SPI driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables synchronous APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__) +#define SPI_USE_WAIT TRUE +#endif + +/** + * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define SPI_USE_MUTUAL_EXCLUSION TRUE +#endif + +#endif /* _HALCONF_H_ */ + +/** @} */ diff --git a/keyboard/stm32_f103_onekey/keymap_plain.c b/keyboard/stm32_f103_onekey/keymap_plain.c new file mode 100644 index 00000000..8de1c027 --- /dev/null +++ b/keyboard/stm32_f103_onekey/keymap_plain.c @@ -0,0 +1,44 @@ +/* +Copyright 2012,2013 Jun Wako + +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 2 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 "keycode.h" +#include "action.h" +#include "action_macro.h" +#include "report.h" +#include "host.h" +#include "print.h" +#include "debug.h" +#include "keymap.h" + +static const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + {{KC_CAPS}}, // test with KC_CAPS, KC_A, KC_BTLD +}; + +static const uint16_t fn_actions[] = { +}; + +/* translates key to keycode */ +uint8_t keymap_key_to_keycode(uint8_t layer, keypos_t key) +{ + return keymaps[(layer)][(key.row)][(key.col)]; +} + +/* translates Fn keycode to action */ +action_t keymap_fn_to_action(uint8_t keycode) +{ + return (action_t){ .code = fn_actions[FN_INDEX(keycode)] }; +} diff --git a/keyboard/stm32_f103_onekey/ld/STM32F103x8.ld b/keyboard/stm32_f103_onekey/ld/STM32F103x8.ld new file mode 100644 index 00000000..54105375 --- /dev/null +++ b/keyboard/stm32_f103_onekey/ld/STM32F103x8.ld @@ -0,0 +1,53 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS 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. + + ChibiOS 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 . +*/ + +/* + * ST32F103xB memory setup. + */ +MEMORY +{ + flash : org = 0x08000000, len = 64k + ram0 : org = 0x20000000, len = 20k + ram1 : org = 0x00000000, len = 0 + ram2 : org = 0x00000000, len = 0 + ram3 : org = 0x00000000, len = 0 + ram4 : org = 0x00000000, len = 0 + ram5 : org = 0x00000000, len = 0 + ram6 : org = 0x00000000, len = 0 + ram7 : org = 0x00000000, len = 0 +} + +/* RAM region to be used for Main stack. This stack accommodates the processing + of all exceptions and interrupts*/ +REGION_ALIAS("MAIN_STACK_RAM", ram0); + +/* RAM region to be used for the process stack. This is the stack used by + the main() function.*/ +REGION_ALIAS("PROCESS_STACK_RAM", ram0); + +/* RAM region to be used for data segment.*/ +REGION_ALIAS("DATA_RAM", ram0); + +/* RAM region to be used for BSS segment.*/ +REGION_ALIAS("BSS_RAM", ram0); + +/* RAM region to be used for the default heap.*/ +REGION_ALIAS("HEAP_RAM", ram0); + +INCLUDE rules.ld diff --git a/keyboard/stm32_f103_onekey/ld/STM32F103x8_stm32duino_bootloader.ld b/keyboard/stm32_f103_onekey/ld/STM32F103x8_stm32duino_bootloader.ld new file mode 100644 index 00000000..425616af --- /dev/null +++ b/keyboard/stm32_f103_onekey/ld/STM32F103x8_stm32duino_bootloader.ld @@ -0,0 +1,53 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS 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. + + ChibiOS 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 . +*/ + +/* + * ST32F103xB memory setup. + */ +MEMORY +{ + flash : org = 0x08002000, len = 64k - 0x2000 + ram0 : org = 0x20000C00, len = 20k - 0xC00 + ram1 : org = 0x00000000, len = 0 + ram2 : org = 0x00000000, len = 0 + ram3 : org = 0x00000000, len = 0 + ram4 : org = 0x00000000, len = 0 + ram5 : org = 0x00000000, len = 0 + ram6 : org = 0x00000000, len = 0 + ram7 : org = 0x00000000, len = 0 +} + +/* RAM region to be used for Main stack. This stack accommodates the processing + of all exceptions and interrupts*/ +REGION_ALIAS("MAIN_STACK_RAM", ram0); + +/* RAM region to be used for the process stack. This is the stack used by + the main() function.*/ +REGION_ALIAS("PROCESS_STACK_RAM", ram0); + +/* RAM region to be used for data segment.*/ +REGION_ALIAS("DATA_RAM", ram0); + +/* RAM region to be used for BSS segment.*/ +REGION_ALIAS("BSS_RAM", ram0); + +/* RAM region to be used for the default heap.*/ +REGION_ALIAS("HEAP_RAM", ram0); + +INCLUDE rules.ld diff --git a/keyboard/stm32_f103_onekey/led.c b/keyboard/stm32_f103_onekey/led.c new file mode 100644 index 00000000..ddacfd8c --- /dev/null +++ b/keyboard/stm32_f103_onekey/led.c @@ -0,0 +1,32 @@ +/* +Copyright 2012 Jun Wako + +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 2 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 "hal.h" +#include "led.h" + + +void led_set(uint8_t usb_led) +{ + if (usb_led & (1< + +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 2 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 "ch.h" +#include "hal.h" + +/* + * scan matrix + */ +#include "print.h" +#include "debug.h" +#include "util.h" +#include "matrix.h" +#include "wait.h" + +#ifndef DEBOUNCE +# define DEBOUNCE 5 +#endif +static uint8_t debouncing = DEBOUNCE; + +/* matrix state(1:on, 0:off) */ +static matrix_row_t matrix[MATRIX_ROWS]; +static matrix_row_t matrix_debouncing[MATRIX_ROWS]; + +static matrix_row_t read_cols(void); +static void init_cols(void); +static void unselect_rows(void); +static void select_row(uint8_t row); + + +inline +uint8_t matrix_rows(void) +{ + return MATRIX_ROWS; +} + +inline +uint8_t matrix_cols(void) +{ + return MATRIX_COLS; +} + +#define LED_ON() do { palClearPad(GPIOC, GPIOC_LED) ;} while (0) +#define LED_OFF() do { palSetPad(GPIOC, GPIOC_LED); } while (0) +#define LED_TGL() do { palTogglePad(GPIOC, GPIOC_LED); } while (0) + +void matrix_init(void) +{ + // initialize row and col + unselect_rows(); + init_cols(); + + // initialize matrix state: all keys off + for (uint8_t i=0; i < MATRIX_ROWS; i++) { + matrix[i] = 0; + matrix_debouncing[i] = 0; + } + + //debug + debug_matrix = true; + LED_ON(); + wait_ms(500); + LED_OFF(); +} + +uint8_t matrix_scan(void) +{ + for (uint8_t i = 0; i < MATRIX_ROWS; i++) { + select_row(i); + wait_us(30); // without this wait read unstable value. + matrix_row_t cols = read_cols(); + if (matrix_debouncing[i] != cols) { + matrix_debouncing[i] = cols; + if (debouncing) { + debug("bounce!: "); debug_hex(debouncing); debug("\n"); + } + debouncing = DEBOUNCE; + } + unselect_rows(); + } + + if (debouncing) { + if (--debouncing) { + wait_ms(1); + } else { + for (uint8_t i = 0; i < MATRIX_ROWS; i++) { + matrix[i] = matrix_debouncing[i]; + } + } + } + + return 1; +} + +inline +bool matrix_is_on(uint8_t row, uint8_t col) +{ + return (matrix[row] & ((matrix_row_t)1< Date: Thu, 3 Dec 2015 08:49:55 +0000 Subject: [PATCH 44/56] Add maple mini code. --- keyboard/stm32_f103_onekey/Makefile | 15 ++- keyboard/stm32_f103_onekey/chconf.h | 8 +- keyboard/stm32_f103_onekey/led.c | 16 ++- keyboard/stm32_f103_onekey/matrix.c | 14 +++ keyboard/stm32_f103_onekey/mcuconf.h | 145 +++++++++++++++++---------- tmk_core/protocol/chibios/usb_main.c | 4 +- 6 files changed, 133 insertions(+), 69 deletions(-) diff --git a/keyboard/stm32_f103_onekey/Makefile b/keyboard/stm32_f103_onekey/Makefile index ea2ee08b..89e58144 100644 --- a/keyboard/stm32_f103_onekey/Makefile +++ b/keyboard/stm32_f103_onekey/Makefile @@ -1,6 +1,5 @@ # Target file name (without extension). PROJECT = ch -TARGET = alps64 # Directory common source files exist TMK_DIR = ../../tmk_core @@ -26,14 +25,14 @@ CONFIG_H = config.h # BOARD = GENERIC_STM32_F103 # GENERIC STM32F103C8T6 board - no bootloader (programmer over serial or SWD) -OPT_DEFS = -MCU_LDSCRIPT = STM32F103x8 -BOARD = GENERIC_STM32_F103 +# OPT_DEFS = +# MCU_LDSCRIPT = STM32F103x8 +# BOARD = GENERIC_STM32_F103 # MAPLE MINI -# OPT_DEFS = -DCORTEX_VTOR_INIT=0x5000 -# MCU_LDSCRIPT = STM32F103xE_maplemini_bootloader.ld -# BOARD = MAPLEMINI_STM32_F103 +OPT_DEFS = -DCORTEX_VTOR_INIT=0x5000 +MCU_LDSCRIPT = STM32F103xE_maplemini_bootloader +BOARD = MAPLEMINI_STM32_F103 ## chip/board settings # the next two should match the directories in @@ -71,7 +70,7 @@ ARMV = 7 ## BOOTMAGIC is not supported on STM32 chips yet. MOUSEKEY_ENABLE = yes # Mouse keys EXTRAKEY_ENABLE = yes # Audio control and System control -CONSOLE_ENABLE = yes # Console for debug +# CONSOLE_ENABLE = yes # Console for debug COMMAND_ENABLE = yes # Commands for debug and configuration SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend NKRO_ENABLE = yes # USB Nkey Rollover diff --git a/keyboard/stm32_f103_onekey/chconf.h b/keyboard/stm32_f103_onekey/chconf.h index 689d4b39..acce6a1f 100644 --- a/keyboard/stm32_f103_onekey/chconf.h +++ b/keyboard/stm32_f103_onekey/chconf.h @@ -46,7 +46,7 @@ * @details Frequency of the system timer that drives the system ticks. This * setting also defines the system tick time unit. */ -#define CH_CFG_ST_FREQUENCY 10000 +#define CH_CFG_ST_FREQUENCY 2000 /** * @brief Time delta constant for the tick-less mode. @@ -124,7 +124,7 @@ * @note This is not related to the compiler optimization options. * @note The default is @p TRUE. */ -#define CH_CFG_OPTIMIZE_SPEED FALSE +#define CH_CFG_OPTIMIZE_SPEED TRUE /** @} */ @@ -281,7 +281,7 @@ * * @note The default is @p TRUE. */ -#define CH_CFG_USE_MEMCORE FALSE +#define CH_CFG_USE_MEMCORE TRUE /** * @brief Heap Allocator APIs. @@ -293,7 +293,7 @@ * @p CH_CFG_USE_SEMAPHORES. * @note Mutexes are recommended. */ -#define CH_CFG_USE_HEAP FALSE +#define CH_CFG_USE_HEAP TRUE /** * @brief Memory Pools Allocator APIs. diff --git a/keyboard/stm32_f103_onekey/led.c b/keyboard/stm32_f103_onekey/led.c index ddacfd8c..3c86963c 100644 --- a/keyboard/stm32_f103_onekey/led.c +++ b/keyboard/stm32_f103_onekey/led.c @@ -22,11 +22,25 @@ along with this program. If not, see . void led_set(uint8_t usb_led) { if (usb_led & (1<CTL &= ~USBx_CTL_RESUME; #endif /* KINETIS_USB_USE_USB0 */ -#elif defined(STM32F0XX) /* K20x || KL2x */ +#elif defined(STM32F0XX) || defined(STM32F1XX) /* K20x || KL2x */ STM32_USB->CNTR |= CNTR_RESUME; chThdSleepMilliseconds(15); STM32_USB->CNTR &= ~CNTR_RESUME; -#else /* STM32F0XX */ +#else /* STM32F0XX || STM32F1XX */ #warning Sending remote wakeup packet not implemented for your platform. #endif /* K20x || KL2x */ } From b110884a6d55a7d126e59a43e6481ca51c52e5e8 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Thu, 3 Dec 2015 09:05:09 +0000 Subject: [PATCH 45/56] Workaround for compiler warnings when console disabled. --- tmk_core/common/command.c | 10 ++++++---- tmk_core/common/nodebug.h | 2 ++ tmk_core/common/print.h | 7 ++++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/tmk_core/common/command.c b/tmk_core/common/command.c index 4ef0b077..69209700 100644 --- a/tmk_core/common/command.c +++ b/tmk_core/common/command.c @@ -350,10 +350,11 @@ static bool command_common(uint8_t code) case KC_N: clear_keyboard(); //Prevents stuck keys. keyboard_nkro = !keyboard_nkro; - if (keyboard_nkro) + if (keyboard_nkro) { print("NKRO: on\n"); - else + } else { print("NKRO: off\n"); + } break; #endif case KC_ESC: @@ -607,10 +608,11 @@ static bool mousekey_console(uint8_t code) print("?"); return false; } - if (mousekey_param) + if (mousekey_param) { xprintf("M%d> ", mousekey_param); - else + } else { print("M>" ); + } return true; } #endif diff --git a/tmk_core/common/nodebug.h b/tmk_core/common/nodebug.h index 93309ada..6ef9f91a 100644 --- a/tmk_core/common/nodebug.h +++ b/tmk_core/common/nodebug.h @@ -18,7 +18,9 @@ along with this program. If not, see . #ifndef NODEBUG_H #define NODEBUG_H 1 +#if !defined(NO_DEBUG) #define NO_DEBUG +#endif #include "debug.h" #undef NO_DEBUG diff --git a/tmk_core/common/print.h b/tmk_core/common/print.h index 1e1b9b40..5f491aba 100644 --- a/tmk_core/common/print.h +++ b/tmk_core/common/print.h @@ -99,9 +99,9 @@ void print_set_sendchar(int8_t (*print_sendchar_func)(uint8_t)); #else /* NO_PRINT */ -#define xprintf -#define print -#define println +#define xprintf(s,...) +#define print(s) +#define println(s) #define print_set_sendchar(func) #define print_dec(data) #define print_decs(data) @@ -127,6 +127,7 @@ void print_set_sendchar(int8_t (*print_sendchar_func)(uint8_t)); #define print_val_bin_reverse8(v) #define print_val_bin_reverse16(v) #define print_val_bin_reverse32(v) +#define init_printf(s,ss) #endif /* NO_PRINT */ From 7aab3bb21a3a62ce85a1f4b042508a335d6ed028 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Thu, 3 Dec 2015 09:24:49 +0000 Subject: [PATCH 46/56] STM32F103x fixes. --- keyboard/stm32_f103_onekey/Makefile | 12 ++++++------ .../boards/GENERIC_STM32_F103/board.h | 4 ++-- keyboard/stm32_f103_onekey/led.c | 7 ++----- keyboard/stm32_f103_onekey/matrix.c | 4 ++-- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/keyboard/stm32_f103_onekey/Makefile b/keyboard/stm32_f103_onekey/Makefile index 89e58144..79ca4238 100644 --- a/keyboard/stm32_f103_onekey/Makefile +++ b/keyboard/stm32_f103_onekey/Makefile @@ -20,9 +20,9 @@ endif CONFIG_H = config.h # GENERIC STM32F103C8T6 board - stm32duino bootloader -# OPT_DEFS = -DCORTEX_VTOR_INIT=0x2000 -# MCU_LDSCRIPT = STM32F103x8_stm32duino_bootloader -# BOARD = GENERIC_STM32_F103 +OPT_DEFS = -DCORTEX_VTOR_INIT=0x2000 +MCU_LDSCRIPT = STM32F103x8_stm32duino_bootloader +BOARD = GENERIC_STM32_F103 # GENERIC STM32F103C8T6 board - no bootloader (programmer over serial or SWD) # OPT_DEFS = @@ -30,9 +30,9 @@ CONFIG_H = config.h # BOARD = GENERIC_STM32_F103 # MAPLE MINI -OPT_DEFS = -DCORTEX_VTOR_INIT=0x5000 -MCU_LDSCRIPT = STM32F103xE_maplemini_bootloader -BOARD = MAPLEMINI_STM32_F103 +# OPT_DEFS = -DCORTEX_VTOR_INIT=0x5000 +# MCU_LDSCRIPT = STM32F103xE_maplemini_bootloader +# BOARD = MAPLEMINI_STM32_F103 ## chip/board settings # the next two should match the directories in diff --git a/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.h b/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.h index ef7318ee..7253e778 100644 --- a/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.h +++ b/keyboard/stm32_f103_onekey/boards/GENERIC_STM32_F103/board.h @@ -143,7 +143,7 @@ /* #define usb_lld_connect_bus(usbp) palClearPad(GPIOB, GPIOB_USB_DISC) */ -#define usb_lld_connect_bus(usbp) +#define usb_lld_connect_bus(usbp) palSetPadMode(GPIOA, 12, PAL_MODE_INPUT); /* * USB bus de-activation macro, required by the USB driver. @@ -151,7 +151,7 @@ /* #define usb_lld_disconnect_bus(usbp) palSetPad(GPIOB, GPIOB_USB_DISC) */ -#define usb_lld_disconnect_bus(usbp) +#define usb_lld_disconnect_bus(usbp) palSetPadMode(GPIOA, 12, PAL_MODE_OUTPUT_PUSHPULL); palClearPad(GPIOA, 12); #if !defined(_FROM_ASM_) #ifdef __cplusplus diff --git a/keyboard/stm32_f103_onekey/led.c b/keyboard/stm32_f103_onekey/led.c index 3c86963c..f5c55f7d 100644 --- a/keyboard/stm32_f103_onekey/led.c +++ b/keyboard/stm32_f103_onekey/led.c @@ -24,23 +24,20 @@ void led_set(uint8_t usb_led) if (usb_led & (1< Date: Thu, 3 Dec 2015 09:26:22 +0000 Subject: [PATCH 47/56] Add maple mini pinout pic. --- .../boards/maple_mini_mapping.png | Bin 0 -> 237977 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 keyboard/stm32_f103_onekey/boards/maple_mini_mapping.png diff --git a/keyboard/stm32_f103_onekey/boards/maple_mini_mapping.png b/keyboard/stm32_f103_onekey/boards/maple_mini_mapping.png new file mode 100644 index 0000000000000000000000000000000000000000..12cfa81db26dd38fb16c80ad58c2e7a073957a6c GIT binary patch literal 237977 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≧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 literal 0 HcmV?d00001 From a75834ee98e94cea329aea224a4daaed1a4bf681 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Fri, 18 Dec 2015 20:50:59 +0000 Subject: [PATCH 48/56] Do timeout when writing to CONSOLE EP queue. Fixes TMK bug #266. --- tmk_core/protocol/chibios/usb_main.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 85548fbb..ee2788e5 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -1323,10 +1323,13 @@ int8_t sendchar(uint8_t c) { return 0; } osalSysUnlock(); - /* should get suspended and wait if the queue is full - * but it's not blocking even if noone is listening, - * because the USB packets are sent anyway */ - return(chOQPut(&console_queue, c)); + /* Timeout after 5us if the queue is full. + * Increase this timeout if too much stuff is getting + * dropped (i.e. the buffer is getting full too fast + * for USB/HIDRAW to dequeue). Another possibility + * for fixing this kind of thing is to increase + * CONSOLE_QUEUE_CAPACITY. */ + return(chOQPutTimeout(&console_queue, c, US2ST(5))); } #else /* CONSOLE_ENABLE */ From dbbd4e8f8d6bf04af4b778d2c8fd14d5417780c6 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Mon, 21 Dec 2015 08:45:26 +0000 Subject: [PATCH 49/56] Chibios: updates for 3.0.4 git. --- keyboard/infinity_chibios/halconf.h | 7 ++ keyboard/stm32_f103_onekey/halconf.h | 7 ++ keyboard/stm32_onekey/halconf.h | 7 ++ keyboard/teensy_lc_onekey/halconf.h | 7 ++ keyboard/teensy_lc_onekey/ld/MKL26Z64.ld | 145 ++++------------------- 5 files changed, 50 insertions(+), 123 deletions(-) diff --git a/keyboard/infinity_chibios/halconf.h b/keyboard/infinity_chibios/halconf.h index 2472ba7e..61a9ac0a 100644 --- a/keyboard/infinity_chibios/halconf.h +++ b/keyboard/infinity_chibios/halconf.h @@ -163,6 +163,13 @@ #define HAL_USE_USB TRUE #endif +/** + * @brief Enables the WDG subsystem. + */ +#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__) +#define HAL_USE_WDG FALSE +#endif + /*===========================================================================*/ /* ADC driver related settings. */ /*===========================================================================*/ diff --git a/keyboard/stm32_f103_onekey/halconf.h b/keyboard/stm32_f103_onekey/halconf.h index d3415c55..e75c1c36 100644 --- a/keyboard/stm32_f103_onekey/halconf.h +++ b/keyboard/stm32_f103_onekey/halconf.h @@ -163,6 +163,13 @@ #define HAL_USE_USB TRUE #endif +/** + * @brief Enables the WDG subsystem. + */ +#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__) +#define HAL_USE_WDG FALSE +#endif + /*===========================================================================*/ /* ADC driver related settings. */ /*===========================================================================*/ diff --git a/keyboard/stm32_onekey/halconf.h b/keyboard/stm32_onekey/halconf.h index d3415c55..e75c1c36 100644 --- a/keyboard/stm32_onekey/halconf.h +++ b/keyboard/stm32_onekey/halconf.h @@ -163,6 +163,13 @@ #define HAL_USE_USB TRUE #endif +/** + * @brief Enables the WDG subsystem. + */ +#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__) +#define HAL_USE_WDG FALSE +#endif + /*===========================================================================*/ /* ADC driver related settings. */ /*===========================================================================*/ diff --git a/keyboard/teensy_lc_onekey/halconf.h b/keyboard/teensy_lc_onekey/halconf.h index 0d4bc4d0..86839166 100644 --- a/keyboard/teensy_lc_onekey/halconf.h +++ b/keyboard/teensy_lc_onekey/halconf.h @@ -163,6 +163,13 @@ #define HAL_USE_USB TRUE #endif +/** + * @brief Enables the WDG subsystem. + */ +#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__) +#define HAL_USE_WDG FALSE +#endif + #endif /* _HALCONF_H_ */ /** @} */ diff --git a/keyboard/teensy_lc_onekey/ld/MKL26Z64.ld b/keyboard/teensy_lc_onekey/ld/MKL26Z64.ld index fd9102de..9e9e9250 100644 --- a/keyboard/teensy_lc_onekey/ld/MKL26Z64.ld +++ b/keyboard/teensy_lc_onekey/ld/MKL26Z64.ld @@ -18,144 +18,43 @@ */ /* - * KL25Z64 memory setup. + * KL26Z64 memory setup. */ - -ENTRY(Reset_Handler) - MEMORY { flash0 : org = 0x00000000, len = 0xc0 flashcfg : org = 0x00000400, len = 0x10 flash : org = 0x00000410, len = 62k - 0x410 eeprom_emu : org = 0x0000F800, len = 2k - ram : org = 0x1FFFF800, len = 8k + ram0 : org = 0x1FFFF800, len = 8k + ram1 : org = 0x00000000, len = 0 + ram2 : org = 0x00000000, len = 0 + ram3 : org = 0x00000000, len = 0 + ram4 : org = 0x00000000, len = 0 + ram5 : org = 0x00000000, len = 0 + ram6 : org = 0x00000000, len = 0 + ram7 : org = 0x00000000, len = 0 } -__ram_start__ = ORIGIN(ram); -__ram_size__ = LENGTH(ram); -__ram_end__ = __ram_start__ + __ram_size__; - __eeprom_workarea_start__ = ORIGIN(eeprom_emu); __eeprom_workarea_size__ = LENGTH(eeprom_emu); __eeprom_workarea_end__ = __eeprom_workarea_start__ + __eeprom_workarea_size__; -SECTIONS -{ - . = 0; +/* RAM region to be used for Main stack. This stack accommodates the processing + of all exceptions and interrupts*/ +REGION_ALIAS("MAIN_STACK_RAM", ram0); - .isr : ALIGN(4) SUBALIGN(4) - { - KEEP(*(.vectors)) - } > flash0 +/* RAM region to be used for the process stack. This is the stack used by + the main() function.*/ +REGION_ALIAS("PROCESS_STACK_RAM", ram0); - .cfmprotect : ALIGN(4) SUBALIGN(4) - { - KEEP(*(.cfmconfig)) - } > flashcfg +/* RAM region to be used for data segment.*/ +REGION_ALIAS("DATA_RAM", ram0); - _text = .; +/* RAM region to be used for BSS segment.*/ +REGION_ALIAS("BSS_RAM", ram0); - constructors : ALIGN(4) SUBALIGN(4) - { - PROVIDE(__init_array_start = .); - KEEP(*(SORT(.init_array.*))) - KEEP(*(.init_array)) - PROVIDE(__init_array_end = .); - } > flash +/* RAM region to be used for the default heap.*/ +REGION_ALIAS("HEAP_RAM", ram0); - destructors : ALIGN(4) SUBALIGN(4) - { - PROVIDE(__fini_array_start = .); - KEEP(*(.fini_array)) - KEEP(*(SORT(.fini_array.*))) - PROVIDE(__fini_array_end = .); - } > flash - - .text : ALIGN(4) SUBALIGN(4) - { - *(.text) - *(.text.*) - *(.rodata) - *(.rodata.*) - *(.glue_7t) - *(.glue_7) - *(.gcc*) - } > flash - - .ARM.extab : - { - *(.ARM.extab* .gnu.linkonce.armextab.*) - } > flash - - .ARM.exidx : { - PROVIDE(__exidx_start = .); - *(.ARM.exidx* .gnu.linkonce.armexidx.*) - PROVIDE(__exidx_end = .); - } > flash - - .eh_frame_hdr : - { - *(.eh_frame_hdr) - } > flash - - .eh_frame : ONLY_IF_RO - { - *(.eh_frame) - } > flash - - .textalign : ONLY_IF_RO - { - . = ALIGN(8); - } > flash - - _etext = .; - _textdata = _etext; - - .stacks : - { - . = ALIGN(8); - __main_stack_base__ = .; - . += __main_stack_size__; - . = ALIGN(8); - __main_stack_end__ = .; - __process_stack_base__ = .; - __main_thread_stack_base__ = .; - . += __process_stack_size__; - . = ALIGN(8); - __process_stack_end__ = .; - __main_thread_stack_end__ = .; - } > ram - - .data : - { - . = ALIGN(4); - PROVIDE(_data = .); - *(.data) - . = ALIGN(4); - *(.data.*) - . = ALIGN(4); - *(.ramtext) - . = ALIGN(4); - PROVIDE(_edata = .); - } > ram AT > flash - - .bss : - { - . = ALIGN(4); - PROVIDE(_bss_start = .); - *(.bss) - . = ALIGN(4); - *(.bss.*) - . = ALIGN(4); - *(COMMON) - . = ALIGN(4); - PROVIDE(_bss_end = .); - } > ram -} - -PROVIDE(end = .); -_end = .; - -__heap_base__ = _end; -__heap_end__ = __ram_end__; +INCLUDE ld/rules_kinetis.ld From a47c3c733320c2c11fc1f7be6215f9a40787cdf1 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Mon, 21 Dec 2015 17:04:23 +0000 Subject: [PATCH 50/56] Chibios: rename example stm32_onekey -> stm32_f072_onekey. --- keyboard/{stm32_onekey => stm32_f072_onekey}/Makefile | 0 keyboard/{stm32_onekey => stm32_f072_onekey}/bootloader_defs.h | 0 keyboard/{stm32_onekey => stm32_f072_onekey}/chconf.h | 0 keyboard/{stm32_onekey => stm32_f072_onekey}/config.h | 0 keyboard/{stm32_onekey => stm32_f072_onekey}/halconf.h | 0 keyboard/{stm32_onekey => stm32_f072_onekey}/keymap_plain.c | 0 keyboard/{stm32_onekey => stm32_f072_onekey}/led.c | 0 keyboard/{stm32_onekey => stm32_f072_onekey}/matrix.c | 0 keyboard/{stm32_onekey => stm32_f072_onekey}/mcuconf.h | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename keyboard/{stm32_onekey => stm32_f072_onekey}/Makefile (100%) rename keyboard/{stm32_onekey => stm32_f072_onekey}/bootloader_defs.h (100%) rename keyboard/{stm32_onekey => stm32_f072_onekey}/chconf.h (100%) rename keyboard/{stm32_onekey => stm32_f072_onekey}/config.h (100%) rename keyboard/{stm32_onekey => stm32_f072_onekey}/halconf.h (100%) rename keyboard/{stm32_onekey => stm32_f072_onekey}/keymap_plain.c (100%) rename keyboard/{stm32_onekey => stm32_f072_onekey}/led.c (100%) rename keyboard/{stm32_onekey => stm32_f072_onekey}/matrix.c (100%) rename keyboard/{stm32_onekey => stm32_f072_onekey}/mcuconf.h (100%) diff --git a/keyboard/stm32_onekey/Makefile b/keyboard/stm32_f072_onekey/Makefile similarity index 100% rename from keyboard/stm32_onekey/Makefile rename to keyboard/stm32_f072_onekey/Makefile diff --git a/keyboard/stm32_onekey/bootloader_defs.h b/keyboard/stm32_f072_onekey/bootloader_defs.h similarity index 100% rename from keyboard/stm32_onekey/bootloader_defs.h rename to keyboard/stm32_f072_onekey/bootloader_defs.h diff --git a/keyboard/stm32_onekey/chconf.h b/keyboard/stm32_f072_onekey/chconf.h similarity index 100% rename from keyboard/stm32_onekey/chconf.h rename to keyboard/stm32_f072_onekey/chconf.h diff --git a/keyboard/stm32_onekey/config.h b/keyboard/stm32_f072_onekey/config.h similarity index 100% rename from keyboard/stm32_onekey/config.h rename to keyboard/stm32_f072_onekey/config.h diff --git a/keyboard/stm32_onekey/halconf.h b/keyboard/stm32_f072_onekey/halconf.h similarity index 100% rename from keyboard/stm32_onekey/halconf.h rename to keyboard/stm32_f072_onekey/halconf.h diff --git a/keyboard/stm32_onekey/keymap_plain.c b/keyboard/stm32_f072_onekey/keymap_plain.c similarity index 100% rename from keyboard/stm32_onekey/keymap_plain.c rename to keyboard/stm32_f072_onekey/keymap_plain.c diff --git a/keyboard/stm32_onekey/led.c b/keyboard/stm32_f072_onekey/led.c similarity index 100% rename from keyboard/stm32_onekey/led.c rename to keyboard/stm32_f072_onekey/led.c diff --git a/keyboard/stm32_onekey/matrix.c b/keyboard/stm32_f072_onekey/matrix.c similarity index 100% rename from keyboard/stm32_onekey/matrix.c rename to keyboard/stm32_f072_onekey/matrix.c diff --git a/keyboard/stm32_onekey/mcuconf.h b/keyboard/stm32_f072_onekey/mcuconf.h similarity index 100% rename from keyboard/stm32_onekey/mcuconf.h rename to keyboard/stm32_f072_onekey/mcuconf.h From b2a3a2653789908e0d5b4770f8ab89d6e8e1d050 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Mon, 21 Dec 2015 17:04:46 +0000 Subject: [PATCH 51/56] Chibios: add makefiles for Teensy 3.x examples. --- keyboard/teensy_lc_onekey/Makefile.3.0 | 2 +- keyboard/teensy_lc_onekey/Makefile.3.2 | 77 ++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 keyboard/teensy_lc_onekey/Makefile.3.2 diff --git a/keyboard/teensy_lc_onekey/Makefile.3.0 b/keyboard/teensy_lc_onekey/Makefile.3.0 index a2f98c7e..dbf12c36 100644 --- a/keyboard/teensy_lc_onekey/Makefile.3.0 +++ b/keyboard/teensy_lc_onekey/Makefile.3.0 @@ -55,7 +55,7 @@ BOARD = PJRC_TEENSY_3 # Cortex version # Teensy LC is cortex-m0; Teensy 3.x are cortex-m4 -MCU = cortex-m0 +MCU = cortex-m4 # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 # I.e. 6 for Teensy LC; 7 for Teensy 3.x diff --git a/keyboard/teensy_lc_onekey/Makefile.3.2 b/keyboard/teensy_lc_onekey/Makefile.3.2 new file mode 100644 index 00000000..41bfa41a --- /dev/null +++ b/keyboard/teensy_lc_onekey/Makefile.3.2 @@ -0,0 +1,77 @@ +# Target file name (without extension). +PROJECT = ch + +# Directory common source files exist +TMK_DIR = ../../tmk_core + +# Directory keyboard dependent files exist +TARGET_DIR = . + +# project specific files +SRC = matrix.c \ + led.c + +ifdef KEYMAP + SRC := keymap_$(KEYMAP).c $(SRC) +else + SRC := keymap_plain.c $(SRC) +endif + +CONFIG_H = config.h + +## chip/board settings +# - the next two should match the directories in +# /os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES) +# - For Teensies, FAMILY = KINETIS and SERIES is either +# KL2x (LC) or K20x (3.0,3.1,3.2). +MCU_FAMILY = KINETIS +MCU_SERIES = K20x + +# Linker script to use +# - it should exist either in /os/common/ports/ARMCMx/compilers/GCC/ld/ +# or /ld/ +# - NOTE: a custom ld script is needed for EEPROM on Teensy LC +# - LDSCRIPT = +# - MKL26Z64 for Teensy LC +# - MK20DX128 for Teensy 3.0 +# - MK20DX256 for Teensy 3.1 and 3.2 +MCU_LDSCRIPT = MK20DX256 + +# Startup code to use +# - it should exist in /os/common/ports/ARMCMx/compilers/GCC/mk/ +# - STARTUP = +# - kl2x for Teensy LC +# - k20x5 for Teensy 3.0 +# - k20x7 for Teensy 3.1 and 3.2 +MCU_STARTUP = k20x7 + +# Board: it should exist either in /os/hal/boards/ +# or /boards +# - BOARD = +# - PJRC_TEENSY_LC for Teensy LC +# - PJRC_TEENSY_3 for Teensy 3.0 +# - PJRC_TEENSY_3_1 for Teensy 3.1 or 3.2 +BOARD = PJRC_TEENSY_3_1 + +# Cortex version +# Teensy LC is cortex-m0; Teensy 3.x are cortex-m4 +MCU = cortex-m4 + +# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 +# I.e. 6 for Teensy LC; 7 for Teensy 3.x +ARMV = 7 + +# Build Options +# comment out to disable the options. +# +BOOTMAGIC_ENABLE = yes # Virtual DIP switch configuration +## (Note that for BOOTMAGIC on Teensy LC you have to use a custom .ld script.) +MOUSEKEY_ENABLE = yes # Mouse keys +EXTRAKEY_ENABLE = yes # Audio control and System control +CONSOLE_ENABLE = yes # Console for debug +COMMAND_ENABLE = yes # Commands for debug and configuration +SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +NKRO_ENABLE = yes # USB Nkey Rollover + +include $(TMK_DIR)/tool/chibios/common.mk +include $(TMK_DIR)/tool/chibios/chibios.mk From 0998a9930dbdfe54add0245630fc6ff5297e5fa9 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Sat, 26 Dec 2015 19:54:33 +0100 Subject: [PATCH 52/56] Chibios: update Teensy 3.x instructions. --- keyboard/teensy_lc_onekey/instructions.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/keyboard/teensy_lc_onekey/instructions.md b/keyboard/teensy_lc_onekey/instructions.md index ed355e29..b8beb108 100644 --- a/keyboard/teensy_lc_onekey/instructions.md +++ b/keyboard/teensy_lc_onekey/instructions.md @@ -4,9 +4,9 @@ These ARM Teensies are now supported through [chibios](http://chibios.org). You'll need to install an ARM toolchain, for instance from [gcc ARM embedded](https://launchpad.net/gcc-arm-embedded) website, or using your favourite package manager. After installing, you should be able to run `arm-none-eabi-gcc -v` in the command prompt and get sensible output. This toolchain is used instead of `avr-gcc`, which is only for AVR chips. Naturally you'll also need the usual development tools (e.g. `make`), just as in the AVR setting. -You'll need this fork/branch of TMK. If you're reading this from your own hard drive, then you already have it ;) Anyway, you can get a zip from [here](https://https://github.com/flabbergast/tmk_keyboard/archive/chibios.zip) {or clone this repo from github and checkout the `chibios` branch}. +You'll need this fork/branch of TMK. If you're reading this from your own hard drive, then you already have it ;) Anyway, you can get a zip from [here](https://github.com/flabbergast/tmk_keyboard/archive/chibios.zip) {or clone this repo from github and checkout the `chibios` branch}. -Next, you'll need ChibiOS. The current release (3.0.2) does not have sufficient Kinetis support, so you'll need to get a patched version from [my fork](https://github.com/flabbergast/ChibiOS/tree/kinetis): you can download a current tree zipped from [here](https://github.com/flabbergast/ChibiOS/archive/kinetis.zip) {or clone that repo from github and checkout the `kinetis` branch}. Unpack the zip, rename the newly created `ChibiOS-kinetis` to `chibios`, and move it to `tmk/tool/chibios/` (so that the ChibiOS files reside in `tmk/tool/chibios/chibios`). +Next, you'll need ChibiOS. The current release (3.0.4) does not have sufficient Kinetis support, so you'll need to get a patched version from [my fork](https://github.com/flabbergast/ChibiOS/tree/kinetis): you can download a current tree zipped from [here](https://github.com/flabbergast/ChibiOS/archive/kinetis.zip) {or clone that repo from github and checkout the `kinetis` branch}. Unpack the zip, rename the newly created `ChibiOS-kinetis` to `chibios`, and move it to `tmk/tool/chibios/` (so that the ChibiOS files reside in `tmk/tool/chibios/chibios`). This should be it. Running `make` in `keyboard/teensy_lc_onekey` should create a working firmware in `build/`, called `ch.hex`. @@ -14,7 +14,7 @@ For more notes about the ChibiOS backend in TMK, see `tmk_core/protocol/chibios/ ## About this onekey example -It's set up for Teensy LC. To use 3.x, you'll need to edit the `Makefile` (and comment out one line in `mcuconf.h`). A sample makefile for Teensy 3.0 is provided as `Makefile.3.0`, can be used without renaming with `make -f Makefile.3.0`. +It's set up for Teensy LC. To use 3.x, you'll need to edit the `Makefile` (and comment out one line in `mcuconf.h`). A sample makefile for Teensy 3.0 is provided as `Makefile.3.0`, can be used without renaming with `make -f Makefile.3.0`. Similarly for Teensy 3.2, there's `Makefile.3.2`. ## Credits @@ -24,7 +24,7 @@ The USB support for Kinetis MCUs is due to RedoX. His ChibiOS fork is also [on g ## Features that are not implemented yet -Currently only the more fancy suspend features are not there (i.e. "breathing" LED during suspend, power saving during suspend, sending a wakeup packet). The rest should work fine (reports either way are welcome). +Currently only the more fancy suspend features are not there (power saving during suspend). The rest should work fine (reports either way are welcome). # Matrix programming notes From 1d6c6609e0f41f760a9eecaab1e19c76f99f9c52 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Sun, 27 Dec 2015 19:32:27 +0100 Subject: [PATCH 53/56] Chibios: Tsy LC is cortex-m0plus. --- keyboard/teensy_lc_onekey/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/keyboard/teensy_lc_onekey/Makefile b/keyboard/teensy_lc_onekey/Makefile index dc489cb7..e4424dda 100644 --- a/keyboard/teensy_lc_onekey/Makefile +++ b/keyboard/teensy_lc_onekey/Makefile @@ -54,8 +54,8 @@ MCU_STARTUP = kl2x BOARD = PJRC_TEENSY_LC # Cortex version -# Teensy LC is cortex-m0; Teensy 3.x are cortex-m4 -MCU = cortex-m0 +# Teensy LC is cortex-m0plus; Teensy 3.x are cortex-m4 +MCU = cortex-m0plus # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 # I.e. 6 for Teensy LC; 7 for Teensy 3.x From 58867094c489e09c79fb0d906f280e3f9db7cc5d Mon Sep 17 00:00:00 2001 From: flabbergast Date: Wed, 30 Dec 2015 10:46:08 +0100 Subject: [PATCH 54/56] Chibios: add 'core/protocol' to the makefiles' search path. --- tmk_core/tool/chibios/chibios.mk | 2 +- tmk_core/tool/chibios/common.mk | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/tmk_core/tool/chibios/chibios.mk b/tmk_core/tool/chibios/chibios.mk index 0f6af53d..a67e067a 100644 --- a/tmk_core/tool/chibios/chibios.mk +++ b/tmk_core/tool/chibios/chibios.mk @@ -152,7 +152,7 @@ INCDIR = $(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \ $(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \ $(CHIBIOS)/os/hal/lib/streams $(CHIBIOS)/os/various \ $(TMK_DIR) $(COMMON_DIR) $(TMK_DIR)/protocol/chibios \ - $(TARGET_DIR) + $(TMK_DIR)/protocol $(TARGET_DIR) # # Project, sources and paths diff --git a/tmk_core/tool/chibios/common.mk b/tmk_core/tool/chibios/common.mk index 883054a4..7e3cccc9 100644 --- a/tmk_core/tool/chibios/common.mk +++ b/tmk_core/tool/chibios/common.mk @@ -83,6 +83,3 @@ OPT_DEFS += -DVERSION=$(shell (git describe --always --dirty || echo 'unknown') ifdef BOOTLOADER_ADDRESS OPT_DEFS += -DBOOTLOADER_ADDRESS=$(BOOTLOADER_ADDRESS) endif - -# Search Path -VPATH += $(TMK_DIR)/common From 5ef7d89406da5e3eda1196805dcc842787fa01cb Mon Sep 17 00:00:00 2001 From: flabbergast Date: Fri, 15 Jan 2016 21:41:51 +0000 Subject: [PATCH 55/56] Chibios: Update to new USB API. --- tmk_core/protocol/chibios/usb_main.c | 131 +++++++++++++++------------ 1 file changed, 73 insertions(+), 58 deletions(-) diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index ee2788e5..9097d4e8 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -52,11 +52,12 @@ uint8_t extra_report_blank[3] = {0}; #endif /* EXTRAKEY_ENABLE */ #ifdef CONSOLE_ENABLE -/* The emission queue */ -output_queue_t console_queue; -static uint8_t console_queue_buffer[CONSOLE_QUEUE_BUFFER_SIZE]; +/* The emission buffers queue */ +output_buffers_queue_t console_buf_queue; +static uint8_t console_queue_buffer[BQ_BUFFER_SIZE(CONSOLE_QUEUE_CAPACITY, CONSOLE_EPSIZE)]; + static virtual_timer_t console_flush_timer; -void console_queue_onotify(io_queue_t *qp); +void console_queue_onotify(io_buffers_queue_t *bqp); static void console_flush_cb(void *arg); #endif /* CONSOLE_ENABLE */ @@ -1019,7 +1020,7 @@ void init_usb_driver(USBDriver *usbp) { chVTObjectInit(&keyboard_idle_timer); #ifdef CONSOLE_ENABLE - oqObjectInit(&console_queue, console_queue_buffer, sizeof(console_queue_buffer), console_queue_onotify, (void *)usbp); + obqObjectInit(&console_buf_queue, console_queue_buffer, CONSOLE_EPSIZE, CONSOLE_QUEUE_CAPACITY, console_queue_onotify, (void*)usbp); chVTObjectInit(&console_flush_timer); #endif } @@ -1093,10 +1094,7 @@ static void keyboard_idle_timer_cb(void *arg) { if(keyboard_idle) { #endif /* NKRO_ENABLE */ /* TODO: are we sure we want the KBD_ENDPOINT? */ - osalSysUnlockFromISR(); - usbPrepareTransmit(usbp, KBD_ENDPOINT, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent)); - osalSysLockFromISR(); - usbStartTransmitI(usbp, KBD_ENDPOINT); + usbStartTransmitI(usbp, KBD_ENDPOINT, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent)); /* rearm the timer */ chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); } @@ -1121,26 +1119,28 @@ void send_keyboard(report_keyboard_t *report) { } osalSysUnlock(); - bool ep_not_ready; #ifdef NKRO_ENABLE if(keyboard_nkro) { /* NKRO protocol */ - usbPrepareTransmit(&USB_DRIVER, NKRO_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t)); /* need to wait until the previous packet has made it through */ - do { - osalSysLock(); - ep_not_ready = usbStartTransmitI(&USB_DRIVER, NKRO_ENDPOINT); - osalSysUnlock(); - } while (ep_not_ready); + /* can rewrite this using the synchronous API, then would wait + * until *after* the packet has been transmitted. I think + * this is more efficient */ + /* busy wait, should be short and not very common */ + osalSysLock(); + while(usbGetTransmitStatusI(&USB_DRIVER, NKRO_ENDPOINT)) + ; + usbStartTransmitI(&USB_DRIVER, NKRO_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t)); + osalSysUnlock(); } else #endif /* NKRO_ENABLE */ { /* boot protocol */ - usbPrepareTransmit(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)report, KBD_EPSIZE); /* need to wait until the previous packet has made it through */ - do { - osalSysLock(); - ep_not_ready = usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT); - osalSysUnlock(); - } while (ep_not_ready); + /* busy wait, should be short and not very common */ + osalSysLock(); + while(usbGetTransmitStatusI(&USB_DRIVER, KBD_ENDPOINT)) + ; + usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)report, KBD_EPSIZE); + osalSysUnlock(); } keyboard_report_sent = *report; } @@ -1171,9 +1171,8 @@ void send_mouse(report_mouse_t *report) { * is this really needed? */ - usbPrepareTransmit(&USB_DRIVER, MOUSE_ENDPOINT, (uint8_t *)report, sizeof(report_mouse_t)); osalSysLock(); - usbStartTransmitI(&USB_DRIVER, MOUSE_ENDPOINT); + usbStartTransmitI(&USB_DRIVER, MOUSE_ENDPOINT, (uint8_t *)report, sizeof(report_mouse_t)); osalSysUnlock(); } @@ -1209,10 +1208,7 @@ static void send_extra_report(uint8_t report_id, uint16_t data) { .usage = data }; - osalSysUnlock(); - usbPrepareTransmit(&USB_DRIVER, EXTRA_ENDPOINT, (uint8_t *)&report, sizeof(report_extra_t)); - osalSysLock(); - usbStartTransmitI(&USB_DRIVER, EXTRA_ENDPOINT); + usbStartTransmitI(&USB_DRIVER, EXTRA_ENDPOINT, (uint8_t *)&report, sizeof(report_extra_t)); osalSysUnlock(); } @@ -1240,20 +1236,32 @@ void send_consumer(uint16_t data) { #ifdef CONSOLE_ENABLE -/* debug IN callback hander */ +/* console IN callback hander */ void console_in_cb(USBDriver *usbp, usbep_t ep) { - (void)ep; + (void)ep; /* should have ep == CONSOLE_ENDPOINT, so use that to save time/space */ + uint8_t *buf; + size_t n; + osalSysLockFromISR(); /* rearm the timer */ chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); - /* Check if there is data to send left in the output queue */ - if(chOQGetFullI(&console_queue) >= CONSOLE_EPSIZE) { - osalSysUnlockFromISR(); - usbPrepareQueuedTransmit(usbp, CONSOLE_ENDPOINT, &console_queue, CONSOLE_EPSIZE); - osalSysLockFromISR(); - usbStartTransmitI(usbp, CONSOLE_ENDPOINT); + /* Freeing the buffer just transmitted, if it was not a zero size packet.*/ + if (usbp->epc[CONSOLE_ENDPOINT]->in_state->txsize > 0U) { + obqReleaseEmptyBufferI(&console_buf_queue); + } + + /* Checking if there is a buffer ready for transmission.*/ + buf = obqGetFullBufferI(&console_buf_queue, &n); + + if (buf != NULL) { + /* The endpoint cannot be busy, we are in the context of the callback, + so it is safe to transmit without a check.*/ + /* Should have n == CONSOLE_EPSIZE; check it? */ + usbStartTransmitI(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE); + } else { + /* Nothing to transmit.*/ } osalSysUnlockFromISR(); @@ -1261,18 +1269,22 @@ void console_in_cb(USBDriver *usbp, usbep_t ep) { /* Callback when data is inserted into the output queue * Called from a locked state */ -void console_queue_onotify(io_queue_t *qp) { - USBDriver *usbp = qGetLink(qp); +void console_queue_onotify(io_buffers_queue_t *bqp) { + size_t n; + USBDriver *usbp = bqGetLinkX(bqp); if(usbGetDriverStateI(usbp) != USB_ACTIVE) return; - if(!usbGetTransmitStatusI(usbp, CONSOLE_ENDPOINT) - && (chOQGetFullI(&console_queue) >= CONSOLE_EPSIZE)) { - osalSysUnlock(); - usbPrepareQueuedTransmit(usbp, CONSOLE_ENDPOINT, &console_queue, CONSOLE_EPSIZE); - osalSysLock(); - usbStartTransmitI(usbp, CONSOLE_ENDPOINT); + /* Checking if there is already a transaction ongoing on the endpoint.*/ + if (!usbGetTransmitStatusI(usbp, CONSOLE_ENDPOINT)) { + /* Trying to get a full buffer.*/ + uint8_t *buf = obqGetFullBufferI(&console_buf_queue, &n); + if (buf != NULL) { + /* Buffer found, starting a new transaction.*/ + /* Should have n == CONSOLE_EPSIZE; check this? */ + usbStartTransmitI(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE); + } } } @@ -1280,8 +1292,6 @@ void console_queue_onotify(io_queue_t *qp) { * callback (called from ISR, unlocked state) */ static void console_flush_cb(void *arg) { USBDriver *usbp = (USBDriver *)arg; - size_t i, n; - uint8_t buf[CONSOLE_EPSIZE]; /* TODO: a solution without extra buffer? */ osalSysLockFromISR(); /* check that the states of things are as they're supposed to */ @@ -1292,23 +1302,28 @@ static void console_flush_cb(void *arg) { return; } - /* don't do anything if the queue is empty or has enough stuff in it */ - if(((n = oqGetFullI(&console_queue)) == 0) || (n >= CONSOLE_EPSIZE)) { + /* If there is already a transaction ongoing then another one cannot be + started.*/ + if (usbGetTransmitStatusI(usbp, CONSOLE_ENDPOINT)) { /* rearm the timer */ chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); osalSysUnlockFromISR(); return; } - /* there's stuff hanging in the queue - so dequeue and send */ - for(i = 0; i < n; i++) - buf[i] = (uint8_t)oqGetI(&console_queue); - for(i = n; i < CONSOLE_EPSIZE; i++) - buf[i] = 0; - osalSysUnlockFromISR(); - usbPrepareTransmit(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE); - osalSysLockFromISR(); - (void)usbStartTransmitI(usbp, CONSOLE_ENDPOINT); + /* Checking if there only a buffer partially filled, if so then it is + enforced in the queue and transmitted.*/ + if(obqTryFlushI(&console_buf_queue)) { + size_t n,i; + uint8_t *buf = obqGetFullBufferI(&console_buf_queue, &n); + + osalDbgAssert(buf != NULL, "queue is empty"); + + /* zero the rest of the buffer (buf should point to allocated space) */ + for(i=n; i Date: Fri, 22 Jan 2016 22:19:02 +0000 Subject: [PATCH 56/56] Chibios: add more guards for transmitting (fix a deadlock bug). --- keyboard/infinity_chibios/halconf.h | 12 ++++++++++++ keyboard/stm32_f072_onekey/halconf.h | 12 ++++++++++++ keyboard/stm32_f103_onekey/halconf.h | 12 ++++++++++++ keyboard/teensy_lc_onekey/halconf.h | 12 ++++++++++++ tmk_core/protocol/chibios/usb_main.c | 22 +++++++++++++++++----- 5 files changed, 65 insertions(+), 5 deletions(-) diff --git a/keyboard/infinity_chibios/halconf.h b/keyboard/infinity_chibios/halconf.h index 61a9ac0a..53538de1 100644 --- a/keyboard/infinity_chibios/halconf.h +++ b/keyboard/infinity_chibios/halconf.h @@ -336,6 +336,18 @@ #define SPI_USE_MUTUAL_EXCLUSION TRUE #endif +/*===========================================================================*/ +/* USB driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables synchronous APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__) +#define USB_USE_WAIT TRUE +#endif + #endif /* _HALCONF_H_ */ /** @} */ diff --git a/keyboard/stm32_f072_onekey/halconf.h b/keyboard/stm32_f072_onekey/halconf.h index e75c1c36..1a450d63 100644 --- a/keyboard/stm32_f072_onekey/halconf.h +++ b/keyboard/stm32_f072_onekey/halconf.h @@ -336,6 +336,18 @@ #define SPI_USE_MUTUAL_EXCLUSION TRUE #endif +/*===========================================================================*/ +/* USB driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables synchronous APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__) +#define USB_USE_WAIT TRUE +#endif + #endif /* _HALCONF_H_ */ /** @} */ diff --git a/keyboard/stm32_f103_onekey/halconf.h b/keyboard/stm32_f103_onekey/halconf.h index e75c1c36..1a450d63 100644 --- a/keyboard/stm32_f103_onekey/halconf.h +++ b/keyboard/stm32_f103_onekey/halconf.h @@ -336,6 +336,18 @@ #define SPI_USE_MUTUAL_EXCLUSION TRUE #endif +/*===========================================================================*/ +/* USB driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables synchronous APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__) +#define USB_USE_WAIT TRUE +#endif + #endif /* _HALCONF_H_ */ /** @} */ diff --git a/keyboard/teensy_lc_onekey/halconf.h b/keyboard/teensy_lc_onekey/halconf.h index 86839166..b1d2cd55 100644 --- a/keyboard/teensy_lc_onekey/halconf.h +++ b/keyboard/teensy_lc_onekey/halconf.h @@ -170,6 +170,18 @@ #define HAL_USE_WDG FALSE #endif +/*===========================================================================*/ +/* USB driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables synchronous APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__) +#define USB_USE_WAIT TRUE +#endif + #endif /* _HALCONF_H_ */ /** @} */ diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 9097d4e8..d248b447 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -1094,7 +1094,9 @@ static void keyboard_idle_timer_cb(void *arg) { if(keyboard_idle) { #endif /* NKRO_ENABLE */ /* TODO: are we sure we want the KBD_ENDPOINT? */ - usbStartTransmitI(usbp, KBD_ENDPOINT, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent)); + if(!usbGetTransmitStatusI(usbp, KBD_ENDPOINT)) { + usbStartTransmitI(usbp, KBD_ENDPOINT, (uint8_t *)&keyboard_report_sent, KBD_EPSIZE); + } /* rearm the timer */ chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); } @@ -1127,8 +1129,13 @@ void send_keyboard(report_keyboard_t *report) { * this is more efficient */ /* busy wait, should be short and not very common */ osalSysLock(); - while(usbGetTransmitStatusI(&USB_DRIVER, NKRO_ENDPOINT)) - ; + if(usbGetTransmitStatusI(&USB_DRIVER, NKRO_ENDPOINT)) { + /* Need to either suspend, or loop and call unlock/lock during + * every iteration - otherwise the system will remain locked, + * no interrupts served, so USB not going through as well. + * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ + osalThreadSuspendS(&(&USB_DRIVER)->epc[NKRO_ENDPOINT]->in_state->thread); + } usbStartTransmitI(&USB_DRIVER, NKRO_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t)); osalSysUnlock(); } else @@ -1137,8 +1144,13 @@ void send_keyboard(report_keyboard_t *report) { /* need to wait until the previous packet has made it through */ /* busy wait, should be short and not very common */ osalSysLock(); - while(usbGetTransmitStatusI(&USB_DRIVER, KBD_ENDPOINT)) - ; + if(usbGetTransmitStatusI(&USB_DRIVER, KBD_ENDPOINT)) { + /* Need to either suspend, or loop and call unlock/lock during + * every iteration - otherwise the system will remain locked, + * no interrupts served, so USB not going through as well. + * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ + osalThreadSuspendS(&(&USB_DRIVER)->epc[KBD_ENDPOINT]->in_state->thread); + } usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)report, KBD_EPSIZE); osalSysUnlock(); }