Compare commits

...
Sign in to create a new pull request.
Author SHA1 Message Date
Aria Edmonds
b201093456
rp2040 respects USB_POLLING_INTERVAL_MS (#227) 2022-08-20 12:46:31 -06:00
JohSchneider
55a7482e07
pico: via: building default keymaps fails on missing raw_hid_receive (#218)
minor build fix: building a default keymap that does not enable VIA(L) leads to linker error, since raw_hid_receive is only available with VIA enabled, implemented in quantum/via.c

Signed-off-by: Johannes Schneider <johschneider@googlemail.com>

Co-authored-by: Johannes <you@example.com>
2022-08-04 10:48:15 -06:00
JohSchneider
973c4588d7
pico: i2c: align code with chibios, avr, documentation (#217)
some defines and function signatures did not quite match the
documented ones - which lead to compile issues while building the same
code that uses an i2c-driver based vial-qmk/driver for multiple
platforms

Signed-off-by: Johannes Schneider <johschneider@googlemail.com>

Co-authored-by: Johannes Schneider <johannes.schneider@leica-geosystems.com>
2022-08-04 05:01:03 -06:00
Ilya Zhuravlev
bd702ab642 pico: initialize eeprom early
otherwise reading qmk settings fails
2022-04-24 16:11:56 -06:00
Ilya Zhuravlev
464ccfbc66 fix uf2 generation 2022-04-23 20:08:57 -06:00
sekigon-gonnoc
b059dfddb5 Indicator LED 2022-04-23 18:56:48 -06:00
sekigon-gonnoc
cbf19a1303 Fix usb descriptor 2022-04-09 22:49:19 -06:00
sekigon-gonnoc
bfa233584e Fix remote wakeup 2022-04-09 22:49:06 -06:00
sekigon-gonnoc
4e6a1f9acc Change rwu send condition 2022-04-09 22:48:42 -06:00
sekigon-gonnoc
2328012e48 remove DMA from ws2812 2022-04-09 22:48:34 -06:00
sekigon-gonnoc
f22ffd6816 Send wakeup signal to host 2022-04-09 22:48:10 -06:00
sekigon-gonnoc
04d147dc5c Replace lsusb by environment variable for flash in QMK MSYS 2022-04-09 22:48:00 -06:00
sekigon-gonnoc
8fd79c3475 Use DMA for ws2812 data transmission
RGB_MATRIX for split keyboard is improved
2022-04-09 22:47:46 -06:00
sekigon-gonnoc
a0fd310399 display copyright 2022-04-09 22:47:27 -06:00
sekigon-gonnoc
f280e180a3 Fix pico/gpio.h 2022-04-09 22:46:56 -06:00
Ilya Zhuravlev
ec3375e040 Fix rp2040 after merge 2022-04-09 22:45:29 -06:00
daskygit
537195a16b Quick bootloader fix, implement type in future. 2022-04-09 22:24:52 -06:00
daskygit
85e002d319 fix conflict mistake 2022-04-09 22:24:52 -06:00
sekigon-gonnoc
d5d74632cc Port eeprom_stm32 to rp2040 2022-04-09 22:24:50 -06:00
sekigon-gonnoc
5ddfbe7cf3 fix makefile 2022-04-09 22:24:07 -06:00
sekigon-gonnoc
746bb85e8a Change RawHID interval to 1 2022-04-09 22:24:07 -06:00
Ilya Zhuravlev
15251c37af Add rp2040 example & fix auto-detect 2022-04-09 22:24:07 -06:00
sekigon-gonnoc
80a5d9b727 Fix bug relating RP2040's usb enumaration errata 2022-04-09 22:24:07 -06:00
sekigon-gonnoc
d2f854f89b 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
2022-04-09 22:24:03 -06:00
98 changed files with 5818 additions and 10 deletions

3
.gitmodules vendored
View file

@ -18,3 +18,6 @@
[submodule "lib/printf"] [submodule "lib/printf"]
path = lib/printf path = lib/printf
url = https://github.com/qmk/printf url = https://github.com/qmk/printf
[submodule "lib/pico-sdk"]
path = lib/pico-sdk
url = https://github.com/raspberrypi/pico-sdk

View file

@ -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/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/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/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 | \ git submodule status --recursive 2>/dev/null | \
while IFS= read -r x; do \ while IFS= read -r x; do \
case "$$x" in \ case "$$x" in \

View file

@ -213,6 +213,10 @@ ifeq ($(strip $(BOOTLOADER)), md-boot)
OPT_DEFS += -DBOOTLOADER_MD_BOOT OPT_DEFS += -DBOOTLOADER_MD_BOOT
BOOTLOADER_TYPE = md_boot BOOTLOADER_TYPE = md_boot
endif endif
ifeq ($(strip $(BOOTLOADER)), RP2040)
OPT_DEFS += -DBOOTLOADER_RP2040
BOOTLOADER_TYPE = RP2040
endif
ifeq ($(strip $(BOOTLOADER_TYPE)),) ifeq ($(strip $(BOOTLOADER_TYPE)),)
$(call CATASTROPHIC_ERROR,Invalid BOOTLOADER,No bootloader specified. Please set an appropriate 'BOOTLOADER' in your keyboard's 'rules.mk' file.) $(call CATASTROPHIC_ERROR,Invalid BOOTLOADER,No bootloader specified. Please set an appropriate 'BOOTLOADER' in your keyboard's 'rules.mk' file.)

View file

@ -177,6 +177,18 @@ ifeq ($(strip $(CONVERT_TO_PROTON_C)), yes)
include platforms/chibios/boards/QMK_PROTON_C/convert_to_proton_c.mk include platforms/chibios/boards/QMK_PROTON_C/convert_to_proton_c.mk
endif 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 include $(BUILDDEFS_PATH)/mcu_selection.mk
# Find all the C source files to be compiled in subfolders. # Find all the C source files to be compiled in subfolders.
@ -261,6 +273,13 @@ ifdef MCU_FAMILY
PLATFORM_KEY=chibios PLATFORM_KEY=chibios
FIRMWARE_FORMAT?=bin FIRMWARE_FORMAT?=bin
OPT_DEFS += -DMCU_$(MCU_FAMILY) OPT_DEFS += -DMCU_$(MCU_FAMILY)
ifeq ($(MCU_FAMILY),PICO)
$(info "PLATFORM RP2040")
PLATFORM=PICO_SDK
PLATFORM_KEY=pico
FIRMWARE_FORMAT=uf2
UF2_FAMILY=RP2040
endif
else ifdef ARM_ATSAM else ifdef ARM_ATSAM
PLATFORM=ARM_ATSAM PLATFORM=ARM_ATSAM
PLATFORM_KEY=arm_atsam PLATFORM_KEY=arm_atsam

View file

@ -211,6 +211,11 @@ else
# arm_atsam EEPROM # arm_atsam EEPROM
OPT_DEFS += -DEEPROM_SAMD OPT_DEFS += -DEEPROM_SAMD
SRC += $(PLATFORM_COMMON_DIR)/eeprom_samd.c SRC += $(PLATFORM_COMMON_DIR)/eeprom_samd.c
SRC += $(PLATFORM_COMMON_DIR)/eeprom.c
else ifeq ($(PLATFORM),PICO_SDK)
OPT_DEFS += -DEEPROM_PICO
SRC += $(PLATFORM_COMMON_DIR)/eeprom.c
SRC += $(PLATFORM_COMMON_DIR)/flash_pico.c
else ifeq ($(PLATFORM),TEST) else ifeq ($(PLATFORM),TEST)
# Test harness "EEPROM" # Test harness "EEPROM"
OPT_DEFS += -DEEPROM_TEST_HARNESS OPT_DEFS += -DEEPROM_TEST_HARNESS

View file

@ -53,7 +53,7 @@ OPT ?= s
# gnu89 = c89 plus GCC extensions # gnu89 = c89 plus GCC extensions
# c99 = ISO C99 standard (not yet fully implemented) # c99 = ISO C99 standard (not yet fully implemented)
# gnu99 = c99 plus GCC extensions # gnu99 = c99 plus GCC extensions
CSTANDARD = -std=gnu99 CSTANDARD = -std=gnu11
# Place -D or -U options here for C sources # 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 Enabling LTO on ChibiOS-targeting boards is known to have a high likelihood of failure.)
$(info If unsure, set LTO_ENABLE = no.) $(info If unsure, set LTO_ENABLE = no.)
endif endif
CDEFS += -flto ifeq ($(PLATFORM),PICO_SDK)
CDEFS += -DLTO_ENABLE $(info LTO is not available for this platform. Disabled automatically.)
else
CDEFS += -flto
CDEFS += -DLTO_ENABLE
endif
endif endif
DEBUG_ENABLE ?= yes DEBUG_ENABLE ?= yes

View file

@ -89,6 +89,10 @@
"type": "string", "type": "string",
"pattern": "^LINE_PIN\\d{1,2}$" "pattern": "^LINE_PIN\\d{1,2}$"
}, },
{
"type": "string",
"pattern": "^\\d{1,2}$"
},
{ {
"type": "number", "type": "number",
"multipleOf": 1 "multipleOf": 1

View file

@ -57,7 +57,7 @@
}, },
"bootloader": { "bootloader": {
"type": "string", "type": "string",
"enum": ["atmel-dfu", "bootloadhid", "bootloadHID", "custom", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "md-boot", "qmk-dfu", "qmk-hid", "stm32-dfu", "stm32duino", "gd32v-dfu", "wb32-dfu", "unknown", "usbasploader", "USBasp", "tinyuf2", "vibl"], "enum": ["atmel-dfu", "bootloadhid", "bootloadHID", "custom", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "md-boot", "qmk-dfu", "qmk-hid", "stm32-dfu", "stm32duino", "gd32v-dfu", "wb32-dfu", "unknown", "usbasploader", "USBasp", "tinyuf2", "vibl", "RP2040"],
}, },
"bootloader_instructions": { "bootloader_instructions": {
"type": "string", "type": "string",

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View file

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

View file

@ -0,0 +1,73 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}
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);
}
}

