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
This commit is contained in:
sekigon-gonnoc 2021-06-27 18:46:12 +09:00 • committed by Ilya Zhuravlev
commit d2f854f89b
82 changed files with 4785 additions and 9 deletions

3
.gitmodules vendored
View file

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

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/vusb ]; then git submodule sync lib/vusb && git submodule update --depth 50 --init lib/vusb; fi
if [ ! -e lib/printf ]; then git submodule sync lib/printf && git submodule update --depth 50 --init lib/printf; fi
if [ ! -e lib/pico-sdk ]; then git submodule sync lib/pico-sdk && git submodule update --depth 50 --init lib/pico-sdk; fi
git submodule status --recursive 2>/dev/null | \
while IFS= read -r x; do \
case "$$x" in \

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
endif
ifeq ($(strip $(CTPIM)), yes)
CONVERT_TO_PICO_MICRO=yes
endif
ifeq ($(strip $(CONVERT_TO_PICO_MICRO)), yes)
include platforms/pico/convert_to_pico_micro.mk
endif
ifneq ($(FORCE_LAYOUT),)
TARGET := $(TARGET)_$(FORCE_LAYOUT)
endif
include $(BUILDDEFS_PATH)/mcu_selection.mk
# Find all the C source files to be compiled in subfolders.
@ -261,6 +273,12 @@ ifdef MCU_FAMILY
PLATFORM_KEY=chibios
FIRMWARE_FORMAT?=bin
OPT_DEFS += -DMCU_$(MCU_FAMILY)
ifeq ($(MCU_FAMILY),PICO)
$(info "PLATFORM RP2040")
PLATFORM=PICO_SDK
PLATFORM_KEY=pico
FIRMWARE_FORMAT=elf
endif
else ifdef ARM_ATSAM
PLATFORM=ARM_ATSAM
PLATFORM_KEY=arm_atsam

View file

@ -207,6 +207,8 @@ else
COMMON_VPATH += $(DRIVER_PATH)/eeprom
SRC += eeprom_driver.c eeprom_transient.c
endif
else ifeq ($(PLATFORM),PICO_SDK)
SRC += $(PLATFORM_COMMON_DIR)/eeprom.c
else ifeq ($(PLATFORM),ARM_ATSAM)
# arm_atsam EEPROM
OPT_DEFS += -DEEPROM_SAMD

View file

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

