diff --git a/.gitmodules b/.gitmodules index 681693a5a4..7bbb7910a1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,3 +18,6 @@ [submodule "lib/printf"] path = lib/printf url = https://github.com/qmk/printf +[submodule "lib/pico-sdk"] + path = lib/pico-sdk + url = https://github.com/raspberrypi/pico-sdk diff --git a/Makefile b/Makefile index 4c2e6a04dc..fc6d016448 100644 --- a/Makefile +++ b/Makefile @@ -401,6 +401,7 @@ ifndef SKIP_GIT if [ ! -e lib/lufa ]; then git submodule sync lib/lufa && git submodule update --depth 50 --init lib/lufa; fi if [ ! -e lib/vusb ]; then git submodule sync lib/vusb && git submodule update --depth 50 --init lib/vusb; fi if [ ! -e lib/printf ]; then git submodule sync lib/printf && git submodule update --depth 50 --init lib/printf; fi + if [ ! -e lib/pico-sdk ]; then git submodule sync lib/pico-sdk && git submodule update --depth 50 --init lib/pico-sdk; fi git submodule status --recursive 2>/dev/null | \ while IFS= read -r x; do \ case "$$x" in \ diff --git a/builddefs/build_keyboard.mk b/builddefs/build_keyboard.mk index 50956c9780..588c070a08 100644 --- a/builddefs/build_keyboard.mk +++ b/builddefs/build_keyboard.mk @@ -177,6 +177,18 @@ ifeq ($(strip $(CONVERT_TO_PROTON_C)), yes) include platforms/chibios/boards/QMK_PROTON_C/convert_to_proton_c.mk endif +ifeq ($(strip $(CTPIM)), yes) + CONVERT_TO_PICO_MICRO=yes +endif + +ifeq ($(strip $(CONVERT_TO_PICO_MICRO)), yes) + include platforms/pico/convert_to_pico_micro.mk +endif + +ifneq ($(FORCE_LAYOUT),) + TARGET := $(TARGET)_$(FORCE_LAYOUT) +endif + include $(BUILDDEFS_PATH)/mcu_selection.mk # Find all the C source files to be compiled in subfolders. @@ -261,6 +273,12 @@ ifdef MCU_FAMILY PLATFORM_KEY=chibios FIRMWARE_FORMAT?=bin OPT_DEFS += -DMCU_$(MCU_FAMILY) + ifeq ($(MCU_FAMILY),PICO) + $(info "PLATFORM RP2040") + PLATFORM=PICO_SDK + PLATFORM_KEY=pico + FIRMWARE_FORMAT=elf + endif else ifdef ARM_ATSAM PLATFORM=ARM_ATSAM PLATFORM_KEY=arm_atsam diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index 4819f66787..661ec2c8b4 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -207,6 +207,8 @@ else COMMON_VPATH += $(DRIVER_PATH)/eeprom SRC += eeprom_driver.c eeprom_transient.c endif + else ifeq ($(PLATFORM),PICO_SDK) + SRC += $(PLATFORM_COMMON_DIR)/eeprom.c else ifeq ($(PLATFORM),ARM_ATSAM) # arm_atsam EEPROM OPT_DEFS += -DEEPROM_SAMD diff --git a/builddefs/common_rules.mk b/builddefs/common_rules.mk index 6dfcf2b7f7..858adc5ae9 100644 --- a/builddefs/common_rules.mk +++ b/builddefs/common_rules.mk @@ -53,7 +53,7 @@ OPT ?= s # gnu89 = c89 plus GCC extensions # c99 = ISO C99 standard (not yet fully implemented) # gnu99 = c99 plus GCC extensions -CSTANDARD = -std=gnu99 +CSTANDARD = -std=gnu11 # Place -D or -U options here for C sources @@ -86,8 +86,12 @@ ifeq ($(strip $(LTO_ENABLE)), yes) $(info Enabling LTO on ChibiOS-targeting boards is known to have a high likelihood of failure.) $(info If unsure, set LTO_ENABLE = no.) endif - CDEFS += -flto - CDEFS += -DLTO_ENABLE + ifeq ($(PLATFORM),PICO_SDK) + $(info LTO is not available for this platform. Disabled automatically.) + else + CDEFS += -flto + CDEFS += -DLTO_ENABLE + endif endif DEBUG_ENABLE ?= yes diff --git a/data/schemas/definitions.jsonschema b/data/schemas/definitions.jsonschema index 46aba52cbd..83cf64f344 100644 --- a/data/schemas/definitions.jsonschema +++ b/data/schemas/definitions.jsonschema @@ -89,6 +89,10 @@ "type": "string", "pattern": "^LINE_PIN\\d{1,2}$" }, + { + "type": "string", + "pattern": "^\\d{1,2}$" + }, { "type": "number", "multipleOf": 1 diff --git a/keyboards/rp2040_example/config.h b/keyboards/rp2040_example/config.h new file mode 100644 index 0000000000..d6141803c2 --- /dev/null +++ b/keyboards/rp2040_example/config.h @@ -0,0 +1,155 @@ +/* +Copyright 2021 sekigon-gonnoc + +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 . +*/ + +#pragma once + +#include "config_common.h" + +/* USB Device descriptor parameter */ +#define VENDOR_ID 0xFEED +#define PRODUCT_ID 0x0000 +#define DEVICE_VER 0x0001 +#define MANUFACTURER sekigon-gonnoc +#define PRODUCT rp2040_example + +/* key matrix size */ +#define MATRIX_ROWS 2 +#define MATRIX_COLS 3 + +#define DEBUG_MATRIX_SCAN_RATE +#define DEBUG_ACTION + +/* + * Keyboard Matrix Assignments + * + * Change this to how you wired your keyboard + * COLS: AVR pins used for columns, left to right + * ROWS: AVR pins used for rows, top to bottom + * DIODE_DIRECTION: COL2ROW = COL = Anode (+), ROW = Cathode (-, marked on diode) + * ROW2COL = ROW = Anode (+), COL = Cathode (-, marked on diode) + * + */ +#define MATRIX_ROW_PINS { 0, 1 } +#define MATRIX_COL_PINS { 2, 3, 4 } +#define UNUSED_PINS + +/* COL2ROW, ROW2COL */ +#define DIODE_DIRECTION COL2ROW + +/* + * Split Keyboard specific options, make sure you have 'SPLIT_KEYBOARD = yes' in your rules.mk, and define SOFT_SERIAL_PIN. + */ +#define SOFT_SERIAL_PIN D0 // or D1, D2, D3, E6 + +//#define LED_NUM_LOCK_PIN B0 +//#define LED_CAPS_LOCK_PIN B1 +//#define LED_SCROLL_LOCK_PIN B2 +//#define LED_COMPOSE_PIN B3 +//#define LED_KANA_PIN B4 + +//#define BACKLIGHT_PIN B7 +//#define BACKLIGHT_LEVELS 3 +//#define BACKLIGHT_BREATHING + +//#define RGB_DI_PIN E2 +//#ifdef RGB_DI_PIN +//# define RGBLED_NUM 16 +//# define RGBLIGHT_HUE_STEP 8 +//# define RGBLIGHT_SAT_STEP 8 +//# define RGBLIGHT_VAL_STEP 8 +//# define RGBLIGHT_LIMIT_VAL 255 /* The maximum brightness level */ +//# define RGBLIGHT_SLEEP /* If defined, the RGB lighting will be switched off when the host goes to sleep */ +/*== all animations enable ==*/ +//# define RGBLIGHT_ANIMATIONS +/*== or choose animations ==*/ +//# define RGBLIGHT_EFFECT_BREATHING +//# define RGBLIGHT_EFFECT_RAINBOW_MOOD +//# define RGBLIGHT_EFFECT_RAINBOW_SWIRL +//# define RGBLIGHT_EFFECT_SNAKE +//# define RGBLIGHT_EFFECT_KNIGHT +//# define RGBLIGHT_EFFECT_CHRISTMAS +//# define RGBLIGHT_EFFECT_STATIC_GRADIENT +//# define RGBLIGHT_EFFECT_RGB_TEST +//# define RGBLIGHT_EFFECT_ALTERNATING +/*== customize breathing effect ==*/ +/*==== (DEFAULT) use fixed table instead of exp() and sin() ====*/ +//# define RGBLIGHT_BREATHE_TABLE_SIZE 256 // 256(default) or 128 or 64 +/*==== use exp() and sin() ====*/ +//# define RGBLIGHT_EFFECT_BREATHE_CENTER 1.85 // 1 to 2.7 +//# define RGBLIGHT_EFFECT_BREATHE_MAX 255 // 0 to 255 +//#endif + +/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */ +#define DEBOUNCE 5 + +/* define if matrix has ghost (lacks anti-ghosting diodes) */ +//#define MATRIX_HAS_GHOST + +/* 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 + +/* If defined, GRAVE_ESC will always act as ESC when CTRL is held. + * This is useful for the Windows task manager shortcut (ctrl+shift+esc). + */ +//#define GRAVE_ESC_CTRL_OVERRIDE + +/* + * Force NKRO + * + * Force NKRO (nKey Rollover) to be enabled by default, regardless of the saved + * state in the bootmagic EEPROM settings. (Note that NKRO must be enabled in the + * makefile for this to work.) + * + * If forced on, NKRO can be disabled via magic key (default = LShift+RShift+N) + * until the next keyboard reset. + * + * NKRO may prevent your keystrokes from being detected in the BIOS, but it is + * fully operational during normal computer usage. + * + * For a less heavy-handed approach, enable NKRO via magic key (LShift+RShift+N) + * or via bootmagic (hold SPACE+N while plugging in the keyboard). Once set by + * bootmagic, NKRO mode will always be enabled until it is toggled again during a + * power-up. + * + */ +//#define FORCE_NKRO + +/* + * 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 + +/* disable these deprecated features by default */ +#define NO_ACTION_MACRO +#define NO_ACTION_FUNCTION + +/* Bootmagic Lite key configuration */ +//#define BOOTMAGIC_LITE_ROW 0 +//#define BOOTMAGIC_LITE_COLUMN 0 diff --git a/keyboards/rp2040_example/info.json b/keyboards/rp2040_example/info.json new file mode 100644 index 0000000000..681b326dfc --- /dev/null +++ b/keyboards/rp2040_example/info.json @@ -0,0 +1,19 @@ +{ + "keyboard_name": "rp2040_example", + "url": "", + "maintainer": "sekigon-gonnoc", + "width": 3, + "height": 2, + "layouts": { + "LAYOUT": { + "layout": [ + {"label": "k00", "x": 0, "y": 0}, + {"label": "k01", "x": 1, "y": 0}, + {"label": "k02", "x": 2, "y": 0}, + + {"label": "k10", "x": 0, "y": 1, "w": 1.5}, + {"label": "k12", "x": 1.5, "y": 1, "w": 1.5} + ] + } + } +} diff --git a/keyboards/rp2040_example/keymaps/adc/keymap.c b/keyboards/rp2040_example/keymaps/adc/keymap.c new file mode 100644 index 0000000000..2ef00700fd --- /dev/null +++ b/keyboards/rp2040_example/keymaps/adc/keymap.c @@ -0,0 +1,85 @@ +/* Copyright 2021 sekigon-gonnoc + * + * 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 QMK_KEYBOARD_H +#include "pico_eeprom.h" +#include "analog.h" + +// Defines names for use in layer keycodes and the keymap +enum layer_names { + _BASE, + _FN +}; + +// Defines the keycodes used by our macros in process_record_user +enum custom_keycodes { + QMKBEST = SAFE_RANGE, + QMKURL +}; + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + /* Base */ + [_BASE] = LAYOUT( + KC_A, KC_1, MO(_FN), + KC_TAB, KC_SPC + ), + [_FN] = LAYOUT( + QMKBEST, QMKURL, _______, + RESET, XXXXXXX + ) +}; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case QMKBEST: + if (record->event.pressed) { + // when keycode QMKBEST is pressed + SEND_STRING("QMK is the best thing ever!"); + } else { + // when keycode QMKBEST is released + } + break; + case QMKURL: + if (record->event.pressed) { + // when keycode QMKURL is pressed + SEND_STRING("https://qmk.fm/\n"); + } else { + // when keycode QMKURL is released + } + break; + + case KC_FN0: + if (record->event.pressed) { + printf("save keymap to eeprom\n"); + pico_eepemu_flash_dynamic_keymap(); + printf("complete\n"); + printf("save eeconfig to eeprom\n"); + pico_eepemu_flash_eeconfig(); + printf("complete\n"); + } + return false; + break; + } + return true; +} + +void matrix_scan_user(void) { + static int cnt = 0; + if (++cnt == 1000) { + cnt = 0; + int16_t ad = analogReadPin(26); + printf("ADC:%d\n", ad); + } +} diff --git a/keyboards/rp2040_example/keymaps/adc/readme.md b/keyboards/rp2040_example/keymaps/adc/readme.md new file mode 100644 index 0000000000..7f32c76f01 --- /dev/null +++ b/keyboards/rp2040_example/keymaps/adc/readme.md @@ -0,0 +1 @@ +# The default keymap for rp2040_example diff --git a/keyboards/rp2040_example/keymaps/adc/rules.mk b/keyboards/rp2040_example/keymaps/adc/rules.mk new file mode 100644 index 0000000000..9ab0f66996 --- /dev/null +++ b/keyboards/rp2040_example/keymaps/adc/rules.mk @@ -0,0 +1,2 @@ + +SRC += analog.c diff --git a/keyboards/rp2040_example/keymaps/default/keymap.c b/keyboards/rp2040_example/keymaps/default/keymap.c new file mode 100644 index 0000000000..3cd7c597f6 --- /dev/null +++ b/keyboards/rp2040_example/keymaps/default/keymap.c @@ -0,0 +1,75 @@ +/* Copyright 2021 sekigon-gonnoc + * + * 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 QMK_KEYBOARD_H +#include "pico_eeprom.h" + +// Defines names for use in layer keycodes and the keymap +enum layer_names { + _BASE, + _FN +}; + +// Defines the keycodes used by our macros in process_record_user +enum custom_keycodes { + QMKBEST = SAFE_RANGE, + QMKURL +}; + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + /* Base */ + [_BASE] = LAYOUT( + KC_A, KC_1, MO(_FN), + KC_TAB, KC_SPC + ), + [_FN] = LAYOUT( + QMKBEST, QMKURL, _______, + RESET, XXXXXXX + ) +}; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case QMKBEST: + if (record->event.pressed) { + // when keycode QMKBEST is pressed + SEND_STRING("QMK is the best thing ever!"); + } else { + // when keycode QMKBEST is released + } + break; + case QMKURL: + if (record->event.pressed) { + // when keycode QMKURL is pressed + SEND_STRING("https://qmk.fm/\n"); + } else { + // when keycode QMKURL is released + } + break; + + case KC_FN0: + if (record->event.pressed) { + printf("save keymap to eeprom\n"); + pico_eepemu_flash_dynamic_keymap(); + printf("complete\n"); + printf("save eeconfig to eeprom\n"); + pico_eepemu_flash_eeconfig(); + printf("complete\n"); + } + return false; + break; + } + return true; +} diff --git a/keyboards/rp2040_example/keymaps/default/readme.md b/keyboards/rp2040_example/keymaps/default/readme.md new file mode 100644 index 0000000000..7f32c76f01 --- /dev/null +++ b/keyboards/rp2040_example/keymaps/default/readme.md @@ -0,0 +1 @@ +# The default keymap for rp2040_example diff --git a/keyboards/rp2040_example/keymaps/rgblight/config.h b/keyboards/rp2040_example/keymaps/rgblight/config.h new file mode 100644 index 0000000000..af26ccf9a3 --- /dev/null +++ b/keyboards/rp2040_example/keymaps/rgblight/config.h @@ -0,0 +1,6 @@ + +#pragma once + +#define RGB_DI_PIN 5 +#define RGBLED_NUM 1 +#define RGBLIGHT_ANIMATIONS diff --git a/keyboards/rp2040_example/keymaps/rgblight/keymap.c b/keyboards/rp2040_example/keymaps/rgblight/keymap.c new file mode 100644 index 0000000000..a4479f7a38 --- /dev/null +++ b/keyboards/rp2040_example/keymaps/rgblight/keymap.c @@ -0,0 +1,83 @@ +/* Copyright 2021 sekigon-gonnoc + * + * 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 QMK_KEYBOARD_H +#include "pico_eeprom.h" + +// Defines names for use in layer keycodes and the keymap +enum layer_names { + _BASE, + _FN +}; + +// Defines the keycodes used by our macros in process_record_user +enum custom_keycodes { + QMKBEST = SAFE_RANGE, + QMKURL +}; + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + /* Base */ + [_BASE] = LAYOUT( + KC_A, KC_1, MO(_FN), + KC_TAB, KC_SPC + ), + [_FN] = LAYOUT( + QMKBEST, QMKURL, _______, + RESET, XXXXXXX + ) +}; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case QMKBEST: + if (record->event.pressed) { + // when keycode QMKBEST is pressed + SEND_STRING("QMK is the best thing ever!"); + } else { + // when keycode QMKBEST is released + } + break; + case QMKURL: + if (record->event.pressed) { + // when keycode QMKURL is pressed + SEND_STRING("https://qmk.fm/\n"); + } else { + // when keycode QMKURL is released + } + break; + + case KC_FN0: + if (record->event.pressed) { + printf("save keymap to eeprom\n"); + pico_eepemu_flash_dynamic_keymap(); + printf("complete\n"); + printf("save eeconfig to eeprom\n"); + pico_eepemu_flash_eeconfig(); + printf("complete\n"); + } + return false; + break; + } + return true; +} + +#include "rgblight.h" + +void keyboard_post_init_user(void) { + rgblight_enable(); + rgblight_mode_noeeprom(35); +} + diff --git a/keyboards/rp2040_example/keymaps/rgblight/readme.md b/keyboards/rp2040_example/keymaps/rgblight/readme.md new file mode 100644 index 0000000000..7f32c76f01 --- /dev/null +++ b/keyboards/rp2040_example/keymaps/rgblight/readme.md @@ -0,0 +1 @@ +# The default keymap for rp2040_example diff --git a/keyboards/rp2040_example/keymaps/rgblight/rules.mk b/keyboards/rp2040_example/keymaps/rgblight/rules.mk new file mode 100644 index 0000000000..74dc0328d5 --- /dev/null +++ b/keyboards/rp2040_example/keymaps/rgblight/rules.mk @@ -0,0 +1,2 @@ + +RGBLIGHT_ENABLE = yes diff --git a/keyboards/rp2040_example/readme.md b/keyboards/rp2040_example/readme.md new file mode 100644 index 0000000000..3b26c5235d --- /dev/null +++ b/keyboards/rp2040_example/readme.md @@ -0,0 +1,24 @@ +# rp2040_example + +* Sample implementation for RP2040. + +* Keyboard Maintainer: [sekigon-gonnoc](https://github.com/sekigon-gonnoc) + +Make example for this keyboard (after setting up your build environment): + + make rp2040_example:default + +Flashing example for this keyboard by `picotool`: + + make rp2040_example:default:flash + +Convert firmware into uf2 format: + + make rp2040_example:default:uf2 + +To replace Pro Micro to Pico Micro, use `CTPIM=yes` (`CONVERT_TO_PICO_MICRO=yes`): + + make :: CTPIM=yes + + +See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs). diff --git a/keyboards/rp2040_example/rp2040_example.c b/keyboards/rp2040_example/rp2040_example.c new file mode 100644 index 0000000000..4c657b24ef --- /dev/null +++ b/keyboards/rp2040_example/rp2040_example.c @@ -0,0 +1,24 @@ +/* Copyright 2021 sekigon-gonnoc + * + * 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 "rp2040_example.h" + +void keyboard_post_init_kb(void) { + debug_enable = true; + debug_keyboard = true; + + keyboard_post_init_user(); +} diff --git a/keyboards/rp2040_example/rp2040_example.h b/keyboards/rp2040_example/rp2040_example.h new file mode 100644 index 0000000000..0b8cc554f1 --- /dev/null +++ b/keyboards/rp2040_example/rp2040_example.h @@ -0,0 +1,35 @@ +/* Copyright 2021 sekigon-gonnoc + * + * 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 . + */ + +#pragma once + +#include "quantum.h" + +/* This is a shortcut to help you visually see your layout. + * + * The first section contains all of the arguments representing the physical + * layout of the board and position of the keys. + * + * The second converts the arguments into a two-dimensional array which + * represents the switch matrix. + */ +#define LAYOUT( \ + k00, k01, k02, \ + k10, k12 \ +) { \ + { k00, k01, k02 }, \ + { k10, KC_NO, k12 } \ +} diff --git a/keyboards/rp2040_example/rules.mk b/keyboards/rp2040_example/rules.mk new file mode 100644 index 0000000000..804390a9a3 --- /dev/null +++ b/keyboards/rp2040_example/rules.mk @@ -0,0 +1,23 @@ +# MCU name +MCU_FAMILY = PICO +MCU_SERIES = RP2040 +MCU = cortex-m0plus + +VIA_ENABLE = yes + +# Build Options +# change yes to no to disable +# +BOOTMAGIC_ENABLE = lite # 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 = no # Commands for debug and configuration +# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend +# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work +NKRO_ENABLE = no # USB Nkey Rollover +BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality +RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow +BLUETOOTH_ENABLE = no # Enable Bluetooth +AUDIO_ENABLE = no # Audio output diff --git a/keyboards/rp2040_split/config.h b/keyboards/rp2040_split/config.h new file mode 100644 index 0000000000..68e0d25475 --- /dev/null +++ b/keyboards/rp2040_split/config.h @@ -0,0 +1,157 @@ +/* +Copyright 2021 sekigon-gonnoc + +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 . +*/ + +#pragma once + +#include "config_common.h" + +/* USB Device descriptor parameter */ +#define VENDOR_ID 0xFEED +#define PRODUCT_ID 0x0000 +#define DEVICE_VER 0x0001 +#define MANUFACTURER sekigon-gonnoc +#define PRODUCT rp2040_split + +/* key matrix size */ +#define MATRIX_ROWS 4 +#define MATRIX_COLS 3 + +#define DEBUG_MATRIX_SCAN_RATE +#define DEBUG_ACTION + +#define MASTER_LEFT + +/* + * Keyboard Matrix Assignments + * + * Change this to how you wired your keyboard + * COLS: AVR pins used for columns, left to right + * ROWS: AVR pins used for rows, top to bottom + * DIODE_DIRECTION: COL2ROW = COL = Anode (+), ROW = Cathode (-, marked on diode) + * ROW2COL = ROW = Anode (+), COL = Cathode (-, marked on diode) + * + */ +#define MATRIX_ROW_PINS { 0, 1 } +#define MATRIX_COL_PINS { 2, 3, 4 } +#define UNUSED_PINS + +/* COL2ROW, ROW2COL */ +#define DIODE_DIRECTION COL2ROW + +/* + * Split Keyboard specific options, make sure you have 'SPLIT_KEYBOARD = yes' in your rules.mk, and define SOFT_SERIAL_PIN. + */ +#define SOFT_SERIAL_PIN 6 + +//#define LED_NUM_LOCK_PIN B0 +//#define LED_CAPS_LOCK_PIN B1 +//#define LED_SCROLL_LOCK_PIN B2 +//#define LED_COMPOSE_PIN B3 +//#define LED_KANA_PIN B4 + +//#define BACKLIGHT_PIN B7 +//#define BACKLIGHT_LEVELS 3 +//#define BACKLIGHT_BREATHING + +//#define RGB_DI_PIN E2 +//#ifdef RGB_DI_PIN +//# define RGBLED_NUM 16 +//# define RGBLIGHT_HUE_STEP 8 +//# define RGBLIGHT_SAT_STEP 8 +//# define RGBLIGHT_VAL_STEP 8 +//# define RGBLIGHT_LIMIT_VAL 255 /* The maximum brightness level */ +//# define RGBLIGHT_SLEEP /* If defined, the RGB lighting will be switched off when the host goes to sleep */ +/*== all animations enable ==*/ +//# define RGBLIGHT_ANIMATIONS +/*== or choose animations ==*/ +//# define RGBLIGHT_EFFECT_BREATHING +//# define RGBLIGHT_EFFECT_RAINBOW_MOOD +//# define RGBLIGHT_EFFECT_RAINBOW_SWIRL +//# define RGBLIGHT_EFFECT_SNAKE +//# define RGBLIGHT_EFFECT_KNIGHT +//# define RGBLIGHT_EFFECT_CHRISTMAS +//# define RGBLIGHT_EFFECT_STATIC_GRADIENT +//# define RGBLIGHT_EFFECT_RGB_TEST +//# define RGBLIGHT_EFFECT_ALTERNATING +/*== customize breathing effect ==*/ +/*==== (DEFAULT) use fixed table instead of exp() and sin() ====*/ +//# define RGBLIGHT_BREATHE_TABLE_SIZE 256 // 256(default) or 128 or 64 +/*==== use exp() and sin() ====*/ +//# define RGBLIGHT_EFFECT_BREATHE_CENTER 1.85 // 1 to 2.7 +//# define RGBLIGHT_EFFECT_BREATHE_MAX 255 // 0 to 255 +//#endif + +/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */ +#define DEBOUNCE 5 + +/* define if matrix has ghost (lacks anti-ghosting diodes) */ +//#define MATRIX_HAS_GHOST + +/* 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 + +/* If defined, GRAVE_ESC will always act as ESC when CTRL is held. + * This is useful for the Windows task manager shortcut (ctrl+shift+esc). + */ +//#define GRAVE_ESC_CTRL_OVERRIDE + +/* + * Force NKRO + * + * Force NKRO (nKey Rollover) to be enabled by default, regardless of the saved + * state in the bootmagic EEPROM settings. (Note that NKRO must be enabled in the + * makefile for this to work.) + * + * If forced on, NKRO can be disabled via magic key (default = LShift+RShift+N) + * until the next keyboard reset. + * + * NKRO may prevent your keystrokes from being detected in the BIOS, but it is + * fully operational during normal computer usage. + * + * For a less heavy-handed approach, enable NKRO via magic key (LShift+RShift+N) + * or via bootmagic (hold SPACE+N while plugging in the keyboard). Once set by + * bootmagic, NKRO mode will always be enabled until it is toggled again during a + * power-up. + * + */ +//#define FORCE_NKRO + +/* + * 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 + +/* disable these deprecated features by default */ +#define NO_ACTION_MACRO +#define NO_ACTION_FUNCTION + +/* Bootmagic Lite key configuration */ +//#define BOOTMAGIC_LITE_ROW 0 +//#define BOOTMAGIC_LITE_COLUMN 0 diff --git a/keyboards/rp2040_split/info.json b/keyboards/rp2040_split/info.json new file mode 100644 index 0000000000..c99dd60670 --- /dev/null +++ b/keyboards/rp2040_split/info.json @@ -0,0 +1,19 @@ +{ + "keyboard_name": "rp2040_split", + "url": "", + "maintainer": "sekigon-gonnoc", + "width": 3, + "height": 2, + "layouts": { + "LAYOUT": { + "layout": [ + {"label": "k00", "x": 0, "y": 0}, + {"label": "k01", "x": 1, "y": 0}, + {"label": "k02", "x": 2, "y": 0}, + + {"label": "k10", "x": 0, "y": 1, "w": 1.5}, + {"label": "k12", "x": 1.5, "y": 1, "w": 1.5} + ] + } + } +} diff --git a/keyboards/rp2040_split/keymaps/default/keymap.c b/keyboards/rp2040_split/keymaps/default/keymap.c new file mode 100644 index 0000000000..60a2b5f472 --- /dev/null +++ b/keyboards/rp2040_split/keymaps/default/keymap.c @@ -0,0 +1,80 @@ +/* Copyright 2021 sekigon-gonnoc + * + * 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 QMK_KEYBOARD_H +#include "pico_eeprom.h" + +// Defines names for use in layer keycodes and the keymap +enum layer_names { + _BASE, + _FN +}; + +// Defines the keycodes used by our macros in process_record_user +enum custom_keycodes { + QMKBEST = SAFE_RANGE, + QMKURL +}; + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + /* Base */ + [_BASE] = LAYOUT( + KC_A, KC_B, MO(_FN), + KC_TAB, KC_SPC, KC_ENT, + KC_C, KC_D, MO(_FN), + KC_TAB, KC_SPC, KC_ENT + ), + [_FN] = LAYOUT( + QMKBEST, QMKURL, _______, + RESET, XXXXXXX, XXXXXXX, + QMKBEST, QMKURL, _______, + RESET, XXXXXXX, XXXXXXX + ) +}; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case QMKBEST: + if (record->event.pressed) { + // when keycode QMKBEST is pressed + SEND_STRING("QMK is the best thing ever!"); + } else { + // when keycode QMKBEST is released + } + break; + case QMKURL: + if (record->event.pressed) { + // when keycode QMKURL is pressed + SEND_STRING("https://qmk.fm/\n"); + } else { + // when keycode QMKURL is released + } + break; + + case KC_FN0: + if (record->event.pressed) { + printf("save keymap to eeprom\n"); + pico_eepemu_flash_dynamic_keymap(); + printf("complete\n"); + printf("save eeconfig to eeprom\n"); + pico_eepemu_flash_eeconfig(); + printf("complete\n"); + } + return false; + break; + } + return true; +} + diff --git a/keyboards/rp2040_split/keymaps/default/readme.md b/keyboards/rp2040_split/keymaps/default/readme.md new file mode 100644 index 0000000000..ee20e70353 --- /dev/null +++ b/keyboards/rp2040_split/keymaps/default/readme.md @@ -0,0 +1 @@ +# The default keymap for rp2040_split diff --git a/keyboards/rp2040_split/readme.md b/keyboards/rp2040_split/readme.md new file mode 100644 index 0000000000..e232c71103 --- /dev/null +++ b/keyboards/rp2040_split/readme.md @@ -0,0 +1,26 @@ +# rp2040_split + +* Sample implementation for RP2040. + +* Keyboard Maintainer: [sekigon-gonnoc](https://github.com/sekigon-gonnoc) + +Since RP2040 does not have VBUS detect ports, `SPLIT_USB_DETECT` is enabled automatically. + +Make split for this keyboard (after setting up your build environment): + + make rp2040_split:default + +Flashing split for this keyboard by `picotool`: + + make rp2040_split:default:flash + +Convert firmware into uf2 format: + + make rp2040_split:default:uf2 + +To replace Pro Micro to Pico Micro, use `CTPIM=yes` (`CONVERT_TO_PICO_MICRO=yes`): + + make :: CTPIM=yes + + +See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs). diff --git a/keyboards/rp2040_split/rp2040_split.c b/keyboards/rp2040_split/rp2040_split.c new file mode 100644 index 0000000000..bfddf8eb78 --- /dev/null +++ b/keyboards/rp2040_split/rp2040_split.c @@ -0,0 +1,23 @@ +/* Copyright 2021 sekigon-gonnoc + * + * 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 "rp2040_split.h" + +void keyboard_post_init_kb(void) { + debug_enable = true; + debug_matrix = true; + keyboard_post_init_user(); +} diff --git a/keyboards/rp2040_split/rp2040_split.h b/keyboards/rp2040_split/rp2040_split.h new file mode 100644 index 0000000000..efced41a2c --- /dev/null +++ b/keyboards/rp2040_split/rp2040_split.h @@ -0,0 +1,39 @@ +/* Copyright 2021 sekigon-gonnoc + * + * 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 . + */ + +#pragma once + +#include "quantum.h" + +/* This is a shortcut to help you visually see your layout. + * + * The first section contains all of the arguments representing the physical + * layout of the board and position of the keys. + * + * The second converts the arguments into a two-dimensional array which + * represents the switch matrix. + */ +#define LAYOUT( \ + k00, k01, k02, \ + k10, k11, k12, \ + k20, k21, k22, \ + k30, k31, k32 \ +) { \ + { k00, k01, k02 }, \ + { k10, k11, k12 }, \ + { k20, k21, k22 }, \ + { k30, k31, k32 }, \ +} diff --git a/keyboards/rp2040_split/rules.mk b/keyboards/rp2040_split/rules.mk new file mode 100644 index 0000000000..4850e94bf0 --- /dev/null +++ b/keyboards/rp2040_split/rules.mk @@ -0,0 +1,24 @@ +# MCU name +MCU_FAMILY = PICO +MCU_SERIES = RP2040 +MCU = cortex-m0plus + +SPLIT_KEYBOARD = yes +VIA_ENABLE = yes + +# Build Options +# change yes to no to disable +# +BOOTMAGIC_ENABLE = lite # 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 = no # Commands for debug and configuration +# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend +# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work +NKRO_ENABLE = no # USB Nkey Rollover +BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality +RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow +BLUETOOTH_ENABLE = no # Enable Bluetooth +AUDIO_ENABLE = no # Audio output diff --git a/lib/pico-sdk b/lib/pico-sdk new file mode 160000 index 0000000000..2062372d20 --- /dev/null +++ b/lib/pico-sdk @@ -0,0 +1 @@ +Subproject commit 2062372d203b372849d573f252cf7c6dc2800c0a diff --git a/platforms/pico/_print.h b/platforms/pico/_print.h new file mode 100644 index 0000000000..235a13cb9a --- /dev/null +++ b/platforms/pico/_print.h @@ -0,0 +1,36 @@ +/* Copyright 2012 Jun Wako */ +/* Very basic print functions, intended to be used with usb_debug_only.c + * http://www.pjrc.com/teensy/ + * Copyright (c) 2008 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: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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. + */ +#pragma once + +#include +#include "pico/stdlib.h" + +// Create user & normal print defines +#define xprintf(...) printf(__VA_ARGS__) +#define xputc(c) putc(c, stdout) +#define print(s) printf(PSTR(s)) +#define println(s) printf(PSTR(s "\r\n")) +#define uprint(s) printf(s) +#define uprintln(s) printf(s) +#define uprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) diff --git a/platforms/pico/_wait.c b/platforms/pico/_wait.c new file mode 100644 index 0000000000..1fbea2dd5e --- /dev/null +++ b/platforms/pico/_wait.c @@ -0,0 +1,89 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __OPTIMIZE__ +# pragma message "Compiler optimizations disabled; wait_cpuclock() won't work as designed" +#endif + +#define CLOCK_DELAY_NOP8 "nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t" + +__attribute__((always_inline)) static inline void wait_cpuclock(unsigned int n) { /* n: 1..135 */ + /* The argument n must be a constant expression. + * That way, compiler optimization will remove unnecessary code. */ + if (n < 1) { + return; + } + if (n > 8) { + unsigned int n8 = n / 8; + n = n - n8 * 8; + switch (n8) { + case 16: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 15: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 14: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 13: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 12: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 11: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 10: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 9: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 8: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 7: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 6: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 5: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 4: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 3: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 2: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 1: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 0: + break; + } + } + switch (n) { + case 8: + asm volatile("nop" ::: "memory"); + case 7: + asm volatile("nop" ::: "memory"); + case 6: + asm volatile("nop" ::: "memory"); + case 5: + asm volatile("nop" ::: "memory"); + case 4: + asm volatile("nop" ::: "memory"); + case 3: + asm volatile("nop" ::: "memory"); + case 2: + asm volatile("nop" ::: "memory"); + case 1: + asm volatile("nop" ::: "memory"); + case 0: + break; + } +} diff --git a/platforms/pico/_wait.h b/platforms/pico/_wait.h new file mode 100644 index 0000000000..51f29e3744 --- /dev/null +++ b/platforms/pico/_wait.h @@ -0,0 +1,24 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "pico/stdlib.h" + +#define wait_ms(ms) busy_wait_ms(ms) +#define wait_us(us) busy_wait_us(us) +#define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY) + +#include "_wait.c" diff --git a/platforms/pico/atomic_util.h b/platforms/pico/atomic_util.h new file mode 100644 index 0000000000..f3d04e1191 --- /dev/null +++ b/platforms/pico/atomic_util.h @@ -0,0 +1,35 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "hardware/sync.h" + +extern uint32_t interrupts; + +static __inline__ uint8_t __interrupt_disable__(void) { + interrupts = save_and_disable_interrupts(); + return 1; +} + +static __inline__ void __interrupt_enable__(const uint8_t *__s) { + restore_interrupts(interrupts); +} + +#define ATOMIC_BLOCK(type) for (type, __ToDo = __interrupt_disable__(); __ToDo; __ToDo = 0) +#define ATOMIC_FORCEON uint8_t sreg_save __attribute__((__cleanup__(__interrupt_enable__))) = 0 + +#define ATOMIC_BLOCK_RESTORESTATE _Static_assert(0, "ATOMIC_BLOCK_RESTORESTATE not implemented") +#define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON) diff --git a/platforms/pico/boards/pico_boards.h b/platforms/pico/boards/pico_boards.h new file mode 100644 index 0000000000..6d66b27ae6 --- /dev/null +++ b/platforms/pico/boards/pico_boards.h @@ -0,0 +1,7 @@ + + +#pragma once + +// #if defined(PICO_BOARD_PROMICRO) +#include "pico_pro_micro.h" +// #endif diff --git a/platforms/pico/boards/pico_pro_micro.h b/platforms/pico/boards/pico_pro_micro.h new file mode 100644 index 0000000000..a4611ade51 --- /dev/null +++ b/platforms/pico/boards/pico_pro_micro.h @@ -0,0 +1,21 @@ + +#pragma once + +#define D3 0 +#define D2 1 +#define D1 2 +#define D0 3 +#define D4 4 +#define C6 5 +#define D7 6 +#define E6 7 +#define B4 8 +#define B5 9 +#define B6 21 +#define B2 23 +#define B3 20 +#define B1 22 +#define F7 26 +#define F6 27 +#define F5 28 +#define F4 29 diff --git a/platforms/pico/bootloader.c b/platforms/pico/bootloader.c new file mode 100644 index 0000000000..d9886acf2f --- /dev/null +++ b/platforms/pico/bootloader.c @@ -0,0 +1,13 @@ + +#include "pico/stdlib.h" +#include "pico/bootrom.h" + +#define FLASH_SIZE (FLASHEND + 1L) + +#if !defined(BOOTLOADER_SIZE) +uint16_t bootloader_start; +#endif + +__attribute__((weak)) void bootloader_jump(void) { + reset_usb_boot(0, 0); +} diff --git a/platforms/pico/convert_to_pico_micro.mk b/platforms/pico/convert_to_pico_micro.mk new file mode 100644 index 0000000000..2cf4484583 --- /dev/null +++ b/platforms/pico/convert_to_pico_micro.mk @@ -0,0 +1,7 @@ + +TARGET := $(TARGET)_pico_micro +MCU_FAMILY := PICO +MCU_SERIES := RP2040 +MCU := cortex-m0plus + +OPT_DEFS += -DCONVERT_TO_PICO_MICRO diff --git a/platforms/pico/drivers/analog.c b/platforms/pico/drivers/analog.c new file mode 100644 index 0000000000..2293cb37fd --- /dev/null +++ b/platforms/pico/drivers/analog.c @@ -0,0 +1,23 @@ +#include "quantum.h" +#include "boards/pico_boards.h" +#include "analog.h" +#undef adc_read + +#include "hardware/adc.h" + +int16_t analogReadPin(pin_t pin) { return adc_read_pico(pinToMux(pin)); } + +__attribute__((weak)) adc_mux pinToMux(pin_t pin) { + switch (pin) { + case 26 ... 29: + return TO_MUX(pin, pin - 26); + default: + return TO_MUX(0, 0xff); + } +} + +int16_t adc_read_pico(adc_mux mux) { + adc_gpio_init(mux.pin); + adc_select_input(mux.adc); + return adc_read(); +} diff --git a/platforms/pico/drivers/analog.h b/platforms/pico/drivers/analog.h new file mode 100644 index 0000000000..9abe4a3e29 --- /dev/null +++ b/platforms/pico/drivers/analog.h @@ -0,0 +1,19 @@ + +#pragma once + +#include +#include "quantum.h" + +typedef struct { + pin_t pin; + uint8_t adc; +} adc_mux; + +#define TO_MUX(i, a) \ + (adc_mux) { i, a } + +int16_t analogReadPin(pin_t pin); +adc_mux pinToMux(pin_t pin); + +#define adc_read(mux) adc_read_pico(mux) +int16_t adc_read_pico(adc_mux mux); diff --git a/platforms/pico/drivers/i2c_master.c b/platforms/pico/drivers/i2c_master.c new file mode 100644 index 0000000000..d2559fa01f --- /dev/null +++ b/platforms/pico/drivers/i2c_master.c @@ -0,0 +1,100 @@ + +#include "quantum.h" +#include "i2c_master.h" +#undef i2c_init +#include + +#include "pico/binary_info.h" +#include "hardware/i2c.h" + +static uint8_t i2c_address; + +static i2c_status_t pico_status_to_qmk(int res) { + if (res == PICO_ERROR_GENERIC) { + return I2C_STATUS_ERROR; + } else if (res == PICO_ERROR_TIMEOUT) { + return I2C_STATUS_TIMEOUT; + } else { + return I2C_STATUS_SUCCESS; + } +} + +void i2c_init_pico(void) { + static bool is_initialised = false; + if (!is_initialised) { + is_initialised = true; + + i2c_init(I2C_DRIVER, 50 * 1000); + gpio_set_function(I2C1_SCL, GPIO_FUNC_I2C); + gpio_set_function(I2C1_SDA, GPIO_FUNC_I2C); + gpio_pull_up(I2C1_SCL); + gpio_pull_up(I2C1_SDA); + // Make the I2C pins available to picotool + bi_decl(bi_2pins_with_func(I2C1_SDA, I2C1_SCL, GPIO_FUNC_I2C)); + } +} + +i2c_status_t i2c_start(uint8_t address) { + i2c_address = address; + return I2C_STATUS_SUCCESS; +} + +i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, + uint16_t timeout) { + i2c_address = address; + + int res = i2c_write_timeout_us(I2C_DRIVER, i2c_address >> 1, data, length, + true, timeout * 1000); + + return pico_status_to_qmk(res); +} + +i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, + uint16_t timeout) { + i2c_address = address; + + int res = i2c_read_timeout_us(I2C_DRIVER, i2c_address >> 1, data, length, + true, timeout * 1000); + + return pico_status_to_qmk(res); +} + +i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, + uint16_t length, uint16_t timeout) { + i2c_address = devaddr; + + uint8_t complete_packet[length + 1]; + for (uint8_t i = 0; i < length; i++) { + complete_packet[i + 1] = data[i]; + } + complete_packet[0] = regaddr; + + int res = + i2c_write_timeout_us(I2C_DRIVER, (i2c_address >> 1), complete_packet, + length + 1, true, timeout * 1000); + + return pico_status_to_qmk(res); +} + +i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, + uint16_t length, uint16_t timeout) { + i2c_address = devaddr; + i2c_write_timeout_us(I2C_DRIVER, (i2c_address >> 1), ®addr, 1, true, + timeout * 1000); + + int res = i2c_read_timeout_us(I2C_DRIVER, i2c_address >> 1, data, length, + true, timeout * 1000); + + return pico_status_to_qmk(res); +} + +void i2c_stop(void) { + // Abort + I2C_DRIVER->hw->enable |= (1 << 1); + I2C_DRIVER->restart_on_next = false; + while (I2C_DRIVER->hw->enable & (1 << 1)) { + continue; + } + // clear abort bit + I2C_DRIVER->hw->clr_tx_abrt; +} diff --git a/platforms/pico/drivers/i2c_master.h b/platforms/pico/drivers/i2c_master.h new file mode 100644 index 0000000000..86de24918d --- /dev/null +++ b/platforms/pico/drivers/i2c_master.h @@ -0,0 +1,53 @@ +/* Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * + * 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 . + */ + +/* This library follows the convention of the AVR i2c_master library. + * As a result addresses are expected to be already shifted (addr << 1). + * I2CD1 is the default driver which corresponds to pins B6 and B7. This + * can be changed. + * Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that + * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. + */ +#pragma once + +#include + +#ifndef I2C1_SCL +# define I2C1_SCL 3 +#endif +#ifndef I2C1_SDA +# define I2C1_SDA 2 +#endif + +#ifndef I2C_DRIVER +# define I2C_DRIVER i2c1 +#endif + +typedef int16_t i2c_status_t; + +#define I2C_STATUS_SUCCESS (0) +#define I2C_STATUS_ERROR (-1) +#define I2C_STATUS_TIMEOUT (-2) + +#define i2c_init() i2c_init_pico() +void i2c_init_pico(void); +i2c_status_t i2c_start(uint8_t address); +i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout); +i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout); +i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout); +i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); +void i2c_stop(void); diff --git a/platforms/pico/drivers/serial.c b/platforms/pico/drivers/serial.c new file mode 100644 index 0000000000..21f353e92f --- /dev/null +++ b/platforms/pico/drivers/serial.c @@ -0,0 +1,425 @@ +/* + * WARNING: be careful changing this code, it is very timing dependent + */ + +#include "quantum.h" +#include "serial.h" +#include "wait.h" + +#include "uart_tx.pio.h" +#include "uart_rx.pio.h" +#include "pio_manager.h" + +#include "pico/stdlib.h" +#include "hardware/timer.h" +#include "hardware/gpio.h" +#include "hardware/sync.h" +#include "hardware/irq.h" +#include + +#ifndef SELECT_SOFT_SERIAL_SPEED +# define SELECT_SOFT_SERIAL_SPEED 1 +// TODO: correct speeds... +#endif + +// Serial pulse period in microseconds. At the moment, going lower than 12 +// causes communication failure +#if SELECT_SOFT_SERIAL_SPEED == 0 +# define SERIAL_DELAY 16 +# define SERIAL_BAUD 921600 +#elif SELECT_SOFT_SERIAL_SPEED == 1 +# define SERIAL_DELAY 16 +# define SERIAL_BAUD 230400 +#elif SELECT_SOFT_SERIAL_SPEED == 2 +# define SERIAL_DELAY 24 +# define SERIAL_BAUD 115200 +#elif SELECT_SOFT_SERIAL_SPEED == 3 +# define SERIAL_DELAY 32 +# define SERIAL_BAUD 57600 +#elif SELECT_SOFT_SERIAL_SPEED == 4 +# define SERIAL_DELAY 48 +# define SERIAL_BAUD 9600 +#elif SELECT_SOFT_SERIAL_SPEED == 5 +# define SERIAL_DELAY 64 +# define SERIAL_BAUD 4800 +#else +# error invalid SELECT_SOFT_SERIAL_SPEED value +#endif + +inline static void serial_delay(void) { wait_us(SERIAL_DELAY); } +inline static void serial_delay_half(void) { wait_us(SERIAL_DELAY / 2); } +inline static void serial_delay_blip(void) { wait_us(1); } +inline static void serial_output(void) { + setPinOutput(SOFT_SERIAL_PIN); + + gpio_set_slew_rate(SOFT_SERIAL_PIN, GPIO_SLEW_RATE_FAST); + gpio_set_drive_strength(SOFT_SERIAL_PIN, GPIO_DRIVE_STRENGTH_12MA); +} +inline static void serial_input(void) { setPinInputHigh(SOFT_SERIAL_PIN); } +inline static bool serial_read_pin(void) { return readPin(SOFT_SERIAL_PIN); } +inline static void serial_low(void) { writePinLow(SOFT_SERIAL_PIN); } +inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); } + +void interrupt_handler(uint gpio, uint32_t events); + +static PIO pio = pio0; +static uint sm_tx, sm_rx; + +inline static void soft_serial_disable_rx(void) { + pio_sm_set_enabled(pio, sm_rx, false); + pio_sm_restart(pio, sm_rx); +} + +inline static void soft_serial_enable_rx(void) { + setPinInputHigh(SOFT_SERIAL_PIN); + // pio_gpio_init is not needed + pio_sm_clear_fifos(pio, sm_rx); + pio_sm_set_enabled(pio, sm_rx, true); +} + +inline static void soft_serial_disable_tx(void) { + pio_sm_set_enabled(pio, sm_tx, false); + pio_sm_restart(pio, sm_tx); +} + +static void soft_serial_enable_tx(void) { + writePinHigh(SOFT_SERIAL_PIN); + setPinOutput(SOFT_SERIAL_PIN); + gpio_set_slew_rate(SOFT_SERIAL_PIN, GPIO_SLEW_RATE_FAST); + gpio_set_drive_strength(SOFT_SERIAL_PIN, GPIO_DRIVE_STRENGTH_12MA); + pio_sm_clear_fifos(pio, sm_tx); + pio_gpio_init(pio, SOFT_SERIAL_PIN); + pio_sm_set_enabled(pio, sm_tx, true); +} + +inline static int soft_serial_pio_init(void) { + sm_rx = pio_manager_get_empty_sm(pio); + + if (sm_rx < 0) return -1; + + int32_t offset = pio_manager_add_program(pio, sm_rx, &uart_rx_mini_program); + + if (offset < 0) return -1; + + uart_rx_mini_program_init(pio, sm_rx, offset, SOFT_SERIAL_PIN, SERIAL_BAUD); + soft_serial_disable_rx(); + + sm_tx = pio_manager_get_empty_sm(pio); + + if (sm_tx < 0) return -1; + + offset = pio_manager_add_program(pio, sm_tx, &uart_tx_program); + + if (offset < 0) return -1; + + writePinHigh(SOFT_SERIAL_PIN); + uart_tx_program_init(pio, sm_tx, offset, SOFT_SERIAL_PIN, SERIAL_BAUD); + soft_serial_disable_tx(); + + return 0; +} + +// Initialize master +void soft_serial_initiator_init(void) { + soft_serial_pio_init(); + + serial_output(); + serial_high(); +} + +// Initialize slave +void soft_serial_target_init(void) { + soft_serial_pio_init(); + + setPinInputHigh(SOFT_SERIAL_PIN); + gpio_set_irq_enabled_with_callback(SOFT_SERIAL_PIN, GPIO_IRQ_EDGE_FALL, + true, interrupt_handler); + irq_set_priority(IO_IRQ_BANK0, PICO_HIGHEST_IRQ_PRIORITY); +} + +// Used by the master to synchronize timing with the slave. +static int __attribute__((noinline)) sync_recv(void) { + serial_input(); + + volatile uint64_t timeout = time_us_64() + 10000; + while (!serial_read_pin() && time_us_64() < timeout) { + // tight_loop_contents(); + } + if (time_us_64() >= timeout) { + // dprintf("serial::NO_RESPONSE0\n"); + return -1; + } + + timeout = time_us_64() + 10000; + while (serial_read_pin() && time_us_64() < timeout) { + // tight_loop_contents(); + } + + if (time_us_64() >= timeout) { + // dprintf("serial::NO_RESPONSE1\n"); + return -1; + } + + timeout = time_us_64() + 10000; + while (!serial_read_pin() && time_us_64() < timeout) { + // tight_loop_contents(); + } + + if (time_us_64() >= timeout) { + // dprintf("serial::NO_RESPONSE2\n"); + return -2; + } + + return 0; +} + +// Used by the slave to send a synchronization signal to the master. +static void __attribute__((noinline)) sync_send(void) { + serial_output(); + + serial_low(); + serial_delay(); + + serial_high(); + while (!serial_read_pin()) { + continue; + } +} + +// Reads a byte from the serial line +static uint16_t __attribute__((noinline)) serial_read_byte(void) { + uint64_t timeout = time_us_64() + 10000; + while (pio_sm_is_rx_fifo_empty(pio, sm_rx) && (time_us_64() < timeout)) { + tight_loop_contents(); + } + + if (time_us_64() >= timeout) { + // dprintf("serial: pio_rx timeout\n"); + return 0xffff; + } else { + return uart_rx_program_getc(pio, sm_rx); + } +} + +// Sends a byte with MSB ordering +static void __attribute__((noinline)) serial_write_byte(uint8_t data) { + uart_tx_program_putc(pio, sm_tx, data); +} + +static void serial_wait_send_complete(void) { + while (pio_sm_get_tx_fifo_level(pio, sm_tx) != 0) { + continue; + } + wait_us(1000000 * 12 / (SERIAL_BAUD)); +} + +// interrupt handle to be used by the slave device +void interrupt_handler(uint gpio, uint32_t events) { + if (gpio != SOFT_SERIAL_PIN || events != GPIO_IRQ_EDGE_FALL) { + return; + } + + uint32_t interrupt_status = save_and_disable_interrupts(); + + gpio_set_irq_enabled_with_callback(SOFT_SERIAL_PIN, GPIO_IRQ_EDGE_FALL, + false, interrupt_handler); + + while (!serial_read_pin()) { + continue; + } + serial_delay_blip(); + + sync_send(); + + uint8_t checksum_computed = 0; + int sstd_index = 0; + int receive_res = 0; + uint8_t checksum_received = 0xff; + + split_transaction_desc_t *trans; + + serial_input(); + while (!serial_read_pin()) { + continue; + } + + // activate receive + soft_serial_enable_rx(); + + do { + sstd_index = serial_read_byte(); + if (sstd_index > NUM_TOTAL_TRANSACTIONS) { + printf("invalid sstd_index:%d\n", sstd_index); + receive_res = -1; + break; + } + + soft_serial_disable_rx(); + + serial_delay(); + sync_send(); + + soft_serial_enable_rx(); + + trans = &split_transaction_table[sstd_index]; + for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { + split_trans_initiator2target_buffer(trans)[i] = serial_read_byte(); + checksum_computed += split_trans_initiator2target_buffer(trans)[i]; + } + checksum_computed ^= 7; + checksum_received = serial_read_byte(); + } while (0); + + soft_serial_disable_rx(); + + if (receive_res < 0) { + // receive timeout error + serial_input(); + gpio_set_irq_enabled_with_callback(SOFT_SERIAL_PIN, GPIO_IRQ_EDGE_FALL, + true, interrupt_handler); + restore_interrupts(interrupt_status); + + return; + } + + serial_delay(); + sync_send(); + + soft_serial_enable_tx(); + + // wait for the sync to finish sending + serial_delay(); + + // Allow any slave processing to occur + if (trans->slave_callback) { + trans->slave_callback(trans->initiator2target_buffer_size, + split_trans_initiator2target_buffer(trans), + trans->target2initiator_buffer_size, + split_trans_target2initiator_buffer(trans)); + } + + uint8_t checksum = 0; + for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { + uint8_t b = split_trans_target2initiator_buffer(trans)[i]; + serial_write_byte(b); + checksum += b; + } + serial_write_byte(checksum ^ 7); + serial_wait_send_complete(); + + + *trans->status = (checksum_computed == checksum_received) + ? TRANSACTION_ACCEPTED + : TRANSACTION_DATA_ERROR; + + + // end transaction + serial_input(); + + // + gpio_set_irq_enabled_with_callback(SOFT_SERIAL_PIN, GPIO_IRQ_EDGE_FALL, + true, interrupt_handler); + + restore_interrupts(interrupt_status); +} + +///////// +// start transaction by initiator +// +// int soft_serial_transaction(int sstd_index) +// +// Returns: +// TRANSACTION_END +// TRANSACTION_NO_RESPONSE +// TRANSACTION_DATA_ERROR +// this code is very time dependent, so we need to disable interrupts +int soft_serial_transaction(int sstd_index) { + if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR; + split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; + if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered + + // TODO: remove extra delay between transactions + serial_delay(); + + // this code is very time dependent, so we need to disable interrupts + uint32_t interrupt_status = save_and_disable_interrupts(); + + // signal to the slave that we want to start a transaction + serial_output(); + serial_low(); + serial_delay_blip(); + serial_high(); + + // wait for the slaves response + if (sync_recv() != 0) { + // dprintf("NACK 1\n"); + restore_interrupts(interrupt_status); + return TRANSACTION_NO_RESPONSE; + } + + // if the slave is present syncronize with it + soft_serial_enable_tx(); + + uint8_t checksum = 0; + // send data to the slave + serial_write_byte(sstd_index); // first chunk is transaction id + serial_wait_send_complete(); + + soft_serial_disable_tx(); + + if (sync_recv() != 0) { + // dprintf("NACK 1.5\n"); + restore_interrupts(interrupt_status); + return TRANSACTION_NO_RESPONSE; + } + + soft_serial_enable_tx(); + serial_delay(); + + for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { + serial_write_byte(split_trans_initiator2target_buffer(trans)[i]); + checksum += split_trans_initiator2target_buffer(trans)[i]; + } + serial_write_byte(checksum ^ 7); + + serial_wait_send_complete(); + + soft_serial_disable_tx(); + + if (sync_recv() != 0) { + // dprintf("NACK 2\n"); + restore_interrupts(interrupt_status); + return TRANSACTION_NO_RESPONSE; + } + + soft_serial_enable_rx(); + + // receive data from the slave + uint8_t checksum_computed = 0; + uint8_t receive_bytes = trans->target2initiator_buffer_size; + for (int i = 0; i < receive_bytes; ++i) { + split_trans_target2initiator_buffer(trans)[i] = serial_read_byte(); + checksum_computed += split_trans_target2initiator_buffer(trans)[i]; + } + checksum_computed ^= 7; + uint8_t checksum_received = serial_read_byte(); + + soft_serial_disable_rx(); + + if ((checksum_computed) != (checksum_received)) { + dprintf("serial::FAIL[%u,%u,%u]\n", checksum_computed, + checksum_received, sstd_index); + serial_output(); + serial_high(); + + restore_interrupts(interrupt_status); + return TRANSACTION_DATA_ERROR; + } + + // always, release the line when not in use + serial_high(); + serial_output(); + + restore_interrupts(interrupt_status); + return TRANSACTION_END; +} diff --git a/platforms/pico/drivers/serial_usart.c b/platforms/pico/drivers/serial_usart.c new file mode 100644 index 0000000000..7c81b16464 --- /dev/null +++ b/platforms/pico/drivers/serial_usart.c @@ -0,0 +1,237 @@ +#include "quantum.h" +#include "serial.h" +#include "print.h" + +#include +#include + +#ifndef USART_CR1_M0 +# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so +#endif + +#ifndef USE_GPIOV1 +// The default PAL alternate modes are used to signal that the pins are used for USART +# ifndef SERIAL_USART_TX_PAL_MODE +# define SERIAL_USART_TX_PAL_MODE 7 +# endif +#endif + +#ifndef SERIAL_USART_DRIVER +# define SERIAL_USART_DRIVER SD1 +#endif + +#ifndef SERIAL_USART_CR1 +# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length +#endif + +#ifndef SERIAL_USART_CR2 +# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits +#endif + +#ifndef SERIAL_USART_CR3 +# define SERIAL_USART_CR3 0 +#endif + +#ifdef SOFT_SERIAL_PIN +# define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN +#endif + +#ifndef SELECT_SOFT_SERIAL_SPEED +# define SELECT_SOFT_SERIAL_SPEED 1 +#endif + +#ifdef SERIAL_USART_SPEED +// Allow advanced users to directly set SERIAL_USART_SPEED +#elif SELECT_SOFT_SERIAL_SPEED == 0 +# define SERIAL_USART_SPEED 460800 +#elif SELECT_SOFT_SERIAL_SPEED == 1 +# define SERIAL_USART_SPEED 230400 +#elif SELECT_SOFT_SERIAL_SPEED == 2 +# define SERIAL_USART_SPEED 115200 +#elif SELECT_SOFT_SERIAL_SPEED == 3 +# define SERIAL_USART_SPEED 57600 +#elif SELECT_SOFT_SERIAL_SPEED == 4 +# define SERIAL_USART_SPEED 38400 +#elif SELECT_SOFT_SERIAL_SPEED == 5 +# define SERIAL_USART_SPEED 19200 +#else +# error invalid SELECT_SOFT_SERIAL_SPEED value +#endif + +#ifndef SERIAL_USART_TIMEOUT +# define SERIAL_USART_TIMEOUT 100 +#endif + +#define HANDSHAKE_MAGIC 7 + +static inline msg_t sdWriteHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size) { + msg_t ret = sdWrite(driver, data, size); + + // Half duplex requires us to read back the data we just wrote - just throw it away + uint8_t dump[size]; + sdRead(driver, dump, size); + + return ret; +} +#undef sdWrite +#define sdWrite sdWriteHalfDuplex + +static inline msg_t sdWriteTimeoutHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size, uint32_t timeout) { + msg_t ret = sdWriteTimeout(driver, data, size, timeout); + + // Half duplex requires us to read back the data we just wrote - just throw it away + uint8_t dump[size]; + sdReadTimeout(driver, dump, size, timeout); + + return ret; +} +#undef sdWriteTimeout +#define sdWriteTimeout sdWriteTimeoutHalfDuplex + +static inline void sdClear(SerialDriver* driver) { + while (sdGetTimeout(driver, TIME_IMMEDIATE) != MSG_TIMEOUT) { + // Do nothing with the data + } +} + +static SerialConfig sdcfg = { + (SERIAL_USART_SPEED), // speed - mandatory + (SERIAL_USART_CR1), // CR1 + (SERIAL_USART_CR2), // CR2 + (SERIAL_USART_CR3) // CR3 +}; + +void handle_soft_serial_slave(void); + +/* + * This thread runs on the slave and responds to transactions initiated + * by the master + */ +static THD_WORKING_AREA(waSlaveThread, 2048); +static THD_FUNCTION(SlaveThread, arg) { + (void)arg; + chRegSetThreadName("slave_transport"); + + while (true) { + handle_soft_serial_slave(); + } +} + +__attribute__((weak)) void usart_init(void) { +#if defined(USE_GPIOV1) + palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_OPENDRAIN); +#else + palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); +#endif +} + +void usart_master_init(void) { + usart_init(); + + sdcfg.cr3 |= USART_CR3_HDSEL; + sdStart(&SERIAL_USART_DRIVER, &sdcfg); +} + +void usart_slave_init(void) { + usart_init(); + + sdcfg.cr3 |= USART_CR3_HDSEL; + sdStart(&SERIAL_USART_DRIVER, &sdcfg); + + // Start transport thread + chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); +} + +static SSTD_t* Transaction_table = NULL; +static uint8_t Transaction_table_size = 0; + +void soft_serial_initiator_init(SSTD_t* sstd_table, int sstd_table_size) { + Transaction_table = sstd_table; + Transaction_table_size = (uint8_t)sstd_table_size; + + usart_master_init(); +} + +void soft_serial_target_init(SSTD_t* sstd_table, int sstd_table_size) { + Transaction_table = sstd_table; + Transaction_table_size = (uint8_t)sstd_table_size; + + usart_slave_init(); +} + +void handle_soft_serial_slave(void) { + uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id + SSTD_t* trans = &Transaction_table[sstd_index]; + + // Always write back the sstd_index as part of a basic handshake + sstd_index ^= HANDSHAKE_MAGIC; + sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index)); + + if (trans->initiator2target_buffer_size) { + sdRead(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size); + } + + if (trans->target2initiator_buffer_size) { + sdWrite(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size); + } + + if (trans->status) { + *trans->status = TRANSACTION_ACCEPTED; + } +} + +///////// +// start transaction by initiator +// +// int soft_serial_transaction(int sstd_index) +// +// Returns: +// TRANSACTION_END +// TRANSACTION_NO_RESPONSE +// TRANSACTION_DATA_ERROR +#ifndef SERIAL_USE_MULTI_TRANSACTION +int soft_serial_transaction(void) { + uint8_t sstd_index = 0; +#else +int soft_serial_transaction(int index) { + uint8_t sstd_index = index; +#endif + + if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; + SSTD_t* trans = &Transaction_table[sstd_index]; + msg_t res = 0; + + sdClear(&SERIAL_USART_DRIVER); + + // First chunk is always transaction id + sdWriteTimeout(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index), TIME_MS2I(SERIAL_USART_TIMEOUT)); + + uint8_t sstd_index_shake = 0xFF; + + // Which we always read back first so that we can error out correctly + // - due to the half duplex limitations on return codes, we always have to read *something* + // - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready + res = sdReadTimeout(&SERIAL_USART_DRIVER, &sstd_index_shake, sizeof(sstd_index_shake), TIME_MS2I(SERIAL_USART_TIMEOUT)); + if (res < 0 || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) { + dprintf("serial::usart_shake NO_RESPONSE\n"); + return TRANSACTION_NO_RESPONSE; + } + + if (trans->initiator2target_buffer_size) { + res = sdWriteTimeout(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); + if (res < 0) { + dprintf("serial::usart_transmit NO_RESPONSE\n"); + return TRANSACTION_NO_RESPONSE; + } + } + + if (trans->target2initiator_buffer_size) { + res = sdReadTimeout(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); + if (res < 0) { + dprintf("serial::usart_receive NO_RESPONSE\n"); + return TRANSACTION_NO_RESPONSE; + } + } + + return TRANSACTION_END; +} diff --git a/platforms/pico/drivers/uart.h b/platforms/pico/drivers/uart.h new file mode 100644 index 0000000000..dbdcdb3086 --- /dev/null +++ b/platforms/pico/drivers/uart.h @@ -0,0 +1,15 @@ + +#pragma once + +#include +#include + +#include "pico/stdlib.h" + +// void uart_init(uint32_t baud); + +void uart_putchar(uint8_t c); + +uint8_t uart_getchar(void); + +bool uart_available(void); diff --git a/platforms/pico/drivers/uart_rx.pio.h b/platforms/pico/drivers/uart_rx.pio.h new file mode 100644 index 0000000000..a1919b476f --- /dev/null +++ b/platforms/pico/drivers/uart_rx.pio.h @@ -0,0 +1,120 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------------ // +// uart_rx_mini // +// ------------ // + +#define uart_rx_mini_wrap_target 0 +#define uart_rx_mini_wrap 3 + +static const uint16_t uart_rx_mini_program_instructions[] = { + // .wrap_target + 0x2020, // 0: wait 0 pin, 0 + 0xea27, // 1: set x, 7 [10] + 0x4001, // 2: in pins, 1 + 0x0642, // 3: jmp x--, 2 [6] + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program uart_rx_mini_program = { + .instructions = uart_rx_mini_program_instructions, + .length = 4, + .origin = -1, +}; + +static inline pio_sm_config uart_rx_mini_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + uart_rx_mini_wrap_target, offset + uart_rx_mini_wrap); + return c; +} + +#include "hardware/clocks.h" +#include "hardware/gpio.h" +static inline void uart_rx_mini_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) { + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); + pio_gpio_init(pio, pin); + gpio_pull_up(pin); + pio_sm_config c = uart_rx_mini_program_get_default_config(offset); + sm_config_set_in_pins(&c, pin); // for WAIT, IN + // Shift to right, autopush enabled + sm_config_set_in_shift(&c, true, true, 8); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + // SM transmits 1 bit per 8 execution cycles. + float div = (float)clock_get_hz(clk_sys) / (8 * baud); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} + +#endif + +// ------- // +// uart_rx // +// ------- // + +#define uart_rx_wrap_target 0 +#define uart_rx_wrap 8 + +static const uint16_t uart_rx_program_instructions[] = { + // .wrap_target + 0x2020, // 0: wait 0 pin, 0 + 0xea27, // 1: set x, 7 [10] + 0x4001, // 2: in pins, 1 + 0x0642, // 3: jmp x--, 2 [6] + 0x00c8, // 4: jmp pin, 8 + 0xc014, // 5: irq nowait 4 rel + 0x20a0, // 6: wait 1 pin, 0 + 0x0000, // 7: jmp 0 + 0x8020, // 8: push block + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program uart_rx_program = { + .instructions = uart_rx_program_instructions, + .length = 9, + .origin = -1, +}; + +static inline pio_sm_config uart_rx_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + uart_rx_wrap_target, offset + uart_rx_wrap); + return c; +} + +static inline void uart_rx_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) { + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); + pio_gpio_init(pio, pin); + gpio_pull_up(pin); + pio_sm_config c = uart_rx_program_get_default_config(offset); + sm_config_set_in_pins(&c, pin); // for WAIT, IN + sm_config_set_jmp_pin(&c, pin); // for JMP + // Shift to right, autopull disabled + sm_config_set_in_shift(&c, true, false, 32); + // Deeper FIFO as we're not doing any TX + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + // SM transmits 1 bit per 8 execution cycles. + float div = (float)clock_get_hz(clk_sys) / (8 * baud); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} +static inline char uart_rx_program_getc(PIO pio, uint sm) { + // 8-bit read from the uppermost byte of the FIFO, as data is left-justified + io_rw_8 *rxfifo_shift = (io_rw_8*)&pio->rxf[sm] + 3; + while (pio_sm_is_rx_fifo_empty(pio, sm)) + tight_loop_contents(); + return (char)*rxfifo_shift; +} + +#endif + diff --git a/platforms/pico/drivers/uart_tx.pio.h b/platforms/pico/drivers/uart_tx.pio.h new file mode 100644 index 0000000000..ff9b7d2a51 --- /dev/null +++ b/platforms/pico/drivers/uart_tx.pio.h @@ -0,0 +1,73 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------- // +// uart_tx // +// ------- // + +#define uart_tx_wrap_target 0 +#define uart_tx_wrap 3 + +static const uint16_t uart_tx_program_instructions[] = { + // .wrap_target + 0x9fa0, // 0: pull block side 1 [7] + 0xf727, // 1: set x, 7 side 0 [7] + 0x6001, // 2: out pins, 1 + 0x0642, // 3: jmp x--, 2 [6] + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program uart_tx_program = { + .instructions = uart_tx_program_instructions, + .length = 4, + .origin = -1, +}; + +static inline pio_sm_config uart_tx_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + uart_tx_wrap_target, offset + uart_tx_wrap); + sm_config_set_sideset(&c, 2, true, false); + return c; +} + +#include "hardware/clocks.h" +static inline void uart_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx, uint baud) { + // Tell PIO to initially drive output-high on the selected pin, then map PIO + // onto that pin with the IO muxes. + pio_sm_set_pins_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx); + pio_sm_set_pindirs_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx); + pio_gpio_init(pio, pin_tx); + pio_sm_config c = uart_tx_program_get_default_config(offset); + // OUT shifts to right, no autopull + sm_config_set_out_shift(&c, true, false, 32); + // We are mapping both OUT and side-set to the same pin, because sometimes + // we need to assert user data onto the pin (with OUT) and sometimes + // assert constant values (start/stop bit) + sm_config_set_out_pins(&c, pin_tx, 1); + sm_config_set_sideset_pins(&c, pin_tx); + // We only need TX, so get an 8-deep FIFO! + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + // SM transmits 1 bit per 8 execution cycles. + float div = (float)clock_get_hz(clk_sys) / (8 * baud); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} +static inline void uart_tx_program_putc(PIO pio, uint sm, char c) { + pio_sm_put_blocking(pio, sm, (uint32_t)c); +} +static inline void uart_tx_program_puts(PIO pio, uint sm, const char *s) { + while (*s) + uart_tx_program_putc(pio, sm, *s++); +} + +#endif + diff --git a/platforms/pico/drivers/ws2812.c b/platforms/pico/drivers/ws2812.c new file mode 100644 index 0000000000..3eea6092ae --- /dev/null +++ b/platforms/pico/drivers/ws2812.c @@ -0,0 +1,51 @@ + +#include + +#include "ws2812.h" +#include "ws2812.pio.h" + +#include "pio_manager.h" +#include "boards/pico_boards.h" + +#include "pico/stdlib.h" + +static PIO pio = pio0; +static int sm = 0; + +static int ws2812_init(void) { + sm = pio_manager_get_empty_sm(pio); + + if (sm < 0) { + return -1; + } + + int32_t offset = pio_manager_add_program(pio, sm, &ws2812_program); + + if (offset < 0) { + return -1; + } + + ws2812_program_init(pio, sm, offset, RGB_DI_PIN, 800000, false); + + return 0; +} + +void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds) { + static bool s_init = false; + if (!s_init) { + if (ws2812_init() == 0) { + s_init = true; + } else { + return; + } + } + + printf("led %d %d %d\n", ledarray[0].r, ledarray[0].g, ledarray[0].b); + + for (int i = 0; i < number_of_leds; i++) { + pio_sm_put_blocking(pio, sm, + (((uint32_t)ledarray[i].r) << 16) | + (((uint32_t)ledarray[i].g) << 24) | + ((uint32_t)ledarray[i].b << 8)); + } +} diff --git a/platforms/pico/drivers/ws2812.pio.h b/platforms/pico/drivers/ws2812.pio.h new file mode 100644 index 0000000000..8cbeea6b72 --- /dev/null +++ b/platforms/pico/drivers/ws2812.pio.h @@ -0,0 +1,114 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------ // +// ws2812 // +// ------ // + +#define ws2812_wrap_target 0 +#define ws2812_wrap 3 + +#define ws2812_T1 2 +#define ws2812_T2 5 +#define ws2812_T3 3 + +static const uint16_t ws2812_program_instructions[] = { + // .wrap_target + 0x6221, // 0: out x, 1 side 0 [2] + 0x1123, // 1: jmp !x, 3 side 1 [1] + 0x1400, // 2: jmp 0 side 1 [4] + 0xa442, // 3: nop side 0 [4] + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program ws2812_program = { + .instructions = ws2812_program_instructions, + .length = 4, + .origin = -1, +}; + +static inline pio_sm_config ws2812_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + ws2812_wrap_target, offset + ws2812_wrap); + sm_config_set_sideset(&c, 1, false, false); + return c; +} + +#include "hardware/clocks.h" +static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) { + pio_gpio_init(pio, pin); + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); + pio_sm_config c = ws2812_program_get_default_config(offset); + sm_config_set_sideset_pins(&c, pin); + sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3; + float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} + +#endif + +// --------------- // +// ws2812_parallel // +// --------------- // + +#define ws2812_parallel_wrap_target 0 +#define ws2812_parallel_wrap 3 + +#define ws2812_parallel_T1 2 +#define ws2812_parallel_T2 5 +#define ws2812_parallel_T3 3 + +static const uint16_t ws2812_parallel_program_instructions[] = { + // .wrap_target + 0x6020, // 0: out x, 32 + 0xa10b, // 1: mov pins, !null [1] + 0xa401, // 2: mov pins, x [4] + 0xa103, // 3: mov pins, null [1] + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program ws2812_parallel_program = { + .instructions = ws2812_parallel_program_instructions, + .length = 4, + .origin = -1, +}; + +static inline pio_sm_config ws2812_parallel_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + ws2812_parallel_wrap_target, offset + ws2812_parallel_wrap); + return c; +} + +#include "hardware/clocks.h" +static inline void ws2812_parallel_program_init(PIO pio, uint sm, uint offset, uint pin_base, uint pin_count, float freq) { + for(uint i=pin_base; i +#include + +// qmk +#include "eeprom.h" +#include "eeconfig.h" +#include "timer.h" +#include "debug.h" +#include "print.h" + +#include "pico_eeprom.h" + +// pico +#include "pico/stdlib.h" +#include "hardware/flash.h" +#include "hardware/sync.h" + +#ifndef DYNAMIC_KEYMAP_EEPROM_ADDR +# ifdef VIA_EEPROM_CUSTOM_CONFIG_ADDR +# define DYNAMIC_KEYMAP_EEPROM_ADDR \ + (VIA_EEPROM_CUSTOM_CONFIG_ADDR + VIA_EEPROM_CUSTOM_CONFIG_SIZE) +# else +# define DYNAMIC_KEYMAP_EEPROM_ADDR (EECONFIG_SIZE + 1) +# endif +#endif + +#ifndef EEPEMU_EECONFIG_SIZE +# define EEPEMU_EECONFIG_SIZE 1024 +#endif +_Static_assert(EEPEMU_EECONFIG_SIZE > EECONFIG_SIZE, + "EEPEMU_EECONFIG Region is too small"); + +#ifndef EEPEMU_KEYMAP_SIZE +# define EEPEMU_KEYMAP_SIZE 4096 +#endif +_Static_assert(EEPEMU_KEYMAP_SIZE > DYNAMIC_KEYMAP_EEPROM_MAX_ADDR, + "EEPEMU_KEYMAP Region is too small"); + +#define ALIGNED_REGION(size, align) ((size + align - 1) / align * align) + +#ifndef EEPEMU_KEYMAP_START_OFFSET +# define EEPEMU_KEYMAP_START_OFFSET \ + (PICO_FLASH_SIZE_BYTES - \ + ALIGNED_REGION(EEPEMU_KEYMAP_SIZE, FLASH_SECTOR_SIZE)) +#endif + +#ifndef EEPEMU_EECONFIG_START_OFFSET +# define EEPEMU_EECONFIG_START_OFFSET \ + (EEPEMU_KEYMAP_START_OFFSET - \ + ALIGNED_REGION(EEPEMU_EECONFIG_SIZE, FLASH_SECTOR_SIZE)) +#endif + +#ifndef EEPEMU_LAZY_WRITEBACK_TIMEOUT +# define EEPEMU_LAZY_WRITEBACK_TIMEOUT \ + 30000 // write back to ROM if 30s elapsed. +#endif + +_Static_assert(EEPEMU_EECONFIG_START_OFFSET % FLASH_SECTOR_SIZE == 0, + "Offset should be aligned to FLASH_SCTOR_SIZE"); +_Static_assert(EEPEMU_KEYMAP_START_OFFSET % FLASH_SECTOR_SIZE == 0, + "Offset should be aligned to FLASH_SCTOR_SIZE"); + +static uint8_t eepemu_eeconfig[EEPEMU_EECONFIG_SIZE]; // 1kB +static uint8_t eepemu_dynamic_keymap[EEPEMU_KEYMAP_SIZE]; // 4kB +static int32_t eeconfig_last_edit; +static int32_t dynamic_keymap_last_edit; + +// Load ROM data to RAM +void pico_eepemu_init(void) { + memcpy(eepemu_eeconfig, (void *)XIP_BASE + EEPEMU_EECONFIG_START_OFFSET, + sizeof(eepemu_eeconfig)); + memcpy(eepemu_dynamic_keymap, (void *)XIP_BASE + EEPEMU_KEYMAP_START_OFFSET, + sizeof(eepemu_dynamic_keymap)); + + eeconfig_last_edit = -1; + dynamic_keymap_last_edit = -1; +} + +void pico_eepemu_lazy_write_back(void) { + if (eeconfig_last_edit >= 0) { + if (timer_elapsed(eeconfig_last_edit) >= + EEPEMU_LAZY_WRITEBACK_TIMEOUT) { + printf("eeconfig is written to ROM\n"); + pico_eepemu_flash_eeconfig(); + } + } + + if (dynamic_keymap_last_edit >= 0) { + if (timer_elapsed(dynamic_keymap_last_edit) >= + EEPEMU_LAZY_WRITEBACK_TIMEOUT) { + printf("Dynamic keymap is written to ROM\n"); + pico_eepemu_flash_dynamic_keymap(); + } + } +} + +void pico_eepemu_flash_eeconfig(void) { + int32_t status = save_and_disable_interrupts(); + + flash_range_erase(EEPEMU_EECONFIG_START_OFFSET, FLASH_SECTOR_SIZE); + flash_range_program(EEPEMU_EECONFIG_START_OFFSET, eepemu_eeconfig, + sizeof(eepemu_eeconfig)); + eeconfig_last_edit = -1; + + restore_interrupts(status); +} + +void pico_eepemu_flash_dynamic_keymap(void) { + int32_t status = save_and_disable_interrupts(); + + flash_range_erase(EEPEMU_KEYMAP_START_OFFSET, FLASH_SECTOR_SIZE); + flash_range_program(EEPEMU_KEYMAP_START_OFFSET, eepemu_dynamic_keymap, + sizeof(eepemu_dynamic_keymap)); + dynamic_keymap_last_edit = -1; + + restore_interrupts(status); +} + +static inline bool is_eeconfig_addr(const void *eeaddr) { + if ((uint32_t)eeaddr < DYNAMIC_KEYMAP_EEPROM_ADDR) { + return true; + } else { + return false; + } +} + +static void *convert_address(const void *addr) { + // TODO Assert address + if (is_eeconfig_addr(addr)) { + return (void *)(eepemu_eeconfig + (uint32_t)addr); + } else { + return (void *)(eepemu_dynamic_keymap + (uint32_t)addr - + DYNAMIC_KEYMAP_EEPROM_ADDR); + } +} + +uint8_t eeprom_read_byte(const uint8_t *__p) { + uint8_t *addr = convert_address(__p); + if (addr == NULL) { + return 0xff; + } + + return *addr; +} + +uint16_t eeprom_read_word(const uint16_t *__p) { + uint16_t *addr = convert_address(__p); + if (addr == NULL) { + return 0xffff; + } + + if ((uint32_t)addr % 2 == 0) { + return *addr; + } else { + uint16_t ret; + memcpy(&ret, addr, sizeof(ret)); + return ret; + } +} + +uint32_t eeprom_read_dword(const uint32_t *__p) { + uint32_t *addr = convert_address(__p); + if (addr == NULL) { + return 0xffffffff; + } + + if ((uint32_t)addr % 4 == 0) { + return *addr; + } else { + uint32_t ret; + memcpy(&ret, addr, sizeof(ret)); + return ret; + } +} + +void eeprom_read_block(void *__dst, const void *__src, size_t __n) { + uint32_t *addr = convert_address(__src); + if (addr == NULL) { + return; + } + + memcpy(__dst, addr, __n); +} + +static void update_last_edit_time(const void *eeaddr) { + if (is_eeconfig_addr(eeaddr)) { + eeconfig_last_edit = timer_read(); + } else { + dynamic_keymap_last_edit = timer_read(); + } +} + +void eeprom_write_byte(uint8_t *__p, uint8_t __value) { + uint8_t *addr = convert_address(__p); + if (addr == NULL) { + return; + } + + *addr = __value; + + update_last_edit_time(__p); +} + +void eeprom_write_word(uint16_t *__p, uint16_t __value) { + uint16_t *addr = convert_address(__p); + if (addr == NULL) { + return; + } + + if ((uint32_t)addr % 2 == 0) { + *addr = __value; + } else { + memcpy(addr, &__value, sizeof(__value)); + } + + update_last_edit_time(__p); +} +void eeprom_write_dword(uint32_t *__p, uint32_t __value) { + uint32_t *addr = convert_address(__p); + if (addr == NULL) { + return; + } + + if ((uint32_t)addr % 4 == 0) { + *addr = __value; + } else { + memcpy(addr, &__value, sizeof(__value)); + } + + update_last_edit_time(__p); +} + +void eeprom_write_block(const void *__src, void *__dst, size_t __n) { + uint32_t *addr = convert_address(__dst); + if (addr == NULL) { + return; + } + + memcpy(addr, __src, __n); + + update_last_edit_time(__dst); +} + +void eeprom_update_byte(uint8_t *__p, uint8_t __value) { + if (eeprom_read_byte(__p) != __value) { + eeprom_write_byte(__p, __value); + } +} + +void eeprom_update_word(uint16_t *__p, uint16_t __value) { + if (eeprom_read_word(__p) != __value) { + eeprom_write_word(__p, __value); + } +} + +void eeprom_update_dword(uint32_t *__p, uint32_t __value) { + if (eeprom_read_dword(__p) != __value) { + eeprom_write_dword(__p, __value); + } +} + +void eeprom_update_block(const void *__src, void *__dst, size_t __n) { + uint32_t *addr = convert_address(__dst); + if (addr == NULL) { + return; + } + + if (memcmp(__src, addr, __n) != 0) { + eeprom_write_block(__src, __dst, __n); + } +} diff --git a/platforms/pico/gpio.h b/platforms/pico/gpio.h new file mode 100644 index 0000000000..34fc4fdf17 --- /dev/null +++ b/platforms/pico/gpio.h @@ -0,0 +1,34 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "pico/stdlib.h" +#include "pin_defs.h" + +typedef uint8_t pin_t; + +#define setPinInput(pin) (gpio_set_dir(pin, GPIO_IN), gpio_set_function(pin, GPIO_FUNC_SIO)) +#define setPinInputHigh(pin) (gpio_set_dir(pin, GPIO_IN), gpio_pull_up(pin), gpio_set_function(pin, GPIO_FUNC_SIO)) +#define setPinInputLow(pin) (gpio_init(pin), gpio_set_dir(pin, GPIO_IN), gpio_pull_down(pin), gpio_set_function(pin, GPIO_FUNC_SIO)) +#define setPinOutput(pin) (gpio_set_function(pin, GPIO_FUNC_SIO), gpio_set_dir(pin, GPIO_OUT)) + +#define writePinHigh(pin) (gpio_put(pin, 1)) +#define writePinLow(pin) (gpio_put(pin, 0)) +#define writePin(pin, level) ((level) ? writePinHigh(pin) : writePinLow(pin)) + +#define readPin(pin) (gpio_get(pin)) + +#define togglePin(pin) (gpio_out(pin, !gpio_get(pin))) diff --git a/platforms/pico/pin_defs.h b/platforms/pico/pin_defs.h new file mode 100644 index 0000000000..df833d3824 --- /dev/null +++ b/platforms/pico/pin_defs.h @@ -0,0 +1,17 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + diff --git a/platforms/pico/platform.c b/platforms/pico/platform.c new file mode 100644 index 0000000000..6afc8bc1d1 --- /dev/null +++ b/platforms/pico/platform.c @@ -0,0 +1,14 @@ + +#include "platform_deps.h" +#include "quantum.h" +#include "hardware/adc.h" + +uint32_t interrupts; + +void platform_setup(void) { + for (int i = 0; i < 32; i++) { + setPinInputHigh(i); + } + + adc_init(); +} diff --git a/platforms/pico/platform.mk b/platforms/pico/platform.mk new file mode 100644 index 0000000000..6b8791a04f --- /dev/null +++ b/platforms/pico/platform.mk @@ -0,0 +1,405 @@ + +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +OBJDUMP = arm-none-eabi-objdump +SIZE = arm-none-eabi-size +AR = arm-none-eabi-ar +NM = arm-none-eabi-nm +HEX = $(OBJCOPY) -O $(FORMAT) +EEP = +BIN = $(OBJCOPY) -O binary + +ALLOW_WARNINGS := yes +OPT_DEFS += -DPROTOCOL_PICO +OPT_DEFS += -DSPLIT_USB_DETECT +PICOTOOL ?= picotool +RP2BOOT_ID ?= "2e8a:0003" + +PICO_SDK_PATH = lib/pico-sdk + +PICO_SDK_EXIST = $(shell test -d $(PICO_SDK_PATH)/src && echo true) +ifneq ($(PICO_SDK_EXIST),true) +$(error pico-sdk does not found at PICO_SDK_PATH. Please setup pico-sdk.) +endif + +GIT_DESCRIBE = $(shell git describe --tags --long --dirty="\\*") + +ifneq ($(PICO_FLASH_SPI_CLKDIV),) + CFLAGS += -DPICO_FLASH_SPI_CLKDIV=$(PICO_FLASH_SPI_CLKDIV) +else + CFLAGS += -DPICO_FLASH_SPI_CLKDIV=4 +endif + +CFLAGS += -DCFG_TUSB_DEBUG=0 +CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_RP2040 +CFLAGS += -DCFG_TUSB_OS=OPT_OS_PICO +CFLAGS += -DLIB_PICO_BIT_OPS=1 +CFLAGS += -DLIB_PICO_BIT_OPS_PICO=1 +CFLAGS += -DLIB_PICO_BOOTSEL_VIA_DOUBLE_RESET=1 +CFLAGS += -DLIB_PICO_DIVIDER=1 +CFLAGS += -DLIB_PICO_DIVIDER_HARDWARE=1 +CFLAGS += -DLIB_PICO_DOUBLE=1 +CFLAGS += -DLIB_PICO_DOUBLE_PICO=1 +CFLAGS += -DLIB_PICO_FIX_RP2040_USB_DEVICE_ENUMERATION=1 +CFLAGS += -DLIB_PICO_FLOAT=1 +CFLAGS += -DLIB_PICO_FLOAT_PICO=1 +CFLAGS += -DLIB_PICO_INT64_OPS=1 +CFLAGS += -DLIB_PICO_INT64_OPS_PICO=1 +CFLAGS += -DLIB_PICO_MALLOC=1 +CFLAGS += -DLIB_PICO_MEM_OPS=1 +CFLAGS += -DLIB_PICO_MEM_OPS_PICO=1 +CFLAGS += -DLIB_PICO_PLATFORM=1 +CFLAGS += -DLIB_PICO_PRINTF=1 +CFLAGS += -DLIB_PICO_PRINTF_PICO=1 +CFLAGS += -DLIB_PICO_RUNTIME=1 +CFLAGS += -DLIB_PICO_STANDARD_LINK=1 +CFLAGS += -DLIB_PICO_STDIO=1 +CFLAGS += -DLIB_PICO_STDIO_UART=1 +CFLAGS += -DLIB_PICO_STDLIB=1 +CFLAGS += -DLIB_PICO_SYNC=1 +CFLAGS += -DLIB_PICO_SYNC_CORE=1 +CFLAGS += -DLIB_PICO_SYNC_CRITICAL_SECTION=1 +CFLAGS += -DLIB_PICO_SYNC_MUTEX=1 +CFLAGS += -DLIB_PICO_SYNC_SEM=1 +CFLAGS += -DLIB_PICO_TIME=1 +CFLAGS += -DLIB_PICO_UTIL=1 +CFLAGS += -DLIB_TINYUSB_BOARD=1 +CFLAGS += -DLIB_TINYUSB_DEVICE=1 +CFLAGS += -DPICO_BOARD=\"pico\" +CFLAGS += -DPICO_BUILD=1 +CFLAGS += -DPICO_COPY_TO_RAM=0 +CFLAGS += -DPICO_CXX_ENABLE_EXCEPTIONS=0 +CFLAGS += -DPICO_NO_FLASH=0 +CFLAGS += -DPICO_NO_HARDWARE=0 +CFLAGS += -DPICO_ON_DEVICE=1 +CFLAGS += -DPICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1 +CFLAGS += -DPICO_TARGET_NAME=\"$(TARGET)\" +CFLAGS += -DPICO_PROGRAM_VERSION_STRING=\"$(GIT_DESCRIBE)\" +CFLAGS += -DPICO_USE_BLOCKED_RAM=0 +CFLAGS += -I$(PICO_SDK_PATH) +CFLAGS += -I$(PICO_SDK_PATH)/src +CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_stdlib/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_flash/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_gpio/include +CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_base/include +CFLAGS += -I$(PICO_SDK_PATH)/src/boards/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_platform/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2040/hardware_regs/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_base/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2040/hardware_structs/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_adc/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_claim/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_dma/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_sync/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_uart/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_i2c/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_spi/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_pio/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_pwm/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_divider/include +CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_time/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_timer/include +CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_sync/include +CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_util/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_runtime/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_clocks/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_irq/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_resets/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_pll/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_vreg/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_watchdog/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_xosc/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_printf/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_bootrom/include +CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_bit_ops/include +CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_divider/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_double/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_int64_ops/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_float/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_malloc/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/boot_stage2/include +CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_binary_info/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_stdio/include +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_stdio_uart/include +CFLAGS += -I$(PICO_SDK_PATH)/lib/tinyusb/src +CFLAGS += -I$(PICO_SDK_PATH)/lib/tinyusb/src/common +CFLAGS += -I$(PICO_SDK_PATH)/lib/tinyusb/hw +CFLAGS += -I$(PICO_SDK_PATH)/lib/tinyusb/src/class/hid +CFLAGS += -I$(PICO_SDK_PATH)/lib/tinyusb/src/class/cdc +CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/include +CFLAGS += -mcpu=cortex-m0plus +CFLAGS += -mthumb +CFLAGS += -O0 -g3 +# CFLAGS += -DNDEBUG +CFLAGS += -Wall +CFLAGS += -Wno-format +CFLAGS += -Wno-unused-function +CFLAGS += -Wno-maybe-uninitialized +CFLAGS += -ffunction-sections +CFLAGS += -fdata-sections +CFLAGS += -std=gnu11 + +ASFLAGS += $(CFLAGS) + +LDFLAGS += -Wl,--build-id=none +LDFLAGS += --specs=nosys.specs +LDFLAGS += -Wl,--wrap=sprintf +LDFLAGS += -Wl,--wrap=snprintf +LDFLAGS += -Wl,--wrap=vsnprintf +LDFLAGS += -Wl,--wrap=__clzsi2 +LDFLAGS += -Wl,--wrap=__clzdi2 +LDFLAGS += -Wl,--wrap=__ctzsi2 +LDFLAGS += -Wl,--wrap=__ctzdi2 +LDFLAGS += -Wl,--wrap=__popcountsi2 +LDFLAGS += -Wl,--wrap=__popcountdi2 +LDFLAGS += -Wl,--wrap=__clz +LDFLAGS += -Wl,--wrap=__clzl +LDFLAGS += -Wl,--wrap=__clzll +LDFLAGS += -Wl,--wrap=__aeabi_idiv +LDFLAGS += -Wl,--wrap=__aeabi_idivmod +LDFLAGS += -Wl,--wrap=__aeabi_ldivmod +LDFLAGS += -Wl,--wrap=__aeabi_uidiv +LDFLAGS += -Wl,--wrap=__aeabi_uidivmod +LDFLAGS += -Wl,--wrap=__aeabi_uldivmod +LDFLAGS += -Wl,--wrap=__aeabi_dadd +LDFLAGS += -Wl,--wrap=__aeabi_ddiv +LDFLAGS += -Wl,--wrap=__aeabi_dmul +LDFLAGS += -Wl,--wrap=__aeabi_drsub +LDFLAGS += -Wl,--wrap=__aeabi_dsub +LDFLAGS += -Wl,--wrap=__aeabi_cdcmpeq +LDFLAGS += -Wl,--wrap=__aeabi_cdrcmple +LDFLAGS += -Wl,--wrap=__aeabi_cdcmple +LDFLAGS += -Wl,--wrap=__aeabi_dcmpeq +LDFLAGS += -Wl,--wrap=__aeabi_dcmplt +LDFLAGS += -Wl,--wrap=__aeabi_dcmple +LDFLAGS += -Wl,--wrap=__aeabi_dcmpge +LDFLAGS += -Wl,--wrap=__aeabi_dcmpgt +LDFLAGS += -Wl,--wrap=__aeabi_dcmpun +LDFLAGS += -Wl,--wrap=__aeabi_i2d +LDFLAGS += -Wl,--wrap=__aeabi_l2d +LDFLAGS += -Wl,--wrap=__aeabi_ui2d +LDFLAGS += -Wl,--wrap=__aeabi_ul2d +LDFLAGS += -Wl,--wrap=__aeabi_d2iz +LDFLAGS += -Wl,--wrap=__aeabi_d2lz +LDFLAGS += -Wl,--wrap=__aeabi_d2uiz +LDFLAGS += -Wl,--wrap=__aeabi_d2ulz +LDFLAGS += -Wl,--wrap=__aeabi_d2f +LDFLAGS += -Wl,--wrap=sqrt +LDFLAGS += -Wl,--wrap=cos +LDFLAGS += -Wl,--wrap=sin +LDFLAGS += -Wl,--wrap=tan +LDFLAGS += -Wl,--wrap=atan2 +LDFLAGS += -Wl,--wrap=exp +LDFLAGS += -Wl,--wrap=log +LDFLAGS += -Wl,--wrap=ldexp +LDFLAGS += -Wl,--wrap=copysign +LDFLAGS += -Wl,--wrap=trunc +LDFLAGS += -Wl,--wrap=floor +LDFLAGS += -Wl,--wrap=ceil +LDFLAGS += -Wl,--wrap=round +LDFLAGS += -Wl,--wrap=sincos +LDFLAGS += -Wl,--wrap=asin +LDFLAGS += -Wl,--wrap=acos +LDFLAGS += -Wl,--wrap=atan +LDFLAGS += -Wl,--wrap=sinh +LDFLAGS += -Wl,--wrap=cosh +LDFLAGS += -Wl,--wrap=tanh +LDFLAGS += -Wl,--wrap=asinh +LDFLAGS += -Wl,--wrap=acosh +LDFLAGS += -Wl,--wrap=atanh +LDFLAGS += -Wl,--wrap=exp2 +LDFLAGS += -Wl,--wrap=log2 +LDFLAGS += -Wl,--wrap=exp10 +LDFLAGS += -Wl,--wrap=log10 +LDFLAGS += -Wl,--wrap=pow +LDFLAGS += -Wl,--wrap=powint +LDFLAGS += -Wl,--wrap=hypot +LDFLAGS += -Wl,--wrap=cbrt +LDFLAGS += -Wl,--wrap=fmod +LDFLAGS += -Wl,--wrap=drem +LDFLAGS += -Wl,--wrap=remainder +LDFLAGS += -Wl,--wrap=remquo +LDFLAGS += -Wl,--wrap=expm1 +LDFLAGS += -Wl,--wrap=log1p +LDFLAGS += -Wl,--wrap=fma +LDFLAGS += -Wl,--wrap=__aeabi_lmul +LDFLAGS += -Wl,--wrap=__aeabi_fadd +LDFLAGS += -Wl,--wrap=__aeabi_fdiv +LDFLAGS += -Wl,--wrap=__aeabi_fmul +LDFLAGS += -Wl,--wrap=__aeabi_frsub +LDFLAGS += -Wl,--wrap=__aeabi_fsub +LDFLAGS += -Wl,--wrap=__aeabi_cfcmpeq +LDFLAGS += -Wl,--wrap=__aeabi_cfrcmple +LDFLAGS += -Wl,--wrap=__aeabi_cfcmple +LDFLAGS += -Wl,--wrap=__aeabi_fcmpeq +LDFLAGS += -Wl,--wrap=__aeabi_fcmplt +LDFLAGS += -Wl,--wrap=__aeabi_fcmple +LDFLAGS += -Wl,--wrap=__aeabi_fcmpge +LDFLAGS += -Wl,--wrap=__aeabi_fcmpgt +LDFLAGS += -Wl,--wrap=__aeabi_fcmpun +LDFLAGS += -Wl,--wrap=__aeabi_i2f +LDFLAGS += -Wl,--wrap=__aeabi_l2f +LDFLAGS += -Wl,--wrap=__aeabi_ui2f +LDFLAGS += -Wl,--wrap=__aeabi_ul2f +LDFLAGS += -Wl,--wrap=__aeabi_f2iz +LDFLAGS += -Wl,--wrap=__aeabi_f2lz +LDFLAGS += -Wl,--wrap=__aeabi_f2uiz +LDFLAGS += -Wl,--wrap=__aeabi_f2ulz +LDFLAGS += -Wl,--wrap=__aeabi_f2d +LDFLAGS += -Wl,--wrap=sqrtf +LDFLAGS += -Wl,--wrap=cosf +LDFLAGS += -Wl,--wrap=sinf +LDFLAGS += -Wl,--wrap=tanf +LDFLAGS += -Wl,--wrap=atan2f +LDFLAGS += -Wl,--wrap=expf +LDFLAGS += -Wl,--wrap=logf +LDFLAGS += -Wl,--wrap=ldexpf +LDFLAGS += -Wl,--wrap=copysignf +LDFLAGS += -Wl,--wrap=truncf +LDFLAGS += -Wl,--wrap=floorf +LDFLAGS += -Wl,--wrap=ceilf +LDFLAGS += -Wl,--wrap=roundf +LDFLAGS += -Wl,--wrap=sincosf +LDFLAGS += -Wl,--wrap=asinf +LDFLAGS += -Wl,--wrap=acosf +LDFLAGS += -Wl,--wrap=atanf +LDFLAGS += -Wl,--wrap=sinhf +LDFLAGS += -Wl,--wrap=coshf +LDFLAGS += -Wl,--wrap=tanhf +LDFLAGS += -Wl,--wrap=asinhf +LDFLAGS += -Wl,--wrap=acoshf +LDFLAGS += -Wl,--wrap=atanhf +LDFLAGS += -Wl,--wrap=exp2f +LDFLAGS += -Wl,--wrap=log2f +LDFLAGS += -Wl,--wrap=exp10f +LDFLAGS += -Wl,--wrap=log10f +LDFLAGS += -Wl,--wrap=powf +LDFLAGS += -Wl,--wrap=powintf +LDFLAGS += -Wl,--wrap=hypotf +LDFLAGS += -Wl,--wrap=cbrtf +LDFLAGS += -Wl,--wrap=fmodf +LDFLAGS += -Wl,--wrap=dremf +LDFLAGS += -Wl,--wrap=remainderf +LDFLAGS += -Wl,--wrap=remquof +LDFLAGS += -Wl,--wrap=expm1f +LDFLAGS += -Wl,--wrap=log1pf +LDFLAGS += -Wl,--wrap=fmaf +LDFLAGS += -Wl,--wrap=malloc +LDFLAGS += -Wl,--wrap=calloc +LDFLAGS += -Wl,--wrap=free +LDFLAGS += -Wl,--wrap=memcpy +LDFLAGS += -Wl,--wrap=memset +LDFLAGS += -Wl,--wrap=__aeabi_memcpy +LDFLAGS += -Wl,--wrap=__aeabi_memset +LDFLAGS += -Wl,--wrap=__aeabi_memcpy4 +LDFLAGS += -Wl,--wrap=__aeabi_memset4 +LDFLAGS += -Wl,--wrap=__aeabi_memcpy8 +LDFLAGS += -Wl,--wrap=__aeabi_memset8 +LDFLAGS += -Wl,-Map=$(TARGET).elf.map +LDFLAGS += -Wl,--script=$(LDSCRIPT) +LDFLAGS += -Wl,--gc-sections +LDFLAGS += -Wl,--wrap=printf +LDFLAGS += -Wl,--wrap=vprintf +LDFLAGS += -Wl,--wrap=puts +LDFLAGS += -Wl,--wrap=putchar +LDFLAGS += -Wl,--wrap=getchar +LDFLAGS += -Wl,--print-memory-usage + +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_stdlib/stdlib.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_adc/adc.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_sync/sync.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_flash/flash.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_gpio/gpio.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_claim/claim.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_dma/dma.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_platform/platform.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_sync/sync.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_uart/uart.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_i2c/i2c.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_spi/spi.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_pio/pio.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_divider/divider.S +SRC += $(PICO_SDK_PATH)/src/common/pico_time/time.c +SRC += $(PICO_SDK_PATH)/src/common/pico_time/timeout_helper.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_timer/timer.c +SRC += $(PICO_SDK_PATH)/src/common/pico_sync/sem.c +SRC += $(PICO_SDK_PATH)/src/common/pico_sync/lock_core.c +SRC += $(PICO_SDK_PATH)/src/common/pico_sync/mutex.c +SRC += $(PICO_SDK_PATH)/src/common/pico_sync/critical_section.c +SRC += $(PICO_SDK_PATH)/src/common/pico_util/datetime.c +SRC += $(PICO_SDK_PATH)/src/common/pico_util/pheap.c +SRC += $(PICO_SDK_PATH)/src/common/pico_util/queue.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_runtime/runtime.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_clocks/clocks.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_irq/irq.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_irq/irq_handler_chain.S +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_pll/pll.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_vreg/vreg.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_watchdog/watchdog.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_xosc/xosc.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_printf/printf.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_bit_ops/bit_ops_aeabi.S +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_bootrom/bootrom.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_divider/divider.S +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_double/double_aeabi.S +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_double/double_init_rom.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_double/double_math.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_double/double_v1_rom_shim.S +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_float/float_aeabi.S +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_float/float_init_rom.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_float/float_math.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_float/float_v1_rom_shim.S +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_malloc/pico_malloc.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_mem_ops/mem_ops_aeabi.S +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_standard_link/crt0.S +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_standard_link/new_delete.cpp +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_standard_link/binary_info.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_stdio/stdio.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_stdio_uart/stdio_uart.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_bootsel_via_double_reset/pico_bootsel_via_double_reset.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/hw/bsp/rp2040/family.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/portable/raspberrypi/rp2040/dcd_rp2040.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/portable/raspberrypi/rp2040/rp2040_usb.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/device/usbd.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/device/usbd_control.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/audio/audio_device.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/cdc/cdc_device.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/dfu/dfu_device.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/dfu/dfu_rt_device.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/hid/hid_device.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/midi/midi_device.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/msc/msc_device.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/usbtmc/usbtmc_device.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/vendor/vendor_device.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/tusb.c +SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/common/tusb_fifo.c +SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/rp2040_usb_device_enumeration.c + +BOOT2INC_DIR += -I$(TMK_PATH)/$(PICO_DIR) +BOOT2INC_DIR += -I$(PICO_SDK_PATH)/src/rp2_common/boot_stage2/include +BOOT2INC_DIR += -I$(PICO_SDK_PATH)/src/rp2_common/boot_stage2/asminclude + +$(KEYBOARD_OUTPUT)/src/bs2_default.o: $(PICO_SDK_PATH)/src/rp2_common/boot_stage2/compile_time_choice.S $(KEYBOARD_OUTPUT)/cflags.txt + $(CC) $(CFLAGS) $(BOOT2INC_DIR) -c -o $@ $^ + +$(KEYBOARD_OUTPUT)/src/bs2_default.elf: $(KEYBOARD_OUTPUT)/src/bs2_default.o + $(CC) $(CFLAGS) -Wl,--build-id=none -nostartfiles -Wl,--script=$(PICO_SDK_PATH)/src/rp2_common/boot_stage2/boot_stage2.ld $^ -o $@ + + +$(KEYBOARD_OUTPUT)/src/bs2_default.bin: $(KEYBOARD_OUTPUT)/src/bs2_default.elf + $(OBJCOPY) -Obinary $^ $@ + +$(KEYBOARD_OUTPUT)/src/bs2_default_padded_checksummed.S: $(KEYBOARD_OUTPUT)/src/bs2_default.bin + $(PICO_SDK_PATH)/src/rp2_common/boot_stage2/pad_checksum -s 0xffffffff $^ $@ + + +flash: $(BUILD_DIR)/$(TRAGET).elf cpfirmware sizeafter + until lsusb | grep -q $(RP2BOOT_ID); do\ + printf "$(MSG_BOOTLOADER_NOT_FOUND)" ;\ + sleep 5 ;\ + done + $(PICOTOOL) load $(BUILD_DIR)/$(TARGET).elf && $(PICOTOOL) reboot + +uf2: $(BUILD_DIR)/$(TARGET).bin + ./util/uf2conv.py -f 0xe48bff56 -b 0x10000000 -o $(TARGET).uf2 $^ diff --git a/platforms/pico/platform_deps.h b/platforms/pico/platform_deps.h new file mode 100644 index 0000000000..2fce02ad4a --- /dev/null +++ b/platforms/pico/platform_deps.h @@ -0,0 +1,18 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "platforms/pico/boards/pico_boards.h" diff --git a/platforms/pico/print.c b/platforms/pico/print.c new file mode 100644 index 0000000000..c631ae578b --- /dev/null +++ b/platforms/pico/print.c @@ -0,0 +1,6 @@ + +#include "sendchar.h" + +void print_set_sendchar(sendchar_func_t func) { + // TODO +} diff --git a/platforms/pico/suspend.c b/platforms/pico/suspend.c new file mode 100644 index 0000000000..2d6e0404ce --- /dev/null +++ b/platforms/pico/suspend.c @@ -0,0 +1,145 @@ +#include +#include "matrix.h" +#include "action.h" +#include "suspend.h" +#include "timer.h" +#include "led.h" +#include "host.h" + +#include "tusb.h" + +#ifdef BACKLIGHT_ENABLE +# include "backlight.h" +#endif + +#ifdef AUDIO_ENABLE +# include "audio.h" +#endif /* AUDIO_ENABLE */ + +#if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) +# include "rgblight.h" +#endif + +/** \brief Suspend idle + * + * FIXME: needs doc + */ +void suspend_idle(uint8_t time) { +} + +// TODO: This needs some cleanup + +/** \brief Run keyboard level Power down + * + * FIXME: needs doc + */ +__attribute__((weak)) void suspend_power_down_user(void) {} +/** \brief Run keyboard level Power down + * + * FIXME: needs doc + */ +__attribute__((weak)) void suspend_power_down_kb(void) { suspend_power_down_user(); } + + +/** \brief Suspend power down + * + * FIXME: needs doc + */ +void suspend_power_down(void) { + if (!tud_suspended()) return; + + suspend_power_down_kb(); + +#ifndef NO_SUSPEND_POWER_DOWN + // Turn off backlight +# ifdef BACKLIGHT_ENABLE + backlight_set(0); +# endif + + // Turn off LED indicators + uint8_t leds_off = 0; +# if defined(BACKLIGHT_CAPS_LOCK) && defined(BACKLIGHT_ENABLE) + if (is_backlight_enabled()) { + // Don't try to turn off Caps Lock indicator as it is backlight and backlight is already off + leds_off |= (1 << USB_LED_CAPS_LOCK); + } +# endif + led_set(leds_off); + + // Turn off audio +# ifdef AUDIO_ENABLE + stop_all_notes(); +# endif + + // Turn off underglow +# if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) + rgblight_suspend(); +# endif + + // Enter sleep state if possible (ie, the MCU has a watchdog timeout interrupt) +# if defined(WDT_vect) + power_down(WDTO_15MS); +# endif +#endif +} + +__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; +} + +/** \brief run user level code immediately after wakeup + * + * FIXME: needs doc + */ +__attribute__((weak)) void suspend_wakeup_init_user(void) {} + +/** \brief run keyboard level code immediately after wakeup + * + * FIXME: needs doc + */ +__attribute__((weak)) void suspend_wakeup_init_kb(void) { suspend_wakeup_init_user(); } + +/** \brief run immediately after wakeup + * + * FIXME: needs doc + */ +void suspend_wakeup_init(void) { + // clear keyboard state + clear_keyboard(); + + // Turn on backlight +#ifdef BACKLIGHT_ENABLE + backlight_init(); +#endif + + // Restore LED indicators + led_set(host_keyboard_leds()); + + // Wake up underglow +#if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) + rgblight_wakeup(); +#endif + + suspend_wakeup_init_kb(); +} + +#if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect) +/* watchdog timeout */ +ISR(WDT_vect) { + // compensate timer for sleep + switch (wdt_timeout) { + case WDTO_15MS: + timer_count += 15 + 2; // WDTO_15MS + 2(from observation) + break; + default:; + } +} +#endif diff --git a/platforms/pico/timer.c b/platforms/pico/timer.c new file mode 100644 index 0000000000..b4e46353a1 --- /dev/null +++ b/platforms/pico/timer.c @@ -0,0 +1,67 @@ +/* +Copyright 2011 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 "timer.h" +#include "pico/stdlib.h" + +/** \brief timer initialization + * + * FIXME: needs doc + */ +void timer_init(void) { +} + +/** \brief timer read + * + * FIXME: needs doc + */ +inline uint16_t timer_read(void) { + + return (to_ms_since_boot(get_absolute_time()) & 0xFFFF); +} + +/** \brief timer read32 + * + * FIXME: needs doc + */ +inline uint32_t timer_read32(void) { + return (to_ms_since_boot(get_absolute_time()) & 0xFFFFFFFF); +} + +/** \brief timer elapsed + * + * FIXME: needs doc + */ +inline uint16_t timer_elapsed(uint16_t last) { + uint32_t t; + + t = timer_read32(); + + return TIMER_DIFF_16((t & 0xFFFF), last); +} + +/** \brief timer elapsed32 + * + * FIXME: needs doc + */ +inline uint32_t timer_elapsed32(uint32_t last) { + uint32_t t; + + t = timer_read32(); + + return TIMER_DIFF_32(t, last); +} diff --git a/quantum/audio/driver_pico_pwm.h b/quantum/audio/driver_pico_pwm.h new file mode 100644 index 0000000000..99ddf32dbf --- /dev/null +++ b/quantum/audio/driver_pico_pwm.h @@ -0,0 +1,2 @@ + +#pragma once diff --git a/quantum/audio/driver_pico_pwm_hardware.c b/quantum/audio/driver_pico_pwm_hardware.c new file mode 100644 index 0000000000..71b5760498 --- /dev/null +++ b/quantum/audio/driver_pico_pwm_hardware.c @@ -0,0 +1,108 @@ +/* +Audio Driver: PWM + +the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match +the frequency of a note to be played back. + +this driver uses the chibios-PWM system to produce a square-wave on specific +output pins that are connected to the PWM hardware. The hardware directly +toggles the pin via its alternate function. see your MCUs data-sheet for which +pin can be driven by what timer - looking for TIMx_CHy and the corresponding +alternate function. + + */ + +#include "audio.h" + +#include "hardware/pwm.h" +#include "hardware/irq.h" +#include "hardware/clocks.h" + +#if !defined(AUDIO_PIN) +# error \ + "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings" +#endif + +extern bool playing_note; +extern bool playing_melody; +extern uint8_t note_timbre; + +#define WRAP_MAX 65534 +#define CLOCK_DIV 100 + +static void pwm_callback(void); + +static uint pwm_clock = 12000000 / CLOCK_DIV; +static volatile uint32_t isr_counter = 0; + +static float channel_1_frequency = 0.0f; +void channel_1_set_frequency(float freq) { + channel_1_frequency = freq; + + if (freq <= 0.0) // a pause/rest has freq=0 + return; + + // enable channel + + uint slice = pwm_gpio_to_slice_num(AUDIO_PIN); + uint16_t wrap = pwm_clock / freq; + pwm_set_wrap(slice, wrap); + pwm_set_gpio_level(AUDIO_PIN, wrap * note_timbre / 100); +} + +float channel_1_get_frequency(void) { return channel_1_frequency; } + +void channel_1_start(void) { + uint slice = pwm_gpio_to_slice_num(AUDIO_PIN); + pwm_set_enabled(slice, true); +} + +void channel_1_stop(void) { + uint slice = pwm_gpio_to_slice_num(AUDIO_PIN); + pwm_set_enabled(slice, false); +} + +void audio_driver_initialize(void) { + channel_1_stop(); + gpio_set_function(AUDIO_PIN, GPIO_FUNC_PWM); + uint slice = pwm_gpio_to_slice_num(AUDIO_PIN); + + pwm_clear_irq(slice); + pwm_set_irq_enabled(slice, true); + irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_callback); + irq_set_enabled(PWM_IRQ_WRAP, true); + + pwm_set_clkdiv(slice, CLOCK_DIV); + pwm_clock = clock_get_hz(clk_sys) / CLOCK_DIV; + pwm_set_wrap(slice, WRAP_MAX); +} + +void audio_driver_start(void) { + channel_1_stop(); + channel_1_start(); + + if (playing_note || playing_melody) { + channel_1_set_frequency(audio_get_processed_frequency(0)); + } +} + +void audio_driver_stop(void) { channel_1_stop(); } + +static void pwm_callback(void) { + pwm_clear_irq(pwm_gpio_to_slice_num(AUDIO_PIN)); + + isr_counter++; + if (isr_counter < channel_1_frequency / (64)) return; + + isr_counter = 0; + bool state_changed = audio_update_state(); + + if (!playing_note && !playing_melody) { + channel_1_stop(); + return; + } + + if (state_changed) { + channel_1_set_frequency(audio_get_processed_frequency(0)); + } +} diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c index 49971ba529..2092231b35 100644 --- a/quantum/dynamic_keymap.c +++ b/quantum/dynamic_keymap.c @@ -421,6 +421,7 @@ void dynamic_keymap_set_buffer(uint16_t offset, uint16_t size, uint8_t *data) { extern uint16_t g_vial_magic_keycode_override; // This overrides the one in quantum/keymap_common.c +#ifndef OVERRIDE_KEYMAP_KEY_TO_KEYCODE uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) { #ifdef VIAL_ENABLE /* Disable any keycode processing while unlocking */ @@ -437,6 +438,7 @@ uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) { return KC_NO; } } +#endif uint8_t dynamic_keymap_macro_get_count(void) { return DYNAMIC_KEYMAP_MACRO_COUNT; diff --git a/quantum/split_common/split_util.c b/quantum/split_common/split_util.c index 7d50adf758..7e3dbb48df 100644 --- a/quantum/split_common/split_util.c +++ b/quantum/split_common/split_util.c @@ -23,6 +23,10 @@ #include "wait.h" #include "usb_util.h" +#ifdef PROTOCOL_PICO +# include "tusb.h" +#endif + #ifdef EE_HANDS # include "eeconfig.h" #endif @@ -57,7 +61,39 @@ static uint8_t connection_errors = 0; volatile bool isLeftHand = true; #if defined(SPLIT_USB_DETECT) -static bool usbIsActive(void) { +# if defined(PROTOCOL_LUFA) +static inline bool usbHasActiveConnection(void) { + return USB_Device_IsAddressSet(); +} +static inline void usbDisable(void) { + USB_Disable(); + USB_DeviceState = DEVICE_STATE_Unattached; +} +# elif defined(PROTOCOL_CHIBIOS) +static inline bool usbHasActiveConnection(void) { + return usbGetDriverStateI(&USBD1) == USB_ACTIVE; +} +static inline void usbDisable(void) { usbStop(&USBD1); } +# elif defined(PROTOCOL_VUSB) +static inline bool usbHasActiveConnection(void) { + usbPoll(); + return usbConfiguration; +} +static inline void usbDisable(void) { usbDeviceDisconnect(); } +# elif defined(PROTOCOL_PICO) +static inline bool usbHasActiveConnection(void) { + tud_task(); + return tud_connected(); +} +static inline void usbDisable(void) { +# warning "No implementation" //TODO implement +} +# else +static inline bool usbHasActiveConnection(void) { return true; } +static inline void usbDisable(void) {} +# endif + +bool usbIsActive(void) { for (uint8_t i = 0; i < (SPLIT_USB_TIMEOUT / SPLIT_USB_TIMEOUT_POLL); i++) { // This will return true if a USB connection has been established if (usb_connected_state()) { @@ -139,7 +175,8 @@ void split_pre_init(void) { if (isLeftHand) { rgblight_set_clipping_range(0, num_rgb_leds_split[0]); } else { - rgblight_set_clipping_range(num_rgb_leds_split[0], num_rgb_leds_split[1]); + rgblight_set_clipping_range(num_rgb_leds_split[0], + num_rgb_leds_split[1]); } #endif diff --git a/tmk_core/protocol.mk b/tmk_core/protocol.mk index 19fd7d2425..ab6aa51eba 100644 --- a/tmk_core/protocol.mk +++ b/tmk_core/protocol.mk @@ -1,8 +1,10 @@ +PROTOCOL_COMMON_DIR = protocol + TMK_COMMON_SRC += \ - $(PROTOCOL_DIR)/host.c \ - $(PROTOCOL_DIR)/report.c \ - $(PROTOCOL_DIR)/usb_device_state.c \ - $(PROTOCOL_DIR)/usb_util.c \ + $(PROTOCOL_COMMON_DIR)/host.c \ + $(PROTOCOL_COMMON_DIR)/report.c \ + $(PROTOCOL_COMMON_DIR)/usb_device_state.c \ + $(PROTOCOL_COMMON_DIR)/usb_util.c \ SHARED_EP_ENABLE = no MOUSE_SHARED_EP ?= yes diff --git a/tmk_core/protocol/midi/bytequeue/interrupt_setting.c b/tmk_core/protocol/midi/bytequeue/interrupt_setting.c index d9c0035946..95541cc1d6 100644 --- a/tmk_core/protocol/midi/bytequeue/interrupt_setting.c +++ b/tmk_core/protocol/midi/bytequeue/interrupt_setting.c @@ -33,6 +33,18 @@ interrupt_setting_t store_and_clear_interrupt(void) { void restore_interrupt_setting(interrupt_setting_t setting) { SREG = setting; } + +#elif defined(PROTOCOL_PICO) +# include "atomic_util.h" + +interrupt_setting_t store_and_clear_interrupt(void) { + return __interrupt_disable__(); +} + +void restore_interrupt_setting(interrupt_setting_t setting) { + __interrupt_enable__(setting); +} + #elif defined(__arm__) # include diff --git a/tmk_core/protocol/pico.mk b/tmk_core/protocol/pico.mk new file mode 100644 index 0000000000..ed3a170227 --- /dev/null +++ b/tmk_core/protocol/pico.mk @@ -0,0 +1,19 @@ +PROTOCOL_DIR = protocol +PICO_DIR = $(PROTOCOL_DIR)/pico + +SRC += $(KEYBOARD_OUTPUT)/src/bs2_default_padded_checksummed.S +LDSCRIPT := $(PICO_SDK_PATH)/src/rp2_common/pico_standard_link/memmap_default.ld + +SRC += $(PICO_DIR)/pico.c +SRC += $(PICO_DIR)/usb_descriptors.c +SRC += $(PICO_DIR)/iusb.c +SRC += $(PICO_DIR)/pio_manager.c +SRC += $(PICO_DIR)/usb_util.c + +VPATH += $(TMK_PATH)/$(PROTOCOL_DIR) +VPATH += $(TMK_PATH)/$(PICO_DIR) +VPATH += $(TMK_PATH)/$(PICO_DIR)/lufa_utils + +CFLAGS += -DPROTOCOL_PICO +CFLAGS += -DGPIO_INPUT_PIN_DELAY=100 +CFLAGS += -DDYNAMIC_KEYMAP_EEPROM_MAX_ADDR=4095 diff --git a/tmk_core/protocol/pico/bs2_default_padded_checksummed.S b/tmk_core/protocol/pico/bs2_default_padded_checksummed.S new file mode 100644 index 0000000000..cf3071cb9d --- /dev/null +++ b/tmk_core/protocol/pico/bs2_default_padded_checksummed.S @@ -0,0 +1,23 @@ +// Padded and checksummed version of: /home/sekigon/pico/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/bs2_default.bin + +.cpu cortex-m0plus +.thumb + +.section .boot2, "ax" + +.byte 0x00, 0xb5, 0x32, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, 0x98, 0x60 +.byte 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2e, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x02, 0x21, 0x59, 0x61 +.byte 0x01, 0x21, 0xf0, 0x22, 0x99, 0x50, 0x2b, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20 +.byte 0x00, 0xf0, 0x44, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x14, 0xd0, 0x06, 0x21, 0x19, 0x66, 0x00, 0xf0 +.byte 0x34, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66, 0x00, 0x20, 0x18, 0x66, 0x1a, 0x66, 0x00, 0xf0 +.byte 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e, 0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21 +.byte 0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60 +.byte 0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21, 0x19, 0x66, 0xa0, 0x21 +.byte 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21, 0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60 +.byte 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49 +.byte 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a, 0x04, 0x20 +.byte 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66 +.byte 0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40 +.byte 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00 +.byte 0xf4, 0x00, 0x00, 0x18, 0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0 +.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xb2, 0x4e, 0x7a diff --git a/tmk_core/protocol/pico/iusb.c b/tmk_core/protocol/pico/iusb.c new file mode 100644 index 0000000000..1847fe2f9a --- /dev/null +++ b/tmk_core/protocol/pico/iusb.c @@ -0,0 +1,91 @@ +#include "iusb.h" +#include "raw_hid.h" + +#ifdef MIDI_ENABLE +# include "qmk_midi.h" +#endif +#include "usb_descriptors.h" + +#include "device/usbd.h" +#include "hid_device.h" +#include "class/midi/midi_device.h" + +/* declarations */ +uint8_t keyboard_leds(void) { + // TODO + return 0; +} + +void send_keyboard(report_keyboard_t *report) { + while (!tud_hid_n_ready(ITF_NUM_HID_KEYBOARD)) { + tud_task(); + if (!tud_ready()) { + return; + } + } + tud_hid_n_report(ITF_NUM_HID_KEYBOARD, 0, report, sizeof(*report)); + tud_task(); +} + +void send_mouse(report_mouse_t *report) { + while (!tud_hid_n_ready(ITF_NUM_HID_MOUSE)) { + tud_task(); + if (!tud_ready()) { + return; + } + } + // Require only 5 bytes. Do not usb report->id + tud_hid_n_report(ITF_NUM_HID_MOUSE, 0, &report->buttons, 5); + tud_task(); +} + +void send_system(uint16_t data) { + while (!tud_hid_n_ready(ITF_NUM_HID_EXTRA)) { + tud_task(); + if (!tud_ready()) { + return; + } + } + tud_hid_n_report(ITF_NUM_HID_EXTRA, REPORT_ID_SYSTEM, &data, 2); + tud_task(); +} + +void send_consumer(uint16_t data) { + while (!tud_hid_n_ready(ITF_NUM_HID_EXTRA)) { + tud_task(); + if (!tud_ready()) { + return; + } + } + tud_hid_n_report(ITF_NUM_HID_EXTRA, REPORT_ID_CONSUMER, &data, 2); + tud_task(); +} + +void raw_hid_send(uint8_t* data, uint8_t length) { + while (!tud_hid_n_ready(ITF_NUM_HID_RAW)) { + tud_task(); + if (!tud_ready()) { + return; + } + } + tud_hid_n_report(ITF_NUM_HID_RAW, 0, data, length); + tud_task(); +} + +#ifdef MIDI_ENABLE +void send_midi_packet(MIDI_EventPacket_t *event) { + tud_midi_packet_write((uint8_t *)event); + tud_task(); +} + +bool recv_midi_packet(MIDI_EventPacket_t *const event) { + if (tud_midi_available()) { + tud_midi_packet_read((uint8_t *)event); + + return true; + } else { + return false; + } +} + +#endif // MIDI_ENABLE diff --git a/tmk_core/protocol/pico/iusb.h b/tmk_core/protocol/pico/iusb.h new file mode 100644 index 0000000000..28bc3590a9 --- /dev/null +++ b/tmk_core/protocol/pico/iusb.h @@ -0,0 +1,12 @@ + +#pragma once + +#include +#include "report.h" + +/* 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); diff --git a/tmk_core/protocol/pico/lufa_utils/LUFA/Drivers/USB/USB.h b/tmk_core/protocol/pico/lufa_utils/LUFA/Drivers/USB/USB.h new file mode 100644 index 0000000000..22f882cbb8 --- /dev/null +++ b/tmk_core/protocol/pico/lufa_utils/LUFA/Drivers/USB/USB.h @@ -0,0 +1,42 @@ +#include "progmem.h" +#include +#include + +#define ATTR_PACKED __attribute__((packed)) +/** Concatenates the given input into a single token, via the C Preprocessor. + * + * \param[in] x First item to concatenate. + * \param[in] y Second item to concatenate. + * + * \return Concatenated version of the input. + */ +#define CONCAT(x, y) x##y + +/** CConcatenates the given input into a single token after macro expansion, via the C Preprocessor. + * + * \param[in] x First item to concatenate. + * \param[in] y Second item to concatenate. + * + * \return Concatenated version of the expanded input. + */ +#define CONCAT_EXPANDED(x, y) CONCAT(x, y) +#define CPU_TO_LE16(x) (x) + +// We don't need anything from the following files, or we have defined it already +#define __LUFA_COMMON_H__ +#define __USBMODE_H__ +#define __USBEVENTS_H__ +#define __HIDPARSER_H__ +#define __USBCONTROLLER_AVR8_H__ + +#define __INCLUDE_FROM_USB_DRIVER +#define __INCLUDE_FROM_HID_DRIVER +#define __INCLUDE_FROM_CDC_DRIVER +#define __INCLUDE_FROM_AUDIO_DRIVER +#define __INCLUDE_FROM_MIDI_DRIVER +#include "lib/lufa/LUFA/Drivers/USB/Class/Common/HIDClassCommon.h" +#include "lib/lufa/LUFA/Drivers/USB/Class/Common/HIDReportData.h" +#include "lib/lufa/LUFA/Drivers/USB/Class/Common/CDCClassCommon.h" +#include "lib/lufa/LUFA/Drivers/USB/Class/Common/AudioClassCommon.h" +#include "lib/lufa/LUFA/Drivers/USB/Class/Common/MIDIClassCommon.h" +#include "lib/lufa/LUFA/Drivers/USB/Core/USBController.h" diff --git a/tmk_core/protocol/pico/msc_disk.c b/tmk_core/protocol/pico/msc_disk.c new file mode 100644 index 0000000000..503baace9e --- /dev/null +++ b/tmk_core/protocol/pico/msc_disk.c @@ -0,0 +1,259 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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. + * + */ + +#include "bsp/board.h" +#include "tusb.h" + +#if CFG_TUD_MSC + +// whether host does safe-eject +static bool ejected = false; + +// Some MCU doesn't have enough 8KB SRAM to store the whole disk +// We will use Flash as read-only disk with board that has +// CFG_EXAMPLE_MSC_READONLY defined + +#define README_CONTENTS \ +"This is tinyusb's MassStorage Class demo.\r\n\r\n\ +If you find any bugs or get any questions, feel free to file an\r\n\ +issue at github.com/hathach/tinyusb" + +enum +{ + DISK_BLOCK_NUM = 16, // 8KB is the smallest size that windows allow to mount + DISK_BLOCK_SIZE = 512 +}; + +#ifdef CFG_EXAMPLE_MSC_READONLY +const +#endif +uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = +{ + //------------- Block0: Boot Sector -------------// + // byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16 = DISK_BLOCK_NUM; + // sector_per_cluster = 1; reserved_sectors = 1; + // fat_num = 1; fat12_root_entry_num = 16; + // sector_per_fat = 1; sector_per_track = 1; head_num = 1; hidden_sectors = 0; + // drive_number = 0x80; media_type = 0xf8; extended_boot_signature = 0x29; + // filesystem_type = "FAT12 "; volume_serial_number = 0x1234; volume_label = "TinyUSB MSC"; + // FAT magic code at offset 510-511 + { + 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00, + 0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T' , 'i' , 'n' , 'y' , 'U' , + 'S' , 'B' , ' ' , 'M' , 'S' , 'C' , 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00, + + // Zero up to 2 last bytes of FAT magic code + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA + }, + + //------------- Block1: FAT12 Table -------------// + { + 0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third entry is cluster end of readme file + }, + + //------------- Block2: Root Directory -------------// + { + // first entry is volume label + 'T' , 'i' , 'n' , 'y' , 'U' , 'S' , 'B' , ' ' , 'M' , 'S' , 'C' , 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // second entry is readme file + 'R' , 'E' , 'A' , 'D' , 'M' , 'E' , ' ' , ' ' , 'T' , 'X' , 'T' , 0x20, 0x00, 0xC6, 0x52, 0x6D, + 0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00, + sizeof(README_CONTENTS)-1, 0x00, 0x00, 0x00 // readme's files size (4 Bytes) + }, + + //------------- Block3: Readme Content -------------// + README_CONTENTS +}; + +// Invoked when received SCSI_CMD_INQUIRY +// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) +{ + (void) lun; + + const char vid[] = "TinyUSB"; + const char pid[] = "Mass Storage"; + const char rev[] = "1.0"; + + memcpy(vendor_id , vid, strlen(vid)); + memcpy(product_id , pid, strlen(pid)); + memcpy(product_rev, rev, strlen(rev)); +} + +// Invoked when received Test Unit Ready command. +// return true allowing host to read/write this LUN e.g SD card inserted +bool tud_msc_test_unit_ready_cb(uint8_t lun) +{ + (void) lun; + + // RAM disk is ready until ejected + if (ejected) { + tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); + return false; + } + + return true; +} + +// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size +// Application update block count and block size +void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) +{ + (void) lun; + + *block_count = DISK_BLOCK_NUM; + *block_size = DISK_BLOCK_SIZE; +} + +// Invoked when received Start Stop Unit command +// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage +// - Start = 1 : active mode, if load_eject = 1 : load disk storage +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) +{ + (void) lun; + (void) power_condition; + + if ( load_eject ) + { + if (start) + { + // load disk storage + }else + { + // unload disk storage + ejected = true; + } + } + + return true; +} + +// Callback invoked when received READ10 command. +// Copy disk's data to buffer (up to bufsize) and return number of copied bytes. +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) +{ + (void) lun; + + uint8_t const* addr = msc_disk[lba] + offset; + memcpy(buffer, addr, bufsize); + + return bufsize; +} + +// Callback invoked when received WRITE10 command. +// Process data in buffer to disk's storage and return number of written bytes +int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) +{ + (void) lun; + +#ifndef CFG_EXAMPLE_MSC_READONLY + uint8_t* addr = msc_disk[lba] + offset; + memcpy(addr, buffer, bufsize); +#else + (void) lba; (void) offset; (void) buffer; +#endif + + return bufsize; +} + +// Callback invoked when received an SCSI command not in built-in list below +// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE +// - READ10 and WRITE10 has their own callbacks +int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) +{ + // read10 & write10 has their own callback and MUST not be handled here + + void const* response = NULL; + uint16_t resplen = 0; + + // most scsi handled is input + bool in_xfer = true; + + switch (scsi_cmd[0]) + { + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + // Host is about to read/write etc ... better not to disconnect disk + resplen = 0; + break; + + default: + // Set Sense = Invalid Command Operation + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + + // negative means error -> tinyusb could stall and/or response with failed status + resplen = -1; + break; + } + + // return resplen must not larger than bufsize + if ( resplen > bufsize ) resplen = bufsize; + + if ( response && (resplen > 0) ) + { + if(in_xfer) + { + memcpy(buffer, response, resplen); + }else + { + // SCSI output + } + } + + return resplen; +} + +#endif diff --git a/tmk_core/protocol/pico/pico.c b/tmk_core/protocol/pico/pico.c new file mode 100644 index 0000000000..ca225154a2 --- /dev/null +++ b/tmk_core/protocol/pico/pico.c @@ -0,0 +1,282 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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. + * + */ + +#include +#include +#include + +#include "raw_hid.h" +#include "bootloader.h" +#include "debug.h" + +#include "pico_eeprom.h" +#include "usb_descriptors.h" + +#include "pico/stdio/driver.h" +#include "hardware/watchdog.h" +#include "hardware/structs/watchdog.h" +#include "bsp/board.h" +#include "tusb.h" + +/* TMK includes */ +#include "report.h" +#include "host.h" +#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" +#include "print.h" + +#ifdef SLEEP_LED_ENABLE +# include "sleep_led.h" +#endif +#ifdef SERIAL_LINK_ENABLE +# include "serial_link/system/serial_link.h" +#endif +#ifdef VISUALIZER_ENABLE +# include "visualizer/visualizer.h" +#endif +#ifdef MIDI_ENABLE +# include "qmk_midi.h" +#endif +#ifdef STM32_EEPROM_ENABLE +# include "eeprom_stm32.h" +#endif +#ifdef EEPROM_DRIVER +# include "eeprom_driver.h" +#endif +#include "suspend.h" +#include "wait.h" + +#include "iusb.h" + +void platform_setup(void); +extern char __StackTop; + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ +static int cdc_in_chars(char* buf, int len) { return 0; } + +static void cdc_write(const char* buf, int length) { + static uint64_t last_avail_time; + if (tud_cdc_connected()) { + for (int i = 0; i < length;) { + int n = length - i; + int avail = tud_cdc_write_available(); + if (n > avail) n = avail; + if (n) { + int n2 = tud_cdc_write(buf + i, n); + tud_task(); + tud_cdc_write_flush(); + i += n2; + last_avail_time = time_us_64(); + } else { + tud_task(); + tud_cdc_write_flush(); + if (!tud_cdc_connected() || (!tud_cdc_write_available() && time_us_64() > last_avail_time + 500000)) { + break; + } + } + } + } +} + +static stdio_driver_t stdio_driver = {.out_chars = cdc_write, .out_flush = NULL, .in_chars = cdc_in_chars, .next = NULL}; + +void pico_cdc_enable_printf(void) { stdio_set_driver_enabled(&stdio_driver, true); } +void pico_cdc_disable_printf(void) { stdio_set_driver_enabled(&stdio_driver, false); } + +/* host struct */ +host_driver_t driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer}; + +static void console_task(void) { + // TODO +} + +static void raw_hid_task(void) { + // TODO +} + +static void midi_ep_task(void) {} + +void protocol_setup(void) { tusb_init(); } + +void protocol_pre_init(void) { + // if (watchdog_caused_reboot() && watchdog_hw->scratch[0] == 0x2040dead) { + // bootloader_jump(); + // } + watchdog_enable(8000, 1); + watchdog_hw->scratch[0] = 0x2040dead; + pico_cdc_enable_printf(); + pico_eepemu_init(); +} + +void protocol_post_init(void) { host_set_driver(&driver); } + +void protocol_pre_task(void) { + tud_task(); // tinyusb device task +} + +void protocol_post_task(void) { + pico_eepemu_lazy_write_back(); +#ifdef CONSOLE_ENABLE + console_task(); +#endif +#ifdef MIDI_ENABLE + midi_ep_task(); +#endif +#ifdef VIRTSER_ENABLE + virtser_task(); +#endif +#ifdef RAW_ENABLE + raw_hid_task(); +#endif + watchdog_update(); +} + +//--------------------------------------------------------------------+ +// Device callbacks +//--------------------------------------------------------------------+ + +// Invoked when device is mounted +void tud_mount_cb(void) {} + +// Invoked when device is unmounted +void tud_umount_cb(void) {} + +// Invoked when usb bus is suspended +// remote_wakeup_en : if host allow us to perform remote wakeup +// Within 7ms, device must draw an average of current less than 2.5 mA from bus +void tud_suspend_cb(bool remote_wakeup_en) { (void)remote_wakeup_en; } + +// Invoked when usb bus is resumed +void tud_resume_cb(void) {} + +// Invoked when cdc when line state changed e.g connected/disconnected +void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) { + (void)itf; + (void)rts; + + // TODO set some indicator + if (dtr) { + // Terminal connected + } else { + // Terminal disconnected + } +} + +__attribute__((weak)) void pico_cdc_change_baudrate_cb(uint32_t baudrate) { + if (baudrate == 1200) { + bootloader_jump(); + } +} + +void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding) { + (void)itf; + pico_cdc_change_baudrate_cb(p_line_coding->bit_rate); +} + +__attribute__((weak)) void pico_cdc_receive_cb(uint8_t const* buf, uint32_t cnt) { + tud_cdc_write(buf, cnt); + tud_cdc_write_flush(); + + if (buf[0] == 's') { + printf("save keymap to eeprom\n"); + pico_eepemu_flash_dynamic_keymap(); + printf("complete\n"); + printf("save eeconfig to eeprom\n"); + pico_eepemu_flash_eeconfig(); + printf("complete\n"); + } else if (buf[0] == 'l') { + printf("init eeprom emulation\n"); + pico_eepemu_init(); + printf("complete\n"); + } else if (buf[0] == 'd') { + printf("debug print "); + if (!debug_enable) { + debug_enable = true; + dprint("true\n"); + } else { + debug_enable = false; + print("false\n"); + } + } +} + +// Invoked when CDC interface received data from host +void tud_cdc_rx_cb(uint8_t itf) { + if (tud_cdc_available()) { + // read datas + char buf[64]; + uint32_t count = tud_cdc_read(buf, sizeof(buf)); + (void)count; + + pico_cdc_receive_cb((uint8_t*)buf, count); + } +} + +// Invoked when sent REPORT successfully to host +// Application can use this to send the next report +// Note: For composite reports, report[0] is report ID +void tud_hid_report_complete_cb(uint8_t itf, uint8_t const* report, uint8_t len) { + (void)itf; + (void)len; +} + +// Invoked when received GET_REPORT control request +// Application must fill buffer report's content and return its length. +// Return zero will cause the stack to STALL request +uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { + // TODO not Implemented + (void)itf; + (void)report_id; + (void)report_type; + (void)buffer; + (void)reqlen; + + return 0; +} + +static uint8_t keyboard_indicator_led; +uint8_t get_keyboard_led_status(void) { return keyboard_indicator_led; } + +__attribute__((weak)) void raw_hid_receive(uint8_t* data, uint8_t length) {} + +// Invoked when received SET_REPORT control request or +// received data on OUT endpoint ( Report ID = 0, Type = 0 ) +void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { + switch (itf) { + case ITF_NUM_HID_RAW: + raw_hid_receive((uint8_t*)buffer, bufsize); + break; + default: + break; + } +} diff --git a/tmk_core/protocol/pico/pico/config_autogen.h b/tmk_core/protocol/pico/pico/config_autogen.h new file mode 100644 index 0000000000..1b59174fd0 --- /dev/null +++ b/tmk_core/protocol/pico/pico/config_autogen.h @@ -0,0 +1,11 @@ +// +// TODO Generate by makefile +// + +// based on PICO_CONFIG_HEADER_FILES: + +#include "boards/include/boards/pico.h" + +// based on PICO_RP2040_CONFIG_HEADER_FILES: + +#include "rp2_common/cmsis/include/cmsis/rename_exceptions.h" diff --git a/tmk_core/protocol/pico/pico/version.h b/tmk_core/protocol/pico/pico/version.h new file mode 100644 index 0000000000..94d1194a54 --- /dev/null +++ b/tmk_core/protocol/pico/pico/version.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +// --------------------------------------- +// THIS FILE IS AUTOGENERATED; DO NOT EDIT +// --------------------------------------- + +#ifndef _PICO_VERSION_H +#define _PICO_VERSION_H + +#define PICO_SDK_VERSION_MAJOR 1 +#define PICO_SDK_VERSION_MINOR 2 +#define PICO_SDK_VERSION_REVISION 0 +#define PICO_SDK_VERSION_STRING "1.2.0" + +#endif diff --git a/tmk_core/protocol/pico/pico_cdc.h b/tmk_core/protocol/pico/pico_cdc.h new file mode 100644 index 0000000000..bcedec6738 --- /dev/null +++ b/tmk_core/protocol/pico/pico_cdc.h @@ -0,0 +1,9 @@ + +#pragma once + +void pico_cdc_enable_printf(void); +void pico_cdc_disable_printf(void); + +// weak functions +void pico_cdc_change_baudrate_cb(uint32_t baudrate); +void pico_cdc_receive_cb(uint8_t const* buf, uint32_t cnt); diff --git a/tmk_core/protocol/pico/pico_eeprom.h b/tmk_core/protocol/pico/pico_eeprom.h new file mode 100644 index 0000000000..729181e274 --- /dev/null +++ b/tmk_core/protocol/pico/pico_eeprom.h @@ -0,0 +1,7 @@ + +#pragma once + +void pico_eepemu_init(void); +void pico_eepemu_flash_eeconfig(void); +void pico_eepemu_flash_dynamic_keymap(void); +void pico_eepemu_lazy_write_back(void); diff --git a/tmk_core/protocol/pico/pio_manager.c b/tmk_core/protocol/pico/pio_manager.c new file mode 100644 index 0000000000..4e86caf5d7 --- /dev/null +++ b/tmk_core/protocol/pico/pio_manager.c @@ -0,0 +1,32 @@ + +#include "pio_manager.h" +#include "hardware/pio.h" + +int32_t pio_manager_get_empty_sm(PIO pio) { + check_pio_param(pio); + for (uint32_t sm = 0; sm < NUM_PIO_STATE_MACHINES; sm++) { + if (!pio_sm_is_claimed(pio, sm)) return sm; + } + + return -1; +} + +int32_t pio_manager_add_program(PIO pio, uint32_t sm, + pio_program_t const* program) { + check_pio_param(pio); + check_sm_param(sm); + + if (pio_sm_is_claimed(pio, sm)) { + return -1; + } + + if (!pio_can_add_program(pio, program)) { + return -2; + } + + pio_sm_claim(pio, sm); + + uint32_t offset = pio_add_program(pio, program); + + return offset; +} diff --git a/tmk_core/protocol/pico/pio_manager.h b/tmk_core/protocol/pico/pio_manager.h new file mode 100644 index 0000000000..046df09525 --- /dev/null +++ b/tmk_core/protocol/pico/pio_manager.h @@ -0,0 +1,8 @@ + +#pragma once + +#include "hardware/pio.h" + +int32_t pio_manager_get_empty_sm(PIO pio); +int32_t pio_manager_add_program(PIO pio, uint32_t sm, + pio_program_t const* program); diff --git a/tmk_core/protocol/pico/tusb_config.h b/tmk_core/protocol/pico/tusb_config.h new file mode 100644 index 0000000000..3a79afa976 --- /dev/null +++ b/tmk_core/protocol/pico/tusb_config.h @@ -0,0 +1,122 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by board.mk +#ifndef CFG_TUSB_MCU + #error CFG_TUSB_MCU must be defined +#endif + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_DEVICE_RHPORT_NUM + #define BOARD_DEVICE_RHPORT_NUM 0 +#endif + +// RHPort max operational speed can defined by board.mk +// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed +#ifndef BOARD_DEVICE_RHPORT_SPEED + #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ + CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56) + #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED + #else + #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED + #endif +#endif + +// Device mode with rhport and speed defined by board.mk +#if BOARD_DEVICE_RHPORT_NUM == 0 + #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) +#elif BOARD_DEVICE_RHPORT_NUM == 1 + #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) +#else + #error "Incorrect RHPort configuration" +#endif + +// This example doesn't use an RTOS +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +// #define CFG_TUSB_DEBUG 0 + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_CDC 1 +#define CFG_TUD_MSC 0 +#define CFG_TUD_HID 5 +#define CFG_TUD_MIDI 1 +#define CFG_TUD_VENDOR 0 + +// CDC FIFO size of TX and RX +#define CFG_TUD_CDC_RX_BUFSIZE 64 +#define CFG_TUD_CDC_TX_BUFSIZE 2048 + +// CDC Endpoint transfer buffer size, more is faster +#define CFG_TUD_CDC_EP_BUFSIZE 64 + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_EP_BUFSIZE 64 + +#define CFG_TUD_MIDI_RX_BUFSIZE 64 +#define CFG_TUD_MIDI_TX_BUFSIZE 64 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/tmk_core/protocol/pico/usb_descriptors.c b/tmk_core/protocol/pico/usb_descriptors.c new file mode 100644 index 0000000000..1d2936811b --- /dev/null +++ b/tmk_core/protocol/pico/usb_descriptors.c @@ -0,0 +1,227 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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. + * + */ + +#include "tusb.h" +#include "report.h" +#include "usb_descriptors.h" +#include "util.h" + +/* A combination of interfaces must have a unique product id, since PC will save + * device driver after the first plug. Same VID/PID with different interface e.g + * MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] HID | MSC | CDC [LSB] + */ +#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) +#define USB_PID \ + (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ + _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4)) + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0110, + + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and + // protocol must be IAD (1) + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = VENDOR_ID, + .idProduct = PRODUCT_ID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const* tud_descriptor_device_cb(void) { + return (uint8_t const*)&desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +#ifdef MIDI_ENABLE +# define MIDI_DESC_LEN TUD_MIDI_DESC_LEN +#else +# define MIDI_DESC_LEN 0 +#endif + +#define CONFIG_TOTAL_LEN \ + (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_DESC_LEN * 4 + \ + TUD_HID_INOUT_DESC_LEN + MIDI_DESC_LEN) + +#define EPNUM_HID_KEYBOARD_IN 0x81 +#define EPNUM_HID_MOUSE_IN 0x82 +#define EPNUM_HID_EXTRA_IN 0x83 +#define EPNUM_HID_RAW_IN 0x84 +#define EPNUM_HID_RAW_OUT 0x04 +#define EPNUM_HID_CONSOLE_IN 0x85 + +#define EPNUM_CDC_NOTIF 0x86 +#define EPNUM_CDC_OUT 0x07 +#define EPNUM_CDC_IN 0x87 + +#ifdef MIDI_ENABLE +# define EPNUM_MIDI_IN 0x88 +# define EPNUM_MIDI_OUT 0x08 +#endif + +static uint8_t const desc_keyboard[] = {TUD_HID_REPORT_DESC_KEYBOARD()}; + +static uint8_t const desc_mouse[] = {TUD_HID_REPORT_DESC_MOUSE()}; + +static uint8_t const desc_extra[] = { + TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(REPORT_ID_CONSUMER)), + TUD_HID_REPORT_DESC_SYSTEM_CONTROL(HID_REPORT_ID(REPORT_ID_SYSTEM)), +}; + +static uint8_t const desc_raw[] = {HID_REPORT_DESC_RAW()}; + +static uint8_t const desc_console[] = {HID_REPORT_DESC_CONSOLE()}; + +uint8_t const* tud_hid_descriptor_report_cb(uint8_t itf) { + switch (itf) { + case ITF_NUM_HID_KEYBOARD: + return desc_keyboard; + case ITF_NUM_HID_MOUSE: + return desc_mouse; + case ITF_NUM_HID_EXTRA: + return desc_extra; + case ITF_NUM_HID_RAW: + return desc_raw; + case ITF_NUM_HID_CONSOLE: + return desc_console; + default: + return NULL; + } +} + +uint8_t const desc_fs_configuration[] = { + // Config number, interface count, string index, total length, attribute, + // power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, + TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + + // Interface number, string index, protocol, report descriptor len, EP In + // address, size & polling interval + TUD_HID_DESCRIPTOR(ITF_NUM_HID_KEYBOARD, 2, HID_ITF_PROTOCOL_NONE, + sizeof(desc_keyboard), EPNUM_HID_KEYBOARD_IN, + CFG_TUD_HID_EP_BUFSIZE, 10), + TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID_RAW, 2, HID_ITF_PROTOCOL_NONE, + sizeof(desc_raw), EPNUM_HID_RAW_OUT, + EPNUM_HID_RAW_IN, CFG_TUD_HID_EP_BUFSIZE, 10), + TUD_HID_DESCRIPTOR(ITF_NUM_HID_MOUSE, 2, HID_ITF_PROTOCOL_NONE, + sizeof(desc_mouse), EPNUM_HID_MOUSE_IN, + CFG_TUD_HID_EP_BUFSIZE, 1), + TUD_HID_DESCRIPTOR(ITF_NUM_HID_EXTRA, 2, HID_ITF_PROTOCOL_NONE, + sizeof(desc_extra), EPNUM_HID_EXTRA_IN, + CFG_TUD_HID_EP_BUFSIZE, 10), + TUD_HID_DESCRIPTOR(ITF_NUM_HID_CONSOLE, 2, HID_ITF_PROTOCOL_NONE, + sizeof(desc_console), EPNUM_HID_CONSOLE_IN, + CFG_TUD_HID_EP_BUFSIZE, 10), + + // Interface number, string index, EP notification address and size, EP data + // address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 2, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, + EPNUM_CDC_IN, 64), + +#ifdef MIDI_ENABLE + TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 2, EPNUM_MIDI_OUT, EPNUM_MIDI_IN, 64) +#endif +}; + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const* tud_descriptor_configuration_cb(uint8_t index) { + (void)index; // for multiple configurations + + return desc_fs_configuration; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const* string_desc_arr[] = { + (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) + STR(MANUFACTURER), // 1: Manufacturer + STR(PRODUCT), // 2: Product + "123456", // 3: Serials, should use chip ID +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void)langid; + + uint8_t chr_count; + + if (index == 0) { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + } else { + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) + return NULL; + + const char* str = string_desc_arr[index]; + + // Cap at max char + chr_count = strlen(str); + if (chr_count > 31) chr_count = 31; + + // Convert ASCII string into UTF-16 + for (uint8_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2); + + return _desc_str; +} diff --git a/tmk_core/protocol/pico/usb_descriptors.h b/tmk_core/protocol/pico/usb_descriptors.h new file mode 100644 index 0000000000..c95d308760 --- /dev/null +++ b/tmk_core/protocol/pico/usb_descriptors.h @@ -0,0 +1,58 @@ + +#pragma once + +#include "hid.h" +#include "usb_descriptor_common.h" +#define RAW_EPSIZE 32 +#define CONSOLE_EPSIZE 32 + +enum { + ITF_NUM_HID_KEYBOARD, + ITF_NUM_HID_RAW, + ITF_NUM_HID_MOUSE, + ITF_NUM_HID_EXTRA, + ITF_NUM_HID_CONSOLE, + ITF_NUM_CDC, + ITF_NUM_CDC_DATA, +#ifdef MIDI_ENABLE + ITF_NUM_MIDI, + ITF_NUM_MIDI_STREAMING, +#endif + ITF_NUM_TOTAL +}; + +#define HID_REPORT_DESC_RAW() \ + HID_USAGE_PAGE_N(RAW_USAGE_PAGE, 2), \ + HID_USAGE(RAW_USAGE_ID), \ + HID_COLLECTION(HID_COLLECTION_APPLICATION), \ + HID_USAGE(0x62), \ + HID_LOGICAL_MIN(0x00), \ + HID_LOGICAL_MAX_N(0x00FF, 2), \ + HID_REPORT_COUNT(RAW_EPSIZE), \ + HID_REPORT_SIZE(0x08), \ + HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ + HID_USAGE(0x63), \ + HID_LOGICAL_MIN(0x00), \ + HID_LOGICAL_MAX_N(0x00FF, 2), \ + HID_REPORT_COUNT(RAW_EPSIZE), \ + HID_REPORT_SIZE(0x08), \ + HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE | HID_NON_VOLATILE), \ + HID_COLLECTION_END + +#define HID_REPORT_DESC_CONSOLE() \ + HID_USAGE_PAGE_N(0xFF31, 2), \ + HID_USAGE(0x74), \ + HID_COLLECTION(HID_COLLECTION_APPLICATION), \ + HID_USAGE(0x75), \ + HID_LOGICAL_MIN(0x00), \ + HID_LOGICAL_MAX_N(0x00FF, 2), \ + HID_REPORT_COUNT(CONSOLE_EPSIZE), \ + HID_REPORT_SIZE(0x08), \ + HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ + HID_USAGE(0x76), \ + HID_LOGICAL_MIN(0x00), \ + HID_LOGICAL_MAX_N(0x00FF, 2), \ + HID_REPORT_COUNT(CONSOLE_EPSIZE), \ + HID_REPORT_SIZE(0x08), \ + HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE | HID_NON_VOLATILE), \ + HID_COLLECTION_END diff --git a/tmk_core/protocol/pico/usb_util.c b/tmk_core/protocol/pico/usb_util.c new file mode 100644 index 0000000000..13072b5bb8 --- /dev/null +++ b/tmk_core/protocol/pico/usb_util.c @@ -0,0 +1,12 @@ + +#include "usb_util.h" + +#include "tusb.h" + +bool usb_connected_state(void) { + tud_task(); + return tud_connected(); +} +void usb_disconnect(void) { +#warning "No implementation" //TODO implement +} diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h index 6c3424145c..683014ac59 100644 --- a/tmk_core/protocol/usb_descriptor.h +++ b/tmk_core/protocol/usb_descriptor.h @@ -291,6 +291,8 @@ enum usb_endpoints { #elif defined(PROTOCOL_CHIBIOS) // ChibiOS gives us number of available user endpoints, not control # define MAX_ENDPOINTS USB_MAX_ENDPOINTS +#elif defined(PROTOCOL_PICO) +# define MAX_ENDPOINTS 16 #endif // TODO - ARM_ATSAM