View file

@ -0,0 +1 @@
# The default keymap for rp2040_example

View file

@ -0,0 +1,2 @@
SRC += analog.c

View file

@ -0,0 +1,62 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_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;
}
return true;
}

View file

@ -0,0 +1 @@
# The default keymap for rp2040_example

View file

@ -0,0 +1,6 @@
#pragma once
#define RGB_DI_PIN 5
#define RGBLED_NUM 1
#define RGBLIGHT_ANIMATIONS

View file

@ -0,0 +1,71 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}
return true;
}
#include "rgblight.h"
void keyboard_post_init_user(void) {
rgblight_enable();
rgblight_mode_noeeprom(35);
}

View file

@ -0,0 +1 @@
# The default keymap for rp2040_example

View file

@ -0,0 +1,2 @@
RGBLIGHT_ENABLE = yes

View file

@ -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 <your_keyboard>:<your_keyamp>:<output option> 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).

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "rp2040_example.h"
void keyboard_post_init_kb(void) {
debug_enable = true;
debug_keyboard = true;
keyboard_post_init_user();
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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 } \
}

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View file

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

View file

@ -0,0 +1,67 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_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;
}
return true;
}

View file

@ -0,0 +1 @@
# The default keymap for rp2040_split

View file

@ -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 <your_keyboard>:<your_keyamp>:<output option> 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).

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "rp2040_split.h"
void keyboard_post_init_kb(void) {
debug_enable = true;
debug_matrix = true;
keyboard_post_init_user();
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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 }, \
}

View file

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

View file

@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/* USB Device descriptor parameter */
#define VENDOR_ID 0xFEED
#define PRODUCT_ID 0x0000
#define DEVICE_VER 0x0001
#define MANUFACTURER Vial
#define PRODUCT RP2040 example
/* key matrix size */
#define MATRIX_ROWS 2
#define MATRIX_COLS 2
#define MATRIX_ROW_PINS { 0, 1 }
#define MATRIX_COL_PINS { 2, 3 }
#define DIODE_DIRECTION COL2ROW
/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */
#define LOCKING_SUPPORT_ENABLE
/* Locking resynchronize hack */
#define LOCKING_RESYNC_ENABLE
/* Use 1000hz polling */
#define USB_POLLING_INTERVAL_MS 1

View file

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include QMK_KEYBOARD_H
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT(
KC_1, KC_2,
KC_3, KC_4
),
[1] = LAYOUT(
KC_A, KC_B,
KC_C, KC_D
)
};

View file

@ -0,0 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#define VIAL_KEYBOARD_UID {0x03, 0x35, 0x69, 0xE1, 0x5C, 0x42, 0xD9, 0x5E}
#define VIAL_UNLOCK_COMBO_ROWS {0, 1}
#define VIAL_UNLOCK_COMBO_COLS {0, 1}

View file

@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include QMK_KEYBOARD_H
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT(
KC_1, KC_2,
KC_3, KC_4
),
[1] = LAYOUT(
KC_A, KC_B,
KC_C, KC_D
),
[2] = LAYOUT(
KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS
),
[3] = LAYOUT(
KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS
)
};

View file

@ -0,0 +1,2 @@
VIA_ENABLE = yes
VIAL_ENABLE = yes

View file

@ -0,0 +1,19 @@
{
"name": "Vial RP2040 Example",
"matrix": {
"rows": 2,
"cols": 2
},
"layouts": {
"keymap": [
[
"0,0",
"0,1"
],
[
"1,0",
"1,1"
]
]
}
}

View file

@ -0,0 +1,22 @@
# MCU name
MCU_FAMILY = PICO
MCU_SERIES = RP2040
BOOTLOADER = RP2040
MCU = cortex-m0plus
# Build Options
# change yes to no to disable
#
BOOTMAGIC_ENABLE = yes
MOUSEKEY_ENABLE = yes # Mouse keys
EXTRAKEY_ENABLE = yes # Audio control and System control
CONSOLE_ENABLE = no # Console for debug
COMMAND_ENABLE = no # Commands for debug and configuration
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
NKRO_ENABLE = no # USB Nkey Rollover
BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow
BLUETOOTH_ENABLE = no # Enable Bluetooth
AUDIO_ENABLE = no # Audio output

View file

@ -0,0 +1,3 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "vial_rp2040.h"

View file

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "quantum.h"
#define LAYOUT( \
K00, K01, \
K10, K11 \
) { \
{ K00, K01 }, \
{ K10, K11 } \
}

1
lib/pico-sdk Submodule

@ -0,0 +1 @@
Subproject commit 2062372d203b372849d573f252cf7c6dc2800c0a

View file

@ -48,6 +48,9 @@ void eeprom_update_block(const void *__src, void *__dst, size_t __n);
#elif defined(EEPROM_SAMD) #elif defined(EEPROM_SAMD)
# include "eeprom_samd.h" # include "eeprom_samd.h"
# define TOTAL_EEPROM_BYTE_COUNT (EEPROM_SIZE) # define TOTAL_EEPROM_BYTE_COUNT (EEPROM_SIZE)
#elif defined(EEPROM_PICO)
# include "eeprom_pico_defs.h"
# define TOTAL_EEPROM_BYTE_COUNT (FEE_DENSITY_BYTES)
#elif defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATtiny85__) #elif defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATtiny85__)
# define TOTAL_EEPROM_BYTE_COUNT 512 # define TOTAL_EEPROM_BYTE_COUNT 512
#elif defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega32A__) #elif defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega32A__)

36
platforms/pico/_print.h Normal file
View file