View file

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

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,85 @@
/* Copyright 2021 sekigon-gonnoc
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <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;
case KC_FN0:
if (record->event.pressed) {
printf("save keymap to eeprom\n");
pico_eepemu_flash_dynamic_keymap();
printf("complete\n");
printf("save eeconfig to eeprom\n");
pico_eepemu_flash_eeconfig();
printf("complete\n");
}
return false;
break;
}
return true;
}
void matrix_scan_user(void) {
static int cnt = 0;
if (++cnt == 1000) {
cnt = 0;
int16_t ad = analogReadPin(26);
printf("ADC:%d\n", ad);
}
}

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,75 @@
/* Copyright 2021 sekigon-gonnoc
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <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;
case KC_FN0:
if (record->event.pressed) {
printf("save keymap to eeprom\n");
pico_eepemu_flash_dynamic_keymap();
printf("complete\n");
printf("save eeconfig to eeprom\n");
pico_eepemu_flash_eeconfig();
printf("complete\n");
}
return false;
break;
}
return true;
}

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,83 @@
/* Copyright 2021 sekigon-gonnoc
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <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;
case KC_FN0:
if (record->event.pressed) {
printf("save keymap to eeprom\n");
pico_eepemu_flash_dynamic_keymap();
printf("complete\n");
printf("save eeconfig to eeprom\n");
pico_eepemu_flash_eeconfig();
printf("complete\n");
}
return false;
break;
}
return true;
}
#include "rgblight.h"
void keyboard_post_init_user(void) {
rgblight_enable();
rgblight_mode_noeeprom(35);
}

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,80 @@
/* Copyright 2021 sekigon-gonnoc
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <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_B, MO(_FN),
KC_TAB, KC_SPC, KC_ENT,
KC_C, KC_D, MO(_FN),
KC_TAB, KC_SPC, KC_ENT
),
[_FN] = LAYOUT(
QMKBEST, QMKURL, _______,
RESET, XXXXXXX, XXXXXXX,
QMKBEST, QMKURL, _______,
RESET, XXXXXXX, XXXXXXX
)
};
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case QMKBEST:
if (record->event.pressed) {
// when keycode QMKBEST is pressed
SEND_STRING("QMK is the best thing ever!");
} else {
// when keycode QMKBEST is released
}
break;
case QMKURL:
if (record->event.pressed) {
// when keycode QMKURL is pressed
SEND_STRING("https://qmk.fm/\n");
} else {
// when keycode QMKURL is released
}
break;
case KC_FN0:
if (record->event.pressed) {
printf("save keymap to eeprom\n");
pico_eepemu_flash_dynamic_keymap();
printf("complete\n");
printf("save eeconfig to eeprom\n");
pico_eepemu_flash_eeconfig();
printf("complete\n");
}
return false;
break;
}
return true;
}

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

1
lib/pico-sdk Submodule

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

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,21 @@
#pragma once
#define D3 0
#define D2 1
#define D1 2
#define D0 3
#define D4 4
#define C6 5
#define D7 6
#define E6 7
#define B4 8
#define B5 9
#define B6 21
#define B2 23
#define B3 20
#define B1 22
#define F7 26
#define F6 27
#define F5 28
#define F4 29

View file

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

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,100 @@
#include "quantum.h"
#include "i2c_master.h"
#undef i2c_init
#include <string.h>
#include "pico/binary_info.h"
#include "hardware/i2c.h"
static uint8_t i2c_address;
static i2c_status_t pico_status_to_qmk(int res) {
if (res == PICO_ERROR_GENERIC) {
return I2C_STATUS_ERROR;
} else if (res == PICO_ERROR_TIMEOUT) {
return I2C_STATUS_TIMEOUT;
} else {
return I2C_STATUS_SUCCESS;
}
}
void i2c_init_pico(void) {
static bool is_initialised = false;
if (!is_initialised) {
is_initialised = true;
i2c_init(I2C_DRIVER, 50 * 1000);
gpio_set_function(I2C1_SCL, GPIO_FUNC_I2C);
gpio_set_function(I2C1_SDA, GPIO_FUNC_I2C);
gpio_pull_up(I2C1_SCL);
gpio_pull_up(I2C1_SDA);
// Make the I2C pins available to picotool
bi_decl(bi_2pins_with_func(I2C1_SDA, I2C1_SCL, GPIO_FUNC_I2C));
}
}
i2c_status_t i2c_start(uint8_t address) {
i2c_address = address;
return I2C_STATUS_SUCCESS;
}
i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length,
uint16_t timeout) {
i2c_address = address;
int res = i2c_write_timeout_us(I2C_DRIVER, i2c_address >> 1, data, length,
true, timeout * 1000);
return pico_status_to_qmk(res);
}
i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length,
uint16_t timeout) {
i2c_address = address;
int res = i2c_read_timeout_us(I2C_DRIVER, i2c_address >> 1, data, length,
true, timeout * 1000);
return pico_status_to_qmk(res);
}
i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data,
uint16_t length, uint16_t timeout) {
i2c_address = devaddr;
uint8_t complete_packet[length + 1];
for (uint8_t i = 0; i < length; i++) {
complete_packet[i + 1] = data[i];
}
complete_packet[0] = regaddr;
int res =
i2c_write_timeout_us(I2C_DRIVER, (i2c_address >> 1), complete_packet,
length + 1, true, timeout * 1000);
return pico_status_to_qmk(res);
}
i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data,
uint16_t length, uint16_t timeout) {
i2c_address = devaddr;
i2c_write_timeout_us(I2C_DRIVER, (i2c_address >> 1), &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,53 @@
/* Copyright 2018 Jack Humbert
* Copyright 2018 Yiancar
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <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>
#ifndef I2C1_SCL
# define I2C1_SCL 3
#endif
#ifndef I2C1_SDA
# define I2C1_SDA 2
#endif
#ifndef I2C_DRIVER
# define I2C_DRIVER i2c1
#endif
typedef int16_t i2c_status_t;
#define I2C_STATUS_SUCCESS (0)
#define I2C_STATUS_ERROR (-1)
#define I2C_STATUS_TIMEOUT (-2)
#define i2c_init() i2c_init_pico()
void i2c_init_pico(void);
i2c_status_t i2c_start(uint8_t address);
i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
void i2c_stop(void);

View file

@ -0,0 +1,425 @@
/*
* WARNING: be careful changing this code, it is very timing dependent
*/
#include "quantum.h"
#include "serial.h"
#include "wait.h"
#include "uart_tx.pio.h"
#include "uart_rx.pio.h"
#include "pio_manager.h"
#include "pico/stdlib.h"
#include "hardware/timer.h"
#include "hardware/gpio.h"
#include "hardware/sync.h"
#include "hardware/irq.h"
#include <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
inline static void serial_delay(void) { wait_us(SERIAL_DELAY); }
inline static void serial_delay_half(void) { wait_us(SERIAL_DELAY / 2); }
inline static void serial_delay_blip(void) { wait_us(1); }
inline static void serial_output(void) {
setPinOutput(SOFT_SERIAL_PIN);
gpio_set_slew_rate(SOFT_SERIAL_PIN, GPIO_SLEW_RATE_FAST);
gpio_set_drive_strength(SOFT_SERIAL_PIN, GPIO_DRIVE_STRENGTH_12MA);
}
inline static void serial_input(void) { setPinInputHigh(SOFT_SERIAL_PIN); }
inline static bool serial_read_pin(void) { return readPin(SOFT_SERIAL_PIN); }
inline static void serial_low(void) { writePinLow(SOFT_SERIAL_PIN); }
inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); }
void interrupt_handler(uint gpio, uint32_t events);
static PIO pio = pio0;
static uint sm_tx, sm_rx;
inline static void soft_serial_disable_rx(void) {
pio_sm_set_enabled(pio, sm_rx, false);
pio_sm_restart(pio, sm_rx);
}
inline static void soft_serial_enable_rx(void) {
setPinInputHigh(SOFT_SERIAL_PIN);
// pio_gpio_init is not needed
pio_sm_clear_fifos(pio, sm_rx);
pio_sm_set_enabled(pio, sm_rx, true);
}
inline static void soft_serial_disable_tx(void) {
pio_sm_set_enabled(pio, sm_tx, false);
pio_sm_restart(pio, sm_tx);
}
static void soft_serial_enable_tx(void) {
writePinHigh(SOFT_SERIAL_PIN);
setPinOutput(SOFT_SERIAL_PIN);
gpio_set_slew_rate(SOFT_SERIAL_PIN, GPIO_SLEW_RATE_FAST);
gpio_set_drive_strength(SOFT_SERIAL_PIN, GPIO_DRIVE_STRENGTH_12MA);
pio_sm_clear_fifos(pio, sm_tx);
pio_gpio_init(pio, SOFT_SERIAL_PIN);
pio_sm_set_enabled(pio, sm_tx, true);
}
inline static int soft_serial_pio_init(void) {
sm_rx = pio_manager_get_empty_sm(pio);
if (sm_rx < 0) return -1;
int32_t offset = pio_manager_add_program(pio, sm_rx, &uart_rx_mini_program);
if (offset < 0) return -1;
uart_rx_mini_program_init(pio, sm_rx, offset, SOFT_SERIAL_PIN, SERIAL_BAUD);
soft_serial_disable_rx();
sm_tx = pio_manager_get_empty_sm(pio);
if (sm_tx < 0) return -1;
offset = pio_manager_add_program(pio, sm_tx, &uart_tx_program);
if (offset < 0) return -1;
writePinHigh(SOFT_SERIAL_PIN);
uart_tx_program_init(pio, sm_tx, offset, SOFT_SERIAL_PIN, SERIAL_BAUD);
soft_serial_disable_tx();
return 0;
}
// Initialize master
void soft_serial_initiator_init(void) {
soft_serial_pio_init();
serial_output();
serial_high();
}
// Initialize slave
void soft_serial_target_init(void) {
soft_serial_pio_init();
setPinInputHigh(SOFT_SERIAL_PIN);
gpio_set_irq_enabled_with_callback(SOFT_SERIAL_PIN, GPIO_IRQ_EDGE_FALL,
true, interrupt_handler);
irq_set_priority(IO_IRQ_BANK0, PICO_HIGHEST_IRQ_PRIORITY);
}
// Used by the master to synchronize timing with the slave.
static int __attribute__((noinline)) sync_recv(void) {
serial_input();
volatile uint64_t timeout = time_us_64() + 10000;
while (!serial_read_pin() && time_us_64() < timeout) {
// tight_loop_contents();
}
if (time_us_64() >= timeout) {
// dprintf("serial::NO_RESPONSE0\n");
return -1;
}
timeout = time_us_64() + 10000;
while (serial_read_pin() && time_us_64() < timeout) {
// tight_loop_contents();
}
if (time_us_64() >= timeout) {
// dprintf("serial::NO_RESPONSE1\n");
return -1;
}
timeout = time_us_64() + 10000;
while (!serial_read_pin() && time_us_64() < timeout) {
// tight_loop_contents();
}
if (time_us_64() >= timeout) {
// dprintf("serial::NO_RESPONSE2\n");
return -2;
}
return 0;
}
// Used by the slave to send a synchronization signal to the master.
static void __attribute__((noinline)) sync_send(void) {
serial_output();
serial_low();
serial_delay();
serial_high();
while (!serial_read_pin()) {
continue;
}
}
// Reads a byte from the serial line
static uint16_t __attribute__((noinline)) serial_read_byte(void) {
uint64_t timeout = time_us_64() + 10000;
while (pio_sm_is_rx_fifo_empty(pio, sm_rx) && (time_us_64() < timeout)) {
tight_loop_contents();
}
if (time_us_64() >= timeout) {
// dprintf("serial: pio_rx timeout\n");
return 0xffff;
} else {
return uart_rx_program_getc(pio, sm_rx);
}
}
// Sends a byte with MSB ordering
static void __attribute__((noinline)) serial_write_byte(uint8_t data) {
uart_tx_program_putc(pio, sm_tx, data);
}
static void serial_wait_send_complete(void) {
while (pio_sm_get_tx_fifo_level(pio, sm_tx) != 0) {
continue;
}
wait_us(1000000 * 12 / (SERIAL_BAUD));
}
// interrupt handle to be used by the slave device
void interrupt_handler(uint gpio, uint32_t events) {
if (gpio != SOFT_SERIAL_PIN || events != GPIO_IRQ_EDGE_FALL) {
return;
}
uint32_t interrupt_status = save_and_disable_interrupts();
gpio_set_irq_enabled_with_callback(SOFT_SERIAL_PIN, GPIO_IRQ_EDGE_FALL,
false, interrupt_handler);
while (!serial_read_pin()) {
continue;
}
serial_delay_blip();
sync_send();
uint8_t checksum_computed = 0;
int sstd_index = 0;
int receive_res = 0;
uint8_t checksum_received = 0xff;
split_transaction_desc_t *trans;
serial_input();
while (!serial_read_pin()) {
continue;
}
// activate receive
soft_serial_enable_rx();
do {
sstd_index = serial_read_byte();
if (sstd_index > NUM_TOTAL_TRANSACTIONS) {
printf("invalid sstd_index:%d\n", sstd_index);
receive_res = -1;
break;
}
soft_serial_disable_rx();
serial_delay();
sync_send();
soft_serial_enable_rx();
trans = &split_transaction_table[sstd_index];
for (int i = 0; i < trans->initiator2target_buffer_size; ++i) {
split_trans_initiator2target_buffer(trans)[i] = serial_read_byte();
checksum_computed += split_trans_initiator2target_buffer(trans)[i];
}
checksum_computed ^= 7;
checksum_received = serial_read_byte();
} while (0);
soft_serial_disable_rx();
if (receive_res < 0) {
// receive timeout error
serial_input();
gpio_set_irq_enabled_with_callback(SOFT_SERIAL_PIN, GPIO_IRQ_EDGE_FALL,
true, interrupt_handler);
restore_interrupts(interrupt_status);
return;
}
serial_delay();
sync_send();
soft_serial_enable_tx();
// wait for the sync to finish sending
serial_delay();
// Allow any slave processing to occur
if (trans->slave_callback) {
trans->slave_callback(trans->initiator2target_buffer_size,
split_trans_initiator2target_buffer(trans),
trans->target2initiator_buffer_size,
split_trans_target2initiator_buffer(trans));
}
uint8_t checksum = 0;
for (int i = 0; i < trans->target2initiator_buffer_size; ++i) {
uint8_t b = split_trans_target2initiator_buffer(trans)[i];
serial_write_byte(b);
checksum += b;
}
serial_write_byte(checksum ^ 7);
serial_wait_send_complete();
*trans->status = (checksum_computed == checksum_received)
? TRANSACTION_ACCEPTED
: TRANSACTION_DATA_ERROR;
// end transaction
serial_input();
//
gpio_set_irq_enabled_with_callback(SOFT_SERIAL_PIN, GPIO_IRQ_EDGE_FALL,
true, interrupt_handler);
restore_interrupts(interrupt_status);
}
/////////
// start transaction by initiator
//
// int soft_serial_transaction(int sstd_index)
//
// Returns:
// TRANSACTION_END
// TRANSACTION_NO_RESPONSE
// TRANSACTION_DATA_ERROR
// this code is very time dependent, so we need to disable interrupts
int soft_serial_transaction(int sstd_index) {
if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR;
split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered
// TODO: remove extra delay between transactions
serial_delay();
// this code is very time dependent, so we need to disable interrupts
uint32_t interrupt_status = save_and_disable_interrupts();
// signal to the slave that we want to start a transaction
serial_output();
serial_low();
serial_delay_blip();
serial_high();
// wait for the slaves response
if (sync_recv() != 0) {
// dprintf("NACK 1\n");
restore_interrupts(interrupt_status);
return TRANSACTION_NO_RESPONSE;
}
// if the slave is present syncronize with it
soft_serial_enable_tx();
uint8_t checksum = 0;
// send data to the slave
serial_write_byte(sstd_index); // first chunk is transaction id
serial_wait_send_complete();
soft_serial_disable_tx();
if (sync_recv() != 0) {
// dprintf("NACK 1.5\n");
restore_interrupts(interrupt_status);
return TRANSACTION_NO_RESPONSE;
}
soft_serial_enable_tx();
serial_delay();
for (int i = 0; i < trans->initiator2target_buffer_size; ++i) {
serial_write_byte(split_trans_initiator2target_buffer(trans)[i]);
checksum += split_trans_initiator2target_buffer(trans)[i];
}
serial_write_byte(checksum ^ 7);
serial_wait_send_complete();
soft_serial_disable_tx();
if (sync_recv() != 0) {
// dprintf("NACK 2\n");
restore_interrupts(interrupt_status);
return TRANSACTION_NO_RESPONSE;
}
soft_serial_enable_rx();
// receive data from the slave
uint8_t checksum_computed = 0;
uint8_t receive_bytes = trans->target2initiator_buffer_size;
for (int i = 0; i < receive_bytes; ++i) {
split_trans_target2initiator_buffer(trans)[i] = serial_read_byte();
checksum_computed += split_trans_target2initiator_buffer(trans)[i];
}
checksum_computed ^= 7;
uint8_t checksum_received = serial_read_byte();
soft_serial_disable_rx();
if ((checksum_computed) != (checksum_received)) {
dprintf("serial::FAIL[%u,%u,%u]\n", checksum_computed,
checksum_received, sstd_index);
serial_output();
serial_high();
restore_interrupts(interrupt_status);
return TRANSACTION_DATA_ERROR;
}
// always, release the line when not in use
serial_high();
serial_output();
restore_interrupts(interrupt_status);
return TRANSACTION_END;
}

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,51 @@
#include <stdio.h>
#include "ws2812.h"
#include "ws2812.pio.h"
#include "pio_manager.h"
#include "boards/pico_boards.h"
#include "pico/stdlib.h"
static PIO pio = pio0;
static int sm = 0;
static int ws2812_init(void) {
sm = pio_manager_get_empty_sm(pio);
if (sm < 0) {
return -1;
}
int32_t offset = pio_manager_add_program(pio, sm, &ws2812_program);
if (offset < 0) {
return -1;
}
ws2812_program_init(pio, sm, offset, RGB_DI_PIN, 800000, false);
return 0;
}
void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds) {
static bool s_init = false;
if (!s_init) {
if (ws2812_init() == 0) {
s_init = true;
} else {
return;
}
}
printf("led %d %d %d\n", ledarray[0].r, ledarray[0].g, ledarray[0].b);
for (int i = 0; i < number_of_leds; i++) {
pio_sm_put_blocking(pio, sm,
(((uint32_t)ledarray[i].r) << 16) |
(((uint32_t)ledarray[i].g) << 24) |
((uint32_t)ledarray[i].b << 8));
}
}

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

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

