From d2f854f89b2d4030d327d29cf8aa8df7ca6fc0ed Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Sun, 27 Jun 2021 18:46:12 +0900 Subject: [PATCH 01/24] PICOSDK Support Build pico-example/cdc_msc sample on qmk build system Add usb descriptors Use usb config in config.h tentative implementation Enable debug print by cdc Process queued transfer before new report Fix dprint hang on Windows resolve warnings Add pico uart/i2c/spi sources Trim mouse report ID before send implement atomic_util Add eeprom emulation Save emulated eeprom by CLI comamnd Delete unused file Implement RGBLIGHT Add pio_manager Implement split Chang default serial speed Print elf size Fix make command Add eeprom save command to keymaps Reset when CDC baudrate is 1200bps Replace pro micro pin config to RP2040 pro micro Add i2c functions Disable LTO automatically Add CTPIM, CONVERT_TO_PICO_MICRO option Add MIDI functions Call usb_task() in midi send function Convert pico-sdk results to i2c_status Add build target uf2 to rp2040 Remove unused parts Fix warnings rp2040 samples: Update readme.md Check pico-sdk setup Implement lazy write back from eepemu to ROM Add PiPi Gherkin Change DDYNAMIC_KEYMAP_EEPROM_MAX_ADDR to 4095 PiPi Gherkin: Increase DYNAMIC_KEYMAP_LAYER_COUNT to 6 Change interface number of RAW to 1 due to VIA restriction Generate bs2_default_padded_checksummed.S at build time Calculate eeprom emulation region Fix warning Add pico/usb_util.c to SRC Fix pico's split Add keyboard_quantizer/rp Add pwm driver Remove unused file Add analog.c Change default FLASH clock div to 4 Fix merge mistakes and issues. rejig files remove ugfx more rejigging remove qmk_main add watchdog back --- .gitmodules | 3 + Makefile | 1 + builddefs/build_keyboard.mk | 18 + builddefs/common_features.mk | 2 + builddefs/common_rules.mk | 10 +- data/schemas/definitions.jsonschema | 4 + keyboards/rp2040_example/config.h | 155 +++++++ keyboards/rp2040_example/info.json | 19 + keyboards/rp2040_example/keymaps/adc/keymap.c | 85 ++++ .../rp2040_example/keymaps/adc/readme.md | 1 + keyboards/rp2040_example/keymaps/adc/rules.mk | 2 + .../rp2040_example/keymaps/default/keymap.c | 75 ++++ .../rp2040_example/keymaps/default/readme.md | 1 + .../rp2040_example/keymaps/rgblight/config.h | 6 + .../rp2040_example/keymaps/rgblight/keymap.c | 83 ++++ .../rp2040_example/keymaps/rgblight/readme.md | 1 + .../rp2040_example/keymaps/rgblight/rules.mk | 2 + keyboards/rp2040_example/readme.md | 24 + keyboards/rp2040_example/rp2040_example.c | 24 + keyboards/rp2040_example/rp2040_example.h | 35 ++ keyboards/rp2040_example/rules.mk | 23 + keyboards/rp2040_split/config.h | 157 +++++++ keyboards/rp2040_split/info.json | 19 + .../rp2040_split/keymaps/default/keymap.c | 80 ++++ .../rp2040_split/keymaps/default/readme.md | 1 + keyboards/rp2040_split/readme.md | 26 ++ keyboards/rp2040_split/rp2040_split.c | 23 + keyboards/rp2040_split/rp2040_split.h | 39 ++ keyboards/rp2040_split/rules.mk | 24 + lib/pico-sdk | 1 + platforms/pico/_print.h | 36 ++ platforms/pico/_wait.c | 89 ++++ platforms/pico/_wait.h | 24 + platforms/pico/atomic_util.h | 35 ++ platforms/pico/boards/pico_boards.h | 7 + platforms/pico/boards/pico_pro_micro.h | 21 + platforms/pico/bootloader.c | 13 + platforms/pico/convert_to_pico_micro.mk | 7 + platforms/pico/drivers/analog.c | 23 + platforms/pico/drivers/analog.h | 19 + platforms/pico/drivers/i2c_master.c | 100 +++++ platforms/pico/drivers/i2c_master.h | 53 +++ platforms/pico/drivers/serial.c | 425 ++++++++++++++++++ platforms/pico/drivers/serial_usart.c | 237 ++++++++++ platforms/pico/drivers/uart.h | 15 + platforms/pico/drivers/uart_rx.pio.h | 120 +++++ platforms/pico/drivers/uart_tx.pio.h | 73 +++ platforms/pico/drivers/ws2812.c | 51 +++ platforms/pico/drivers/ws2812.pio.h | 114 +++++ platforms/pico/eeprom.c | 272 +++++++++++ platforms/pico/gpio.h | 34 ++ platforms/pico/pin_defs.h | 17 + platforms/pico/platform.c | 14 + platforms/pico/platform.mk | 405 +++++++++++++++++ platforms/pico/platform_deps.h | 18 + platforms/pico/print.c | 6 + platforms/pico/suspend.c | 145 ++++++ platforms/pico/timer.c | 67 +++ quantum/audio/driver_pico_pwm.h | 2 + quantum/audio/driver_pico_pwm_hardware.c | 108 +++++ quantum/dynamic_keymap.c | 2 + quantum/split_common/split_util.c | 41 +- tmk_core/protocol.mk | 10 +- .../midi/bytequeue/interrupt_setting.c | 12 + tmk_core/protocol/pico.mk | 19 + .../pico/bs2_default_padded_checksummed.S | 23 + tmk_core/protocol/pico/iusb.c | 91 ++++ tmk_core/protocol/pico/iusb.h | 12 + .../pico/lufa_utils/LUFA/Drivers/USB/USB.h | 42 ++ tmk_core/protocol/pico/msc_disk.c | 259 +++++++++++ tmk_core/protocol/pico/pico.c | 282 ++++++++++++ tmk_core/protocol/pico/pico/config_autogen.h | 11 + tmk_core/protocol/pico/pico/version.h | 19 + tmk_core/protocol/pico/pico_cdc.h | 9 + tmk_core/protocol/pico/pico_eeprom.h | 7 + tmk_core/protocol/pico/pio_manager.c | 32 ++ tmk_core/protocol/pico/pio_manager.h | 8 + tmk_core/protocol/pico/tusb_config.h | 122 +++++ tmk_core/protocol/pico/usb_descriptors.c | 227 ++++++++++ tmk_core/protocol/pico/usb_descriptors.h | 58 +++ tmk_core/protocol/pico/usb_util.c | 12 + tmk_core/protocol/usb_descriptor.h | 2 + 82 files changed, 4785 insertions(+), 9 deletions(-) create mode 100644 keyboards/rp2040_example/config.h create mode 100644 keyboards/rp2040_example/info.json create mode 100644 keyboards/rp2040_example/keymaps/adc/keymap.c create mode 100644 keyboards/rp2040_example/keymaps/adc/readme.md create mode 100644 keyboards/rp2040_example/keymaps/adc/rules.mk create mode 100644 keyboards/rp2040_example/keymaps/default/keymap.c create mode 100644 keyboards/rp2040_example/keymaps/default/readme.md create mode 100644 keyboards/rp2040_example/keymaps/rgblight/config.h create mode 100644 keyboards/rp2040_example/keymaps/rgblight/keymap.c create mode 100644 keyboards/rp2040_example/keymaps/rgblight/readme.md create mode 100644 keyboards/rp2040_example/keymaps/rgblight/rules.mk create mode 100644 keyboards/rp2040_example/readme.md create mode 100644 keyboards/rp2040_example/rp2040_example.c create mode 100644 keyboards/rp2040_example/rp2040_example.h create mode 100644 keyboards/rp2040_example/rules.mk create mode 100644 keyboards/rp2040_split/config.h create mode 100644 keyboards/rp2040_split/info.json create mode 100644 keyboards/rp2040_split/keymaps/default/keymap.c create mode 100644 keyboards/rp2040_split/keymaps/default/readme.md create mode 100644 keyboards/rp2040_split/readme.md create mode 100644 keyboards/rp2040_split/rp2040_split.c create mode 100644 keyboards/rp2040_split/rp2040_split.h create mode 100644 keyboards/rp2040_split/rules.mk create mode 160000 lib/pico-sdk create mode 100644 platforms/pico/_print.h create mode 100644 platforms/pico/_wait.c create mode 100644 platforms/pico/_wait.h create mode 100644 platforms/pico/atomic_util.h create mode 100644 platforms/pico/boards/pico_boards.h create mode 100644 platforms/pico/boards/pico_pro_micro.h create mode 100644 platforms/pico/bootloader.c create mode 100644 platforms/pico/convert_to_pico_micro.mk create mode 100644 platforms/pico/drivers/analog.c create mode 100644 platforms/pico/drivers/analog.h create mode 100644 platforms/pico/drivers/i2c_master.c create mode 100644 platforms/pico/drivers/i2c_master.h create mode 100644 platforms/pico/drivers/serial.c create mode 100644 platforms/pico/drivers/serial_usart.c create mode 100644 platforms/pico/drivers/uart.h create mode 100644 platforms/pico/drivers/uart_rx.pio.h create mode 100644 platforms/pico/drivers/uart_tx.pio.h create mode 100644 platforms/pico/drivers/ws2812.c create mode 100644 platforms/pico/drivers/ws2812.pio.h create mode 100644 platforms/pico/eeprom.c create mode 100644 platforms/pico/gpio.h create mode 100644 platforms/pico/pin_defs.h create mode 100644 platforms/pico/platform.c create mode 100644 platforms/pico/platform.mk create mode 100644 platforms/pico/platform_deps.h create mode 100644 platforms/pico/print.c create mode 100644 platforms/pico/suspend.c create mode 100644 platforms/pico/timer.c create mode 100644 quantum/audio/driver_pico_pwm.h create mode 100644 quantum/audio/driver_pico_pwm_hardware.c create mode 100644 tmk_core/protocol/pico.mk create mode 100644 tmk_core/protocol/pico/bs2_default_padded_checksummed.S create mode 100644 tmk_core/protocol/pico/iusb.c create mode 100644 tmk_core/protocol/pico/iusb.h create mode 100644 tmk_core/protocol/pico/lufa_utils/LUFA/Drivers/USB/USB.h create mode 100644 tmk_core/protocol/pico/msc_disk.c create mode 100644 tmk_core/protocol/pico/pico.c create mode 100644 tmk_core/protocol/pico/pico/config_autogen.h create mode 100644 tmk_core/protocol/pico/pico/version.h create mode 100644 tmk_core/protocol/pico/pico_cdc.h create mode 100644 tmk_core/protocol/pico/pico_eeprom.h create mode 100644 tmk_core/protocol/pico/pio_manager.c create mode 100644 tmk_core/protocol/pico/pio_manager.h create mode 100644 tmk_core/protocol/pico/tusb_config.h create mode 100644 tmk_core/protocol/pico/usb_descriptors.c create mode 100644 tmk_core/protocol/pico/usb_descriptors.h create mode 100644 tmk_core/protocol/pico/usb_util.c 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 From 80a5d9b72732914f83c2359d5c28c3502909b40d Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Sun, 28 Nov 2021 16:37:45 +0900 Subject: [PATCH 02/24] Fix bug relating RP2040's usb enumaration errata --- platforms/pico/drivers/serial.c | 18 ++++++++++++------ platforms/pico/gpio.h | 8 ++++---- platforms/pico/platform.c | 1 + 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/platforms/pico/drivers/serial.c b/platforms/pico/drivers/serial.c index 21f353e92f..15e71c5215 100644 --- a/platforms/pico/drivers/serial.c +++ b/platforms/pico/drivers/serial.c @@ -46,16 +46,24 @@ # error invalid SELECT_SOFT_SERIAL_SPEED value #endif +#if SOFT_SERIAL_PIN == 15 +# error GPIO15 cannot be used as SOFT_SERIAL_PIN +#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) { + gpio_set_function(SOFT_SERIAL_PIN, GPIO_FUNC_SIO); 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 void serial_input(void) { + gpio_set_function(SOFT_SERIAL_PIN, GPIO_FUNC_SIO); + 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); } @@ -71,7 +79,7 @@ inline static void soft_serial_disable_rx(void) { } inline static void soft_serial_enable_rx(void) { - setPinInputHigh(SOFT_SERIAL_PIN); + serial_input(); // pio_gpio_init is not needed pio_sm_clear_fifos(pio, sm_rx); pio_sm_set_enabled(pio, sm_rx, true); @@ -84,9 +92,7 @@ inline static void soft_serial_disable_tx(void) { 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); + serial_output(); pio_sm_clear_fifos(pio, sm_tx); pio_gpio_init(pio, SOFT_SERIAL_PIN); pio_sm_set_enabled(pio, sm_tx, true); @@ -131,7 +137,7 @@ void soft_serial_initiator_init(void) { void soft_serial_target_init(void) { soft_serial_pio_init(); - setPinInputHigh(SOFT_SERIAL_PIN); + serial_input(); 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); diff --git a/platforms/pico/gpio.h b/platforms/pico/gpio.h index 34fc4fdf17..0c2efbe151 100644 --- a/platforms/pico/gpio.h +++ b/platforms/pico/gpio.h @@ -20,10 +20,10 @@ 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 setPinInput(pin) (gpio_set_dir(pin, GPIO_IN)) +#define setPinInputHigh(pin) (gpio_set_dir(pin, GPIO_IN), gpio_pull_up(pin)) +#define setPinInputLow(pin) (gpio_init(pin), gpio_set_dir(pin, GPIO_IN), gpio_pull_down(pin)) +#define setPinOutput(pin) (gpio_set_dir(pin, GPIO_OUT)) #define writePinHigh(pin) (gpio_put(pin, 1)) #define writePinLow(pin) (gpio_put(pin, 0)) diff --git a/platforms/pico/platform.c b/platforms/pico/platform.c index 6afc8bc1d1..b578dd4a8e 100644 --- a/platforms/pico/platform.c +++ b/platforms/pico/platform.c @@ -7,6 +7,7 @@ uint32_t interrupts; void platform_setup(void) { for (int i = 0; i < 32; i++) { + gpio_init(i); setPinInputHigh(i); } From 15251c37af7bc620558cb40a37988fa7e8e15d5d Mon Sep 17 00:00:00 2001 From: Ilya Zhuravlev Date: Sat, 18 Dec 2021 18:09:48 -0500 Subject: [PATCH 03/24] Add rp2040 example & fix auto-detect --- keyboards/vial_example/vial_rp2040/config.h | 27 +++++++++++++++++++ .../vial_rp2040/keymaps/default/keymap.c | 15 +++++++++++ .../vial_rp2040/keymaps/vial/config.h | 7 +++++ .../vial_rp2040/keymaps/vial/keymap.c | 25 +++++++++++++++++ .../vial_rp2040/keymaps/vial/rules.mk | 2 ++ .../vial_rp2040/keymaps/vial/vial.json | 19 +++++++++++++ keyboards/vial_example/vial_rp2040/rules.mk | 21 +++++++++++++++ .../vial_example/vial_rp2040/vial_rp2040.c | 3 +++ .../vial_example/vial_rp2040/vial_rp2040.h | 13 +++++++++ tmk_core/protocol/pico/usb_descriptors.c | 4 +++ 10 files changed, 136 insertions(+) create mode 100644 keyboards/vial_example/vial_rp2040/config.h create mode 100644 keyboards/vial_example/vial_rp2040/keymaps/default/keymap.c create mode 100644 keyboards/vial_example/vial_rp2040/keymaps/vial/config.h create mode 100644 keyboards/vial_example/vial_rp2040/keymaps/vial/keymap.c create mode 100644 keyboards/vial_example/vial_rp2040/keymaps/vial/rules.mk create mode 100644 keyboards/vial_example/vial_rp2040/keymaps/vial/vial.json create mode 100644 keyboards/vial_example/vial_rp2040/rules.mk create mode 100644 keyboards/vial_example/vial_rp2040/vial_rp2040.c create mode 100644 keyboards/vial_example/vial_rp2040/vial_rp2040.h diff --git a/keyboards/vial_example/vial_rp2040/config.h b/keyboards/vial_example/vial_rp2040/config.h new file mode 100644 index 0000000000..ef7cf4d8c7 --- /dev/null +++ b/keyboards/vial_example/vial_rp2040/config.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/* USB Device descriptor parameter */ +#define VENDOR_ID 0xFEED +#define PRODUCT_ID 0x0000 +#define DEVICE_VER 0x0001 +#define MANUFACTURER Vial +#define PRODUCT RP2040 example + +/* key matrix size */ +#define MATRIX_ROWS 2 +#define MATRIX_COLS 2 + +#define MATRIX_ROW_PINS { 0, 1 } +#define MATRIX_COL_PINS { 2, 3 } + +#define DIODE_DIRECTION COL2ROW + +/* 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 + +/* Use 1000hz polling */ +#define USB_POLLING_INTERVAL_MS 1 diff --git a/keyboards/vial_example/vial_rp2040/keymaps/default/keymap.c b/keyboards/vial_example/vial_rp2040/keymaps/default/keymap.c new file mode 100644 index 0000000000..6c76cbf73d --- /dev/null +++ b/keyboards/vial_example/vial_rp2040/keymaps/default/keymap.c @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include QMK_KEYBOARD_H + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + [0] = LAYOUT( + KC_1, KC_2, + KC_3, KC_4 + ), + + [1] = LAYOUT( + KC_A, KC_B, + KC_C, KC_D + ) +}; diff --git a/keyboards/vial_example/vial_rp2040/keymaps/vial/config.h b/keyboards/vial_example/vial_rp2040/keymaps/vial/config.h new file mode 100644 index 0000000000..b8d1d729ff --- /dev/null +++ b/keyboards/vial_example/vial_rp2040/keymaps/vial/config.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#define VIAL_KEYBOARD_UID {0x03, 0x35, 0x69, 0xE1, 0x5C, 0x42, 0xD9, 0x5E} +#define VIAL_UNLOCK_COMBO_ROWS {0, 1} +#define VIAL_UNLOCK_COMBO_COLS {0, 1} diff --git a/keyboards/vial_example/vial_rp2040/keymaps/vial/keymap.c b/keyboards/vial_example/vial_rp2040/keymaps/vial/keymap.c new file mode 100644 index 0000000000..c8fb892589 --- /dev/null +++ b/keyboards/vial_example/vial_rp2040/keymaps/vial/keymap.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include QMK_KEYBOARD_H + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + [0] = LAYOUT( + KC_1, KC_2, + KC_3, KC_4 + ), + + [1] = LAYOUT( + KC_A, KC_B, + KC_C, KC_D + ), + + [2] = LAYOUT( + KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS + ), + + [3] = LAYOUT( + KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS + ) +}; diff --git a/keyboards/vial_example/vial_rp2040/keymaps/vial/rules.mk b/keyboards/vial_example/vial_rp2040/keymaps/vial/rules.mk new file mode 100644 index 0000000000..4f7618e9b2 --- /dev/null +++ b/keyboards/vial_example/vial_rp2040/keymaps/vial/rules.mk @@ -0,0 +1,2 @@ +VIA_ENABLE = yes +VIAL_ENABLE = yes diff --git a/keyboards/vial_example/vial_rp2040/keymaps/vial/vial.json b/keyboards/vial_example/vial_rp2040/keymaps/vial/vial.json new file mode 100644 index 0000000000..5bbb2c5c66 --- /dev/null +++ b/keyboards/vial_example/vial_rp2040/keymaps/vial/vial.json @@ -0,0 +1,19 @@ +{ + "name": "Vial RP2040 Example", + "matrix": { + "rows": 2, + "cols": 2 + }, + "layouts": { + "keymap": [ + [ + "0,0", + "0,1" + ], + [ + "1,0", + "1,1" + ] + ] + } +} diff --git a/keyboards/vial_example/vial_rp2040/rules.mk b/keyboards/vial_example/vial_rp2040/rules.mk new file mode 100644 index 0000000000..797f9860bd --- /dev/null +++ b/keyboards/vial_example/vial_rp2040/rules.mk @@ -0,0 +1,21 @@ +# MCU name +MCU_FAMILY = PICO +MCU_SERIES = RP2040 +MCU = cortex-m0plus + +# Build Options +# change yes to no to disable +# +BOOTMAGIC_ENABLE = yes +MOUSEKEY_ENABLE = yes # Mouse keys +EXTRAKEY_ENABLE = yes # Audio control and System control +CONSOLE_ENABLE = no # 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/vial_example/vial_rp2040/vial_rp2040.c b/keyboards/vial_example/vial_rp2040/vial_rp2040.c new file mode 100644 index 0000000000..2ffcfd355d --- /dev/null +++ b/keyboards/vial_example/vial_rp2040/vial_rp2040.c @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "vial_rp2040.h" diff --git a/keyboards/vial_example/vial_rp2040/vial_rp2040.h b/keyboards/vial_example/vial_rp2040/vial_rp2040.h new file mode 100644 index 0000000000..12c2bedb5f --- /dev/null +++ b/keyboards/vial_example/vial_rp2040/vial_rp2040.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "quantum.h" + +#define LAYOUT( \ + K00, K01, \ + K10, K11 \ +) { \ + { K00, K01 }, \ + { K10, K11 } \ +} diff --git a/tmk_core/protocol/pico/usb_descriptors.c b/tmk_core/protocol/pico/usb_descriptors.c index 1d2936811b..43d57060ad 100644 --- a/tmk_core/protocol/pico/usb_descriptors.c +++ b/tmk_core/protocol/pico/usb_descriptors.c @@ -185,7 +185,11 @@ char const* string_desc_arr[] = { (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) STR(MANUFACTURER), // 1: Manufacturer STR(PRODUCT), // 2: Product +#ifdef VIAL_ENABLE + SERIAL_NUMBER, +#else "123456", // 3: Serials, should use chip ID +#endif }; static uint16_t _desc_str[32]; From 746bb85e8a6d47029760e5de902dcc0fc6967cff Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Sat, 11 Dec 2021 17:15:36 +0900 Subject: [PATCH 04/24] Change RawHID interval to 1 --- tmk_core/protocol/pico/usb_descriptors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmk_core/protocol/pico/usb_descriptors.c b/tmk_core/protocol/pico/usb_descriptors.c index 43d57060ad..e3b90194a9 100644 --- a/tmk_core/protocol/pico/usb_descriptors.c +++ b/tmk_core/protocol/pico/usb_descriptors.c @@ -146,7 +146,7 @@ uint8_t const desc_fs_configuration[] = { 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), + EPNUM_HID_RAW_IN, CFG_TUD_HID_EP_BUFSIZE, 1), 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), From 5ddfbe7cf3060ab0cc6b10b1759305597ea9c4b4 Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Fri, 17 Dec 2021 22:51:47 +0900 Subject: [PATCH 05/24] fix makefile --- platforms/pico/platform.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/platforms/pico/platform.mk b/platforms/pico/platform.mk index 6b8791a04f..565fa1e9d5 100644 --- a/platforms/pico/platform.mk +++ b/platforms/pico/platform.mk @@ -381,6 +381,7 @@ 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 + @mkdir -p $(KEYBOARD_OUTPUT)/src $(CC) $(CFLAGS) $(BOOT2INC_DIR) -c -o $@ $^ $(KEYBOARD_OUTPUT)/src/bs2_default.elf: $(KEYBOARD_OUTPUT)/src/bs2_default.o From d5d74632cc1a5881f58ddc5c0ef92272ee77ff27 Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Mon, 27 Dec 2021 10:41:39 +0900 Subject: [PATCH 06/24] Port eeprom_stm32 to rp2040 --- builddefs/common_features.mk | 4 + keyboards/rp2040_example/keymaps/adc/keymap.c | 12 - .../rp2040_example/keymaps/default/keymap.c | 13 - .../rp2040_example/keymaps/rgblight/keymap.c | 12 - .../rp2040_split/keymaps/default/keymap.c | 13 - platforms/pico/eeprom.c | 908 +++++++++++++----- platforms/pico/eeprom_pico.h | 33 + platforms/pico/eeprom_pico_defs.h | 12 + platforms/pico/flash_pico.c | 201 ++++ platforms/pico/flash_pico.h | 44 + tmk_core/protocol/pico/pico.c | 20 +- tmk_core/protocol/pico/pico_eeprom.h | 7 - 12 files changed, 995 insertions(+), 284 deletions(-) create mode 100644 platforms/pico/eeprom_pico.h create mode 100644 platforms/pico/eeprom_pico_defs.h create mode 100644 platforms/pico/flash_pico.c create mode 100644 platforms/pico/flash_pico.h delete mode 100644 tmk_core/protocol/pico/pico_eeprom.h diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index 661ec2c8b4..c8535d8300 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -213,6 +213,10 @@ else # arm_atsam EEPROM OPT_DEFS += -DEEPROM_SAMD SRC += $(PLATFORM_COMMON_DIR)/eeprom_samd.c + SRC += $(PLATFORM_COMMON_DIR)/eeprom.c + else ifeq ($(PLATFORM),PICO_SDK) + SRC += $(PLATFORM_COMMON_DIR)/eeprom.c + SRC += $(PLATFORM_COMMON_DIR)/flash_pico.c else ifeq ($(PLATFORM),TEST) # Test harness "EEPROM" OPT_DEFS += -DEEPROM_TEST_HARNESS diff --git a/keyboards/rp2040_example/keymaps/adc/keymap.c b/keyboards/rp2040_example/keymaps/adc/keymap.c index 2ef00700fd..88dc046c1c 100644 --- a/keyboards/rp2040_example/keymaps/adc/keymap.c +++ b/keyboards/rp2040_example/keymaps/adc/keymap.c @@ -59,18 +59,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { // 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/keymap.c b/keyboards/rp2040_example/keymaps/default/keymap.c index 3cd7c597f6..eda75e2ed0 100644 --- a/keyboards/rp2040_example/keymaps/default/keymap.c +++ b/keyboards/rp2040_example/keymaps/default/keymap.c @@ -14,7 +14,6 @@ * 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 { @@ -58,18 +57,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { // 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/rgblight/keymap.c b/keyboards/rp2040_example/keymaps/rgblight/keymap.c index a4479f7a38..1bcae85fc9 100644 --- a/keyboards/rp2040_example/keymaps/rgblight/keymap.c +++ b/keyboards/rp2040_example/keymaps/rgblight/keymap.c @@ -58,18 +58,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { // 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/keymap.c b/keyboards/rp2040_split/keymaps/default/keymap.c index 60a2b5f472..8ab9ad498e 100644 --- a/keyboards/rp2040_split/keymaps/default/keymap.c +++ b/keyboards/rp2040_split/keymaps/default/keymap.c @@ -14,7 +14,6 @@ * 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 { @@ -62,18 +61,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { // 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/platforms/pico/eeprom.c b/platforms/pico/eeprom.c index 56f90431d9..f222882e62 100644 --- a/platforms/pico/eeprom.c +++ b/platforms/pico/eeprom.c @@ -1,272 +1,748 @@ +/* + * This software is experimental and a work in progress. + * Under no circumstances should these files be used in relation to any critical system(s). + * Use of these files is at your own risk. + * + * 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. + * + * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by + * Artur F. + * + * Modifications for QMK and STM32F303 by Yiancar + * Modifications to add flash wear leveling by Ilya Zhuravlev + * Modifications to increase flash density by Don Kjer + * Modifications for QMK and RP2040 by sekigon-gonnoc + */ -#include #include - -// qmk -#include "eeprom.h" -#include "eeconfig.h" -#include "timer.h" +#include +#include "util.h" #include "debug.h" -#include "print.h" +#include "platforms/pico/flash_pico.h" +#include "hardware/watchdog.h" -#include "pico_eeprom.h" +/* + * We emulate eeprom by writing a snapshot compacted view of eeprom contents, + * followed by a write log of any change since that snapshot: + * + * === SIMULATED EEPROM CONTENTS === + * + * ┌─ Compacted ┬ Write Log ─┐ + * │............│[BYTE][BYTE]│ + * │FFFF....FFFF│[WRD0][WRD1]│ + * │FFFFFFFFFFFF│[WORD][NEXT]│ + * │....FFFFFFFF│[BYTE][WRD0]│ + * ├────────────┼────────────┤ + * └──PAGE_BASE │ │ + * PAGE_LAST─┴─WRITE_BASE │ + * WRITE_LAST ┘ + * + * Compacted contents are the 1's complement of the actual EEPROM contents. + * e.g. An 'FFFF' represents a '0000' value. + * + * The size of the 'compacted' area is equal to the size of the 'emulated' eeprom. + * The size of the compacted-area and write log are configurable, and the combined + * size of Compacted + WriteLog is a multiple FEE_PAGE_SIZE, which is MCU dependent. + * Simulated Eeprom contents are located at the end of available flash space. + * + * The following configuration defines can be set: + * + * FEE_PAGE_COUNT # Total number of pages to use for eeprom simulation (Compact + Write log) + * FEE_DENSITY_BYTES # Size of simulated eeprom. (Defaults to half the space allocated by FEE_PAGE_COUNT) + * NOTE: The current implementation does not include page swapping, + * and FEE_DENSITY_BYTES will consume that amount of RAM as a cached view of actual EEPROM contents. + * + * The maximum size of FEE_DENSITY_BYTES is currently 16384. The write log size equals + * FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES. + * The larger the write log, the less frequently the compacted area needs to be rewritten. + * + * + * *** General Algorithm *** + * + * During initialization: + * The contents of the Compacted-flash area are loaded and the 1's complement value + * is cached into memory (e.g. 0xFFFF in Flash represents 0x0000 in cache). + * Write log entries are processed until a 0xFFFF is reached. + * Each log entry updates a byte or word in the cache. + * + * During reads: + * EEPROM contents are given back directly from the cache in memory. + * + * During writes: + * The contents of the cache is updated first. + * If the Compacted-flash area corresponding to the write address is unprogrammed, the 1's complement of the value is written directly into Compacted-flash + * Otherwise: + * If the write log is full, erase both the Compacted-flash area and the Write log, then write cached contents to the Compacted-flash area. + * Otherwise a Write log entry is constructed and appended to the next free position in the Write log. + * + * + * *** Write Log Structure *** + * + * Write log entries allow for optimized byte writes to addresses below 128. Writing 0 or 1 words are also optimized when word-aligned. + * + * === WRITE LOG ENTRY FORMATS === + * + * ╔═══ Byte-Entry ══╗ + * ║0XXXXXXX║YYYYYYYY║ + * ║ └──┬──┘║└──┬───┘║ + * ║ Address║ Value ║ + * ╚════════╩════════╝ + * 0 <= Address < 0x80 (128) + * + * ╔ Word-Encoded 0 ╗ + * ║100XXXXXXXXXXXXX║ + * ║ │└─────┬─────┘║ + * ║ │Address >> 1 ║ + * ║ └── Value: 0 ║ + * ╚════════════════╝ + * 0 <= Address <= 0x3FFE (16382) + * + * ╔ Word-Encoded 1 ╗ + * ║101XXXXXXXXXXXXX║ + * ║ │└─────┬─────┘║ + * ║ │Address >> 1 ║ + * ║ └── Value: 1 ║ + * ╚════════════════╝ + * 0 <= Address <= 0x3FFE (16382) + * + * ╔═══ Reserved ═══╗ + * ║110XXXXXXXXXXXXX║ + * ╚════════════════╝ + * + * ╔═══════════ Word-Next ═══════════╗ + * ║111XXXXXXXXXXXXX║YYYYYYYYYYYYYYYY║ + * ║ └─────┬─────┘║└───────┬──────┘║ + * ║(Address-128)>>1║ ~Value ║ + * ╚════════════════╩════════════════╝ + * ( 0 <= Address < 0x0080 (128): Reserved) + * 0x80 <= Address <= 0x3FFE (16382) + * + * Write Log entry ranges: + * 0x0000 ... 0x7FFF - Byte-Entry; address is (Entry & 0x7F00) >> 4; value is (Entry & 0xFF) + * 0x8000 ... 0x9FFF - Word-Encoded 0; address is (Entry & 0x1FFF) << 1; value is 0 + * 0xA000 ... 0xBFFF - Word-Encoded 1; address is (Entry & 0x1FFF) << 1; value is 1 + * 0xC000 ... 0xDFFF - Reserved + * 0xE000 ... 0xFFBF - Word-Next; address is (Entry & 0x1FFF) << 1 + 0x80; value is ~(Next_Entry) + * 0xFFC0 ... 0xFFFE - Reserved + * 0xFFFF - Unprogrammed + * + */ -// pico -#include "pico/stdlib.h" -#include "hardware/flash.h" -#include "hardware/sync.h" +#include "eeprom_pico_defs.h" +#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT) || !defined(FEE_MCU_FLASH_SIZE) || !defined(FEE_PAGE_BASE_ADDRESS) +# error "not implemented." +#endif -#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) +/* These bits are used for optimizing encoding of bytes, 0 and 1 */ +#define FEE_WORD_ENCODING 0x8000 +#define FEE_VALUE_NEXT 0x6000 +#define FEE_VALUE_RESERVED 0x4000 +#define FEE_VALUE_ENCODED 0x2000 +#define FEE_BYTE_RANGE 0x80 + +/* Addressable range 16KByte: 0 <-> (0x1FFF << 1) */ +#define FEE_ADDRESS_MAX_SIZE 0x4000 + +/* Flash word value after erase */ +#define FEE_EMPTY_WORD ((uint16_t)0xFFFF) + +/* Magic number indicating the page is used for FEE */ +#define FEE_MAGIC_DWORD ((uint32_t)0x20400FEE) + +/* Size of combined compacted eeprom and write log pages */ +#define FEE_DENSITY_MAX_SIZE (FEE_PAGE_COUNT * FEE_PAGE_SIZE) + +#ifndef FEE_MCU_FLASH_SIZE_IGNORE_CHECK /* *TODO: Get rid of this check */ +# if FEE_DENSITY_MAX_SIZE > (FEE_MCU_FLASH_SIZE * 1024) +# pragma message STR(FEE_DENSITY_MAX_SIZE) " > " STR(FEE_MCU_FLASH_SIZE * 1024) +# error emulated eeprom: FEE_DENSITY_MAX_SIZE is greater than available flash size # 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)) +/* Size of emulated eeprom */ +#ifdef FEE_DENSITY_BYTES +# if (FEE_DENSITY_BYTES > FEE_DENSITY_MAX_SIZE) +# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE) +# error emulated eeprom: FEE_DENSITY_BYTES exceeds FEE_DENSITY_MAX_SIZE +# endif +# if (FEE_DENSITY_BYTES == FEE_DENSITY_MAX_SIZE) +# pragma message STR(FEE_DENSITY_BYTES) " == " STR(FEE_DENSITY_MAX_SIZE) +# warning emulated eeprom: FEE_DENSITY_BYTES leaves no room for a write log. This will greatly increase the flash wear rate! +# endif +# if FEE_DENSITY_BYTES > FEE_ADDRESS_MAX_SIZE +# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_ADDRESS_MAX_SIZE) +# error emulated eeprom: FEE_DENSITY_BYTES is greater than FEE_ADDRESS_MAX_SIZE allows +# endif +# if ((FEE_DENSITY_BYTES) % 2) == 1 +# error emulated eeprom: FEE_DENSITY_BYTES must be even +# endif +#else +/* Default to half of allocated space used for emulated eeprom, half for write log */ +# define FEE_DENSITY_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE / 2) #endif -#ifndef EEPEMU_EECONFIG_START_OFFSET -# define EEPEMU_EECONFIG_START_OFFSET \ - (EEPEMU_KEYMAP_START_OFFSET - \ - ALIGNED_REGION(EEPEMU_EECONFIG_SIZE, FLASH_SECTOR_SIZE)) +/* Size of write log */ +#ifdef FEE_WRITE_LOG_BYTES +# if ((FEE_DENSITY_BYTES + FEE_WRITE_LOG_BYTES) > FEE_DENSITY_MAX_SIZE) +# pragma message STR(FEE_DENSITY_BYTES) " + " STR(FEE_WRITE_LOG_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE) +# error emulated eeprom: FEE_WRITE_LOG_BYTES exceeds remaining FEE_DENSITY_MAX_SIZE +# endif +# if ((FEE_WRITE_LOG_BYTES) % 2) == 1 +# error emulated eeprom: FEE_WRITE_LOG_BYTES must be even +# endif +#else +/* Default to use all remaining space */ +# define FEE_WRITE_LOG_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES) #endif -#ifndef EEPEMU_LAZY_WRITEBACK_TIMEOUT -# define EEPEMU_LAZY_WRITEBACK_TIMEOUT \ - 30000 // write back to ROM if 30s elapsed. +/* Start of the emulated eeprom compacted flash area */ +#define FEE_COMPACTED_BASE_ADDRESS FEE_PAGE_BASE_ADDRESS +/* End of the emulated eeprom compacted flash area */ +#define FEE_COMPACTED_LAST_ADDRESS (FEE_COMPACTED_BASE_ADDRESS + FEE_DENSITY_BYTES) +/* Start of the emulated eeprom write log */ +#define FEE_WRITE_LOG_BASE_ADDRESS FEE_COMPACTED_LAST_ADDRESS +/* End of the emulated eeprom write log */ +#define FEE_WRITE_LOG_LAST_ADDRESS (FEE_WRITE_LOG_BASE_ADDRESS + FEE_WRITE_LOG_BYTES) + +#if defined(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) && (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR >= FEE_DENSITY_BYTES) +# error emulated eeprom: DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is greater than the FEE_DENSITY_BYTES available #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"); +/* In-memory contents of emulated eeprom for faster access */ +/* *TODO: Implement page swapping */ +static uint16_t WordBuf[FEE_DENSITY_BYTES / 2]; +static uint8_t *DataBuf = (uint8_t *)WordBuf; -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; +/* Pointer to the first available slot within the write log */ +static uint16_t *empty_slot; -// 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)); +static void eeprom_clear(void); - eeconfig_last_edit = -1; - dynamic_keymap_last_edit = -1; +// #define DEBUG_EEPROM_OUTPUT + +/* + * Debug print utils + */ + +#if defined(DEBUG_EEPROM_OUTPUT) + +# define debug_eeprom debug_enable +# define eeprom_println(s) dprintln(s) +# define eeprom_printf(fmt, ...) dprintf(fmt, ##__VA_ARGS__); + +#else /* NO_DEBUG */ + +# define debug_eeprom false +# define eeprom_println(s) +# define eeprom_printf(fmt, ...) + +#endif /* NO_DEBUG */ + +void print_eeprom(void) { +#ifndef NO_DEBUG + int empty_rows = 0; + for (uint16_t i = 0; i < FEE_DENSITY_BYTES; i++) { + if (i % 16 == 0) { + if (i >= FEE_DENSITY_BYTES - 16) { + /* Make sure we display the last row */ + empty_rows = 0; + } + /* Check if this row is uninitialized */ + ++empty_rows; + for (uint16_t j = 0; j < 16; j++) { + if (DataBuf[i + j]) { + empty_rows = 0; + break; + } + } + if (empty_rows > 1) { + /* Repeat empty row */ + if (empty_rows == 2) { + /* Only display the first repeat empty row */ + println("*"); + } + i += 15; + continue; + } + xprintf("%04x", i); + } + if (i % 8 == 0) print(" "); + + xprintf(" %02x", DataBuf[i]); + if ((i + 1) % 16 == 0) { + println(""); + } + + watchdog_update(); + } +#endif } -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(); +uint16_t EEPROM_Init(void) { + /* Load emulated eeprom contents from compacted flash into memory */ + uint16_t *src = (uint16_t *)FEE_COMPACTED_BASE_ADDRESS; + uint16_t *dest = (uint16_t *)DataBuf; + for (; src < (uint16_t *)FEE_COMPACTED_LAST_ADDRESS; ++src, ++dest) { + *dest = ~*(uint16_t*)((void*)src + XIP_BASE); + } + + if (debug_eeprom) { + println("EEPROM_Init Compacted Pages:"); + print_eeprom(); + println("EEPROM_Init Write Log:"); + } + + if (*(uint32_t*)(FEE_WRITE_LOG_BASE_ADDRESS + XIP_BASE) != FEE_MAGIC_DWORD) { + eeprom_clear(); + } + + /* Replay write log */ + uint16_t *log_addr; + for (log_addr = (uint16_t *)(FEE_WRITE_LOG_BASE_ADDRESS + 4); log_addr < (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS; ++log_addr) { + + watchdog_update(); + + uint16_t address = *(uint16_t*)((void*)log_addr + XIP_BASE); + if (address == FEE_EMPTY_WORD) { + break; + } + /* Check for lowest 128-bytes optimization */ + if (!(address & FEE_WORD_ENCODING)) { + uint8_t bvalue = (uint8_t)address; + address >>= 8; + DataBuf[address] = bvalue; + eeprom_printf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue); + } else { + uint16_t wvalue; + /* Check if value is in next word */ + if ((address & FEE_VALUE_NEXT) == FEE_VALUE_NEXT) { + /* Read value from next word */ + if (++log_addr >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) { + break; + } + wvalue = ~*(uint16_t *)((void *)log_addr + XIP_BASE); + if (!wvalue) { + eeprom_printf("Incomplete write at log_addr: 0x%04x;\n", (uint32_t)log_addr); + /* Possibly incomplete write. Ignore and continue */ + continue; + } + address &= 0x1FFF; + address <<= 1; + /* Writes to addresses less than 128 are byte log entries */ + address += FEE_BYTE_RANGE; + } else { + /* Reserved for future use */ + if (address & FEE_VALUE_RESERVED) { + eeprom_printf("Reserved encoded value at log_addr: 0x%04x;\n", (uint32_t)log_addr); + continue; + } + /* Optimization for 0 or 1 values. */ + wvalue = (address & FEE_VALUE_ENCODED) >> 13; + address &= 0x1FFF; + address <<= 1; + } + if (address < FEE_DENSITY_BYTES) { + eeprom_printf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue); + *(uint16_t *)(&DataBuf[address]) = wvalue; + } else { + eeprom_printf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue); + } } } - 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(); + empty_slot = log_addr; + + if (debug_eeprom) { + println("EEPROM_Init Final DataBuf:"); + print_eeprom(); + eeprom_printf("Write Log Usage: %d/%d bytes\n ", + (uint32_t)empty_slot - FEE_WRITE_LOG_BASE_ADDRESS, + FEE_WRITE_LOG_BYTES); + } + + return FEE_DENSITY_BYTES; +} + +/* Clear flash contents (doesn't touch in-memory DataBuf) */ +static void eeprom_clear(void) { + FLASH_Unlock(); + + for (uint16_t page_num = 0; page_num < FEE_PAGE_COUNT; ++page_num) { + eeprom_printf("FLASH_ErasePage(0x%04x)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE))); + FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)); + } + + FLASH_ProgramHalfWord(FEE_WRITE_LOG_BASE_ADDRESS, FEE_MAGIC_DWORD & 0xffff); + FLASH_ProgramHalfWord(FEE_WRITE_LOG_BASE_ADDRESS + 2, + FEE_MAGIC_DWORD >> 16); + + FLASH_Lock(); + + empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; + eeprom_printf("eeprom_clear empty_slot: 0x%08x\n", (uint32_t)empty_slot); +} + +/* Erase emulated eeprom */ +void EEPROM_Erase(void) { + eeprom_println("EEPROM_Erase"); + /* Erase compacted pages and write log */ + eeprom_clear(); + /* re-initialize to reset DataBuf */ + EEPROM_Init(); +} + +/* Compact write log */ +static uint8_t eeprom_compact(void) { + /* Erase compacted pages and write log */ + eeprom_clear(); + + FLASH_Unlock(); + + FLASH_Status final_status = FLASH_COMPLETE; + + /* Write emulated eeprom contents from memory to compacted flash */ + uint16_t *src = (uint16_t *)DataBuf; + uintptr_t dest = FEE_COMPACTED_BASE_ADDRESS; + uint16_t value; + for (; dest < FEE_COMPACTED_LAST_ADDRESS; ++src, dest += 2) { + value = *src; + if (value) { + eeprom_printf("FLASH_ProgramHalfWord(0x%04x, 0x%04x)\n", (uint32_t)dest, ~value); + FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value); + if (status != FLASH_COMPLETE) final_status = status; } } + + FLASH_Lock(); + + if (debug_eeprom) { + println("eeprom_compacted:"); + print_eeprom(); + } + + return final_status; } -void pico_eepemu_flash_eeconfig(void) { - int32_t status = save_and_disable_interrupts(); +static uint8_t eeprom_write_direct_entry(uint16_t Address) { + /* Check if we can just write this directly to the compacted flash area */ + uintptr_t directAddress = FEE_COMPACTED_BASE_ADDRESS + (Address & 0xFFFE); + if (*(uint16_t *)((void *)directAddress + XIP_BASE) == FEE_EMPTY_WORD) { + /* Write the value directly to the compacted area without a log entry */ + uint16_t value = ~*(uint16_t *)(&DataBuf[Address & 0xFFFE]); + /* Early exit if a write isn't needed */ + if (value == FEE_EMPTY_WORD) return FLASH_COMPLETE; - 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; + FLASH_Unlock(); - restore_interrupts(status); + eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value); + FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value); + + FLASH_Lock(); + return status; + } + return 0; } -void pico_eepemu_flash_dynamic_keymap(void) { - int32_t status = save_and_disable_interrupts(); +static uint8_t eeprom_write_log_word_entry(uint16_t Address) { + FLASH_Status final_status = FLASH_COMPLETE; - 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; + uint16_t value = *(uint16_t *)(&DataBuf[Address]); + eeprom_printf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value); - restore_interrupts(status); -} - -static inline bool is_eeconfig_addr(const void *eeaddr) { - if ((uint32_t)eeaddr < DYNAMIC_KEYMAP_EEPROM_ADDR) { - return true; + /* MSB signifies the lowest 128-byte optimization is not in effect */ + uint16_t encoding = FEE_WORD_ENCODING; + uint8_t entry_size; + if (value <= 1) { + encoding |= value << 13; + entry_size = 2; } else { - return false; + encoding |= FEE_VALUE_NEXT; + entry_size = 4; + /* Writes to addresses less than 128 are byte log entries */ + Address -= FEE_BYTE_RANGE; } + + /* if we can't find an empty spot, we must compact emulated eeprom */ + if (empty_slot > (uint16_t *)(FEE_WRITE_LOG_LAST_ADDRESS - entry_size)) { + /* compact the write log into the compacted flash area */ + return eeprom_compact(); + } + + /* Word log writes should be word-aligned. Take back a bit */ + Address >>= 1; + Address |= encoding; + + /* ok we found a place let's write our data */ + FLASH_Unlock(); + + /* address */ + eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, Address); + final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address); + + /* value */ + if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) { + eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, ~value); + FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value); + if (status != FLASH_COMPLETE) final_status = status; + } + + FLASH_Lock(); + + return final_status; } -static void *convert_address(const void *addr) { - // TODO Assert address - if (is_eeconfig_addr(addr)) { - return (void *)(eepemu_eeconfig + (uint32_t)addr); +static uint8_t eeprom_write_log_byte_entry(uint16_t Address) { + eeprom_printf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]); + + /* if couldn't find an empty spot, we must compact emulated eeprom */ + if (empty_slot >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) { + /* compact the write log into the compacted flash area */ + return eeprom_compact(); + } + + /* ok we found a place let's write our data */ + FLASH_Unlock(); + + /* Pack address and value into the same word */ + uint16_t value = (Address << 8) | DataBuf[Address]; + + /* write to flash */ + eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, value); + FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, value); + + FLASH_Lock(); + + return status; +} + +uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) { + /* if the address is out-of-bounds, do nothing */ + if (Address >= FEE_DENSITY_BYTES) { + eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte); + return FLASH_BAD_ADDRESS; + } + + /* if the value is the same, don't bother writing it */ + if (DataBuf[Address] == DataByte) { + eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte); + return 0; + } + + /* keep DataBuf cache in sync */ + DataBuf[Address] = DataByte; + eeprom_printf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]); + + /* perform the write into flash memory */ + /* First, attempt to write directly into the compacted flash area */ + FLASH_Status status = eeprom_write_direct_entry(Address); + if (!status) { + /* Otherwise append to the write log */ + if (Address < FEE_BYTE_RANGE) { + status = eeprom_write_log_byte_entry(Address); + } else { + status = eeprom_write_log_word_entry(Address & 0xFFFE); + } + } + if (status != 0 && status != FLASH_COMPLETE) { + eeprom_printf("EEPROM_WriteDataByte [STATUS == %d]\n", status); + } + return status; +} + +uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) { + /* if the address is out-of-bounds, do nothing */ + if (Address >= FEE_DENSITY_BYTES) { + eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord); + return FLASH_BAD_ADDRESS; + } + + /* Check for word alignment */ + FLASH_Status final_status = FLASH_COMPLETE; + if (Address % 2) { + final_status = EEPROM_WriteDataByte(Address, DataWord); + FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8); + if (status != FLASH_COMPLETE) final_status = status; + if (final_status != 0 && final_status != FLASH_COMPLETE) { + eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); + } + return final_status; + } + + /* if the value is the same, don't bother writing it */ + uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]); + if (oldValue == DataWord) { + eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord); + return 0; + } + + /* keep DataBuf cache in sync */ + *(uint16_t *)(&DataBuf[Address]) = DataWord; + eeprom_printf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address])); + + /* perform the write into flash memory */ + /* First, attempt to write directly into the compacted flash area */ + final_status = eeprom_write_direct_entry(Address); + if (!final_status) { + /* Otherwise append to the write log */ + /* Check if we need to fall back to byte write */ + if (Address < FEE_BYTE_RANGE) { + final_status = FLASH_COMPLETE; + /* Only write a byte if it has changed */ + if ((uint8_t)oldValue != (uint8_t)DataWord) { + final_status = eeprom_write_log_byte_entry(Address); + } + FLASH_Status status = FLASH_COMPLETE; + /* Only write a byte if it has changed */ + if ((oldValue >> 8) != (DataWord >> 8)) { + status = eeprom_write_log_byte_entry(Address + 1); + } + if (status != FLASH_COMPLETE) final_status = status; + } else { + final_status = eeprom_write_log_word_entry(Address); + } + } + if (final_status != 0 && final_status != FLASH_COMPLETE) { + eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); + } + return final_status; +} + +uint8_t EEPROM_ReadDataByte(uint16_t Address) { + uint8_t DataByte = 0xFF; + + if (Address < FEE_DENSITY_BYTES) { + DataByte = DataBuf[Address]; + } + + eeprom_printf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte); + + return DataByte; +} + +uint16_t EEPROM_ReadDataWord(uint16_t Address) { + uint16_t DataWord = 0xFFFF; + + if (Address < FEE_DENSITY_BYTES - 1) { + /* Check word alignment */ + if (Address % 2) { + DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8); + } else { + DataWord = *(uint16_t *)(&DataBuf[Address]); + } + } + + eeprom_printf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord); + + return DataWord; +} + +/***************************************************************************** + * Wrap library in AVR style functions. + *******************************************************************************/ +uint8_t eeprom_read_byte(const uint8_t *Address) { return EEPROM_ReadDataByte((const uintptr_t)Address); } + +void eeprom_write_byte(uint8_t *Address, uint8_t Value) { EEPROM_WriteDataByte((uintptr_t)Address, Value); } + +void eeprom_update_byte(uint8_t *Address, uint8_t Value) { EEPROM_WriteDataByte((uintptr_t)Address, Value); } + +uint16_t eeprom_read_word(const uint16_t *Address) { return EEPROM_ReadDataWord((const uintptr_t)Address); } + +void eeprom_write_word(uint16_t *Address, uint16_t Value) { EEPROM_WriteDataWord((uintptr_t)Address, Value); } + +void eeprom_update_word(uint16_t *Address, uint16_t Value) { EEPROM_WriteDataWord((uintptr_t)Address, Value); } + +uint32_t eeprom_read_dword(const uint32_t *Address) { + const uint16_t p = (const uintptr_t)Address; + /* Check word alignment */ + if (p % 2) { + /* Not aligned */ + return (uint32_t)EEPROM_ReadDataByte(p) | (uint32_t)(EEPROM_ReadDataWord(p + 1) << 8) | (uint32_t)(EEPROM_ReadDataByte(p + 3) << 24); } else { - return (void *)(eepemu_dynamic_keymap + (uint32_t)addr - - DYNAMIC_KEYMAP_EEPROM_ADDR); + /* Aligned */ + return EEPROM_ReadDataWord(p) | (EEPROM_ReadDataWord(p + 2) << 16); } } -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; +void eeprom_write_dword(uint32_t *Address, uint32_t Value) { + uint16_t p = (const uintptr_t)Address; + /* Check word alignment */ + if (p % 2) { + /* Not aligned */ + EEPROM_WriteDataByte(p, (uint8_t)Value); + EEPROM_WriteDataWord(p + 1, (uint16_t)(Value >> 8)); + EEPROM_WriteDataByte(p + 3, (uint8_t)(Value >> 24)); } else { - uint16_t ret; - memcpy(&ret, addr, sizeof(ret)); - return ret; + /* Aligned */ + EEPROM_WriteDataWord(p, (uint16_t)Value); + EEPROM_WriteDataWord(p + 2, (uint16_t)(Value >> 16)); } } -uint32_t eeprom_read_dword(const uint32_t *__p) { - uint32_t *addr = convert_address(__p); - if (addr == NULL) { - return 0xffffffff; +void eeprom_update_dword(uint32_t *Address, uint32_t Value) { eeprom_write_dword(Address, Value); } + +void eeprom_read_block(void *buf, const void *addr, size_t len) { + const uint8_t *src = (const uint8_t *)addr; + uint8_t * dest = (uint8_t *)buf; + + /* Check word alignment */ + if (len && (uintptr_t)src % 2) { + /* Read the unaligned first byte */ + *dest++ = eeprom_read_byte(src++); + --len; } - if ((uint32_t)addr % 4 == 0) { - return *addr; - } else { - uint32_t ret; - memcpy(&ret, addr, sizeof(ret)); - return ret; + uint16_t value; + bool aligned = ((uintptr_t)dest % 2 == 0); + while (len > 1) { + value = eeprom_read_word((uint16_t *)src); + if (aligned) { + *(uint16_t *)dest = value; + dest += 2; + } else { + *dest++ = value; + *dest++ = value >> 8; + } + src += 2; + len -= 2; + } + if (len) { + *dest = eeprom_read_byte(src); } } -void eeprom_read_block(void *__dst, const void *__src, size_t __n) { - uint32_t *addr = convert_address(__src); - if (addr == NULL) { - return; +void eeprom_write_block(const void *buf, void *addr, size_t len) { + uint8_t * dest = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + + /* Check word alignment */ + if (len && (uintptr_t)dest % 2) { + /* Write the unaligned first byte */ + eeprom_write_byte(dest++, *src++); + --len; } - memcpy(__dst, addr, __n); -} + uint16_t value; + bool aligned = ((uintptr_t)src % 2 == 0); + while (len > 1) { + if (aligned) { + value = *(uint16_t *)src; + } else { + value = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8); + } + eeprom_write_word((uint16_t *)dest, value); + dest += 2; + src += 2; + len -= 2; + } -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(); + if (len) { + eeprom_write_byte(dest, *src); } } -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); - } -} +void eeprom_update_block(const void *buf, void *addr, size_t len) { eeprom_write_block(buf, addr, len); } diff --git a/platforms/pico/eeprom_pico.h b/platforms/pico/eeprom_pico.h new file mode 100644 index 0000000000..8fcfb556b8 --- /dev/null +++ b/platforms/pico/eeprom_pico.h @@ -0,0 +1,33 @@ +/* + * This software is experimental and a work in progress. + * Under no circumstances should these files be used in relation to any critical system(s). + * Use of these files is at your own risk. + * + * 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. + * + * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by + * Artur F. + * + * Modifications for QMK and STM32F303 by Yiancar + * + * This library assumes 8-bit data locations. To add a new MCU, please provide the flash + * page size and the total flash size in Kb. The number of available pages must be a multiple + * of 2. Only half of the pages account for the total EEPROM size. + * This library also assumes that the pages are not used by the firmware. + */ + +#pragma once + +uint16_t EEPROM_Init(void); +void EEPROM_Erase(void); +uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte); +uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord); +uint8_t EEPROM_ReadDataByte(uint16_t Address); +uint16_t EEPROM_ReadDataWord(uint16_t Address); + +void print_eeprom(void); diff --git a/platforms/pico/eeprom_pico_defs.h b/platforms/pico/eeprom_pico_defs.h new file mode 100644 index 0000000000..edf0a7ff2c --- /dev/null +++ b/platforms/pico/eeprom_pico_defs.h @@ -0,0 +1,12 @@ + +#pragma once + +#include "hardware/flash.h" +#include "hardware/structs/ssi.h" + +#define FEE_PAGE_SIZE FLASH_SECTOR_SIZE +#define FEE_PAGE_COUNT 2 +#define FEE_MCU_FLASH_SIZE (PICO_FLASH_SIZE_BYTES / 1024) +#define FEE_PAGE_BASE_ADDRESS \ + (PICO_FLASH_SIZE_BYTES - FEE_PAGE_SIZE * FEE_PAGE_COUNT) + diff --git a/platforms/pico/flash_pico.c b/platforms/pico/flash_pico.c new file mode 100644 index 0000000000..186109e1e9 --- /dev/null +++ b/platforms/pico/flash_pico.c @@ -0,0 +1,201 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#include "flash_pico.h" + +#include "pico/bootrom.h" +#include "hardware/flash.h" +#include "hardware/sync.h" +#include "hardware/structs/ssi.h" +#include "hardware/structs/ioqspi.h" + +#define BOOT2_SIZE_WORDS 64 +#define FLASHCMD_PAGE_PROGRAM 0x02 +#define FLASHCMD_READ_STATUS 0x05 +#define FLASHCMD_WRITE_ENABLE 0x06 + +static ssi_hw_t *const ssi = (ssi_hw_t *)XIP_SSI_BASE; + +// Sanity check +#undef static_assert +#define static_assert(cond, x) extern int static_assert[(cond) ? 1 : -1] +check_hw_layout(ssi_hw_t, ssienr, SSI_SSIENR_OFFSET); +check_hw_layout(ssi_hw_t, spi_ctrlr0, SSI_SPI_CTRLR0_OFFSET); + +static uint32_t boot2_copyout[BOOT2_SIZE_WORDS]; +static bool boot2_copyout_valid = false; + +static void __no_inline_not_in_flash_func(flash_init_boot2_copyout)(void) { + if (boot2_copyout_valid) return; + for (int i = 0; i < BOOT2_SIZE_WORDS; ++i) + boot2_copyout[i] = ((uint32_t *)XIP_BASE)[i]; + __compiler_memory_barrier(); + boot2_copyout_valid = true; +} + +static void __no_inline_not_in_flash_func(flash_enable_xip_via_boot2)(void) { + ((void (*)(void))boot2_copyout + 1)(); +} + +// Bitbanging the chip select using IO overrides, in case RAM-resident IRQs +// are still running, and the FIFO bottoms out. (the bootrom does the same) +static void __no_inline_not_in_flash_func(flash_cs_force)(bool high) { + uint32_t field_val = high ? IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH + : IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW; + hw_write_masked(&ioqspi_hw->io[1].ctrl, + field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB, + IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS); +} + +// Also allow any unbounded loops to check whether the above abort condition +// was asserted, and terminate early +static int __no_inline_not_in_flash_func(flash_was_aborted)(void) { + return *(io_rw_32 *)(IO_QSPI_BASE + IO_QSPI_GPIO_QSPI_SD1_CTRL_OFFSET) & + IO_QSPI_GPIO_QSPI_SD1_CTRL_INOVER_BITS; +} + +// Put bytes from one buffer, and get bytes into another buffer. +// These can be the same buffer. +// If tx is NULL then send zeroes. +// If rx is NULL then all read data will be dropped. +// +// If rx_skip is nonzero, this many bytes will first be consumed from the FIFO, +// before reading a further count bytes into *rx. +// E.g. if you have written a command+address just before calling this function. +static void __no_inline_not_in_flash_func(flash_put_get)(const uint8_t *tx, + uint8_t * rx, + size_t count, + size_t rx_skip) { + // Make sure there is never more data in flight than the depth of the RX + // FIFO. Otherwise, when we are interrupted for long periods, hardware + // will overflow the RX FIFO. + const uint max_in_flight = 16 - 2; // account for data internal to SSI + size_t tx_count = count; + size_t rx_count = count; + while (tx_count || rx_skip || rx_count) { + // NB order of reads, for pessimism rather than optimism + uint32_t tx_level = ssi_hw->txflr; + uint32_t rx_level = ssi_hw->rxflr; + bool did_something = + false; // Expect this to be folded into control flow, not register + if (tx_count && tx_level + rx_level < max_in_flight) { + ssi->dr0 = (uint32_t)(tx ? *tx++ : 0); + --tx_count; + did_something = true; + } + if (rx_level) { + uint8_t rxbyte = ssi->dr0; + did_something = true; + if (rx_skip) { + --rx_skip; + } else { + if (rx) *rx++ = rxbyte; + --rx_count; + } + } + // APB load costs 4 cycles, so only do it on idle loops (our budget is + // 48 cyc/byte) + if (!did_something && __builtin_expect(flash_was_aborted(), 0)) break; + } + flash_cs_force(1); +} + +// Convenience wrapper for above +// (And it's hard for the debug host to get the tight timing between +// cmd DR0 write and the remaining data) +static void __no_inline_not_in_flash_func(_flash_do_cmd)(uint8_t cmd, + const uint8_t *tx, + uint8_t * rx, + size_t count) { + flash_cs_force(0); + ssi->dr0 = cmd; + flash_put_get(tx, rx, count, 1); +} + +// Timing of this one is critical, so do not expose the symbol to debugger etc +static void __no_inline_not_in_flash_func(flash_put_cmd_addr)(uint8_t cmd, + uint32_t addr) { + flash_cs_force(0); + addr |= cmd << 24; + for (int i = 0; i < 4; ++i) { + ssi->dr0 = addr >> 24; + addr <<= 8; + } +} + +// Poll the flash status register until the busy bit (LSB) clears +static void __no_inline_not_in_flash_func(flash_wait_ready)(void) { + uint8_t stat; + do { + _flash_do_cmd(FLASHCMD_READ_STATUS, NULL, &stat, 1); + } while (stat & 0x1 && !flash_was_aborted()); +} + +// Set the WEL bit (needed before any program/erase operation) +static void __no_inline_not_in_flash_func(flash_enable_write)(void) { + _flash_do_cmd(FLASHCMD_WRITE_ENABLE, NULL, NULL, 0); +} + +static void __no_inline_not_in_flash_func(pico_program_half_word)( + uint32_t flash_offs, uint16_t data) { + rom_connect_internal_flash_fn connect_internal_flash = + (rom_connect_internal_flash_fn)rom_func_lookup_inline( + ROM_FUNC_CONNECT_INTERNAL_FLASH); + rom_flash_exit_xip_fn flash_exit_xip = + (rom_flash_exit_xip_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP); + rom_flash_range_program_fn flash_range_program = + (rom_flash_range_program_fn)rom_func_lookup_inline( + ROM_FUNC_FLASH_RANGE_PROGRAM); + rom_flash_flush_cache_fn flash_flush_cache = + (rom_flash_flush_cache_fn)rom_func_lookup_inline( + ROM_FUNC_FLASH_FLUSH_CACHE); + assert(connect_internal_flash && flash_exit_xip && flash_range_program && + flash_flush_cache); + flash_init_boot2_copyout(); + + __compiler_memory_barrier(); + + connect_internal_flash(); + flash_exit_xip(); + + flash_enable_write(); + flash_put_cmd_addr(FLASHCMD_PAGE_PROGRAM, flash_offs); + flash_put_get((uint8_t *)&data, NULL, 2, 4); + flash_wait_ready(); + + flash_flush_cache(); // Note this is needed to remove CSn IO force as well + // as cache flushing + flash_enable_xip_via_boot2(); +} + +FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) { + return FLASH_COMPLETE; +} + +FLASH_Status FLASH_ErasePage(uint32_t Page_Address) { + flash_range_erase(Page_Address, FLASH_SECTOR_SIZE); + return FLASH_COMPLETE; +} + +FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) { + pico_program_half_word(Address, Data); + return FLASH_COMPLETE; +} + +__attribute__((weak)) void pico_before_flash_operation(void) {} +__attribute__((weak)) void pico_after_flash_operation(void) {} + +static int intr_stat; + +void FLASH_Lock(void) { + restore_interrupts(intr_stat); + pico_after_flash_operation(); +} +void FLASH_Unlock(void) { + intr_stat = save_and_disable_interrupts(); + pico_before_flash_operation(); +} diff --git a/platforms/pico/flash_pico.h b/platforms/pico/flash_pico.h new file mode 100644 index 0000000000..6c66642ec5 --- /dev/null +++ b/platforms/pico/flash_pico.h @@ -0,0 +1,44 @@ +/* + * This software is experimental and a work in progress. + * Under no circumstances should these files be used in relation to any critical system(s). + * Use of these files is at your own risk. + * + * 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. + * + * This files are free to use from https://github.com/rogerclarkmelbourne/Arduino_STM32 and + * https://github.com/leaflabs/libmaple + * + * Modifications for QMK and STM32F303 by Yiancar + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef FLASH_STM32_MOCKED +extern uint8_t FlashBuf[MOCK_FLASH_SIZE]; +#endif + +typedef enum { FLASH_BUSY = 1, FLASH_ERROR_PG, FLASH_ERROR_WRP, FLASH_ERROR_OPT, FLASH_COMPLETE, FLASH_TIMEOUT, FLASH_BAD_ADDRESS } FLASH_Status; + +#define IS_FLASH_ADDRESS(ADDRESS) (((ADDRESS) >= 0x08000000) && ((ADDRESS) < 0x0807FFFF)) + +FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout); +FLASH_Status FLASH_ErasePage(uint32_t Page_Address); +FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data); + +void FLASH_Unlock(void); +void FLASH_Lock(void); + +#ifdef __cplusplus +} +#endif diff --git a/tmk_core/protocol/pico/pico.c b/tmk_core/protocol/pico/pico.c index ca225154a2..4152a50b46 100644 --- a/tmk_core/protocol/pico/pico.c +++ b/tmk_core/protocol/pico/pico.c @@ -31,7 +31,7 @@ #include "bootloader.h" #include "debug.h" -#include "pico_eeprom.h" +#include "eeprom_pico.h" #include "usb_descriptors.h" #include "pico/stdio/driver.h" @@ -135,7 +135,7 @@ void protocol_pre_init(void) { watchdog_enable(8000, 1); watchdog_hw->scratch[0] = 0x2040dead; pico_cdc_enable_printf(); - pico_eepemu_init(); + EEPROM_Init(); } void protocol_post_init(void) { host_set_driver(&driver); } @@ -145,7 +145,6 @@ void protocol_pre_task(void) { } void protocol_post_task(void) { - pico_eepemu_lazy_write_back(); #ifdef CONSOLE_ENABLE console_task(); #endif @@ -207,16 +206,15 @@ __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"); + if (buf[0] == 'b') { + bootloader_jump(); } else if (buf[0] == 'l') { printf("init eeprom emulation\n"); - pico_eepemu_init(); + EEPROM_Init(); + printf("complete\n"); + } else if (buf[0] == 'c') { + printf("clear eeprom emulation\n"); + EEPROM_Erase(); printf("complete\n"); } else if (buf[0] == 'd') { printf("debug print "); diff --git a/tmk_core/protocol/pico/pico_eeprom.h b/tmk_core/protocol/pico/pico_eeprom.h deleted file mode 100644 index 729181e274..0000000000 --- a/tmk_core/protocol/pico/pico_eeprom.h +++ /dev/null @@ -1,7 +0,0 @@ - -#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); From 85e002d319fdc643060228bddee16b35454ec5a0 Mon Sep 17 00:00:00 2001 From: daskygit <32983009+daskygit@users.noreply.github.com> Date: Thu, 30 Dec 2021 22:18:01 +0000 Subject: [PATCH 07/24] fix conflict mistake --- builddefs/common_features.mk | 2 -- 1 file changed, 2 deletions(-) diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index c8535d8300..fd54374587 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -207,8 +207,6 @@ 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 From 537195a16bf73a0ab21cc3c2ac3cfeb762206768 Mon Sep 17 00:00:00 2001 From: daskygit <32983009+daskygit@users.noreply.github.com> Date: Thu, 30 Dec 2021 22:33:42 +0000 Subject: [PATCH 08/24] Quick bootloader fix, implement type in future. --- platforms/pico/bootloaders/none.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 platforms/pico/bootloaders/none.c diff --git a/platforms/pico/bootloaders/none.c b/platforms/pico/bootloaders/none.c new file mode 100644 index 0000000000..c464f59b09 --- /dev/null +++ b/platforms/pico/bootloaders/none.c @@ -0,0 +1,14 @@ +#include "bootloader.h" + +#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); +} From ec3375e0404c8e1a19c2b40bcadab3e136410ae4 Mon Sep 17 00:00:00 2001 From: Ilya Zhuravlev Date: Sat, 9 Apr 2022 22:45:29 -0600 Subject: [PATCH 09/24] Fix rp2040 after merge --- builddefs/bootloader.mk | 4 ++++ builddefs/common_features.mk | 1 + data/schemas/keyboard.jsonschema | 2 +- keyboards/vial_example/vial_rp2040/rules.mk | 1 + platforms/eeprom.h | 3 +++ platforms/pico/bootloaders/{none.c => RP2040.c} | 0 platforms/pico/eeprom_pico_defs.h | 1 + platforms/pico/suspend.c | 9 --------- 8 files changed, 11 insertions(+), 10 deletions(-) rename platforms/pico/bootloaders/{none.c => RP2040.c} (100%) diff --git a/builddefs/bootloader.mk b/builddefs/bootloader.mk index dfbe6232c6..0158e97b06 100644 --- a/builddefs/bootloader.mk +++ b/builddefs/bootloader.mk @@ -213,6 +213,10 @@ ifeq ($(strip $(BOOTLOADER)), md-boot) OPT_DEFS += -DBOOTLOADER_MD_BOOT BOOTLOADER_TYPE = md_boot endif +ifeq ($(strip $(BOOTLOADER)), RP2040) + OPT_DEFS += -DBOOTLOADER_RP2040 + BOOTLOADER_TYPE = RP2040 +endif ifeq ($(strip $(BOOTLOADER_TYPE)),) $(call CATASTROPHIC_ERROR,Invalid BOOTLOADER,No bootloader specified. Please set an appropriate 'BOOTLOADER' in your keyboard's 'rules.mk' file.) diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index fd54374587..c1e13e568f 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -213,6 +213,7 @@ else SRC += $(PLATFORM_COMMON_DIR)/eeprom_samd.c SRC += $(PLATFORM_COMMON_DIR)/eeprom.c else ifeq ($(PLATFORM),PICO_SDK) + OPT_DEFS += -DEEPROM_PICO SRC += $(PLATFORM_COMMON_DIR)/eeprom.c SRC += $(PLATFORM_COMMON_DIR)/flash_pico.c else ifeq ($(PLATFORM),TEST) diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema index ffe34b2b93..485b43b236 100644 --- a/data/schemas/keyboard.jsonschema +++ b/data/schemas/keyboard.jsonschema @@ -57,7 +57,7 @@ }, "bootloader": { "type": "string", - "enum": ["atmel-dfu", "bootloadhid", "bootloadHID", "custom", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "md-boot", "qmk-dfu", "qmk-hid", "stm32-dfu", "stm32duino", "gd32v-dfu", "wb32-dfu", "unknown", "usbasploader", "USBasp", "tinyuf2", "vibl"], + "enum": ["atmel-dfu", "bootloadhid", "bootloadHID", "custom", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "md-boot", "qmk-dfu", "qmk-hid", "stm32-dfu", "stm32duino", "gd32v-dfu", "wb32-dfu", "unknown", "usbasploader", "USBasp", "tinyuf2", "vibl", "RP2040"], }, "bootloader_instructions": { "type": "string", diff --git a/keyboards/vial_example/vial_rp2040/rules.mk b/keyboards/vial_example/vial_rp2040/rules.mk index 797f9860bd..de8fbfe07e 100644 --- a/keyboards/vial_example/vial_rp2040/rules.mk +++ b/keyboards/vial_example/vial_rp2040/rules.mk @@ -1,6 +1,7 @@ # MCU name MCU_FAMILY = PICO MCU_SERIES = RP2040 +BOOTLOADER = RP2040 MCU = cortex-m0plus # Build Options diff --git a/platforms/eeprom.h b/platforms/eeprom.h index 091e6e4400..764b5b7c59 100644 --- a/platforms/eeprom.h +++ b/platforms/eeprom.h @@ -48,6 +48,9 @@ void eeprom_update_block(const void *__src, void *__dst, size_t __n); #elif defined(EEPROM_SAMD) # include "eeprom_samd.h" # define TOTAL_EEPROM_BYTE_COUNT (EEPROM_SIZE) +#elif defined(EEPROM_PICO) +# include "eeprom_pico_defs.h" +# define TOTAL_EEPROM_BYTE_COUNT (FEE_DENSITY_BYTES) #elif defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATtiny85__) # define TOTAL_EEPROM_BYTE_COUNT 512 #elif defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega32A__) diff --git a/platforms/pico/bootloaders/none.c b/platforms/pico/bootloaders/RP2040.c similarity index 100% rename from platforms/pico/bootloaders/none.c rename to platforms/pico/bootloaders/RP2040.c diff --git a/platforms/pico/eeprom_pico_defs.h b/platforms/pico/eeprom_pico_defs.h index edf0a7ff2c..f1cdad498c 100644 --- a/platforms/pico/eeprom_pico_defs.h +++ b/platforms/pico/eeprom_pico_defs.h @@ -10,3 +10,4 @@ #define FEE_PAGE_BASE_ADDRESS \ (PICO_FLASH_SIZE_BYTES - FEE_PAGE_SIZE * FEE_PAGE_COUNT) +#define FEE_DENSITY_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE / 2) diff --git a/platforms/pico/suspend.c b/platforms/pico/suspend.c index 2d6e0404ce..c28b6d8afe 100644 --- a/platforms/pico/suspend.c +++ b/platforms/pico/suspend.c @@ -85,15 +85,6 @@ void suspend_power_down(void) { __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 * From f280e180a306cad5dfb770216bcdc433ed8a5d2a Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Mon, 10 Jan 2022 10:43:51 +0900 Subject: [PATCH 10/24] Fix pico/gpio.h --- platforms/pico/gpio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/pico/gpio.h b/platforms/pico/gpio.h index 0c2efbe151..0cfe5409ca 100644 --- a/platforms/pico/gpio.h +++ b/platforms/pico/gpio.h @@ -20,7 +20,7 @@ typedef uint8_t pin_t; -#define setPinInput(pin) (gpio_set_dir(pin, GPIO_IN)) +#define setPinInput(pin) (gpio_disable_pulls(pin), gpio_set_dir(pin, GPIO_IN)) #define setPinInputHigh(pin) (gpio_set_dir(pin, GPIO_IN), gpio_pull_up(pin)) #define setPinInputLow(pin) (gpio_init(pin), gpio_set_dir(pin, GPIO_IN), gpio_pull_down(pin)) #define setPinOutput(pin) (gpio_set_dir(pin, GPIO_OUT)) From a0fd3103995ccb0c5bf97c0df4ce9f96ac2ca9a3 Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Mon, 10 Jan 2022 11:21:56 +0900 Subject: [PATCH 11/24] display copyright --- platforms/pico/boards/pico_pro_micro.h | 15 +++++++++++++++ platforms/pico/bootloader.c | 15 +++++++++++++++ platforms/pico/platform.c | 15 +++++++++++++++ tmk_core/protocol/pico/iusb.c | 16 ++++++++++++++++ tmk_core/protocol/pico/iusb.h | 15 +++++++++++++++ tmk_core/protocol/pico/pico_cdc.h | 15 +++++++++++++++ tmk_core/protocol/pico/pio_manager.c | 15 +++++++++++++++ tmk_core/protocol/pico/pio_manager.h | 15 +++++++++++++++ tmk_core/protocol/pico/usb_descriptors.c | 1 + tmk_core/protocol/pico/usb_util.c | 15 +++++++++++++++ 10 files changed, 137 insertions(+) diff --git a/platforms/pico/boards/pico_pro_micro.h b/platforms/pico/boards/pico_pro_micro.h index a4611ade51..102b88f12f 100644 --- a/platforms/pico/boards/pico_pro_micro.h +++ b/platforms/pico/boards/pico_pro_micro.h @@ -1,3 +1,18 @@ +/* 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 diff --git a/platforms/pico/bootloader.c b/platforms/pico/bootloader.c index d9886acf2f..f83b97981a 100644 --- a/platforms/pico/bootloader.c +++ b/platforms/pico/bootloader.c @@ -1,3 +1,18 @@ +/* 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 "pico/stdlib.h" #include "pico/bootrom.h" diff --git a/platforms/pico/platform.c b/platforms/pico/platform.c index b578dd4a8e..7fda003b3c 100644 --- a/platforms/pico/platform.c +++ b/platforms/pico/platform.c @@ -1,3 +1,18 @@ +/* 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 "platform_deps.h" #include "quantum.h" diff --git a/tmk_core/protocol/pico/iusb.c b/tmk_core/protocol/pico/iusb.c index 1847fe2f9a..60eb754b4b 100644 --- a/tmk_core/protocol/pico/iusb.c +++ b/tmk_core/protocol/pico/iusb.c @@ -1,3 +1,19 @@ +/* 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 "iusb.h" #include "raw_hid.h" diff --git a/tmk_core/protocol/pico/iusb.h b/tmk_core/protocol/pico/iusb.h index 28bc3590a9..0c38452074 100644 --- a/tmk_core/protocol/pico/iusb.h +++ b/tmk_core/protocol/pico/iusb.h @@ -1,3 +1,18 @@ +/* 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 diff --git a/tmk_core/protocol/pico/pico_cdc.h b/tmk_core/protocol/pico/pico_cdc.h index bcedec6738..a83741a869 100644 --- a/tmk_core/protocol/pico/pico_cdc.h +++ b/tmk_core/protocol/pico/pico_cdc.h @@ -1,3 +1,18 @@ +/* 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 diff --git a/tmk_core/protocol/pico/pio_manager.c b/tmk_core/protocol/pico/pio_manager.c index 4e86caf5d7..8fdb95bc3c 100644 --- a/tmk_core/protocol/pico/pio_manager.c +++ b/tmk_core/protocol/pico/pio_manager.c @@ -1,3 +1,18 @@ +/* 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 "pio_manager.h" #include "hardware/pio.h" diff --git a/tmk_core/protocol/pico/pio_manager.h b/tmk_core/protocol/pico/pio_manager.h index 046df09525..2e2e7a0ec2 100644 --- a/tmk_core/protocol/pico/pio_manager.h +++ b/tmk_core/protocol/pico/pio_manager.h @@ -1,3 +1,18 @@ +/* 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 diff --git a/tmk_core/protocol/pico/usb_descriptors.c b/tmk_core/protocol/pico/usb_descriptors.c index e3b90194a9..38827cde64 100644 --- a/tmk_core/protocol/pico/usb_descriptors.c +++ b/tmk_core/protocol/pico/usb_descriptors.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * + * Modifications for QMK and RP2040 by sekigon-gonnoc */ #include "tusb.h" diff --git a/tmk_core/protocol/pico/usb_util.c b/tmk_core/protocol/pico/usb_util.c index 13072b5bb8..d4f738c92a 100644 --- a/tmk_core/protocol/pico/usb_util.c +++ b/tmk_core/protocol/pico/usb_util.c @@ -1,3 +1,18 @@ +/* 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 "usb_util.h" From 8fd79c3475e186329e15285a65ddd3a7e2d9ed5d Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Sat, 22 Jan 2022 20:51:03 +0900 Subject: [PATCH 12/24] Use DMA for ws2812 data transmission RGB_MATRIX for split keyboard is improved --- platforms/pico/drivers/serial.c | 10 +++++----- platforms/pico/drivers/ws2812.c | 31 +++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/platforms/pico/drivers/serial.c b/platforms/pico/drivers/serial.c index 15e71c5215..116dc6f5dc 100644 --- a/platforms/pico/drivers/serial.c +++ b/platforms/pico/drivers/serial.c @@ -68,7 +68,7 @@ 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 void interrupt_handler(uint gpio, uint32_t events); static PIO pio = pio0; static uint sm_tx, sm_rx; @@ -144,7 +144,7 @@ void soft_serial_target_init(void) { } // Used by the master to synchronize timing with the slave. -static int __attribute__((noinline)) sync_recv(void) { +static int __no_inline_not_in_flash_func(sync_recv)(void) { serial_input(); volatile uint64_t timeout = time_us_64() + 10000; @@ -180,7 +180,7 @@ static int __attribute__((noinline)) sync_recv(void) { } // Used by the slave to send a synchronization signal to the master. -static void __attribute__((noinline)) sync_send(void) { +static void __no_inline_not_in_flash_func(sync_send)(void) { serial_output(); serial_low(); @@ -220,7 +220,7 @@ static void serial_wait_send_complete(void) { } // interrupt handle to be used by the slave device -void interrupt_handler(uint gpio, uint32_t events) { +static void __no_inline_not_in_flash_func(interrupt_handler)(uint gpio, uint32_t events) { if (gpio != SOFT_SERIAL_PIN || events != GPIO_IRQ_EDGE_FALL) { return; } @@ -339,7 +339,7 @@ void interrupt_handler(uint gpio, uint32_t events) { // 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) { +int __no_inline_not_in_flash_func(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 diff --git a/platforms/pico/drivers/ws2812.c b/platforms/pico/drivers/ws2812.c index 3eea6092ae..91eb8eadc5 100644 --- a/platforms/pico/drivers/ws2812.c +++ b/platforms/pico/drivers/ws2812.c @@ -1,16 +1,21 @@ +// Copyright 2022 sekigon-gonnoc +// SPDX-License-Identifier: GPL-2.0-or-later #include #include "ws2812.h" #include "ws2812.pio.h" +#include "atomic_util.h" #include "pio_manager.h" #include "boards/pico_boards.h" #include "pico/stdlib.h" +#include "hardware/dma.h" -static PIO pio = pio0; -static int sm = 0; +static PIO pio = pio0; +static int sm = 0; +static int dma_ch = 1; static int ws2812_init(void) { sm = pio_manager_get_empty_sm(pio); @@ -27,6 +32,15 @@ static int ws2812_init(void) { ws2812_program_init(pio, sm, offset, RGB_DI_PIN, 800000, false); + dma_channel_config conf = dma_channel_get_default_config(dma_ch); + + channel_config_set_read_increment(&conf, true); + channel_config_set_write_increment(&conf, false); + channel_config_set_transfer_data_size(&conf, DMA_SIZE_32); + channel_config_set_dreq(&conf, pio_get_dreq(pio, sm, true)); + + dma_channel_set_config(dma_ch, &conf, false); + dma_channel_set_write_addr(dma_ch, &pio->txf[sm], false); return 0; } @@ -40,12 +54,13 @@ void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds) { } } - printf("led %d %d %d\n", ledarray[0].r, ledarray[0].g, ledarray[0].b); - + uint32_t leddata[RGBLED_NUM]; 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)); + leddata[i] = (((uint32_t)ledarray[i].g) << 24) | + (((uint32_t)ledarray[i].r) << 16) | + ((uint32_t)ledarray[i].b << 8); } + + dma_channel_transfer_from_buffer_now(dma_ch, leddata, number_of_leds); + dma_channel_wait_for_finish_blocking(dma_ch); } From 04d147dc5cfd67a150cd8b262d6eb1443ec506d0 Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Sun, 23 Jan 2022 18:13:53 +0900 Subject: [PATCH 13/24] Replace lsusb by environment variable for flash in QMK MSYS --- platforms/pico/platform.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platforms/pico/platform.mk b/platforms/pico/platform.mk index 565fa1e9d5..75d81828a5 100644 --- a/platforms/pico/platform.mk +++ b/platforms/pico/platform.mk @@ -14,6 +14,7 @@ OPT_DEFS += -DPROTOCOL_PICO OPT_DEFS += -DSPLIT_USB_DETECT PICOTOOL ?= picotool RP2BOOT_ID ?= "2e8a:0003" +LSUSB ?= lsusb PICO_SDK_PATH = lib/pico-sdk @@ -396,7 +397,7 @@ $(KEYBOARD_OUTPUT)/src/bs2_default_padded_checksummed.S: $(KEYBOARD_OUTPUT)/src/ flash: $(BUILD_DIR)/$(TRAGET).elf cpfirmware sizeafter - until lsusb | grep -q $(RP2BOOT_ID); do\ + until $(LSUSB) | grep -q $(RP2BOOT_ID); do\ printf "$(MSG_BOOTLOADER_NOT_FOUND)" ;\ sleep 5 ;\ done From f22ffd6816bbb1aae903e5e0b521882b52867f07 Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Mon, 24 Jan 2022 20:21:04 +0900 Subject: [PATCH 14/24] Send wakeup signal to host --- tmk_core/protocol/pico/iusb.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tmk_core/protocol/pico/iusb.c b/tmk_core/protocol/pico/iusb.c index 60eb754b4b..725a31e633 100644 --- a/tmk_core/protocol/pico/iusb.c +++ b/tmk_core/protocol/pico/iusb.c @@ -33,6 +33,10 @@ uint8_t keyboard_leds(void) { } void send_keyboard(report_keyboard_t *report) { + if (tud_ready() && tud_suspended()){ + tud_remote_wakeup(); + } + while (!tud_hid_n_ready(ITF_NUM_HID_KEYBOARD)) { tud_task(); if (!tud_ready()) { @@ -44,6 +48,10 @@ void send_keyboard(report_keyboard_t *report) { } void send_mouse(report_mouse_t *report) { + if (tud_ready() && tud_suspended()){ + tud_remote_wakeup(); + } + while (!tud_hid_n_ready(ITF_NUM_HID_MOUSE)) { tud_task(); if (!tud_ready()) { From 2328012e48234d21d87a481462f225bb634945d9 Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Sun, 30 Jan 2022 10:48:50 +0900 Subject: [PATCH 15/24] remove DMA from ws2812 --- platforms/pico/drivers/ws2812.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/platforms/pico/drivers/ws2812.c b/platforms/pico/drivers/ws2812.c index 91eb8eadc5..d35107f9e3 100644 --- a/platforms/pico/drivers/ws2812.c +++ b/platforms/pico/drivers/ws2812.c @@ -11,11 +11,9 @@ #include "boards/pico_boards.h" #include "pico/stdlib.h" -#include "hardware/dma.h" -static PIO pio = pio0; -static int sm = 0; -static int dma_ch = 1; +static PIO pio = pio0; +static int sm = 0; static int ws2812_init(void) { sm = pio_manager_get_empty_sm(pio); @@ -32,15 +30,6 @@ static int ws2812_init(void) { ws2812_program_init(pio, sm, offset, RGB_DI_PIN, 800000, false); - dma_channel_config conf = dma_channel_get_default_config(dma_ch); - - channel_config_set_read_increment(&conf, true); - channel_config_set_write_increment(&conf, false); - channel_config_set_transfer_data_size(&conf, DMA_SIZE_32); - channel_config_set_dreq(&conf, pio_get_dreq(pio, sm, true)); - - dma_channel_set_config(dma_ch, &conf, false); - dma_channel_set_write_addr(dma_ch, &pio->txf[sm], false); return 0; } @@ -54,13 +43,14 @@ void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds) { } } - uint32_t leddata[RGBLED_NUM]; + __interrupt_disable__(); + for (int i = 0; i < number_of_leds; i++) { - leddata[i] = (((uint32_t)ledarray[i].g) << 24) | - (((uint32_t)ledarray[i].r) << 16) | - ((uint32_t)ledarray[i].b << 8); + pio_sm_put_blocking(pio, sm, + (((uint32_t)ledarray[i].r) << 16) | + (((uint32_t)ledarray[i].g) << 24) | + ((uint32_t)ledarray[i].b << 8)); } - dma_channel_transfer_from_buffer_now(dma_ch, leddata, number_of_leds); - dma_channel_wait_for_finish_blocking(dma_ch); + __interrupt_enable__(NULL); } From 4e6a1f9accd79155de86a90b7a63404648caf595 Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Tue, 1 Mar 2022 16:05:29 +0900 Subject: [PATCH 16/24] Change rwu send condition --- tmk_core/protocol/pico/iusb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tmk_core/protocol/pico/iusb.c b/tmk_core/protocol/pico/iusb.c index 725a31e633..becf0aafcb 100644 --- a/tmk_core/protocol/pico/iusb.c +++ b/tmk_core/protocol/pico/iusb.c @@ -33,7 +33,7 @@ uint8_t keyboard_leds(void) { } void send_keyboard(report_keyboard_t *report) { - if (tud_ready() && tud_suspended()){ + if (tud_suspended()) { tud_remote_wakeup(); } @@ -48,7 +48,7 @@ void send_keyboard(report_keyboard_t *report) { } void send_mouse(report_mouse_t *report) { - if (tud_ready() && tud_suspended()){ + if (tud_suspended()) { tud_remote_wakeup(); } From bfa233584e92bd61f6679760f51d0332e5e1612a Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Sun, 3 Apr 2022 16:29:46 +0900 Subject: [PATCH 17/24] Fix remote wakeup --- tmk_core/protocol/pico/iusb.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tmk_core/protocol/pico/iusb.c b/tmk_core/protocol/pico/iusb.c index becf0aafcb..9dc3ea82e9 100644 --- a/tmk_core/protocol/pico/iusb.c +++ b/tmk_core/protocol/pico/iusb.c @@ -25,6 +25,7 @@ #include "device/usbd.h" #include "hid_device.h" #include "class/midi/midi_device.h" +#include "device/dcd.h" /* declarations */ uint8_t keyboard_leds(void) { @@ -35,6 +36,9 @@ uint8_t keyboard_leds(void) { void send_keyboard(report_keyboard_t *report) { if (tud_suspended()) { tud_remote_wakeup(); + busy_wait_ms(15); + dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); + return; } while (!tud_hid_n_ready(ITF_NUM_HID_KEYBOARD)) { @@ -50,6 +54,9 @@ void send_keyboard(report_keyboard_t *report) { void send_mouse(report_mouse_t *report) { if (tud_suspended()) { tud_remote_wakeup(); + busy_wait_ms(15); + dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); + return; } while (!tud_hid_n_ready(ITF_NUM_HID_MOUSE)) { From cbf19a1303d8b171693882b30d64fd80de235b8d Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Sun, 3 Apr 2022 16:32:16 +0900 Subject: [PATCH 18/24] Fix usb descriptor --- tmk_core/protocol/pico/usb_descriptors.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tmk_core/protocol/pico/usb_descriptors.c b/tmk_core/protocol/pico/usb_descriptors.c index 38827cde64..c32cb6c63d 100644 --- a/tmk_core/protocol/pico/usb_descriptors.c +++ b/tmk_core/protocol/pico/usb_descriptors.c @@ -47,14 +47,14 @@ tusb_desc_device_t const desc_device = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = 0x0110, + .bcdUSB = 0x0200, // 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, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, From b059dfddb5c1d847f5cf226086980402b7697559 Mon Sep 17 00:00:00 2001 From: sekigon-gonnoc <43873124+sekigon-gonnoc@users.noreply.github.com> Date: Sat, 3 Jul 2021 11:09:32 +0900 Subject: [PATCH 19/24] Indicator LED --- tmk_core/protocol/pico/iusb.c | 6 ++---- tmk_core/protocol/pico/pico.c | 6 ++++-- tmk_core/protocol/pico/usb_descriptors.h | 2 ++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tmk_core/protocol/pico/iusb.c b/tmk_core/protocol/pico/iusb.c index 9dc3ea82e9..cbf7b9fc1c 100644 --- a/tmk_core/protocol/pico/iusb.c +++ b/tmk_core/protocol/pico/iusb.c @@ -27,10 +27,8 @@ #include "class/midi/midi_device.h" #include "device/dcd.h" -/* declarations */ uint8_t keyboard_leds(void) { - // TODO - return 0; + return get_keyboard_led_status(); } void send_keyboard(report_keyboard_t *report) { @@ -92,7 +90,7 @@ void send_consumer(uint16_t data) { tud_task(); } -void raw_hid_send(uint8_t* data, uint8_t length) { +void raw_hid_send(uint8_t *data, uint8_t length) { while (!tud_hid_n_ready(ITF_NUM_HID_RAW)) { tud_task(); if (!tud_ready()) { diff --git a/tmk_core/protocol/pico/pico.c b/tmk_core/protocol/pico/pico.c index 4152a50b46..b5807fb877 100644 --- a/tmk_core/protocol/pico/pico.c +++ b/tmk_core/protocol/pico/pico.c @@ -265,12 +265,14 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t 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_KEYBOARD: + // LED status + keyboard_indicator_led = buffer[0]; + break; case ITF_NUM_HID_RAW: raw_hid_receive((uint8_t*)buffer, bufsize); break; diff --git a/tmk_core/protocol/pico/usb_descriptors.h b/tmk_core/protocol/pico/usb_descriptors.h index c95d308760..ee0cb6b111 100644 --- a/tmk_core/protocol/pico/usb_descriptors.h +++ b/tmk_core/protocol/pico/usb_descriptors.h @@ -56,3 +56,5 @@ enum { HID_REPORT_SIZE(0x08), \ HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE | HID_NON_VOLATILE), \ HID_COLLECTION_END + +uint8_t get_keyboard_led_status(void); From 464ccfbc66ef54823e6658b75eb8a84a80e5b21f Mon Sep 17 00:00:00 2001 From: Ilya Zhuravlev Date: Sat, 23 Apr 2022 20:08:57 -0600 Subject: [PATCH 20/24] fix uf2 generation --- builddefs/build_keyboard.mk | 3 ++- platforms/pico/platform.mk | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/builddefs/build_keyboard.mk b/builddefs/build_keyboard.mk index 588c070a08..f8bd538674 100644 --- a/builddefs/build_keyboard.mk +++ b/builddefs/build_keyboard.mk @@ -277,7 +277,8 @@ ifdef MCU_FAMILY $(info "PLATFORM RP2040") PLATFORM=PICO_SDK PLATFORM_KEY=pico - FIRMWARE_FORMAT=elf + FIRMWARE_FORMAT=uf2 + UF2_FAMILY=RP2040 endif else ifdef ARM_ATSAM PLATFORM=ARM_ATSAM diff --git a/platforms/pico/platform.mk b/platforms/pico/platform.mk index 75d81828a5..f5e0c83b58 100644 --- a/platforms/pico/platform.mk +++ b/platforms/pico/platform.mk @@ -402,6 +402,3 @@ flash: $(BUILD_DIR)/$(TRAGET).elf cpfirmware sizeafter 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 $^ From bd702ab6422eaf290b11cfa27337f73c9bdfb232 Mon Sep 17 00:00:00 2001 From: Ilya Zhuravlev Date: Sun, 24 Apr 2022 16:11:56 -0600 Subject: [PATCH 21/24] pico: initialize eeprom early otherwise reading qmk settings fails --- tmk_core/protocol/pico/pico.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tmk_core/protocol/pico/pico.c b/tmk_core/protocol/pico/pico.c index b5807fb877..52fdae51f7 100644 --- a/tmk_core/protocol/pico/pico.c +++ b/tmk_core/protocol/pico/pico.c @@ -126,7 +126,7 @@ static void raw_hid_task(void) { static void midi_ep_task(void) {} -void protocol_setup(void) { tusb_init(); } +void protocol_setup(void) { EEPROM_Init(); tusb_init(); } void protocol_pre_init(void) { // if (watchdog_caused_reboot() && watchdog_hw->scratch[0] == 0x2040dead) { @@ -135,7 +135,6 @@ void protocol_pre_init(void) { watchdog_enable(8000, 1); watchdog_hw->scratch[0] = 0x2040dead; pico_cdc_enable_printf(); - EEPROM_Init(); } void protocol_post_init(void) { host_set_driver(&driver); } From 973c4588d7eba2672838e13a84f5ca4e9ce86258 Mon Sep 17 00:00:00 2001 From: JohSchneider Date: Thu, 4 Aug 2022 11:01:03 +0000 Subject: [PATCH 22/24] pico: i2c: align code with chibios, avr, documentation (#217) some defines and function signatures did not quite match the documented ones - which lead to compile issues while building the same code that uses an i2c-driver based vial-qmk/driver for multiple platforms Signed-off-by: Johannes Schneider Co-authored-by: Johannes Schneider --- platforms/pico/drivers/i2c_master.c | 23 +++++++++++++++++------ platforms/pico/drivers/i2c_master.h | 18 ++++++------------ 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/platforms/pico/drivers/i2c_master.c b/platforms/pico/drivers/i2c_master.c index d2559fa01f..dc8f6b7c22 100644 --- a/platforms/pico/drivers/i2c_master.c +++ b/platforms/pico/drivers/i2c_master.c @@ -7,6 +7,17 @@ #include "pico/binary_info.h" #include "hardware/i2c.h" +#ifndef I2C1_SCL_PIN +# define I2C1_SCL_PIN 3 +#endif +#ifndef I2C1_SDA_PIN +# define I2C1_SDA_PIN 2 +#endif + +#ifndef I2C_DRIVER +# define I2C_DRIVER i2c1 +#endif + static uint8_t i2c_address; static i2c_status_t pico_status_to_qmk(int res) { @@ -25,16 +36,16 @@ void i2c_init_pico(void) { 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); + gpio_set_function(I2C1_SCL_PIN, GPIO_FUNC_I2C); + gpio_set_function(I2C1_SDA_PIN, GPIO_FUNC_I2C); + gpio_pull_up(I2C1_SCL_PIN); + gpio_pull_up(I2C1_SDA_PIN); // Make the I2C pins available to picotool - bi_decl(bi_2pins_with_func(I2C1_SDA, I2C1_SCL, GPIO_FUNC_I2C)); + bi_decl(bi_2pins_with_func(I2C1_SDA_PIN, I2C1_SCL_PIN, GPIO_FUNC_I2C)); } } -i2c_status_t i2c_start(uint8_t address) { +i2c_status_t i2c_start(uint8_t address, uint16_t timeout) { i2c_address = address; return I2C_STATUS_SUCCESS; } diff --git a/platforms/pico/drivers/i2c_master.h b/platforms/pico/drivers/i2c_master.h index 86de24918d..5c18609d6b 100644 --- a/platforms/pico/drivers/i2c_master.h +++ b/platforms/pico/drivers/i2c_master.h @@ -26,26 +26,20 @@ #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) +/* collides with: + * pico-sdk/src/rp2_comon/hardware_i2c/include/hardware/i2c.h::i2c_init + * hence the bodge... + */ #define i2c_init() i2c_init_pico() void i2c_init_pico(void); -i2c_status_t i2c_start(uint8_t address); + +i2c_status_t i2c_start(uint8_t address, uint16_t timeout); 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); From 55a7482e076fc04800a3b4912a324756a47c95b5 Mon Sep 17 00:00:00 2001 From: JohSchneider Date: Thu, 4 Aug 2022 16:48:15 +0000 Subject: [PATCH 23/24] pico: via: building default keymaps fails on missing raw_hid_receive (#218) minor build fix: building a default keymap that does not enable VIA(L) leads to linker error, since raw_hid_receive is only available with VIA enabled, implemented in quantum/via.c Signed-off-by: Johannes Schneider Co-authored-by: Johannes --- tmk_core/protocol/pico/pico.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tmk_core/protocol/pico/pico.c b/tmk_core/protocol/pico/pico.c index 52fdae51f7..85be79c5c4 100644 --- a/tmk_core/protocol/pico/pico.c +++ b/tmk_core/protocol/pico/pico.c @@ -272,9 +272,11 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t rep // LED status keyboard_indicator_led = buffer[0]; break; +#ifdef VIA_ENABLE case ITF_NUM_HID_RAW: raw_hid_receive((uint8_t*)buffer, bufsize); break; +#endif default: break; } From b201093456e0e973d82181a33f131a1c9e4cd1f6 Mon Sep 17 00:00:00 2001 From: Aria Edmonds <8436007+ar1a@users.noreply.github.com> Date: Sun, 21 Aug 2022 02:46:31 +0800 Subject: [PATCH 24/24] rp2040 respects USB_POLLING_INTERVAL_MS (#227) --- tmk_core/protocol/pico/usb_descriptors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmk_core/protocol/pico/usb_descriptors.c b/tmk_core/protocol/pico/usb_descriptors.c index c32cb6c63d..8365463b6b 100644 --- a/tmk_core/protocol/pico/usb_descriptors.c +++ b/tmk_core/protocol/pico/usb_descriptors.c @@ -144,7 +144,7 @@ uint8_t const desc_fs_configuration[] = { // 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), + CFG_TUD_HID_EP_BUFSIZE, USB_POLLING_INTERVAL_MS), 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, 1),