@ -0,0 +1,36 @@
/* Copyright 2012 Jun Wako <wakojun@gmail.com> */
/* 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 <stdio.h>
#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__)

89
platforms/pico/_wait.c Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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;
}
}

24
platforms/pico/_wait.h Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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"

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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)

View file

@ -0,0 +1,7 @@
#pragma once
// #if defined(PICO_BOARD_PROMICRO)
#include "pico_pro_micro.h"
// #endif

View file

@ -0,0 +1,36 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#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

View file

@ -0,0 +1,28 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View file

@ -0,0 +1,14 @@
#include "bootloader.h"
#include "pico/stdlib.h"
#include "pico/bootrom.h"
#define FLASH_SIZE (FLASHEND + 1L)
#if !defined(BOOTLOADER_SIZE)
uint16_t bootloader_start;
#endif
__attribute__((weak)) void bootloader_jump(void) {
reset_usb_boot(0, 0);
}

View file

@ -0,0 +1,7 @@
TARGET := $(TARGET)_pico_micro
MCU_FAMILY := PICO
MCU_SERIES := RP2040
MCU := cortex-m0plus
OPT_DEFS += -DCONVERT_TO_PICO_MICRO

View file

@ -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();
}

View file

@ -0,0 +1,19 @@
#pragma once
#include <stdint.h>
#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);

View file

@ -0,0 +1,111 @@
#include "quantum.h"
#include "i2c_master.h"
#undef i2c_init
#include <string.h>
#include "pico/binary_info.h"
#include "hardware/i2c.h"
#ifndef I2C1_SCL_PIN
# define I2C1_SCL_PIN 3
#endif
#ifndef I2C1_SDA_PIN
# define I2C1_SDA_PIN 2
#endif
#ifndef I2C_DRIVER
# define I2C_DRIVER i2c1
#endif
static uint8_t i2c_address;
static i2c_status_t pico_status_to_qmk(int res) {
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_PIN, GPIO_FUNC_I2C);
gpio_set_function(I2C1_SDA_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C1_SCL_PIN);
gpio_pull_up(I2C1_SDA_PIN);
// Make the I2C pins available to picotool
bi_decl(bi_2pins_with_func(I2C1_SDA_PIN, I2C1_SCL_PIN, GPIO_FUNC_I2C));
}
}
i2c_status_t i2c_start(uint8_t address, uint16_t timeout) {
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), &regaddr, 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;
}

View file

@ -0,0 +1,47 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
/* 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 <stdint.h>
typedef int16_t i2c_status_t;
#define I2C_STATUS_SUCCESS (0)
#define I2C_STATUS_ERROR (-1)
#define I2C_STATUS_TIMEOUT (-2)
/* collides with:
* pico-sdk/src/rp2_comon/hardware_i2c/include/hardware/i2c.h::i2c_init
* hence the bodge...
*/
#define i2c_init() i2c_init_pico()
void i2c_init_pico(void);
i2c_status_t i2c_start(uint8_t address, uint16_t timeout);
i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
void i2c_stop(void);

View file