@ -0,0 +1,272 @@
#include <string.h>
#include <stdio.h>
// qmk
#include "eeprom.h"
#include "eeconfig.h"
#include "timer.h"
#include "debug.h"
#include "print.h"
#include "pico_eeprom.h"
// pico
#include "pico/stdlib.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#ifndef DYNAMIC_KEYMAP_EEPROM_ADDR
# ifdef VIA_EEPROM_CUSTOM_CONFIG_ADDR
# define DYNAMIC_KEYMAP_EEPROM_ADDR \
(VIA_EEPROM_CUSTOM_CONFIG_ADDR + VIA_EEPROM_CUSTOM_CONFIG_SIZE)
# else
# define DYNAMIC_KEYMAP_EEPROM_ADDR (EECONFIG_SIZE + 1)
# endif
#endif
#ifndef EEPEMU_EECONFIG_SIZE
# define EEPEMU_EECONFIG_SIZE 1024
#endif
_Static_assert(EEPEMU_EECONFIG_SIZE > EECONFIG_SIZE,
"EEPEMU_EECONFIG Region is too small");
#ifndef EEPEMU_KEYMAP_SIZE
# define EEPEMU_KEYMAP_SIZE 4096
#endif
_Static_assert(EEPEMU_KEYMAP_SIZE > DYNAMIC_KEYMAP_EEPROM_MAX_ADDR,
"EEPEMU_KEYMAP Region is too small");
#define ALIGNED_REGION(size, align) ((size + align - 1) / align * align)
#ifndef EEPEMU_KEYMAP_START_OFFSET
# define EEPEMU_KEYMAP_START_OFFSET \
(PICO_FLASH_SIZE_BYTES - \
ALIGNED_REGION(EEPEMU_KEYMAP_SIZE, FLASH_SECTOR_SIZE))
#endif
#ifndef EEPEMU_EECONFIG_START_OFFSET
# define EEPEMU_EECONFIG_START_OFFSET \
(EEPEMU_KEYMAP_START_OFFSET - \
ALIGNED_REGION(EEPEMU_EECONFIG_SIZE, FLASH_SECTOR_SIZE))
#endif
#ifndef EEPEMU_LAZY_WRITEBACK_TIMEOUT
# define EEPEMU_LAZY_WRITEBACK_TIMEOUT \
30000 // write back to ROM if 30s elapsed.
#endif
_Static_assert(EEPEMU_EECONFIG_START_OFFSET % FLASH_SECTOR_SIZE == 0,
"Offset should be aligned to FLASH_SCTOR_SIZE");
_Static_assert(EEPEMU_KEYMAP_START_OFFSET % FLASH_SECTOR_SIZE == 0,
"Offset should be aligned to FLASH_SCTOR_SIZE");
static uint8_t eepemu_eeconfig[EEPEMU_EECONFIG_SIZE]; // 1kB
static uint8_t eepemu_dynamic_keymap[EEPEMU_KEYMAP_SIZE]; // 4kB
static int32_t eeconfig_last_edit;
static int32_t dynamic_keymap_last_edit;
// Load ROM data to RAM
void pico_eepemu_init(void) {
memcpy(eepemu_eeconfig, (void *)XIP_BASE + EEPEMU_EECONFIG_START_OFFSET,
sizeof(eepemu_eeconfig));
memcpy(eepemu_dynamic_keymap, (void *)XIP_BASE + EEPEMU_KEYMAP_START_OFFSET,
sizeof(eepemu_dynamic_keymap));
eeconfig_last_edit = -1;
dynamic_keymap_last_edit = -1;
}
void pico_eepemu_lazy_write_back(void) {
if (eeconfig_last_edit >= 0) {
if (timer_elapsed(eeconfig_last_edit) >=
EEPEMU_LAZY_WRITEBACK_TIMEOUT) {
printf("eeconfig is written to ROM\n");
pico_eepemu_flash_eeconfig();
}
}
if (dynamic_keymap_last_edit >= 0) {
if (timer_elapsed(dynamic_keymap_last_edit) >=
EEPEMU_LAZY_WRITEBACK_TIMEOUT) {
printf("Dynamic keymap is written to ROM\n");
pico_eepemu_flash_dynamic_keymap();
}
}
}
void pico_eepemu_flash_eeconfig(void) {
int32_t status = save_and_disable_interrupts();
flash_range_erase(EEPEMU_EECONFIG_START_OFFSET, FLASH_SECTOR_SIZE);
flash_range_program(EEPEMU_EECONFIG_START_OFFSET, eepemu_eeconfig,
sizeof(eepemu_eeconfig));
eeconfig_last_edit = -1;
restore_interrupts(status);
}
void pico_eepemu_flash_dynamic_keymap(void) {
int32_t status = save_and_disable_interrupts();
flash_range_erase(EEPEMU_KEYMAP_START_OFFSET, FLASH_SECTOR_SIZE);
flash_range_program(EEPEMU_KEYMAP_START_OFFSET, eepemu_dynamic_keymap,
sizeof(eepemu_dynamic_keymap));
dynamic_keymap_last_edit = -1;
restore_interrupts(status);
}
static inline bool is_eeconfig_addr(const void *eeaddr) {
if ((uint32_t)eeaddr < DYNAMIC_KEYMAP_EEPROM_ADDR) {
return true;
} else {
return false;
}
}
static void *convert_address(const void *addr) {
// TODO Assert address
if (is_eeconfig_addr(addr)) {
return (void *)(eepemu_eeconfig + (uint32_t)addr);
} else {
return (void *)(eepemu_dynamic_keymap + (uint32_t)addr -
DYNAMIC_KEYMAP_EEPROM_ADDR);
}
}
uint8_t eeprom_read_byte(const uint8_t *__p) {
uint8_t *addr = convert_address(__p);
if (addr == NULL) {
return 0xff;
}
return *addr;
}
uint16_t eeprom_read_word(const uint16_t *__p) {
uint16_t *addr = convert_address(__p);
if (addr == NULL) {
return 0xffff;
}
if ((uint32_t)addr % 2 == 0) {
return *addr;
} else {
uint16_t ret;
memcpy(&ret, addr, sizeof(ret));
return ret;
}
}
uint32_t eeprom_read_dword(const uint32_t *__p) {
uint32_t *addr = convert_address(__p);
if (addr == NULL) {
return 0xffffffff;
}
if ((uint32_t)addr % 4 == 0) {
return *addr;
} else {
uint32_t ret;
memcpy(&ret, addr, sizeof(ret));
return ret;
}
}
void eeprom_read_block(void *__dst, const void *__src, size_t __n) {
uint32_t *addr = convert_address(__src);
if (addr == NULL) {
return;
}
memcpy(__dst, addr, __n);
}
static void update_last_edit_time(const void *eeaddr) {
if (is_eeconfig_addr(eeaddr)) {
eeconfig_last_edit = timer_read();
} else {
dynamic_keymap_last_edit = timer_read();
}
}
void eeprom_write_byte(uint8_t *__p, uint8_t __value) {
uint8_t *addr = convert_address(__p);
if (addr == NULL) {
return;
}
*addr = __value;
update_last_edit_time(__p);
}
void eeprom_write_word(uint16_t *__p, uint16_t __value) {
uint16_t *addr = convert_address(__p);
if (addr == NULL) {
return;
}
if ((uint32_t)addr % 2 == 0) {
*addr = __value;
} else {
memcpy(addr, &__value, sizeof(__value));
}
update_last_edit_time(__p);
}
void eeprom_write_dword(uint32_t *__p, uint32_t __value) {
uint32_t *addr = convert_address(__p);
if (addr == NULL) {
return;
}
if ((uint32_t)addr % 4 == 0) {
*addr = __value;
} else {
memcpy(addr, &__value, sizeof(__value));
}
update_last_edit_time(__p);
}
void eeprom_write_block(const void *__src, void *__dst, size_t __n) {
uint32_t *addr = convert_address(__dst);
if (addr == NULL) {
return;
}
memcpy(addr, __src, __n);
update_last_edit_time(__dst);
}
void eeprom_update_byte(uint8_t *__p, uint8_t __value) {
if (eeprom_read_byte(__p) != __value) {
eeprom_write_byte(__p, __value);
}
}
void eeprom_update_word(uint16_t *__p, uint16_t __value) {
if (eeprom_read_word(__p) != __value) {
eeprom_write_word(__p, __value);
}
}
void eeprom_update_dword(uint32_t *__p, uint32_t __value) {
if (eeprom_read_dword(__p) != __value) {
eeprom_write_dword(__p, __value);
}
}
void eeprom_update_block(const void *__src, void *__dst, size_t __n) {
uint32_t *addr = convert_address(__dst);
if (addr == NULL) {
return;
}
if (memcmp(__src, addr, __n) != 0) {
eeprom_write_block(__src, __dst, __n);
}
}

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_set_dir(pin, GPIO_IN), gpio_set_function(pin, GPIO_FUNC_SIO))
#define setPinInputHigh(pin) (gpio_set_dir(pin, GPIO_IN), gpio_pull_up(pin), gpio_set_function(pin, GPIO_FUNC_SIO))
#define setPinInputLow(pin) (gpio_init(pin), gpio_set_dir(pin, GPIO_IN), gpio_pull_down(pin), gpio_set_function(pin, GPIO_FUNC_SIO))
#define setPinOutput(pin) (gpio_set_function(pin, GPIO_FUNC_SIO), gpio_set_dir(pin, GPIO_OUT))
#define writePinHigh(pin) (gpio_put(pin, 1))
#define writePinLow(pin) (gpio_put(pin, 0))
#define writePin(pin, level) ((level) ? writePinHigh(pin) : writePinLow(pin))
#define readPin(pin) (gpio_get(pin))
#define togglePin(pin) (gpio_out(pin, !gpio_get(pin)))

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

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

