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] 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