@ -0,0 +1,431 @@
/*
* 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 <stdio.h>
#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
#if SOFT_SERIAL_PIN == 15
# error GPIO15 cannot be used as SOFT_SERIAL_PIN
#endif
inline static void serial_delay(void) { wait_us(SERIAL_DELAY); }
inline static void serial_delay_half(void) { wait_us(SERIAL_DELAY / 2); }
inline static void serial_delay_blip(void) { wait_us(1); }
inline static void serial_output(void) {
gpio_set_function(SOFT_SERIAL_PIN, GPIO_FUNC_SIO);
setPinOutput(SOFT_SERIAL_PIN);
gpio_set_slew_rate(SOFT_SERIAL_PIN, GPIO_SLEW_RATE_FAST);
gpio_set_drive_strength(SOFT_SERIAL_PIN, GPIO_DRIVE_STRENGTH_12MA);
}
inline static void serial_input(void) {
gpio_set_function(SOFT_SERIAL_PIN, GPIO_FUNC_SIO);
setPinInputHigh(SOFT_SERIAL_PIN);
}
inline static bool serial_read_pin(void) { return readPin(SOFT_SERIAL_PIN); }
inline static void serial_low(void) { writePinLow(SOFT_SERIAL_PIN); }
inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); }
static 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) {
serial_input();
// 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);
serial_output();
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();
serial_input();
gpio_set_irq_enabled_with_callback(SOFT_SERIAL_PIN, GPIO_IRQ_EDGE_FALL,
true, interrupt_handler);
irq_set_priority(IO_IRQ_BANK0, PICO_HIGHEST_IRQ_PRIORITY);
}
// Used by the master to synchronize timing with the slave.
static int __no_inline_not_in_flash_func(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 __no_inline_not_in_flash_func(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
static void __no_inline_not_in_flash_func(interrupt_handler)(uint gpio, uint32_t events) {
if (gpio != SOFT_SERIAL_PIN || events != GPIO_IRQ_EDGE_FALL) {
return;
}
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 __no_inline_not_in_flash_func(soft_serial_transaction)(int sstd_index) {
if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR;
split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered
// 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;
}

View file

@ -0,0 +1,237 @@
#include "quantum.h"
#include "serial.h"
#include "print.h"
#include <ch.h>
#include <hal.h>
#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;
}

View file

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#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);

View file

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

View file

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

View file

@ -0,0 +1,56 @@
// Copyright 2022 sekigon-gonnoc
// SPDX-License-Identifier: GPL-2.0-or-later
#include <stdio.h>
#include "ws2812.h"
#include "ws2812.pio.h"
#include "atomic_util.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;
}
}
__interrupt_disable__();
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));
}
__interrupt_enable__(NULL);
}

View file

@ -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<pin_base+pin_count; i++) {
pio_gpio_init(pio, i);
}
pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count, true);
pio_sm_config c = ws2812_parallel_program_get_default_config(offset);
sm_config_set_out_shift(&c, true, true, 32);
sm_config_set_out_pins(&c, pin_base, pin_count);
sm_config_set_set_pins(&c, pin_base, pin_count);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
int cycles_per_bit = ws2812_parallel_T1 + ws2812_parallel_T2 + ws2812_parallel_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

748
platforms/pico/eeprom.c Normal file
View file

@ -0,0 +1,748 @@
/*
* This software is experimental and a work in progress.
* Under no circumstances should these files be used in relation to any critical system(s).
* Use of these files is at your own risk.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by
* Artur F.
*
* Modifications for QMK and STM32F303 by Yiancar
* Modifications to add flash wear leveling by Ilya Zhuravlev
* Modifications to increase flash density by Don Kjer
* Modifications for QMK and RP2040 by sekigon-gonnoc
*/
#include <stdio.h>
#include <stdbool.h>
#include "util.h"
#include "debug.h"
#include "platforms/pico/flash_pico.h"
#include "hardware/watchdog.h"
/*
* We emulate eeprom by writing a snapshot compacted view of eeprom contents,
* followed by a write log of any change since that snapshot:
*
* === SIMULATED EEPROM CONTENTS ===
*
* ┌─ Compacted ┬ Write Log ─┐
* │............│[BYTE][BYTE]│
* │FFFF....FFFF│[WRD0][WRD1]│
* │FFFFFFFFFFFF│[WORD][NEXT]│
* │....FFFFFFFF│[BYTE][WRD0]│
* ├────────────┼────────────┤
* └──PAGE_BASE │ │
* PAGE_LAST─┴─WRITE_BASE │
* WRITE_LAST ┘
*
* Compacted contents are the 1's complement of the actual EEPROM contents.
* e.g. An 'FFFF' represents a '0000' value.
*
* The size of the 'compacted' area is equal to the size of the 'emulated' eeprom.
* The size of the compacted-area and write log are configurable, and the combined
* size of Compacted + WriteLog is a multiple FEE_PAGE_SIZE, which is MCU dependent.
* Simulated Eeprom contents are located at the end of available flash space.
*
* The following configuration defines can be set:
*
* FEE_PAGE_COUNT # Total number of pages to use for eeprom simulation (Compact + Write log)
* FEE_DENSITY_BYTES # Size of simulated eeprom. (Defaults to half the space allocated by FEE_PAGE_COUNT)
* NOTE: The current implementation does not include page swapping,
* and FEE_DENSITY_BYTES will consume that amount of RAM as a cached view of actual EEPROM contents.
*
* The maximum size of FEE_DENSITY_BYTES is currently 16384. The write log size equals
* FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES.
* The larger the write log, the less frequently the compacted area needs to be rewritten.
*
*
* *** General Algorithm ***
*
* During initialization:
* The contents of the Compacted-flash area are loaded and the 1's complement value
* is cached into memory (e.g. 0xFFFF in Flash represents 0x0000 in cache).
* Write log entries are processed until a 0xFFFF is reached.
* Each log entry updates a byte or word in the cache.
*
* During reads:
* EEPROM contents are given back directly from the cache in memory.
*
* During writes:
* The contents of the cache is updated first.
* If the Compacted-flash area corresponding to the write address is unprogrammed, the 1's complement of the value is written directly into Compacted-flash
* Otherwise:
* If the write log is full, erase both the Compacted-flash area and the Write log, then write cached contents to the Compacted-flash area.
* Otherwise a Write log entry is constructed and appended to the next free position in the Write log.
*
*
* *** Write Log Structure ***
*
* Write log entries allow for optimized byte writes to addresses below 128. Writing 0 or 1 words are also optimized when word-aligned.
*
* === WRITE LOG ENTRY FORMATS ===
*
* ╔═══ Byte-Entry ══╗
* ║0XXXXXXX║YYYYYYYY║
* ║ └──┬──┘║└──┬───┘║
* ║ Address║ Value ║
* ╚════════╩════════╝
* 0 <= Address < 0x80 (128)
*
* ╔ Word-Encoded 0 ╗
* ║100XXXXXXXXXXXXX║
* ║ │└─────┬─────┘║
* ║ │Address >> 1 ║
* ║ └── Value: 0 ║
* ╚════════════════╝
* 0 <= Address <= 0x3FFE (16382)
*
* ╔ Word-Encoded 1 ╗
* ║101XXXXXXXXXXXXX║
* ║ │└─────┬─────┘║
* ║ │Address >> 1 ║
* ║ └── Value: 1 ║
* ╚════════════════╝
* 0 <= Address <= 0x3FFE (16382)
*
* ╔═══ Reserved ═══╗
* ║110XXXXXXXXXXXXX║
* ╚════════════════╝
*
* ╔═══════════ Word-Next ═══════════╗
* ║111XXXXXXXXXXXXX║YYYYYYYYYYYYYYYY║
* ║ └─────┬─────┘║└───────┬──────┘║
* ║(Address-128)>>1║ ~Value ║
* ╚════════════════╩════════════════╝
* ( 0 <= Address < 0x0080 (128): Reserved)
* 0x80 <= Address <= 0x3FFE (16382)
*
* Write Log entry ranges:
* 0x0000 ... 0x7FFF - Byte-Entry; address is (Entry & 0x7F00) >> 4; value is (Entry & 0xFF)
* 0x8000 ... 0x9FFF - Word-Encoded 0; address is (Entry & 0x1FFF) << 1; value is 0
* 0xA000 ... 0xBFFF - Word-Encoded 1; address is (Entry & 0x1FFF) << 1; value is 1
* 0xC000 ... 0xDFFF - Reserved
* 0xE000 ... 0xFFBF - Word-Next; address is (Entry & 0x1FFF) << 1 + 0x80; value is ~(Next_Entry)
* 0xFFC0 ... 0xFFFE - Reserved
* 0xFFFF - Unprogrammed
*
*/
#include "eeprom_pico_defs.h"
#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT) || !defined(FEE_MCU_FLASH_SIZE) || !defined(FEE_PAGE_BASE_ADDRESS)
# error "not implemented."
#endif
/* These bits are used for optimizing encoding of bytes, 0 and 1 */
#define FEE_WORD_ENCODING 0x8000
#define FEE_VALUE_NEXT 0x6000
#define FEE_VALUE_RESERVED 0x4000
#define FEE_VALUE_ENCODED 0x2000
#define FEE_BYTE_RANGE 0x80
/* Addressable range 16KByte: 0 <-> (0x1FFF << 1) */
#define FEE_ADDRESS_MAX_SIZE 0x4000
/* Flash word value after erase */
#define FEE_EMPTY_WORD ((uint16_t)0xFFFF)
/* Magic number indicating the page is used for FEE */
#define FEE_MAGIC_DWORD ((uint32_t)0x20400FEE)
/* Size of combined compacted eeprom and write log pages */
#define FEE_DENSITY_MAX_SIZE (FEE_PAGE_COUNT * FEE_PAGE_SIZE)
#ifndef FEE_MCU_FLASH_SIZE_IGNORE_CHECK /* *TODO: Get rid of this check */
# if FEE_DENSITY_MAX_SIZE > (FEE_MCU_FLASH_SIZE * 1024)
# pragma message STR(FEE_DENSITY_MAX_SIZE) " > " STR(FEE_MCU_FLASH_SIZE * 1024)
# error emulated eeprom: FEE_DENSITY_MAX_SIZE is greater than available flash size
# endif
#endif
/* Size of emulated eeprom */
#ifdef FEE_DENSITY_BYTES
# if (FEE_DENSITY_BYTES > FEE_DENSITY_MAX_SIZE)
# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE)
# error emulated eeprom: FEE_DENSITY_BYTES exceeds FEE_DENSITY_MAX_SIZE
# endif
# if (FEE_DENSITY_BYTES == FEE_DENSITY_MAX_SIZE)
# pragma message STR(FEE_DENSITY_BYTES) " == " STR(FEE_DENSITY_MAX_SIZE)
# warning emulated eeprom: FEE_DENSITY_BYTES leaves no room for a write log. This will greatly increase the flash wear rate!
# endif
# if FEE_DENSITY_BYTES > FEE_ADDRESS_MAX_SIZE
# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_ADDRESS_MAX_SIZE)
# error emulated eeprom: FEE_DENSITY_BYTES is greater than FEE_ADDRESS_MAX_SIZE allows
# endif
# if ((FEE_DENSITY_BYTES) % 2) == 1
# error emulated eeprom: FEE_DENSITY_BYTES must be even
# endif
#else
/* Default to half of allocated space used for emulated eeprom, half for write log */
# define FEE_DENSITY_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE / 2)
#endif
/* Size of write log */
#ifdef FEE_WRITE_LOG_BYTES
# if ((FEE_DENSITY_BYTES + FEE_WRITE_LOG_BYTES) > FEE_DENSITY_MAX_SIZE)
# pragma message STR(FEE_DENSITY_BYTES) " + " STR(FEE_WRITE_LOG_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE)
# error emulated eeprom: FEE_WRITE_LOG_BYTES exceeds remaining FEE_DENSITY_MAX_SIZE
# endif
# if ((FEE_WRITE_LOG_BYTES) % 2) == 1
# error emulated eeprom: FEE_WRITE_LOG_BYTES must be even
# endif
#else
/* Default to use all remaining space */
# define FEE_WRITE_LOG_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES)
#endif
/* Start of the emulated eeprom compacted flash area */
#define FEE_COMPACTED_BASE_ADDRESS FEE_PAGE_BASE_ADDRESS
/* End of the emulated eeprom compacted flash area */
#define FEE_COMPACTED_LAST_ADDRESS (FEE_COMPACTED_BASE_ADDRESS + FEE_DENSITY_BYTES)
/* Start of the emulated eeprom write log */
#define FEE_WRITE_LOG_BASE_ADDRESS FEE_COMPACTED_LAST_ADDRESS
/* End of the emulated eeprom write log */
#define FEE_WRITE_LOG_LAST_ADDRESS (FEE_WRITE_LOG_BASE_ADDRESS + FEE_WRITE_LOG_BYTES)
#if defined(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) && (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR >= FEE_DENSITY_BYTES)
# error emulated eeprom: DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is greater than the FEE_DENSITY_BYTES available
#endif
/* In-memory contents of emulated eeprom for faster access */
/* *TODO: Implement page swapping */
static uint16_t WordBuf[FEE_DENSITY_BYTES / 2];
static uint8_t *DataBuf = (uint8_t *)WordBuf;
/* Pointer to the first available slot within the write log */
static uint16_t *empty_slot;
static void eeprom_clear(void);
// #define DEBUG_EEPROM_OUTPUT
/*
* Debug print utils
*/
#if defined(DEBUG_EEPROM_OUTPUT)
# define debug_eeprom debug_enable
# define eeprom_println(s) dprintln(s)
# define eeprom_printf(fmt, ...) dprintf(fmt, ##__VA_ARGS__);
#else /* NO_DEBUG */
# define debug_eeprom false
# define eeprom_println(s)
# define eeprom_printf(fmt, ...)
#endif /* NO_DEBUG */
void print_eeprom(void) {
#ifndef NO_DEBUG
int empty_rows = 0;
for (uint16_t i = 0; i < FEE_DENSITY_BYTES; i++) {
if (i % 16 == 0) {
if (i >= FEE_DENSITY_BYTES - 16) {
/* Make sure we display the last row */
empty_rows = 0;
}
/* Check if this row is uninitialized */
++empty_rows;
for (uint16_t j = 0; j < 16; j++) {
if (DataBuf[i + j]) {
empty_rows = 0;
break;
}
}
if (empty_rows > 1) {
/* Repeat empty row */
if (empty_rows == 2) {
/* Only display the first repeat empty row */
println("*");
}
i += 15;
continue;
}
xprintf("%04x", i);
}
if (i % 8 == 0) print(" ");
xprintf(" %02x", DataBuf[i]);
if ((i + 1) % 16 == 0) {
println("");
}
watchdog_update();
}
#endif
}
uint16_t EEPROM_Init(void) {
/* Load emulated eeprom contents from compacted flash into memory */
uint16_t *src = (uint16_t *)FEE_COMPACTED_BASE_ADDRESS;
uint16_t *dest = (uint16_t *)DataBuf;
for (; src < (uint16_t *)FEE_COMPACTED_LAST_ADDRESS; ++src, ++dest) {
*dest = ~*(uint16_t*)((void*)src + XIP_BASE);
}
if (debug_eeprom) {
println("EEPROM_Init Compacted Pages:");
print_eeprom();
println("EEPROM_Init Write Log:");
}
if (*(uint32_t*)(FEE_WRITE_LOG_BASE_ADDRESS + XIP_BASE) != FEE_MAGIC_DWORD) {
eeprom_clear();
}
/* Replay write log */
uint16_t *log_addr;
for (log_addr = (uint16_t *)(FEE_WRITE_LOG_BASE_ADDRESS + 4); log_addr < (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS; ++log_addr) {
watchdog_update();
uint16_t address = *(uint16_t*)((void*)log_addr + XIP_BASE);
if (address == FEE_EMPTY_WORD) {
break;
}
/* Check for lowest 128-bytes optimization */
if (!(address & FEE_WORD_ENCODING)) {
uint8_t bvalue = (uint8_t)address;
address >>= 8;
DataBuf[address] = bvalue;
eeprom_printf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue);
} else {
uint16_t wvalue;
/* Check if value is in next word */
if ((address & FEE_VALUE_NEXT) == FEE_VALUE_NEXT) {
/* Read value from next word */
if (++log_addr >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
break;
}
wvalue = ~*(uint16_t *)((void *)log_addr + XIP_BASE);
if (!wvalue) {
eeprom_printf("Incomplete write at log_addr: 0x%04x;\n", (uint32_t)log_addr);
/* Possibly incomplete write. Ignore and continue */
continue;
}
address &= 0x1FFF;
address <<= 1;
/* Writes to addresses less than 128 are byte log entries */
address += FEE_BYTE_RANGE;
} else {
/* Reserved for future use */
if (address & FEE_VALUE_RESERVED) {
eeprom_printf("Reserved encoded value at log_addr: 0x%04x;\n", (uint32_t)log_addr);
continue;
}
/* Optimization for 0 or 1 values. */
wvalue = (address & FEE_VALUE_ENCODED) >> 13;
address &= 0x1FFF;
address <<= 1;
}
if (address < FEE_DENSITY_BYTES) {
eeprom_printf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue);
*(uint16_t *)(&DataBuf[address]) = wvalue;
} else {
eeprom_printf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue);
}
}
}
empty_slot = log_addr;
if (debug_eeprom) {
println("EEPROM_Init Final DataBuf:");
print_eeprom();
eeprom_printf("Write Log Usage: %d/%d bytes\n ",
(uint32_t)empty_slot - FEE_WRITE_LOG_BASE_ADDRESS,
FEE_WRITE_LOG_BYTES);
}
return FEE_DENSITY_BYTES;
}
/* Clear flash contents (doesn't touch in-memory DataBuf) */
static void eeprom_clear(void) {
FLASH_Unlock();
for (uint16_t page_num = 0; page_num < FEE_PAGE_COUNT; ++page_num) {
eeprom_printf("FLASH_ErasePage(0x%04x)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)));
FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE));
}
FLASH_ProgramHalfWord(FEE_WRITE_LOG_BASE_ADDRESS, FEE_MAGIC_DWORD & 0xffff);
FLASH_ProgramHalfWord(FEE_WRITE_LOG_BASE_ADDRESS + 2,
FEE_MAGIC_DWORD >> 16);
FLASH_Lock();
empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS;
eeprom_printf("eeprom_clear empty_slot: 0x%08x\n", (uint32_t)empty_slot);
}
/* Erase emulated eeprom */
void EEPROM_Erase(void) {
eeprom_println("EEPROM_Erase");
/* Erase compacted pages and write log */
eeprom_clear();
/* re-initialize to reset DataBuf */
EEPROM_Init();
}
/* Compact write log */
static uint8_t eeprom_compact(void) {
/* Erase compacted pages and write log */
eeprom_clear();
FLASH_Unlock();
FLASH_Status final_status = FLASH_COMPLETE;
/* Write emulated eeprom contents from memory to compacted flash */
uint16_t *src = (uint16_t *)DataBuf;
uintptr_t dest = FEE_COMPACTED_BASE_ADDRESS;
uint16_t value;
for (; dest < FEE_COMPACTED_LAST_ADDRESS; ++src, dest += 2) {
value = *src;
if (value) {
eeprom_printf("FLASH_ProgramHalfWord(0x%04x, 0x%04x)\n", (uint32_t)dest, ~value);
FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value);
if (status != FLASH_COMPLETE) final_status = status;
}
}
FLASH_Lock();
if (debug_eeprom) {
println("eeprom_compacted:");
print_eeprom();
}
return final_status;
}
static uint8_t eeprom_write_direct_entry(uint16_t Address) {
/* Check if we can just write this directly to the compacted flash area */
uintptr_t directAddress = FEE_COMPACTED_BASE_ADDRESS + (Address & 0xFFFE);
if (*(uint16_t *)((void *)directAddress + XIP_BASE) == FEE_EMPTY_WORD) {
/* Write the value directly to the compacted area without a log entry */
uint16_t value = ~*(uint16_t *)(&DataBuf[Address & 0xFFFE]);
/* Early exit if a write isn't needed */
if (value == FEE_EMPTY_WORD) return FLASH_COMPLETE;
FLASH_Unlock();
eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value);
FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value);
FLASH_Lock();
return status;
}
return 0;
}
static uint8_t eeprom_write_log_word_entry(uint16_t Address) {
FLASH_Status final_status = FLASH_COMPLETE;
uint16_t value = *(uint16_t *)(&DataBuf[Address]);
eeprom_printf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value);
/* MSB signifies the lowest 128-byte optimization is not in effect */
uint16_t encoding = FEE_WORD_ENCODING;
uint8_t entry_size;
if (value <= 1) {
encoding |= value << 13;
entry_size = 2;
} else {
encoding |= FEE_VALUE_NEXT;
entry_size = 4;
/* Writes to addresses less than 128 are byte log entries */
Address -= FEE_BYTE_RANGE;
}
/* if we can't find an empty spot, we must compact emulated eeprom */
if (empty_slot > (uint16_t *)(FEE_WRITE_LOG_LAST_ADDRESS - entry_size)) {
/* compact the write log into the compacted flash area */
return eeprom_compact();
}
/* Word log writes should be word-aligned. Take back a bit */
Address >>= 1;
Address |= encoding;
/* ok we found a place let's write our data */
FLASH_Unlock();
/* address */
eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, Address);
final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address);
/* value */
if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) {
eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, ~value);
FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value);
if (status != FLASH_COMPLETE) final_status = status;
}
FLASH_Lock();
return final_status;
}
static uint8_t eeprom_write_log_byte_entry(uint16_t Address) {
eeprom_printf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]);
/* if couldn't find an empty spot, we must compact emulated eeprom */
if (empty_slot >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
/* compact the write log into the compacted flash area */
return eeprom_compact();
}
/* ok we found a place let's write our data */
FLASH_Unlock();
/* Pack address and value into the same word */
uint16_t value = (Address << 8) | DataBuf[Address];
/* write to flash */
eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, value);
FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, value);
FLASH_Lock();
return status;
}
uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {
/* if the address is out-of-bounds, do nothing */
if (Address >= FEE_DENSITY_BYTES) {
eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte);
return FLASH_BAD_ADDRESS;
}
/* if the value is the same, don't bother writing it */
if (DataBuf[Address] == DataByte) {
eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte);
return 0;
}
/* keep DataBuf cache in sync */
DataBuf[Address] = DataByte;
eeprom_printf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]);
/* perform the write into flash memory */
/* First, attempt to write directly into the compacted flash area */
FLASH_Status status = eeprom_write_direct_entry(Address);
if (!status) {
/* Otherwise append to the write log */
if (Address < FEE_BYTE_RANGE) {
status = eeprom_write_log_byte_entry(Address);
} else {
status = eeprom_write_log_word_entry(Address & 0xFFFE);
}
}
if (status != 0 && status != FLASH_COMPLETE) {
eeprom_printf("EEPROM_WriteDataByte [STATUS == %d]\n", status);
}
return status;
}
uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) {
/* if the address is out-of-bounds, do nothing */
if (Address >= FEE_DENSITY_BYTES) {
eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord);
return FLASH_BAD_ADDRESS;
}
/* Check for word alignment */
FLASH_Status final_status = FLASH_COMPLETE;
if (Address % 2) {
final_status = EEPROM_WriteDataByte(Address, DataWord);
FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8);
if (status != FLASH_COMPLETE) final_status = status;
if (final_status != 0 && final_status != FLASH_COMPLETE) {
eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
}
return final_status;
}
/* if the value is the same, don't bother writing it */
uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]);
if (oldValue == DataWord) {
eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord);
return 0;
}
/* keep DataBuf cache in sync */
*(uint16_t *)(&DataBuf[Address]) = DataWord;
eeprom_printf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address]));
/* perform the write into flash memory */
/* First, attempt to write directly into the compacted flash area */
final_status = eeprom_write_direct_entry(Address);
if (!final_status) {
/* Otherwise append to the write log */
/* Check if we need to fall back to byte write */
if (Address < FEE_BYTE_RANGE) {
final_status = FLASH_COMPLETE;
/* Only write a byte if it has changed */
if ((uint8_t)oldValue != (uint8_t)DataWord) {
final_status = eeprom_write_log_byte_entry(Address);
}
FLASH_Status status = FLASH_COMPLETE;
/* Only write a byte if it has changed */
if ((oldValue >> 8) != (DataWord >> 8)) {
status = eeprom_write_log_byte_entry(Address + 1);
}
if (status != FLASH_COMPLETE) final_status = status;
} else {
final_status = eeprom_write_log_word_entry(Address);
}
}
if (final_status != 0 && final_status != FLASH_COMPLETE) {
eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
}
return final_status;
}
uint8_t EEPROM_ReadDataByte(uint16_t Address) {
uint8_t DataByte = 0xFF;
if (Address < FEE_DENSITY_BYTES) {
DataByte = DataBuf[Address];
}
eeprom_printf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte);
return DataByte;
}
uint16_t EEPROM_ReadDataWord(uint16_t Address) {
uint16_t DataWord = 0xFFFF;
if (Address < FEE_DENSITY_BYTES - 1) {
/* Check word alignment */
if (Address % 2) {
DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8);
} else {
DataWord = *(uint16_t *)(&DataBuf[Address]);
}
}
eeprom_printf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord);
return DataWord;
}
/*****************************************************************************
* Wrap library in AVR style functions.
*******************************************************************************/
uint8_t eeprom_read_byte(const uint8_t *Address) { return EEPROM_ReadDataByte((const uintptr_t)Address); }
void eeprom_write_byte(uint8_t *Address, uint8_t Value) { EEPROM_WriteDataByte((uintptr_t)Address, Value); }
void eeprom_update_byte(uint8_t *Address, uint8_t Value) { EEPROM_WriteDataByte((uintptr_t)Address, Value); }
uint16_t eeprom_read_word(const uint16_t *Address) { return EEPROM_ReadDataWord((const uintptr_t)Address); }
void eeprom_write_word(uint16_t *Address, uint16_t Value) { EEPROM_WriteDataWord((uintptr_t)Address, Value); }
void eeprom_update_word(uint16_t *Address, uint16_t Value) { EEPROM_WriteDataWord((uintptr_t)Address, Value); }
uint32_t eeprom_read_dword(const uint32_t *Address) {
const uint16_t p = (const uintptr_t)Address;
/* Check word alignment */
if (p % 2) {
/* Not aligned */
return (uint32_t)EEPROM_ReadDataByte(p) | (uint32_t)(EEPROM_ReadDataWord(p + 1) << 8) | (uint32_t)(EEPROM_ReadDataByte(p + 3) << 24);
} else {
/* Aligned */
return EEPROM_ReadDataWord(p) | (EEPROM_ReadDataWord(p + 2) << 16);
}
}
void eeprom_write_dword(uint32_t *Address, uint32_t Value) {
uint16_t p = (const uintptr_t)Address;
/* Check word alignment */
if (p % 2) {
/* Not aligned */
EEPROM_WriteDataByte(p, (uint8_t)Value);
EEPROM_WriteDataWord(p + 1, (uint16_t)(Value >> 8));
EEPROM_WriteDataByte(p + 3, (uint8_t)(Value >> 24));
} else {
/* Aligned */
EEPROM_WriteDataWord(p, (uint16_t)Value);
EEPROM_WriteDataWord(p + 2, (uint16_t)(Value >> 16));
}
}
void eeprom_update_dword(uint32_t *Address, uint32_t Value) { eeprom_write_dword(Address, Value); }
void eeprom_read_block(void *buf, const void *addr, size_t len) {
const uint8_t *src = (const uint8_t *)addr;
uint8_t * dest = (uint8_t *)buf;
/* Check word alignment */
if (len && (uintptr_t)src % 2) {
/* Read the unaligned first byte */
*dest++ = eeprom_read_byte(src++);
--len;
}
uint16_t value;
bool aligned = ((uintptr_t)dest % 2 == 0);
while (len > 1) {
value = eeprom_read_word((uint16_t *)src);
if (aligned) {
*(uint16_t *)dest = value;
dest += 2;
} else {
*dest++ = value;
*dest++ = value >> 8;
}
src += 2;
len -= 2;
}
if (len) {
*dest = eeprom_read_byte(src);
}
}
void eeprom_write_block(const void *buf, void *addr, size_t len) {
uint8_t * dest = (uint8_t *)addr;
const uint8_t *src = (const uint8_t *)buf;
/* Check word alignment */
if (len && (uintptr_t)dest % 2) {
/* Write the unaligned first byte */
eeprom_write_byte(dest++, *src++);
--len;
}
uint16_t value;
bool aligned = ((uintptr_t)src % 2 == 0);
while (len > 1) {
if (aligned) {
value = *(uint16_t *)src;
} else {
value = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8);
}
eeprom_write_word((uint16_t *)dest, value);
dest += 2;
src += 2;
len -= 2;
}
if (len) {
eeprom_write_byte(dest, *src);
}
}
void eeprom_update_block(const void *buf, void *addr, size_t len) { eeprom_write_block(buf, addr, len); }