@ -0,0 +1,14 @@
#include "platform_deps.h"
#include "quantum.h"
#include "hardware/adc.h"
uint32_t interrupts;
void platform_setup(void) {
for (int i = 0; i < 32; i++) {
setPinInputHigh(i);
}
adc_init();
}

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

@ -0,0 +1,405 @@
CC = arm-none-eabi-gcc
OBJCOPY = arm-none-eabi-objcopy
OBJDUMP = arm-none-eabi-objdump
SIZE = arm-none-eabi-size
AR = arm-none-eabi-ar
NM = arm-none-eabi-nm
HEX = $(OBJCOPY) -O $(FORMAT)
EEP =
BIN = $(OBJCOPY) -O binary
ALLOW_WARNINGS := yes
OPT_DEFS += -DPROTOCOL_PICO
OPT_DEFS += -DSPLIT_USB_DETECT
PICOTOOL ?= picotool
RP2BOOT_ID ?= "2e8a:0003"
PICO_SDK_PATH = lib/pico-sdk
PICO_SDK_EXIST = $(shell test -d $(PICO_SDK_PATH)/src && echo true)
ifneq ($(PICO_SDK_EXIST),true)
$(error pico-sdk does not found at PICO_SDK_PATH. Please setup pico-sdk.)
endif
GIT_DESCRIBE = $(shell git describe --tags --long --dirty="\\*")
ifneq ($(PICO_FLASH_SPI_CLKDIV),)
CFLAGS += -DPICO_FLASH_SPI_CLKDIV=$(PICO_FLASH_SPI_CLKDIV)
else
CFLAGS += -DPICO_FLASH_SPI_CLKDIV=4
endif
CFLAGS += -DCFG_TUSB_DEBUG=0
CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_RP2040
CFLAGS += -DCFG_TUSB_OS=OPT_OS_PICO
CFLAGS += -DLIB_PICO_BIT_OPS=1
CFLAGS += -DLIB_PICO_BIT_OPS_PICO=1
CFLAGS += -DLIB_PICO_BOOTSEL_VIA_DOUBLE_RESET=1
CFLAGS += -DLIB_PICO_DIVIDER=1
CFLAGS += -DLIB_PICO_DIVIDER_HARDWARE=1
CFLAGS += -DLIB_PICO_DOUBLE=1
CFLAGS += -DLIB_PICO_DOUBLE_PICO=1
CFLAGS += -DLIB_PICO_FIX_RP2040_USB_DEVICE_ENUMERATION=1
CFLAGS += -DLIB_PICO_FLOAT=1
CFLAGS += -DLIB_PICO_FLOAT_PICO=1
CFLAGS += -DLIB_PICO_INT64_OPS=1
CFLAGS += -DLIB_PICO_INT64_OPS_PICO=1
CFLAGS += -DLIB_PICO_MALLOC=1
CFLAGS += -DLIB_PICO_MEM_OPS=1
CFLAGS += -DLIB_PICO_MEM_OPS_PICO=1
CFLAGS += -DLIB_PICO_PLATFORM=1
CFLAGS += -DLIB_PICO_PRINTF=1
CFLAGS += -DLIB_PICO_PRINTF_PICO=1
CFLAGS += -DLIB_PICO_RUNTIME=1
CFLAGS += -DLIB_PICO_STANDARD_LINK=1
CFLAGS += -DLIB_PICO_STDIO=1
CFLAGS += -DLIB_PICO_STDIO_UART=1
CFLAGS += -DLIB_PICO_STDLIB=1
CFLAGS += -DLIB_PICO_SYNC=1
CFLAGS += -DLIB_PICO_SYNC_CORE=1
CFLAGS += -DLIB_PICO_SYNC_CRITICAL_SECTION=1
CFLAGS += -DLIB_PICO_SYNC_MUTEX=1
CFLAGS += -DLIB_PICO_SYNC_SEM=1
CFLAGS += -DLIB_PICO_TIME=1
CFLAGS += -DLIB_PICO_UTIL=1
CFLAGS += -DLIB_TINYUSB_BOARD=1
CFLAGS += -DLIB_TINYUSB_DEVICE=1
CFLAGS += -DPICO_BOARD=\"pico\"
CFLAGS += -DPICO_BUILD=1
CFLAGS += -DPICO_COPY_TO_RAM=0
CFLAGS += -DPICO_CXX_ENABLE_EXCEPTIONS=0
CFLAGS += -DPICO_NO_FLASH=0
CFLAGS += -DPICO_NO_HARDWARE=0
CFLAGS += -DPICO_ON_DEVICE=1
CFLAGS += -DPICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1
CFLAGS += -DPICO_TARGET_NAME=\"$(TARGET)\"
CFLAGS += -DPICO_PROGRAM_VERSION_STRING=\"$(GIT_DESCRIBE)\"
CFLAGS += -DPICO_USE_BLOCKED_RAM=0
CFLAGS += -I$(PICO_SDK_PATH)
CFLAGS += -I$(PICO_SDK_PATH)/src
CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_stdlib/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_flash/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_gpio/include
CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_base/include
CFLAGS += -I$(PICO_SDK_PATH)/src/boards/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_platform/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2040/hardware_regs/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_base/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2040/hardware_structs/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_adc/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_claim/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_dma/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_sync/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_uart/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_i2c/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_spi/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_pio/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_pwm/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_divider/include
CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_time/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_timer/include
CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_sync/include
CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_util/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_runtime/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_clocks/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_irq/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_resets/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_pll/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_vreg/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_watchdog/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/hardware_xosc/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_printf/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_bootrom/include
CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_bit_ops/include
CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_divider/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_double/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_int64_ops/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_float/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_malloc/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/boot_stage2/include
CFLAGS += -I$(PICO_SDK_PATH)/src/common/pico_binary_info/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_stdio/include
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_stdio_uart/include
CFLAGS += -I$(PICO_SDK_PATH)/lib/tinyusb/src
CFLAGS += -I$(PICO_SDK_PATH)/lib/tinyusb/src/common
CFLAGS += -I$(PICO_SDK_PATH)/lib/tinyusb/hw
CFLAGS += -I$(PICO_SDK_PATH)/lib/tinyusb/src/class/hid
CFLAGS += -I$(PICO_SDK_PATH)/lib/tinyusb/src/class/cdc
CFLAGS += -I$(PICO_SDK_PATH)/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/include
CFLAGS += -mcpu=cortex-m0plus
CFLAGS += -mthumb
CFLAGS += -O0 -g3
# CFLAGS += -DNDEBUG
CFLAGS += -Wall
CFLAGS += -Wno-format
CFLAGS += -Wno-unused-function
CFLAGS += -Wno-maybe-uninitialized
CFLAGS += -ffunction-sections
CFLAGS += -fdata-sections
CFLAGS += -std=gnu11
ASFLAGS += $(CFLAGS)
LDFLAGS += -Wl,--build-id=none
LDFLAGS += --specs=nosys.specs
LDFLAGS += -Wl,--wrap=sprintf
LDFLAGS += -Wl,--wrap=snprintf
LDFLAGS += -Wl,--wrap=vsnprintf
LDFLAGS += -Wl,--wrap=__clzsi2
LDFLAGS += -Wl,--wrap=__clzdi2
LDFLAGS += -Wl,--wrap=__ctzsi2
LDFLAGS += -Wl,--wrap=__ctzdi2
LDFLAGS += -Wl,--wrap=__popcountsi2
LDFLAGS += -Wl,--wrap=__popcountdi2
LDFLAGS += -Wl,--wrap=__clz
LDFLAGS += -Wl,--wrap=__clzl
LDFLAGS += -Wl,--wrap=__clzll
LDFLAGS += -Wl,--wrap=__aeabi_idiv
LDFLAGS += -Wl,--wrap=__aeabi_idivmod
LDFLAGS += -Wl,--wrap=__aeabi_ldivmod
LDFLAGS += -Wl,--wrap=__aeabi_uidiv
LDFLAGS += -Wl,--wrap=__aeabi_uidivmod
LDFLAGS += -Wl,--wrap=__aeabi_uldivmod
LDFLAGS += -Wl,--wrap=__aeabi_dadd
LDFLAGS += -Wl,--wrap=__aeabi_ddiv
LDFLAGS += -Wl,--wrap=__aeabi_dmul
LDFLAGS += -Wl,--wrap=__aeabi_drsub
LDFLAGS += -Wl,--wrap=__aeabi_dsub
LDFLAGS += -Wl,--wrap=__aeabi_cdcmpeq
LDFLAGS += -Wl,--wrap=__aeabi_cdrcmple
LDFLAGS += -Wl,--wrap=__aeabi_cdcmple
LDFLAGS += -Wl,--wrap=__aeabi_dcmpeq
LDFLAGS += -Wl,--wrap=__aeabi_dcmplt
LDFLAGS += -Wl,--wrap=__aeabi_dcmple
LDFLAGS += -Wl,--wrap=__aeabi_dcmpge
LDFLAGS += -Wl,--wrap=__aeabi_dcmpgt
LDFLAGS += -Wl,--wrap=__aeabi_dcmpun
LDFLAGS += -Wl,--wrap=__aeabi_i2d
LDFLAGS += -Wl,--wrap=__aeabi_l2d
LDFLAGS += -Wl,--wrap=__aeabi_ui2d
LDFLAGS += -Wl,--wrap=__aeabi_ul2d
LDFLAGS += -Wl,--wrap=__aeabi_d2iz
LDFLAGS += -Wl,--wrap=__aeabi_d2lz
LDFLAGS += -Wl,--wrap=__aeabi_d2uiz
LDFLAGS += -Wl,--wrap=__aeabi_d2ulz
LDFLAGS += -Wl,--wrap=__aeabi_d2f
LDFLAGS += -Wl,--wrap=sqrt
LDFLAGS += -Wl,--wrap=cos
LDFLAGS += -Wl,--wrap=sin
LDFLAGS += -Wl,--wrap=tan
LDFLAGS += -Wl,--wrap=atan2
LDFLAGS += -Wl,--wrap=exp
LDFLAGS += -Wl,--wrap=log
LDFLAGS += -Wl,--wrap=ldexp
LDFLAGS += -Wl,--wrap=copysign
LDFLAGS += -Wl,--wrap=trunc
LDFLAGS += -Wl,--wrap=floor
LDFLAGS += -Wl,--wrap=ceil
LDFLAGS += -Wl,--wrap=round
LDFLAGS += -Wl,--wrap=sincos
LDFLAGS += -Wl,--wrap=asin
LDFLAGS += -Wl,--wrap=acos
LDFLAGS += -Wl,--wrap=atan
LDFLAGS += -Wl,--wrap=sinh
LDFLAGS += -Wl,--wrap=cosh
LDFLAGS += -Wl,--wrap=tanh
LDFLAGS += -Wl,--wrap=asinh
LDFLAGS += -Wl,--wrap=acosh
LDFLAGS += -Wl,--wrap=atanh
LDFLAGS += -Wl,--wrap=exp2
LDFLAGS += -Wl,--wrap=log2
LDFLAGS += -Wl,--wrap=exp10
LDFLAGS += -Wl,--wrap=log10
LDFLAGS += -Wl,--wrap=pow
LDFLAGS += -Wl,--wrap=powint
LDFLAGS += -Wl,--wrap=hypot
LDFLAGS += -Wl,--wrap=cbrt
LDFLAGS += -Wl,--wrap=fmod
LDFLAGS += -Wl,--wrap=drem
LDFLAGS += -Wl,--wrap=remainder
LDFLAGS += -Wl,--wrap=remquo
LDFLAGS += -Wl,--wrap=expm1
LDFLAGS += -Wl,--wrap=log1p
LDFLAGS += -Wl,--wrap=fma
LDFLAGS += -Wl,--wrap=__aeabi_lmul
LDFLAGS += -Wl,--wrap=__aeabi_fadd
LDFLAGS += -Wl,--wrap=__aeabi_fdiv
LDFLAGS += -Wl,--wrap=__aeabi_fmul
LDFLAGS += -Wl,--wrap=__aeabi_frsub
LDFLAGS += -Wl,--wrap=__aeabi_fsub
LDFLAGS += -Wl,--wrap=__aeabi_cfcmpeq
LDFLAGS += -Wl,--wrap=__aeabi_cfrcmple
LDFLAGS += -Wl,--wrap=__aeabi_cfcmple
LDFLAGS += -Wl,--wrap=__aeabi_fcmpeq
LDFLAGS += -Wl,--wrap=__aeabi_fcmplt
LDFLAGS += -Wl,--wrap=__aeabi_fcmple
LDFLAGS += -Wl,--wrap=__aeabi_fcmpge
LDFLAGS += -Wl,--wrap=__aeabi_fcmpgt
LDFLAGS += -Wl,--wrap=__aeabi_fcmpun
LDFLAGS += -Wl,--wrap=__aeabi_i2f
LDFLAGS += -Wl,--wrap=__aeabi_l2f
LDFLAGS += -Wl,--wrap=__aeabi_ui2f
LDFLAGS += -Wl,--wrap=__aeabi_ul2f
LDFLAGS += -Wl,--wrap=__aeabi_f2iz
LDFLAGS += -Wl,--wrap=__aeabi_f2lz
LDFLAGS += -Wl,--wrap=__aeabi_f2uiz
LDFLAGS += -Wl,--wrap=__aeabi_f2ulz
LDFLAGS += -Wl,--wrap=__aeabi_f2d
LDFLAGS += -Wl,--wrap=sqrtf
LDFLAGS += -Wl,--wrap=cosf
LDFLAGS += -Wl,--wrap=sinf
LDFLAGS += -Wl,--wrap=tanf
LDFLAGS += -Wl,--wrap=atan2f
LDFLAGS += -Wl,--wrap=expf
LDFLAGS += -Wl,--wrap=logf
LDFLAGS += -Wl,--wrap=ldexpf
LDFLAGS += -Wl,--wrap=copysignf
LDFLAGS += -Wl,--wrap=truncf
LDFLAGS += -Wl,--wrap=floorf
LDFLAGS += -Wl,--wrap=ceilf
LDFLAGS += -Wl,--wrap=roundf
LDFLAGS += -Wl,--wrap=sincosf
LDFLAGS += -Wl,--wrap=asinf
LDFLAGS += -Wl,--wrap=acosf
LDFLAGS += -Wl,--wrap=atanf
LDFLAGS += -Wl,--wrap=sinhf
LDFLAGS += -Wl,--wrap=coshf
LDFLAGS += -Wl,--wrap=tanhf
LDFLAGS += -Wl,--wrap=asinhf
LDFLAGS += -Wl,--wrap=acoshf
LDFLAGS += -Wl,--wrap=atanhf
LDFLAGS += -Wl,--wrap=exp2f
LDFLAGS += -Wl,--wrap=log2f
LDFLAGS += -Wl,--wrap=exp10f
LDFLAGS += -Wl,--wrap=log10f
LDFLAGS += -Wl,--wrap=powf
LDFLAGS += -Wl,--wrap=powintf
LDFLAGS += -Wl,--wrap=hypotf
LDFLAGS += -Wl,--wrap=cbrtf
LDFLAGS += -Wl,--wrap=fmodf
LDFLAGS += -Wl,--wrap=dremf
LDFLAGS += -Wl,--wrap=remainderf
LDFLAGS += -Wl,--wrap=remquof
LDFLAGS += -Wl,--wrap=expm1f
LDFLAGS += -Wl,--wrap=log1pf
LDFLAGS += -Wl,--wrap=fmaf
LDFLAGS += -Wl,--wrap=malloc
LDFLAGS += -Wl,--wrap=calloc
LDFLAGS += -Wl,--wrap=free
LDFLAGS += -Wl,--wrap=memcpy
LDFLAGS += -Wl,--wrap=memset
LDFLAGS += -Wl,--wrap=__aeabi_memcpy
LDFLAGS += -Wl,--wrap=__aeabi_memset
LDFLAGS += -Wl,--wrap=__aeabi_memcpy4
LDFLAGS += -Wl,--wrap=__aeabi_memset4
LDFLAGS += -Wl,--wrap=__aeabi_memcpy8
LDFLAGS += -Wl,--wrap=__aeabi_memset8
LDFLAGS += -Wl,-Map=$(TARGET).elf.map
LDFLAGS += -Wl,--script=$(LDSCRIPT)
LDFLAGS += -Wl,--gc-sections
LDFLAGS += -Wl,--wrap=printf
LDFLAGS += -Wl,--wrap=vprintf
LDFLAGS += -Wl,--wrap=puts
LDFLAGS += -Wl,--wrap=putchar
LDFLAGS += -Wl,--wrap=getchar
LDFLAGS += -Wl,--print-memory-usage
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_stdlib/stdlib.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_adc/adc.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_sync/sync.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_flash/flash.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_gpio/gpio.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_claim/claim.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_dma/dma.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_platform/platform.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_sync/sync.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_uart/uart.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_i2c/i2c.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_spi/spi.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_pio/pio.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_divider/divider.S
SRC += $(PICO_SDK_PATH)/src/common/pico_time/time.c
SRC += $(PICO_SDK_PATH)/src/common/pico_time/timeout_helper.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_timer/timer.c
SRC += $(PICO_SDK_PATH)/src/common/pico_sync/sem.c
SRC += $(PICO_SDK_PATH)/src/common/pico_sync/lock_core.c
SRC += $(PICO_SDK_PATH)/src/common/pico_sync/mutex.c
SRC += $(PICO_SDK_PATH)/src/common/pico_sync/critical_section.c
SRC += $(PICO_SDK_PATH)/src/common/pico_util/datetime.c
SRC += $(PICO_SDK_PATH)/src/common/pico_util/pheap.c
SRC += $(PICO_SDK_PATH)/src/common/pico_util/queue.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_runtime/runtime.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_clocks/clocks.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_irq/irq.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_irq/irq_handler_chain.S
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_pll/pll.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_vreg/vreg.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_watchdog/watchdog.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/hardware_xosc/xosc.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_printf/printf.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_bit_ops/bit_ops_aeabi.S
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_bootrom/bootrom.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_divider/divider.S
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_double/double_aeabi.S
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_double/double_init_rom.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_double/double_math.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_double/double_v1_rom_shim.S
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_float/float_aeabi.S
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_float/float_init_rom.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_float/float_math.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_float/float_v1_rom_shim.S
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_malloc/pico_malloc.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_mem_ops/mem_ops_aeabi.S
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_standard_link/crt0.S
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_standard_link/new_delete.cpp
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_standard_link/binary_info.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_stdio/stdio.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_stdio_uart/stdio_uart.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_bootsel_via_double_reset/pico_bootsel_via_double_reset.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/hw/bsp/rp2040/family.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/portable/raspberrypi/rp2040/dcd_rp2040.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/portable/raspberrypi/rp2040/rp2040_usb.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/device/usbd.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/device/usbd_control.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/audio/audio_device.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/cdc/cdc_device.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/dfu/dfu_device.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/dfu/dfu_rt_device.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/hid/hid_device.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/midi/midi_device.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/msc/msc_device.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/usbtmc/usbtmc_device.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/class/vendor/vendor_device.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/tusb.c
SRC += $(PICO_SDK_PATH)/lib/tinyusb/src/common/tusb_fifo.c
SRC += $(PICO_SDK_PATH)/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/rp2040_usb_device_enumeration.c
BOOT2INC_DIR += -I$(TMK_PATH)/$(PICO_DIR)
BOOT2INC_DIR += -I$(PICO_SDK_PATH)/src/rp2_common/boot_stage2/include
BOOT2INC_DIR += -I$(PICO_SDK_PATH)/src/rp2_common/boot_stage2/asminclude
$(KEYBOARD_OUTPUT)/src/bs2_default.o: $(PICO_SDK_PATH)/src/rp2_common/boot_stage2/compile_time_choice.S $(KEYBOARD_OUTPUT)/cflags.txt
$(CC) $(CFLAGS) $(BOOT2INC_DIR) -c -o $@ $^
$(KEYBOARD_OUTPUT)/src/bs2_default.elf: $(KEYBOARD_OUTPUT)/src/bs2_default.o
$(CC) $(CFLAGS) -Wl,--build-id=none -nostartfiles -Wl,--script=$(PICO_SDK_PATH)/src/rp2_common/boot_stage2/boot_stage2.ld $^ -o $@
$(KEYBOARD_OUTPUT)/src/bs2_default.bin: $(KEYBOARD_OUTPUT)/src/bs2_default.elf
$(OBJCOPY) -Obinary $^ $@
$(KEYBOARD_OUTPUT)/src/bs2_default_padded_checksummed.S: $(KEYBOARD_OUTPUT)/src/bs2_default.bin
$(PICO_SDK_PATH)/src/rp2_common/boot_stage2/pad_checksum -s 0xffffffff $^ $@
flash: $(BUILD_DIR)/$(TRAGET).elf cpfirmware sizeafter
until lsusb | grep -q $(RP2BOOT_ID); do\
printf "$(MSG_BOOTLOADER_NOT_FOUND)" ;\
sleep 5 ;\
done
$(PICOTOOL) load $(BUILD_DIR)/$(TARGET).elf && $(PICOTOOL) reboot
uf2: $(BUILD_DIR)/$(TARGET).bin
./util/uf2conv.py -f 0xe48bff56 -b 0x10000000 -o $(TARGET).uf2 $^

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
}

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