View file

@ -0,0 +1,33 @@
/*
* This software is experimental and a work in progress.
* Under no circumstances should these files be used in relation to any critical system(s).
* Use of these files is at your own risk.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by
* Artur F.
*
* Modifications for QMK and STM32F303 by Yiancar
*
* This library assumes 8-bit data locations. To add a new MCU, please provide the flash
* page size and the total flash size in Kb. The number of available pages must be a multiple
* of 2. Only half of the pages account for the total EEPROM size.
* This library also assumes that the pages are not used by the firmware.
*/
#pragma once
uint16_t EEPROM_Init(void);
void EEPROM_Erase(void);
uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte);
uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord);
uint8_t EEPROM_ReadDataByte(uint16_t Address);
uint16_t EEPROM_ReadDataWord(uint16_t Address);
void print_eeprom(void);

View file

@ -0,0 +1,13 @@
#pragma once
#include "hardware/flash.h"
#include "hardware/structs/ssi.h"
#define FEE_PAGE_SIZE FLASH_SECTOR_SIZE
#define FEE_PAGE_COUNT 2
#define FEE_MCU_FLASH_SIZE (PICO_FLASH_SIZE_BYTES / 1024)
#define FEE_PAGE_BASE_ADDRESS \
(PICO_FLASH_SIZE_BYTES - FEE_PAGE_SIZE * FEE_PAGE_COUNT)
#define FEE_DENSITY_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE / 2)