@ -0,0 +1,145 @@
#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) {}
bool suspend_wakeup_condition(void) {
matrix_power_up();
matrix_scan();
matrix_power_down();
for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
if (matrix_get_row(r)) return true;
}
return false;
}
/** \brief run user level code immediately after wakeup
*
* FIXME: needs doc
*/
__attribute__((weak)) void suspend_wakeup_init_user(void) {}
/** \brief run keyboard level code immediately after wakeup
*
* FIXME: needs doc
*/
__attribute__((weak)) void suspend_wakeup_init_kb(void) { suspend_wakeup_init_user(); }
/** \brief run immediately after wakeup
*
* FIXME: needs doc
*/
void suspend_wakeup_init(void) {
// clear keyboard state
clear_keyboard();
// Turn on backlight
#ifdef BACKLIGHT_ENABLE
backlight_init();
#endif
// Restore LED indicators
led_set(host_keyboard_leds());
// Wake up underglow
#if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
rgblight_wakeup();
#endif
suspend_wakeup_init_kb();
}
#if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect)
/* watchdog timeout */
ISR(WDT_vect) {
// compensate timer for sleep
switch (wdt_timeout) {
case WDTO_15MS:
timer_count += 15 + 2; // WDTO_15MS + 2(from observation)
break;
default:;
}
}
#endif

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

View file

@ -23,6 +23,10 @@
#include "wait.h"
#include "usb_util.h"
#ifdef PROTOCOL_PICO
# include "tusb.h"
#endif
#ifdef EE_HANDS
# include "eeconfig.h"
#endif
@ -57,7 +61,39 @@ static uint8_t connection_errors = 0;
volatile bool isLeftHand = true;
#if defined(SPLIT_USB_DETECT)
static bool usbIsActive(void) {
# if defined(PROTOCOL_LUFA)
static inline bool usbHasActiveConnection(void) {
return USB_Device_IsAddressSet();
}
static inline void usbDisable(void) {
USB_Disable();
USB_DeviceState = DEVICE_STATE_Unattached;
}
# elif defined(PROTOCOL_CHIBIOS)
static inline bool usbHasActiveConnection(void) {
return usbGetDriverStateI(&USBD1) == USB_ACTIVE;
}
static inline void usbDisable(void) { usbStop(&USBD1); }
# elif defined(PROTOCOL_VUSB)
static inline bool usbHasActiveConnection(void) {
usbPoll();
return usbConfiguration;
}
static inline void usbDisable(void) { usbDeviceDisconnect(); }
# elif defined(PROTOCOL_PICO)
static inline bool usbHasActiveConnection(void) {
tud_task();
return tud_connected();
}
static inline void usbDisable(void) {
# warning "No implementation" //TODO implement
}
# else
static inline bool usbHasActiveConnection(void) { return true; }
static inline void usbDisable(void) {}
# endif
bool usbIsActive(void) {
for (uint8_t i = 0; i < (SPLIT_USB_TIMEOUT / SPLIT_USB_TIMEOUT_POLL); i++) {
// This will return true if a USB connection has been established
if (usb_connected_state()) {
@ -139,7 +175,8 @@ void split_pre_init(void) {
if (isLeftHand) {
rgblight_set_clipping_range(0, num_rgb_leds_split[0]);
} else {
rgblight_set_clipping_range(num_rgb_leds_split[0], num_rgb_leds_split[1]);
rgblight_set_clipping_range(num_rgb_leds_split[0],
num_rgb_leds_split[1]);
}
#endif

View file

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

View file

@ -33,6 +33,18 @@ interrupt_setting_t store_and_clear_interrupt(void) {
void restore_interrupt_setting(interrupt_setting_t setting) {
SREG = setting;
}
#elif defined(PROTOCOL_PICO)
# include "atomic_util.h"
interrupt_setting_t store_and_clear_interrupt(void) {
return __interrupt_disable__();
}
void restore_interrupt_setting(interrupt_setting_t setting) {
__interrupt_enable__(setting);
}
#elif defined(__arm__)
# include <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,91 @@
#include "iusb.h"
#include "raw_hid.h"
#ifdef MIDI_ENABLE
# include "qmk_midi.h"
#endif
#include "usb_descriptors.h"
#include "device/usbd.h"
#include "hid_device.h"
#include "class/midi/midi_device.h"
/* declarations */
uint8_t keyboard_leds(void) {
// TODO
return 0;
}
void send_keyboard(report_keyboard_t *report) {
while (!tud_hid_n_ready(ITF_NUM_HID_KEYBOARD)) {
tud_task();
if (!tud_ready()) {
return;
}
}
tud_hid_n_report(ITF_NUM_HID_KEYBOARD, 0, report, sizeof(*report));
tud_task();
}
void send_mouse(report_mouse_t *report) {
while (!tud_hid_n_ready(ITF_NUM_HID_MOUSE)) {
tud_task();
if (!tud_ready()) {
return;
}
}
// Require only 5 bytes. Do not usb report->id
tud_hid_n_report(ITF_NUM_HID_MOUSE, 0, &report->buttons, 5);
tud_task();
}
void send_system(uint16_t data) {
while (!tud_hid_n_ready(ITF_NUM_HID_EXTRA)) {
tud_task();
if (!tud_ready()) {
return;
}
}
tud_hid_n_report(ITF_NUM_HID_EXTRA, REPORT_ID_SYSTEM, &data, 2);
tud_task();
}
void send_consumer(uint16_t data) {
while (!tud_hid_n_ready(ITF_NUM_HID_EXTRA)) {
tud_task();
if (!tud_ready()) {
return;
}
}
tud_hid_n_report(ITF_NUM_HID_EXTRA, REPORT_ID_CONSUMER, &data, 2);
tud_task();
}
void raw_hid_send(uint8_t* data, uint8_t length) {
while (!tud_hid_n_ready(ITF_NUM_HID_RAW)) {
tud_task();
if (!tud_ready()) {
return;
}
}
tud_hid_n_report(ITF_NUM_HID_RAW, 0, data, length);
tud_task();
}
#ifdef MIDI_ENABLE
void send_midi_packet(MIDI_EventPacket_t *event) {
tud_midi_packet_write((uint8_t *)event);
tud_task();
}
bool recv_midi_packet(MIDI_EventPacket_t *const event) {
if (tud_midi_available()) {
tud_midi_packet_read((uint8_t *)event);
return true;
} else {
return false;
}
}
#endif // MIDI_ENABLE

View file

@ -0,0 +1,12 @@
#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,282 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "raw_hid.h"
#include "bootloader.h"
#include "debug.h"
#include "pico_eeprom.h"
#include "usb_descriptors.h"
#include "pico/stdio/driver.h"
#include "hardware/watchdog.h"
#include "hardware/structs/watchdog.h"
#include "bsp/board.h"
#include "tusb.h"
/* TMK includes */
#include "report.h"
#include "host.h"
#include "host_driver.h"
#include "keyboard.h"
#include "action.h"
#include "action_util.h"
#include "mousekey.h"
#include "led.h"
#include "sendchar.h"
#include "debug.h"
#include "print.h"
#ifdef SLEEP_LED_ENABLE
# include "sleep_led.h"
#endif
#ifdef SERIAL_LINK_ENABLE
# include "serial_link/system/serial_link.h"
#endif
#ifdef VISUALIZER_ENABLE
# include "visualizer/visualizer.h"
#endif
#ifdef MIDI_ENABLE
# include "qmk_midi.h"
#endif
#ifdef STM32_EEPROM_ENABLE
# include "eeprom_stm32.h"
#endif
#ifdef EEPROM_DRIVER
# include "eeprom_driver.h"
#endif
#include "suspend.h"
#include "wait.h"
#include "iusb.h"
void platform_setup(void);
extern char __StackTop;
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
static int cdc_in_chars(char* buf, int len) { return 0; }
static void cdc_write(const char* buf, int length) {
static uint64_t last_avail_time;
if (tud_cdc_connected()) {
for (int i = 0; i < length;) {
int n = length - i;
int avail = tud_cdc_write_available();
if (n > avail) n = avail;
if (n) {
int n2 = tud_cdc_write(buf + i, n);
tud_task();
tud_cdc_write_flush();
i += n2;
last_avail_time = time_us_64();
} else {
tud_task();
tud_cdc_write_flush();
if (!tud_cdc_connected() || (!tud_cdc_write_available() && time_us_64() > last_avail_time + 500000)) {
break;
}
}
}
}
}
static stdio_driver_t stdio_driver = {.out_chars = cdc_write, .out_flush = NULL, .in_chars = cdc_in_chars, .next = NULL};
void pico_cdc_enable_printf(void) { stdio_set_driver_enabled(&stdio_driver, true); }
void pico_cdc_disable_printf(void) { stdio_set_driver_enabled(&stdio_driver, false); }
/* host struct */
host_driver_t driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer};
static void console_task(void) {
// TODO
}
static void raw_hid_task(void) {
// TODO
}
static void midi_ep_task(void) {}
void protocol_setup(void) { tusb_init(); }
void protocol_pre_init(void) {
// if (watchdog_caused_reboot() && watchdog_hw->scratch[0] == 0x2040dead) {
// bootloader_jump();
// }
watchdog_enable(8000, 1);
watchdog_hw->scratch[0] = 0x2040dead;
pico_cdc_enable_printf();
pico_eepemu_init();
}
void protocol_post_init(void) { host_set_driver(&driver); }
void protocol_pre_task(void) {
tud_task(); // tinyusb device task
}
void protocol_post_task(void) {
pico_eepemu_lazy_write_back();
#ifdef CONSOLE_ENABLE
console_task();
#endif
#ifdef MIDI_ENABLE
midi_ep_task();
#endif
#ifdef VIRTSER_ENABLE
virtser_task();
#endif
#ifdef RAW_ENABLE
raw_hid_task();
#endif
watchdog_update();
}
//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void) {}
// Invoked when device is unmounted
void tud_umount_cb(void) {}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en) { (void)remote_wakeup_en; }
// Invoked when usb bus is resumed
void tud_resume_cb(void) {}
// Invoked when cdc when line state changed e.g connected/disconnected
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
(void)itf;
(void)rts;
// TODO set some indicator
if (dtr) {
// Terminal connected
} else {
// Terminal disconnected
}
}
__attribute__((weak)) void pico_cdc_change_baudrate_cb(uint32_t baudrate) {
if (baudrate == 1200) {
bootloader_jump();
}
}
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding) {
(void)itf;
pico_cdc_change_baudrate_cb(p_line_coding->bit_rate);
}
__attribute__((weak)) void pico_cdc_receive_cb(uint8_t const* buf, uint32_t cnt) {
tud_cdc_write(buf, cnt);
tud_cdc_write_flush();
if (buf[0] == 's') {
printf("save keymap to eeprom\n");
pico_eepemu_flash_dynamic_keymap();
printf("complete\n");
printf("save eeconfig to eeprom\n");
pico_eepemu_flash_eeconfig();
printf("complete\n");
} else if (buf[0] == 'l') {
printf("init eeprom emulation\n");
pico_eepemu_init();
printf("complete\n");
} else if (buf[0] == 'd') {
printf("debug print ");
if (!debug_enable) {
debug_enable = true;
dprint("true\n");
} else {
debug_enable = false;
print("false\n");
}
}
}
// Invoked when CDC interface received data from host
void tud_cdc_rx_cb(uint8_t itf) {
if (tud_cdc_available()) {
// read datas
char buf[64];
uint32_t count = tud_cdc_read(buf, sizeof(buf));
(void)count;
pico_cdc_receive_cb((uint8_t*)buf, count);
}
}
// Invoked when sent REPORT successfully to host
// Application can use this to send the next report
// Note: For composite reports, report[0] is report ID
void tud_hid_report_complete_cb(uint8_t itf, uint8_t const* report, uint8_t len) {
(void)itf;
(void)len;
}
// Invoked when received GET_REPORT control request
// Application must fill buffer report's content and return its length.
// Return zero will cause the stack to STALL request
uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) {
// TODO not Implemented
(void)itf;
(void)report_id;
(void)report_type;
(void)buffer;
(void)reqlen;
return 0;
}
static uint8_t keyboard_indicator_led;
uint8_t get_keyboard_led_status(void) { return keyboard_indicator_led; }
__attribute__((weak)) void raw_hid_receive(uint8_t* data, uint8_t length) {}
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) {
switch (itf) {
case ITF_NUM_HID_RAW:
raw_hid_receive((uint8_t*)buffer, bufsize);
break;
default:
break;
}
}

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,9 @@
#pragma once
void pico_cdc_enable_printf(void);
void pico_cdc_disable_printf(void);
// weak functions
void pico_cdc_change_baudrate_cb(uint32_t baudrate);
void pico_cdc_receive_cb(uint8_t const* buf, uint32_t cnt);