201
platforms/pico/flash_pico.c Normal file
View file

@ -0,0 +1,201 @@
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "flash_pico.h"
#include "pico/bootrom.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#include "hardware/structs/ssi.h"
#include "hardware/structs/ioqspi.h"
#define BOOT2_SIZE_WORDS 64
#define FLASHCMD_PAGE_PROGRAM 0x02
#define FLASHCMD_READ_STATUS 0x05
#define FLASHCMD_WRITE_ENABLE 0x06
static ssi_hw_t *const ssi = (ssi_hw_t *)XIP_SSI_BASE;
// Sanity check
#undef static_assert
#define static_assert(cond, x) extern int static_assert[(cond) ? 1 : -1]
check_hw_layout(ssi_hw_t, ssienr, SSI_SSIENR_OFFSET);
check_hw_layout(ssi_hw_t, spi_ctrlr0, SSI_SPI_CTRLR0_OFFSET);
static uint32_t boot2_copyout[BOOT2_SIZE_WORDS];
static bool boot2_copyout_valid = false;
static void __no_inline_not_in_flash_func(flash_init_boot2_copyout)(void) {
if (boot2_copyout_valid) return;
for (int i = 0; i < BOOT2_SIZE_WORDS; ++i)
boot2_copyout[i] = ((uint32_t *)XIP_BASE)[i];
__compiler_memory_barrier();
boot2_copyout_valid = true;
}
static void __no_inline_not_in_flash_func(flash_enable_xip_via_boot2)(void) {
((void (*)(void))boot2_copyout + 1)();
}
// Bitbanging the chip select using IO overrides, in case RAM-resident IRQs
// are still running, and the FIFO bottoms out. (the bootrom does the same)
static void __no_inline_not_in_flash_func(flash_cs_force)(bool high) {
uint32_t field_val = high ? IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH
: IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW;
hw_write_masked(&ioqspi_hw->io[1].ctrl,
field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB,
IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS);
}
// Also allow any unbounded loops to check whether the above abort condition
// was asserted, and terminate early
static int __no_inline_not_in_flash_func(flash_was_aborted)(void) {
return *(io_rw_32 *)(IO_QSPI_BASE + IO_QSPI_GPIO_QSPI_SD1_CTRL_OFFSET) &
IO_QSPI_GPIO_QSPI_SD1_CTRL_INOVER_BITS;
}
// Put bytes from one buffer, and get bytes into another buffer.
// These can be the same buffer.
// If tx is NULL then send zeroes.
// If rx is NULL then all read data will be dropped.
//
// If rx_skip is nonzero, this many bytes will first be consumed from the FIFO,
// before reading a further count bytes into *rx.
// E.g. if you have written a command+address just before calling this function.
static void __no_inline_not_in_flash_func(flash_put_get)(const uint8_t *tx,
uint8_t * rx,
size_t count,
size_t rx_skip) {
// Make sure there is never more data in flight than the depth of the RX
// FIFO. Otherwise, when we are interrupted for long periods, hardware
// will overflow the RX FIFO.
const uint max_in_flight = 16 - 2; // account for data internal to SSI
size_t tx_count = count;
size_t rx_count = count;
while (tx_count || rx_skip || rx_count) {
// NB order of reads, for pessimism rather than optimism
uint32_t tx_level = ssi_hw->txflr;
uint32_t rx_level = ssi_hw->rxflr;
bool did_something =
false; // Expect this to be folded into control flow, not register
if (tx_count && tx_level + rx_level < max_in_flight) {
ssi->dr0 = (uint32_t)(tx ? *tx++ : 0);
--tx_count;
did_something = true;
}
if (rx_level) {
uint8_t rxbyte = ssi->dr0;
did_something = true;
if (rx_skip) {
--rx_skip;
} else {
if (rx) *rx++ = rxbyte;
--rx_count;
}
}
// APB load costs 4 cycles, so only do it on idle loops (our budget is
// 48 cyc/byte)
if (!did_something && __builtin_expect(flash_was_aborted(), 0)) break;
}
flash_cs_force(1);
}
// Convenience wrapper for above
// (And it's hard for the debug host to get the tight timing between
// cmd DR0 write and the remaining data)
static void __no_inline_not_in_flash_func(_flash_do_cmd)(uint8_t cmd,
const uint8_t *tx,
uint8_t * rx,
size_t count) {
flash_cs_force(0);
ssi->dr0 = cmd;
flash_put_get(tx, rx, count, 1);
}
// Timing of this one is critical, so do not expose the symbol to debugger etc
static void __no_inline_not_in_flash_func(flash_put_cmd_addr)(uint8_t cmd,
uint32_t addr) {
flash_cs_force(0);
addr |= cmd << 24;
for (int i = 0; i < 4; ++i) {
ssi->dr0 = addr >> 24;
addr <<= 8;
}
}
// Poll the flash status register until the busy bit (LSB) clears
static void __no_inline_not_in_flash_func(flash_wait_ready)(void) {
uint8_t stat;
do {
_flash_do_cmd(FLASHCMD_READ_STATUS, NULL, &stat, 1);
} while (stat & 0x1 && !flash_was_aborted());
}
// Set the WEL bit (needed before any program/erase operation)
static void __no_inline_not_in_flash_func(flash_enable_write)(void) {
_flash_do_cmd(FLASHCMD_WRITE_ENABLE, NULL, NULL, 0);
}
static void __no_inline_not_in_flash_func(pico_program_half_word)(
uint32_t flash_offs, uint16_t data) {
rom_connect_internal_flash_fn connect_internal_flash =
(rom_connect_internal_flash_fn)rom_func_lookup_inline(
ROM_FUNC_CONNECT_INTERNAL_FLASH);
rom_flash_exit_xip_fn flash_exit_xip =
(rom_flash_exit_xip_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP);
rom_flash_range_program_fn flash_range_program =
(rom_flash_range_program_fn)rom_func_lookup_inline(
ROM_FUNC_FLASH_RANGE_PROGRAM);
rom_flash_flush_cache_fn flash_flush_cache =
(rom_flash_flush_cache_fn)rom_func_lookup_inline(
ROM_FUNC_FLASH_FLUSH_CACHE);
assert(connect_internal_flash && flash_exit_xip && flash_range_program &&
flash_flush_cache);
flash_init_boot2_copyout();
__compiler_memory_barrier();
connect_internal_flash();
flash_exit_xip();
flash_enable_write();
flash_put_cmd_addr(FLASHCMD_PAGE_PROGRAM, flash_offs);
flash_put_get((uint8_t *)&data, NULL, 2, 4);
flash_wait_ready();
flash_flush_cache(); // Note this is needed to remove CSn IO force as well
// as cache flushing
flash_enable_xip_via_boot2();
}
FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) {
return FLASH_COMPLETE;
}
FLASH_Status FLASH_ErasePage(uint32_t Page_Address) {
flash_range_erase(Page_Address, FLASH_SECTOR_SIZE);
return FLASH_COMPLETE;
}
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) {
pico_program_half_word(Address, Data);
return FLASH_COMPLETE;
}
__attribute__((weak)) void pico_before_flash_operation(void) {}
__attribute__((weak)) void pico_after_flash_operation(void) {}
static int intr_stat;
void FLASH_Lock(void) {
restore_interrupts(intr_stat);
pico_after_flash_operation();
}
void FLASH_Unlock(void) {
intr_stat = save_and_disable_interrupts();
pico_before_flash_operation();
}