View file

@ -0,0 +1,7 @@
#pragma once
void pico_eepemu_init(void);
void pico_eepemu_flash_eeconfig(void);
void pico_eepemu_flash_dynamic_keymap(void);
void pico_eepemu_lazy_write_back(void);

View file

@ -0,0 +1,32 @@
#include "pio_manager.h"
#include "hardware/pio.h"
int32_t pio_manager_get_empty_sm(PIO pio) {
check_pio_param(pio);
for (uint32_t sm = 0; sm < NUM_PIO_STATE_MACHINES; sm++) {
if (!pio_sm_is_claimed(pio, sm)) return sm;
}
return -1;
}
int32_t pio_manager_add_program(PIO pio, uint32_t sm,
pio_program_t const* program) {
check_pio_param(pio);
check_sm_param(sm);
if (pio_sm_is_claimed(pio, sm)) {
return -1;
}
if (!pio_can_add_program(pio, program)) {
return -2;
}
pio_sm_claim(pio, sm);
uint32_t offset = pio_add_program(pio, program);
return offset;
}

View file

@ -0,0 +1,8 @@
#pragma once
#include "hardware/pio.h"
int32_t pio_manager_get_empty_sm(PIO pio);
int32_t pio_manager_add_program(PIO pio, uint32_t sm,
pio_program_t const* program);

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,227 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include "tusb.h"
#include "report.h"
#include "usb_descriptors.h"
#include "util.h"
/* A combination of interfaces must have a unique product id, since PC will save
* device driver after the first plug. Same VID/PID with different interface e.g
* MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n))
#define USB_PID \
(0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4))
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0110,
// Use Interface Association Descriptor (IAD) for CDC
// As required by USB Specs IAD's subclass must be common class (2) and
// protocol must be IAD (1)
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = VENDOR_ID,
.idProduct = PRODUCT_ID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const* tud_descriptor_device_cb(void) {
return (uint8_t const*)&desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
#ifdef MIDI_ENABLE
# define MIDI_DESC_LEN TUD_MIDI_DESC_LEN
#else
# define MIDI_DESC_LEN 0
#endif
#define CONFIG_TOTAL_LEN \
(TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_DESC_LEN * 4 + \
TUD_HID_INOUT_DESC_LEN + MIDI_DESC_LEN)
#define EPNUM_HID_KEYBOARD_IN 0x81
#define EPNUM_HID_MOUSE_IN 0x82
#define EPNUM_HID_EXTRA_IN 0x83
#define EPNUM_HID_RAW_IN 0x84
#define EPNUM_HID_RAW_OUT 0x04
#define EPNUM_HID_CONSOLE_IN 0x85
#define EPNUM_CDC_NOTIF 0x86
#define EPNUM_CDC_OUT 0x07
#define EPNUM_CDC_IN 0x87
#ifdef MIDI_ENABLE
# define EPNUM_MIDI_IN 0x88
# define EPNUM_MIDI_OUT 0x08
#endif
static uint8_t const desc_keyboard[] = {TUD_HID_REPORT_DESC_KEYBOARD()};
static uint8_t const desc_mouse[] = {TUD_HID_REPORT_DESC_MOUSE()};
static uint8_t const desc_extra[] = {
TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(REPORT_ID_CONSUMER)),
TUD_HID_REPORT_DESC_SYSTEM_CONTROL(HID_REPORT_ID(REPORT_ID_SYSTEM)),
};
static uint8_t const desc_raw[] = {HID_REPORT_DESC_RAW()};
static uint8_t const desc_console[] = {HID_REPORT_DESC_CONSOLE()};
uint8_t const* tud_hid_descriptor_report_cb(uint8_t itf) {
switch (itf) {
case ITF_NUM_HID_KEYBOARD:
return desc_keyboard;
case ITF_NUM_HID_MOUSE:
return desc_mouse;
case ITF_NUM_HID_EXTRA:
return desc_extra;
case ITF_NUM_HID_RAW:
return desc_raw;
case ITF_NUM_HID_CONSOLE:
return desc_console;
default:
return NULL;
}
}
uint8_t const desc_fs_configuration[] = {
// Config number, interface count, string index, total length, attribute,
// power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, protocol, report descriptor len, EP In
// address, size & polling interval
TUD_HID_DESCRIPTOR(ITF_NUM_HID_KEYBOARD, 2, HID_ITF_PROTOCOL_NONE,
sizeof(desc_keyboard), EPNUM_HID_KEYBOARD_IN,
CFG_TUD_HID_EP_BUFSIZE, 10),
TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID_RAW, 2, HID_ITF_PROTOCOL_NONE,
sizeof(desc_raw), EPNUM_HID_RAW_OUT,
EPNUM_HID_RAW_IN, CFG_TUD_HID_EP_BUFSIZE, 10),
TUD_HID_DESCRIPTOR(ITF_NUM_HID_MOUSE, 2, HID_ITF_PROTOCOL_NONE,
sizeof(desc_mouse), EPNUM_HID_MOUSE_IN,
CFG_TUD_HID_EP_BUFSIZE, 1),
TUD_HID_DESCRIPTOR(ITF_NUM_HID_EXTRA, 2, HID_ITF_PROTOCOL_NONE,
sizeof(desc_extra), EPNUM_HID_EXTRA_IN,
CFG_TUD_HID_EP_BUFSIZE, 10),
TUD_HID_DESCRIPTOR(ITF_NUM_HID_CONSOLE, 2, HID_ITF_PROTOCOL_NONE,
sizeof(desc_console), EPNUM_HID_CONSOLE_IN,
CFG_TUD_HID_EP_BUFSIZE, 10),
// Interface number, string index, EP notification address and size, EP data
// address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 2, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT,
EPNUM_CDC_IN, 64),
#ifdef MIDI_ENABLE
TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 2, EPNUM_MIDI_OUT, EPNUM_MIDI_IN, 64)
#endif
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const* tud_descriptor_configuration_cb(uint8_t index) {
(void)index; // for multiple configurations
return desc_fs_configuration;
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
char const* string_desc_arr[] = {
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
STR(MANUFACTURER), // 1: Manufacturer
STR(PRODUCT), // 2: Product
"123456", // 3: Serials, should use chip ID
};
static uint16_t _desc_str[32];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long
// enough for transfer to complete
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
(void)langid;
uint8_t chr_count;
if (index == 0) {
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
} else {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])))
return NULL;
const char* str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
if (chr_count > 31) chr_count = 31;
// Convert ASCII string into UTF-16
for (uint8_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2);
return _desc_str;
}

View file

@ -0,0 +1,58 @@
#pragma once
#include "hid.h"
#include "usb_descriptor_common.h"
#define RAW_EPSIZE 32
#define CONSOLE_EPSIZE 32
enum {
ITF_NUM_HID_KEYBOARD,
ITF_NUM_HID_RAW,
ITF_NUM_HID_MOUSE,
ITF_NUM_HID_EXTRA,
ITF_NUM_HID_CONSOLE,
ITF_NUM_CDC,
ITF_NUM_CDC_DATA,
#ifdef MIDI_ENABLE
ITF_NUM_MIDI,
ITF_NUM_MIDI_STREAMING,
#endif
ITF_NUM_TOTAL
};
#define HID_REPORT_DESC_RAW() \
HID_USAGE_PAGE_N(RAW_USAGE_PAGE, 2), \
HID_USAGE(RAW_USAGE_ID), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_USAGE(0x62), \
HID_LOGICAL_MIN(0x00), \
HID_LOGICAL_MAX_N(0x00FF, 2), \
HID_REPORT_COUNT(RAW_EPSIZE), \
HID_REPORT_SIZE(0x08), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_USAGE(0x63), \
HID_LOGICAL_MIN(0x00), \
HID_LOGICAL_MAX_N(0x00FF, 2), \
HID_REPORT_COUNT(RAW_EPSIZE), \
HID_REPORT_SIZE(0x08), \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE | HID_NON_VOLATILE), \
HID_COLLECTION_END
#define HID_REPORT_DESC_CONSOLE() \
HID_USAGE_PAGE_N(0xFF31, 2), \
HID_USAGE(0x74), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_USAGE(0x75), \
HID_LOGICAL_MIN(0x00), \
HID_LOGICAL_MAX_N(0x00FF, 2), \
HID_REPORT_COUNT(CONSOLE_EPSIZE), \
HID_REPORT_SIZE(0x08), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_USAGE(0x76), \
HID_LOGICAL_MIN(0x00), \
HID_LOGICAL_MAX_N(0x00FF, 2), \
HID_REPORT_COUNT(CONSOLE_EPSIZE), \
HID_REPORT_SIZE(0x08), \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE | HID_NON_VOLATILE), \
HID_COLLECTION_END

View file

@ -0,0 +1,12 @@
#include "usb_util.h"
#include "tusb.h"
bool usb_connected_state(void) {
tud_task();
return tud_connected();
}
void usb_disconnect(void) {
#warning "No implementation" //TODO implement
}

View file

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