View file

@ -0,0 +1,44 @@
/*
* This software is experimental and a work in progress.
* Under no circumstances should these files be used in relation to any critical system(s).
* Use of these files is at your own risk.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This files are free to use from https://github.com/rogerclarkmelbourne/Arduino_STM32 and
* https://github.com/leaflabs/libmaple
*
* Modifications for QMK and STM32F303 by Yiancar
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#ifdef FLASH_STM32_MOCKED
extern uint8_t FlashBuf[MOCK_FLASH_SIZE];
#endif
typedef enum { FLASH_BUSY = 1, FLASH_ERROR_PG, FLASH_ERROR_WRP, FLASH_ERROR_OPT, FLASH_COMPLETE, FLASH_TIMEOUT, FLASH_BAD_ADDRESS } FLASH_Status;
#define IS_FLASH_ADDRESS(ADDRESS) (((ADDRESS) >= 0x08000000) && ((ADDRESS) < 0x0807FFFF))
FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
FLASH_Status FLASH_ErasePage(uint32_t Page_Address);
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
void FLASH_Unlock(void);
void FLASH_Lock(void);
#ifdef __cplusplus
}
#endif

34
platforms/pico/gpio.h Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "pico/stdlib.h"
#include "pin_defs.h"
typedef uint8_t pin_t;
#define setPinInput(pin) (gpio_disable_pulls(pin), gpio_set_dir(pin, GPIO_IN))
#define setPinInputHigh(pin) (gpio_set_dir(pin, GPIO_IN), gpio_pull_up(pin))
#define setPinInputLow(pin) (gpio_init(pin), gpio_set_dir(pin, GPIO_IN), gpio_pull_down(pin))
#define setPinOutput(pin) (gpio_set_dir(pin, GPIO_OUT))
#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)))

17
platforms/pico/pin_defs.h Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once

30
platforms/pico/platform.c Normal file
View file

@ -0,0 +1,30 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#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++) {
gpio_init(i);
setPinInputHigh(i);
}
adc_init();
}

404
platforms/pico/platform.mk Normal file
View file

@ -0,0 +1,404 @@
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"
LSUSB ?= lsusb
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
@mkdir -p $(KEYBOARD_OUTPUT)/src
$(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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "platforms/pico/boards/pico_boards.h"

6
platforms/pico/print.c Normal file
View file

@ -0,0 +1,6 @@
#include "sendchar.h"
void print_set_sendchar(sendchar_func_t func) {
// TODO
}

136
platforms/pico/suspend.c Normal file
View file

@ -0,0 +1,136 @@
#include <stdbool.h>
#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) {}
/** \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

67
platforms/pico/timer.c Normal file
View file

@ -0,0 +1,67 @@
/*
Copyright 2011 Jun Wako <wakojun@gmail.com>
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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View file

@ -0,0 +1,2 @@
#pragma once

View file

@ -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));
}
}

View file

@ -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; extern uint16_t g_vial_magic_keycode_override;
// This overrides the one in quantum/keymap_common.c // 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) { uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) {
#ifdef VIAL_ENABLE #ifdef VIAL_ENABLE
/* Disable any keycode processing while unlocking */ /* 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; return KC_NO;
} }
} }
#endif
uint8_t dynamic_keymap_macro_get_count(void) { uint8_t dynamic_keymap_macro_get_count(void) {
return DYNAMIC_KEYMAP_MACRO_COUNT; return DYNAMIC_KEYMAP_MACRO_COUNT;

View file

@ -23,6 +23,10 @@
#include "wait.h" #include "wait.h"
#include "usb_util.h" #include "usb_util.h"
#ifdef PROTOCOL_PICO
# include "tusb.h"
#endif
#ifdef EE_HANDS #ifdef EE_HANDS
# include "eeconfig.h" # include "eeconfig.h"
#endif #endif
@ -57,7 +61,39 @@ static uint8_t connection_errors = 0;
volatile bool isLeftHand = true; volatile bool isLeftHand = true;
#if defined(SPLIT_USB_DETECT) #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++) { 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 // This will return true if a USB connection has been established
if (usb_connected_state()) { if (usb_connected_state()) {
@ -139,7 +175,8 @@ void split_pre_init(void) {
if (isLeftHand) { if (isLeftHand) {
rgblight_set_clipping_range(0, num_rgb_leds_split[0]); rgblight_set_clipping_range(0, num_rgb_leds_split[0]);
} else { } 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 #endif

View file

@ -1,8 +1,10 @@
PROTOCOL_COMMON_DIR = protocol
TMK_COMMON_SRC += \ TMK_COMMON_SRC += \
$(PROTOCOL_DIR)/host.c \ $(PROTOCOL_COMMON_DIR)/host.c \
$(PROTOCOL_DIR)/report.c \ $(PROTOCOL_COMMON_DIR)/report.c \
$(PROTOCOL_DIR)/usb_device_state.c \ $(PROTOCOL_COMMON_DIR)/usb_device_state.c \
$(PROTOCOL_DIR)/usb_util.c \ $(PROTOCOL_COMMON_DIR)/usb_util.c \
SHARED_EP_ENABLE = no SHARED_EP_ENABLE = no
MOUSE_SHARED_EP ?= yes MOUSE_SHARED_EP ?= yes

View file

@ -33,6 +33,18 @@ interrupt_setting_t store_and_clear_interrupt(void) {
void restore_interrupt_setting(interrupt_setting_t setting) { void restore_interrupt_setting(interrupt_setting_t setting) {
SREG = 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__) #elif defined(__arm__)
# include <ch.h> # include <ch.h>

19
tmk_core/protocol/pico.mk Normal file
View file

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

View file

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

View file

@ -0,0 +1,120 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#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"
#include "device/dcd.h"
uint8_t keyboard_leds(void) {
return get_keyboard_led_status();
}
void send_keyboard(report_keyboard_t *report) {
if (tud_suspended()) {
tud_remote_wakeup();
busy_wait_ms(15);
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
return;
}
while (!tud_hid_n_ready(ITF_NUM_HID_KEYBOARD)) {
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) {
if (tud_suspended()) {
tud_remote_wakeup();
busy_wait_ms(15);
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
return;
}
while (!tud_hid_n_ready(ITF_NUM_HID_MOUSE)) {
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

View file

@ -0,0 +1,27 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#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);

View file

@ -0,0 +1,42 @@
#include "progmem.h"
#include <stddef.h>
#include <inttypes.h>
#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"

View file

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

View file

@ -0,0 +1,283 @@
/*
* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "raw_hid.h"
#include "bootloader.h"
#include "debug.h"
#include "eeprom_pico.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) { EEPROM_Init(); 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();
}
void protocol_post_init(void) { host_set_driver(&driver); }
void protocol_pre_task(void) {
tud_task(); // tinyusb device task
}
void protocol_post_task(void) {
#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] == 'b') {
bootloader_jump();
} else if (buf[0] == 'l') {
printf("init eeprom emulation\n");
EEPROM_Init();
printf("complete\n");
} else if (buf[0] == 'c') {
printf("clear eeprom emulation\n");
EEPROM_Erase();
printf("complete\n");
} else if (buf[0] == 'd') {
printf("debug print ");
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; }
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) {
switch (itf) {
case ITF_NUM_HID_KEYBOARD:
// LED status
keyboard_indicator_led = buffer[0];
break;
#ifdef VIA_ENABLE
case ITF_NUM_HID_RAW:
raw_hid_receive((uint8_t*)buffer, bufsize);
break;
#endif
default:
break;
}
}

View file

@ -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"

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);

View file

@ -0,0 +1,47 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);

View file

@ -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_ */

View file

@ -0,0 +1,232 @@
/*
* 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.
*
* Modifications for QMK and RP2040 by sekigon-gonnoc
*/
#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 = 0x0200,
// Use Interface Association Descriptor (IAD) for CDC
// As required by USB Specs IAD's subclass must be common class (2) and
// protocol must be IAD (1)
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.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, USB_POLLING_INTERVAL_MS),
TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID_RAW, 2, HID_ITF_PROTOCOL_NONE,
sizeof(desc_raw), EPNUM_HID_RAW_OUT,
EPNUM_HID_RAW_IN, CFG_TUD_HID_EP_BUFSIZE, 1),
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
#ifdef VIAL_ENABLE
SERIAL_NUMBER,
#else
"123456", // 3: Serials, should use chip ID
#endif
};
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;
}

View file

@ -0,0 +1,60 @@
#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
uint8_t get_keyboard_led_status(void);

View file

@ -0,0 +1,27 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#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
}

View file

@ -291,6 +291,8 @@ enum usb_endpoints {
#elif defined(PROTOCOL_CHIBIOS) #elif defined(PROTOCOL_CHIBIOS)
// ChibiOS gives us number of available user endpoints, not control // ChibiOS gives us number of available user endpoints, not control
# define MAX_ENDPOINTS USB_MAX_ENDPOINTS # define MAX_ENDPOINTS USB_MAX_ENDPOINTS
#elif defined(PROTOCOL_PICO)
# define MAX_ENDPOINTS 16
#endif #endif
// TODO - ARM_ATSAM // TODO - ARM_ATSAM