[Core] Add Raspberry Pi RP2040 support (#14877)

* Disable RESET keycode because of naming conflicts

* Add Pico SDK as submodule

* Add RP2040 build support to QMK

* Adjust USB endpoint structs for RP2040

* Add RP2040 bootloader and double-tap reset routine

* Add generic and pro micro RP2040 boards

* Add RP2040 onekey keyboard

* Add WS2812 PIO DMA enabled driver and documentation

Supports regular and open-drain output configuration. RP2040 GPIOs are
sadly not 5V tolerant, so this is a bit use-less or needs extra hardware
or you take the risk to fry your hardware.

* Adjust SIO Driver for RP2040

* Adjust I2C Driver for RP2040

* Adjust SPI Driver for RP2040

* Add PIO serial driver and documentation

* Add general RP2040 documentation

* Apply suggestions from code review

Co-authored-by: Nick Brassel <nick@tzarc.org>

Co-authored-by: Nick Brassel <nick@tzarc.org>
This commit is contained in:
Stefan Kerkmann 2022-06-30 13:19:27 +02:00 committed by GitHub
parent d206c1791e
commit d717396708
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 2026 additions and 96 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/qmk/pico-sdk.git

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

@ -200,6 +200,10 @@ ifeq ($(strip $(BOOTLOADER)), tinyuf2)
OPT_DEFS += -DBOOTLOADER_TINYUF2
BOOTLOADER_TYPE = tinyuf2
endif
ifeq ($(strip $(BOOTLOADER)), rp2040)
OPT_DEFS += -DBOOTLOADER_RP2040
BOOTLOADER_TYPE = rp2040
endif
ifeq ($(strip $(BOOTLOADER)), halfkay)
OPT_DEFS += -DBOOTLOADER_HALFKAY
BOOTLOADER_TYPE = halfkay

View File

@ -547,7 +547,7 @@ ifeq ($(strip $(BACKLIGHT_ENABLE)), yes)
endif
endif
VALID_WS2812_DRIVER_TYPES := bitbang pwm spi i2c
VALID_WS2812_DRIVER_TYPES := bitbang pwm spi i2c vendor
WS2812_DRIVER ?= bitbang
ifeq ($(strip $(WS2812_DRIVER_REQUIRED)), yes)
@ -637,7 +637,7 @@ ifneq ($(strip $(DEBOUNCE_TYPE)), custom)
endif
VALID_SERIAL_DRIVER_TYPES := bitbang usart
VALID_SERIAL_DRIVER_TYPES := bitbang usart vendor
SERIAL_DRIVER ?= bitbang
ifeq ($(filter $(SERIAL_DRIVER),$(VALID_SERIAL_DRIVER_TYPES)),)

View File

@ -116,6 +116,41 @@ ifneq ($(findstring MK66FX1M0, $(MCU)),)
BOARD ?= PJRC_TEENSY_3_6
endif
ifneq ($(findstring RP2040, $(MCU)),)
# Cortex version
MCU = cortex-m0plus
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
CHIBIOS_PORT = ARMv6-M-RP2
## chip/board settings
# - the next two should match the directories in
# <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)
# OR
# <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = RP
MCU_SERIES = RP2040
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
STARTUPLD_CONTRIB = $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/ld
MCU_LDSCRIPT ?= RP2040_FLASH
LDFLAGS += -L $(STARTUPLD_CONTRIB)
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= rp2040
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= GENERIC_PROMICRO_RP2040
# Default UF2 Bootloader settings
UF2_FAMILY ?= RP2040
FIRMWARE_FORMAT ?= uf2
endif
ifneq ($(findstring STM32F042, $(MCU)),)
# Cortex version
MCU = cortex-m0

View File

@ -101,6 +101,10 @@
"type": "string",
"pattern": "^LINE_PIN\\d{1,2}$"
},
{
"type": "string",
"pattern": "^GP\\d{1,2}$"
},
{
"type": "integer"
},

View File

@ -42,7 +42,7 @@
},
"processor": {
"type": "string",
"enum": ["cortex-m0", "cortex-m0plus", "cortex-m3", "cortex-m4", "MKL26Z64", "MK20DX128", "MK20DX256", "MK66FX1M0", "STM32F042", "STM32F072", "STM32F103", "STM32F303", "STM32F401", "STM32F405", "STM32F407", "STM32F411", "STM32F446", "STM32G431", "STM32G474", "STM32L412", "STM32L422", "STM32L432", "STM32L433", "STM32L442", "STM32L443", "GD32VF103", "WB32F3G71", "WB32FQ95", "atmega16u2", "atmega32u2", "atmega16u4", "atmega32u4", "at90usb162", "at90usb646", "at90usb647", "at90usb1286", "at90usb1287", "atmega32a", "atmega328p", "atmega328", "attiny85", "unknown"]
"enum": ["cortex-m0", "cortex-m0plus", "cortex-m3", "cortex-m4", "MKL26Z64", "MK20DX128", "MK20DX256", "MK66FX1M0", "RP2040", "STM32F042", "STM32F072", "STM32F103", "STM32F303", "STM32F401", "STM32F405", "STM32F407", "STM32F411", "STM32F446", "STM32G431", "STM32G474", "STM32L412", "STM32L422", "STM32L432", "STM32L433", "STM32L442", "STM32L443", "GD32VF103", "WB32F3G71", "WB32FQ95", "atmega16u2", "atmega32u2", "atmega16u4", "atmega32u4", "at90usb162", "at90usb646", "at90usb647", "at90usb1286", "at90usb1287", "atmega32a", "atmega328p", "atmega328", "attiny85", "unknown"]
},
"audio": {
"type": "object",
@ -86,7 +86,7 @@
},
"bootloader": {
"type": "string",
"enum": ["atmel-dfu", "bootloadhid", "bootloadHID", "custom", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "md-boot", "qmk-dfu", "qmk-hid", "stm32-dfu", "stm32duino", "gd32v-dfu", "wb32-dfu", "unknown", "usbasploader", "USBasp", "tinyuf2"],
"enum": ["atmel-dfu", "bootloadhid", "bootloadHID", "custom", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "md-boot", "qmk-dfu", "qmk-hid", "stm32-dfu", "stm32duino", "gd32v-dfu", "wb32-dfu", "unknown", "usbasploader", "USBasp", "tinyuf2", "rp2040"],
},
"bootloader_instructions": {
"type": "string",

View File

@ -165,6 +165,7 @@
* Arm/ChibiOS
* [Selecting an MCU](platformdev_selecting_arm_mcu.md)
* [Early initialization](platformdev_chibios_earlyinit.md)
* [Raspberry Pi RP2040](platformdev_rp2040.md)
* QMK Reference
* [Contributing to QMK](contributing.md)

View File

@ -65,6 +65,12 @@ You can also use any ARM chip with USB that [ChibiOS](https://www.chibios.org) s
* [MK66FX1M0](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/k-series-cortex-m4/k6x-ethernet/kinetis-k66-180-mhz-dual-high-speed-full-speed-usbs-2mb-flash-microcontrollers-mcus-based-on-arm-cortex-m4-core:K66_180)
* PJRC Teensy 3.6
### Raspberry Pi
* [RP2040](https://www.raspberrypi.com/documentation/microcontrollers/rp2040.html)
For a detailed overview about the RP2040 support by QMK see the [dedicated RP2040 page](platformdev_rp2040.md).
## Atmel ATSAM
There is limited support for one of Atmel's ATSAM microcontrollers, that being the [ATSAMD51J18A](https://www.microchip.com/wwwproducts/en/ATSAMD51J18A) used by the [Massdrop keyboards](https://github.com/qmk/qmk_firmware/tree/master/keyboards/massdrop). However, it is not recommended to design a board with this microcontroller as the support is quite specialized to Massdrop hardware.

View File

@ -362,3 +362,42 @@ CLI Flashing sequence:
### `make` Targets
* `:uf2-split-left` and `:uf2-split-right`: Flashes the firmware but also sets the handedness setting in EEPROM by generating a side specific firmware.
## Raspberry Pi RP2040 UF2
The `rules.mk` setting for this bootloader is `rp2040`, and can be specified at the keymap or user level.
To ensure compatibility with the rp2040 bootloader, make sure this block is present in your `rules.mk`:
```make
# Bootloader selection
BOOTLOADER = rp2040
```
Compatible flashers:
* Any application able to copy a file from one place to another, such as _macOS Finder_ or _Windows Explorer_.
Flashing sequence:
1. Enter the bootloader using any of the following methods:
* Tap the `QK_BOOTLOADER` keycode
* Hold the `BOOTSEL` button on the PCB while plugin in the usb cable.
* Double-tap the `RESET` button on the PCB<sup>1</sup>.
2. Wait for the OS to detect the device
3. Copy the .uf2 file to the new USB disk
4. Wait for the keyboard to become available
or
CLI Flashing sequence:
1. Enter the bootloader using any of the following methods:
* Tap the `QK_BOOTLOADER` keycode
* Hold the `BOOTSEL` button on the PCB while plugin in the usb cable.
* Double-tap the `RESET` button on the PCB<sup>1</sup>.
2. Wait for the OS to detect the device
3. Flash via QMK CLI eg. `qmk flash --keyboard handwired/onekey/rpi_pico --keymap default`
4. Wait for the keyboard to become available
<sup>1</sup>: This works only if QMK was compiled with `RP2040_BOOTLOADER_DOUBLE_TAP_RESET` defined.

125
docs/platformdev_rp2040.md Normal file
View File

@ -0,0 +1,125 @@
# Raspberry Pi RP2040
The following table shows the current driver status for peripherals on RP2040 MCUs:
| System | Support |
| ------------------------------------ | ---------------------------------------------- |
| [ADC driver](adc_driver.md) | Support planned (no ETA) |
| [Audio](audio_driver.md) | Support planned (no ETA) |
| [I2C driver](i2c_driver.md) | :heavy_check_mark: |
| [SPI driver](spi_driver.md) | :heavy_check_mark: |
| [WS2812 driver](ws2812_driver.md) | :heavy_check_mark: using `PIO` driver |
| [External EEPROMs](eeprom_driver.md) | :heavy_check_mark: using `I2C` or `SPI` driver |
| [EEPROM emulation](eeprom_driver.md) | Support planned (no ETA) |
| [serial driver](serial_driver.md) | :heavy_check_mark: using `SIO` or `PIO` driver |
| [UART driver](uart_driver.md) | Support planned (no ETA) |
## GPIO
<img alt="Raspberry Pi Pico pinout" src="https://i.imgur.com/nLaiYDE.jpg" width="48%"/>
<img alt="Sparkfun RP2040 Pro Micro pinout" src="https://i.imgur.com/1TPAhrs.jpg" width="48%"/>
!> The GPIO pins of the RP2040 are not 5V tolerant!
### Pin nomenclature
To address individual pins on the RP2040, QMK uses the `GPx` abbreviation -- where the `x` stands for the GPIO number of the pin. This number can likely be found on the official pinout diagram of your board. Note that these GPIO numbers match the RP2040 MCU datasheet, and don't necessarily match the number you see printed on the board. For instance the Raspberry Pi Pico uses numbers from 1 to 40 for their pins, but these are not identical to the RP2040's GPIO numbers. So if you want to use the pin 11 of the Pico for your keyboard, you would refer to it as `GP8` in the config files.
### Alternate functions
The RP2040 features flexible GPIO function multiplexing, this means that every pin can be connected to nearly all the internal peripherals like I2C, SPI, UART or PWM. This allows for flexible PCB designs that are much less restricted in the selection of GPIO pins. To find out which pin can use which peripheral refer to the official [Raspberry PI RP2040 datasheet](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#page=14) section 1.4.3 GPIO functions.
## Selecting hardware peripherals and drivers
QMK RP2040 support builds upon ChibiOS and thus follows their convention for activating drivers and associated hardware peripherals. These tables only give a quick overview which values have to be used, please refer to the ChibiOS specific sections on the driver pages.
### I2C Driver
| RP2040 Peripheral | `mcuconf.h` values | `I2C_DRIVER` |
| ----------------- | ------------------ | ------------ |
| `I2C0` | `RP_I2C_USE_I2C0` | `I2CD1` |
| `I2C1` | `RP_I2C_USE_I2C1` | `I2CD2` |
To configure the I2C driver please read the [ChibiOS/ARM](i2c_driver.md#arm-configuration) section.
### SPI Driver
| RP2040 Peripheral | `mcuconf.h` values | `SPI_DRIVER` |
| ----------------- | ------------------ | ------------ |
| `SPI0` | `RP_SPI_USE_SPI0` | `SPID0` |
| `SPI1` | `RP_SPI_USE_SPI1` | `SPID1` |
To configure the SPI driver please read the [ChibiOS/ARM](spi_driver.md#chibiosarm-configuration) section.
## Double-tap reset boot-loader entry :id=double-tap
The double-tap reset mechanism is an alternate way in QMK to enter the embedded mass storage UF2 boot-loader of the RP2040. It enables bootloader entry by a fast double-tap of the reset pin on start up, which is similar to the behavior of AVR Pro Micros. This feature activated by default for the Pro Micro RP2040 board, but has to be configured for other boards. To activate it, add the following options to your keyboards `config.h` file:
```c
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET // Activates the double-tap behavior
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 200U // Timeout window in ms in which the double tap can occur.
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK 0U // Specify a optional status led which blinks when entering the bootloader
```
## Pre-defined RP2040 boards
QMK defines two boards that you can choose from to base your RP2040 powered keyboard upon. These boards provide pre-configured default pins and drivers.
### Generic Pro Micro RP2040
This is the default board that is chosen, unless any other RP2040 board is selected in your keyboards `rules.mk` file. It assumes a pin layout for the I2C, SPI and Serial drivers which is identical to the Sparkfun Pro Micro RP2040, however all values can be overwritten by defining them in your keyboards `config.h` file. The [double-tap](#double-tap) reset to enter boot-loader behavior is activated by default.
| Driver configuration define | Value |
| -------------------------------------------------------------------------- | ------------------------------------ |
| **I2C driver** | |
| `I2C_DRIVER` | `I2CD2` |
| `I2C1_SDA_PIN` | `GP2` |
| `I2C1_SCL_PIN` | `GP3` |
| **SPI driver** | |
| `SPI_DRIVER` | `SPID0` |
| `SPI_SCK_PIN` | `GP18` |
| `SPI_MISO_PIN` | `GP20` |
| `SPI_MOSI_PIN` | `GP19` |
| **Serial driver** | |
| `SERIAL_USART_DRIVER` ([SIO Driver](serial_driver.md#the-sio-driver) only) | `SIOD0` |
| `SOFT_SERIAL_PIN` | undefined, use `SERIAL_USART_TX_PIN` |
| `SERIAL_USART_TX_PIN` | `GP0` |
| `SERIAL_USART_RX_PIN` | `GP1` |
?> The pin-outs of Adafruit's KB2040 and Boardsource's Blok both deviate from the Sparkfun Pro Micro RP2040. Lookup the pin-out of these boards and adjust your keyboards pin definition accordingly if you want to use these boards.
### Generic RP2040 board
This board can be chosen as a base for RP2040 keyboards which configure all necessary pins and drivers themselves and do not wish to leverage the configuration matching the Generic Pro Micro RP2040 board. Thus it doesn't provide any pre-configured pins or drivers. To select this board add the following line to your keyboards `rules.mk` file.
```make
BOARD = GENERIC_RP_RP2040
```
## Split keyboard support
Split keyboards are fully supported using the [serial driver](serial_driver.md) in both full-duplex and half-duplex configurations. Two driver subsystems are supported by the RP2040, the hardware UART based `SIO` and the Programmable IO based `PIO` driver.
| Feature | [SIO Driver](serial_driver.md#the-sio-driver) | [PIO Driver](serial_driver.md#the-pio-driver) |
| ----------------------------- | --------------------------------------------- | --------------------------------------------- |
| Half-Duplex operation | | :heavy_check_mark: |
| Full-Duplex operation | :heavy_check_mark: | :heavy_check_mark: |
| `TX` and `RX` pin swapping | | :heavy_check_mark: |
| Any GPIO as `TX` and `RX` pin | Only UART capable pins | :heavy_check_mark: |
| Simple configuration | | :heavy_check_mark: |
The `PIO` driver is much more flexible then the `SIO` driver, the only "downside" is the usage of `PIO` resources which in turn are not available for advanced user programs. Under normal circumstances, this resource allocation will be a non-issue.
## RP2040 second stage bootloader selection
As the RP2040 does not have any internal flash memory it depends on an external SPI flash memory chip to store and execute instructions from. To successfully interact with a wide variety of these chips a second stage bootloader that is compatible with the chosen external flash memory has to be supplied with each firmware image. By default an `W25Q080` compatible bootloader is assumed, but others can be chosen by adding one of the defines listed in the table below to your keyboards `config.h` file.
| Compatible with flash chip | Selection |
| :------------------------- | ---------------------------------- |
| W25Q080 | Selected by default |
| AT25SF128A | `#define RP2040_FLASH_AT25SF128A` |
| GD25Q64CS | `#define RP2040_FLASH_GD25Q64CS` |
| W25X10CL | `#define RP2040_FLASH_W25X10CL` |
| IS25LP080 | `#define RP2040_FLASH_IS25LP080` |
| Generic 03H flash | `#define RP2040_FLASH_GENERIC_03H` |

View File

@ -74,11 +74,11 @@ Targeting ARM boards based on ChibiOS, where communication is offloaded to a USA
+-------+ +-------+
```
Only one GPIO pin is needed for the Half-duplex driver, as only one wire is used for receiving and transmitting data. This pin is refereed to as the `SERIAL_USART_TX_PIN` in the configuration. Take care that the pin you chose can act as the TX pin of the USART peripheral. A simple TRS or USB cable provides enough conductors for this driver to work. As the split connection is configured to work in open-drain mode, an **external pull-up resistor is needed to keep the line high**. Resistor values of 1.5kΩ to 8.2kΩ are known to work.
Only one GPIO pin is needed for the Half-duplex driver, as only one wire is used for receiving and transmitting data. This pin is referred to as the `SERIAL_USART_TX_PIN` in the configuration. Take care that the pin you chose can act as the TX pin of the USART peripheral. A simple TRS or USB cable provides enough conductors for this driver to work. As the split connection is configured to work in open-drain mode, an **external pull-up resistor is needed to keep the line high**. Resistor values of 1.5kΩ to 8.2kΩ are known to work.
### Setup
To use the Half-duplex driver follow these steps to activate it.
To use the Half-duplex driver follow these steps to activate it. If you target the Raspberry Pi RP2040 PIO implementation skip step 1.
1. Change the `SERIAL_DRIVER` to `usart` in your keyboards `rules.mk` file:
@ -86,7 +86,13 @@ To use the Half-duplex driver follow these steps to activate it.
SERIAL_DRIVER = usart
```
2. Configure the hardware of your keyboard via the `config.h` file:
2. (RP2040 PIO only!) Change the `SERIAL_DRIVER` to `vendor` in your keyboards `rules.mk` file:
```make
SERIAL_DRIVER = vendor
```
3. Configure the hardware of your keyboard via the `config.h` file:
```c
#define SERIAL_USART_TX_PIN B6 // The GPIO pin that is used split communication.
@ -99,7 +105,7 @@ For STM32 MCUs several GPIO configuration options can be changed as well. See th
#define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7
```
3. Decide either for ChibiOS `SERIAL` or `SIO` subsystem, see the section ["Choosing a ChibiOS driver subsystem"](#choosing-a-chibios-driver-subsystem).
1. Decide either for `SERIAL`, `SIO` or `PIO` subsystem, see the section ["Choosing a driver subsystem"](#choosing-a-driver-subsystem).
<hr>
@ -125,11 +131,11 @@ Targeting ARM boards based on ChibiOS where communication is offloaded to an USA
Two GPIO pins are needed for the Full-duplex driver, as two distinct wires are used for receiving and transmitting data. The pin transmitting data is the `TX` pin and refereed to as the `SERIAL_USART_TX_PIN`, the pin receiving data is the `RX` pin and refereed to as the `SERIAL_USART_RX_PIN` in this configuration. Please note that `TX` pin of the master half has to be connected with the `RX` pin of the slave half and the `RX` pin of the master half has to be connected with the `TX` pin of the slave half! Usually this pin swap has to be done outside of the MCU e.g. with cables or on the PCB. Some MCUs like the STM32F303 used on the Proton-C allow this pin swap directly inside the MCU. A simple TRRS or USB cable provides enough conductors for this driver to work.
To use this driver the usart peripherals `TX` and `RX` pins must be configured with the correct Alternate-functions. If you are using a Proton-C everything is already setup, same is true for STM32F103 MCUs. For MCUs which are using a modern flexible GPIO configuration you have to specify these by setting `SERIAL_USART_TX_PAL_MODE` and `SERIAL_USART_RX_PAL_MODE`. Reefer to the corresponding datasheets of your MCU or find those settings in the section ["Alternate Functions for selected STM32 MCUs"](#alternate-functions-for-selected-stm32-mcus).
To use this driver the usart peripherals `TX` and `RX` pins must be configured with the correct Alternate-functions. If you are using a Proton-C everything is already setup, same is true for STM32F103 MCUs. For MCUs which are using a modern flexible GPIO configuration you have to specify these by setting `SERIAL_USART_TX_PAL_MODE` and `SERIAL_USART_RX_PAL_MODE`. Refer to the corresponding datasheets of your MCU or find those settings in the section ["Alternate Functions for selected STM32 MCUs"](#alternate-functions-for-selected-stm32-mcus).
### Setup
To use the Full-duplex driver follow these steps to activate it.
To use the Full-duplex driver follow these steps to activate it. If you target the Raspberry Pi RP2040 PIO implementation skip step 1.
1. Change the `SERIAL_DRIVER` to `usart` in your keyboards `rules.mk` file:
@ -137,7 +143,13 @@ To use the Full-duplex driver follow these steps to activate it.
SERIAL_DRIVER = usart
```
2. Configure the hardware of your keyboard via the `config.h` file:
2. (RP2040 PIO only!) Change the `SERIAL_DRIVER` to `vendor` in your keyboards `rules.mk` file:
```make
SERIAL_DRIVER = vendor
```
3. Configure the hardware of your keyboard via the `config.h` file:
```c
#define SERIAL_USART_FULL_DUPLEX // Enable full duplex operation mode.
@ -153,11 +165,11 @@ For STM32 MCUs several GPIO configuration options, including the ability for `TX
#define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7
```
3. Decide either for ChibiOS `SERIAL` or `SIO` subsystem, see the section ["Choosing a ChibiOS driver subsystem"](#choosing-a-chibios-driver-subsystem).
1. Decide either for `SERIAL`, `SIO` or `PIO` subsystem, see the section ["Choosing a driver subsystem"](#choosing-a-driver-subsystem).
<hr>
## Choosing a ChibiOS driver subsystem
## Choosing a driver subsystem
### The `SERIAL` driver
@ -219,6 +231,17 @@ Where 'n' matches the peripheral number of your selected USART on the MCU.
#define SERIAL_USART_DRIVER SIOD3
```
### The `PIO` driver
The `PIO` subsystem is a Raspberry Pi RP2040 specific implementation, using the integrated PIO peripheral and is therefore only available on this MCU. Because of the flexible nature of the PIO peripherals, **any** GPIO pin can be used as a `TX` or `RX` pin. Half-duplex and Full-duplex operation is fully supported. The Half-duplex operation mode uses the built-in pull-ups and GPIO manipulation on the RP2040 to drive the line high by default. An external pull-up is therefore not necessary.
Configure the hardware via your config.h:
```c
#define SERIAL_PIO_USE_PIO1 // Force the usage of PIO1 peripheral, by default the Serial implementation uses the PIO0 peripheral
```
The Serial PIO program uses 2 state machines, 13 instructions and the complete interrupt handler of the PIO peripheral it is running on.
<hr>
## Advanced Configuration

View File

@ -11,11 +11,12 @@ These LEDs are called "addressable" because instead of using a wire per color, e
## Supported Driver Types
| | AVR | ARM |
|----------|--------------------|--------------------|
| -------- | ------------------ | ------------------ |
| bit bang | :heavy_check_mark: | :heavy_check_mark: |
| I2C | :heavy_check_mark: | |
| SPI | | :heavy_check_mark: |
| PWM | | :heavy_check_mark: |
| PIO | | :heavy_check_mark: |
## Driver configuration
@ -55,7 +56,7 @@ The WS2812 LED communication topology depends on a serialized timed window. Diff
You can tune these parameters through the definition of the following macros:
| Macro | Default | AVR | ARM |
|---------------------|--------------------------------------------|--------------------|--------------------|
| --------------- | ---------------------------- | ------------------ | ------------------ |
| `WS2812_TIMING` | `1250` | :heavy_check_mark: | :heavy_check_mark: |
| `WS2812_T0H` | `350` | :heavy_check_mark: | :heavy_check_mark: |
| `WS2812_T0L` | `WS2812_TIMING - WS2812_T0H` | | :heavy_check_mark: |
@ -108,7 +109,7 @@ To adjust the baudrate at which the SPI peripheral is configured, users will nee
Only divisors of 2, 4, 8, 16, 32, 64, 128 and 256 are supported by hardware.
| Define | Default | Description |
|--------------------|-------|-------------------------------------|
| -------------------- | ------- | ----------------------------------- |
| `WS2812_SPI_DIVISOR` | `16` | SPI source clock peripheral divisor |
#### Testing Notes
@ -116,7 +117,7 @@ Only divisors of 2, 4, 8, 16, 32, 64, 128 and 256 are supported by hardware.
While not an exhaustive list, the following table provides the scenarios that have been partially validated:
| | SPI1 | SPI2 | SPI3 |
|------|---------------------------------------------|-----------------------------------------|-----------------------|
| ---- | ------------------------------------------- | --------------------------------------- | --------------------- |
| f072 | ? | B15 :heavy_check_mark: (needs SCK: B13) | N/A |
| f103 | A7 :heavy_check_mark: | B15 :heavy_check_mark: | N/A |
| f303 | A7 :heavy_check_mark: B5 :heavy_check_mark: | B15 :heavy_check_mark: | B5 :heavy_check_mark: |
@ -151,7 +152,7 @@ You must also turn on the PWM feature in your halconf.h and mcuconf.h
While not an exhaustive list, the following table provides the scenarios that have been partially validated:
| | Status |
|-|-|
| --------- | ------------------ |
| f072 | ? |
| f103 | :heavy_check_mark: |
| f303 | :heavy_check_mark: |
@ -159,6 +160,23 @@ While not an exhaustive list, the following table provides the scenarios that ha
*Other supported ChibiOS boards and/or pins may function, it will be highly chip and configuration dependent.*
### PIO
Targeting Raspberry Pi RP2040 boards only where WS2812 support is offloaded to an dedicated PIO implementation. This offloads processing of the WS2812 protocol from the MCU to a dedicated PIO program using DMA transfers.
To configure it, add this to your rules.mk:
```make
WS2812_DRIVER = vendor
```
Configure the hardware via your config.h:
```c
#define WS2812_PIO_USE_PIO1 // Force the usage of PIO1 peripheral, by default the WS2812 implementation uses the PIO0 peripheral
```
The WS2812 PIO programm uses 1 state machine, 4 instructions and does not use any interrupt handlers.
### Push Pull and Open Drain Configuration
The default configuration is a push pull on the defined pin.
This can be configured for bitbang, PWM and SPI.

View File

@ -1 +1,12 @@
// Copyright 2022 QMK
// SPDX-License-Identifier: GPL-2.0-or-later
#include "onekey.h"
void keyboard_post_init_kb(void) {
debug_enable = true;
debug_matrix = true;
debug_keyboard = true;
debug_mouse = true;
keyboard_post_init_user();
}

View File

@ -0,0 +1,17 @@
// Copyright 2022 Stefan Kerkmann
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "config_common.h"
#define PRODUCT Onekey Raspberry Pi RP2040
#define MATRIX_COL_PINS { GP4 }
#define MATRIX_ROW_PINS { GP5 }
#define DEBUG_MATRIX_SCAN_RATE
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED GP25
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 500U
#define RGB_DI_PIN A1

View File

@ -0,0 +1,12 @@
# Raspberry Pi 2040 onekey
To trigger keypress, short together pins *GP4* and *GP5*.
Double-tap reset to enter bootloader mode. Copy the built uf2 file to the device by dragging the file to the new USB disk.
## Supported Hardware
* Raspberry Pi Pico
* SparkFun Pro Micro - RP2040
* Adafruit KB2040 - RP2040 Kee Boar
* ...and many more RP2040 based development boards

View File

@ -0,0 +1,3 @@
# MCU name
MCU = RP2040
BOOTLOADER = rp2040

1
lib/pico-sdk Submodule

@ -0,0 +1 @@
Subproject commit 07edde8e49890d2172bbc272aacc119f999df063

View File

@ -14,7 +14,7 @@ QMK_FIRMWARE_UPSTREAM = 'qmk/qmk_firmware'
MAX_KEYBOARD_SUBFOLDERS = 5
# Supported processor types
CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'MK66FX1M0', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F405', 'STM32F407', 'STM32F411', 'STM32F446', 'STM32G431', 'STM32G474', 'STM32L412', 'STM32L422', 'STM32L432', 'STM32L433', 'STM32L442', 'STM32L443', 'GD32VF103', 'WB32F3G71', 'WB32FQ95'
CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'MK66FX1M0', 'RP2040', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F405', 'STM32F407', 'STM32F411', 'STM32F446', 'STM32G431', 'STM32G474', 'STM32L412', 'STM32L422', 'STM32L432', 'STM32L433', 'STM32L442', 'STM32L443', 'GD32VF103', 'WB32F3G71', 'WB32FQ95'
LUFA_PROCESSORS = 'at90usb162', 'atmega16u2', 'atmega32u2', 'atmega16u4', 'atmega32u4', 'at90usb646', 'at90usb647', 'at90usb1286', 'at90usb1287', None
VUSB_PROCESSORS = 'atmega32a', 'atmega328p', 'atmega328', 'attiny85'

View File

@ -21,6 +21,11 @@
# include <hal.h>
#endif
/* Include the vendor specific pin defs */
#if __has_include_next("_pin_defs.h")
# include_next "_pin_defs.h"
#endif
#define A0 PAL_LINE(GPIOA, 0)
#define A1 PAL_LINE(GPIOA, 1)
#define A2 PAL_LINE(GPIOA, 2)

View File

@ -0,0 +1,9 @@
# List of all the board related files.
BOARDSRC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040/board.c
# Required include directories
BOARDINC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040
# Shared variables
ALLCSRC += $(BOARDSRC)
ALLINC += $(BOARDINC)

View File

@ -0,0 +1,12 @@
// Copyright 2022 Stefan Kerkmann
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include_next "board.h"
#undef BOARD_RP_PICO_RP2040
#define BOARD_GENERIC_PROMICRO_RP2040
#undef BOARD_NAME
#define BOARD_NAME "Pro Micro RP2040"

View File

@ -0,0 +1,13 @@
// Copyright 2022 Stefan Kerkmann
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#define CH_CFG_SMP_MODE TRUE
#define CH_CFG_ST_RESOLUTION 32
#define CH_CFG_ST_FREQUENCY 1000000
#define CH_CFG_INTERVALS_SIZE 32
#define CH_CFG_TIME_TYPES_SIZE 32
#define CH_CFG_ST_TIMEDELTA 20
#include_next <chconf.h>

View File

@ -0,0 +1,62 @@
// Copyright 2022 Stefan Kerkmann
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
/**======================
** I2C Driver
*========================**/
#if !defined(I2C_DRIVER)
# define I2C_DRIVER I2CD2
#endif
#if !defined(I2C1_SDA_PIN)
# define I2C1_SDA_PIN GP2
#endif
#if !defined(I2C1_SCL_PIN)
# define I2C1_SCL_PIN GP3
#endif
/**======================
** SPI Driver
*========================**/
#if !defined(SPI_DRIVER)
# define SPI_DRIVER SPID0
#endif
#if !defined(SPI_SCK_PIN)
# define SPI_SCK_PIN GP18
#endif
#if !defined(SPI_MISO_PIN)
# define SPI_MISO_PIN GP20
#endif
#if !defined(SPI_MOSI_PIN)
# define SPI_MOSI_PIN GP19
#endif
/**======================
** SERIAL Driver
*========================**/
#if !defined(SERIAL_USART_DRIVER)
# define SERIAL_USART_DRIVER SIOD0
#endif
#if !defined(SERIAL_USART_TX_PIN) && !defined(SOFT_SERIAL_PIN)
# define SERIAL_USART_TX_PIN GP0
#endif
#if !defined(SERIAL_USART_RX_PIN)
# define SERIAL_USART_RX_PIN GP1
#endif
/**======================
** Double-tap
*========================**/
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET

View File

@ -0,0 +1,98 @@
/*
ChibiOS - Copyright (C) 2006..2021 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef MCUCONF_H
#define MCUCONF_H
/*
* RP2040_MCUCONF drivers configuration.
*
* IRQ priorities:
* 3...0 Lowest...Highest.
*
* DMA priorities:
* 0...1 Lowest...Highest.
*/
#define RP2040_MCUCONF
/*
* HAL driver system settings.
*/
#define RP_NO_INIT FALSE
#define RP_CORE1_START FALSE
#define RP_CORE1_VECTORS_TABLE _vectors
#define RP_CORE1_ENTRY_POINT _crt0_c1_entry
#define RP_CORE1_STACK_END __c1_main_stack_end__
/*
* IRQ system settings.
*/
#define RP_IRQ_SYSTICK_PRIORITY 2
#define RP_IRQ_TIMER_ALARM0_PRIORITY 2
#define RP_IRQ_TIMER_ALARM1_PRIORITY 2
#define RP_IRQ_TIMER_ALARM2_PRIORITY 2
#define RP_IRQ_TIMER_ALARM3_PRIORITY 2
#define RP_IRQ_UART0_PRIORITY 3
#define RP_IRQ_UART1_PRIORITY 3
#define RP_IRQ_SPI0_PRIORITY 2
#define RP_IRQ_SPI1_PRIORITY 2
#define RP_IRQ_USB0_PRIORITY 3
#define RP_IRQ_I2C0_PRIORITY 2
#define RP_IRQ_I2C1_PRIORITY 2
/*
* ADC driver system settings.
*/
#define RP_ADC_USE_ADC1 FALSE
/*
* SIO driver system settings.
*/
#define RP_SIO_USE_UART0 TRUE
#define RP_SIO_USE_UART1 FALSE
/*
* SPI driver system settings.
*/
#define RP_SPI_USE_SPI0 TRUE
#define RP_SPI_USE_SPI1 FALSE
#define RP_SPI_SPI0_RX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY
#define RP_SPI_SPI0_TX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY
#define RP_SPI_SPI1_RX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY
#define RP_SPI_SPI1_TX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY
#define RP_SPI_SPI0_DMA_PRIORITY 1
#define RP_SPI_SPI1_DMA_PRIORITY 1
#define RP_SPI_DMA_ERROR_HOOK(spip)
/*
* I2C driver system settings.
*/
#define RP_I2C_USE_I2C0 FALSE
#define RP_I2C_USE_I2C1 TRUE
#define RP_I2C_BUSY_TIMEOUT 50
#define RP_I2C_ADDRESS_MODE_10BIT FALSE
/*
* USB driver system settings.
*/
#define RP_USB_USE_USBD0 TRUE
#define RP_USB_FORCE_VBUS_DETECT TRUE
#define RP_USE_EXTERNAL_VBUS_DETECT FALSE
#define RP_USB_USE_SOF_INTR TRUE
#define RP_USB_USE_ERROR_DATA_SEQ_INTR FALSE
#endif /* MCUCONF_H */

View File

@ -0,0 +1,9 @@
# List of all the board related files.
BOARDSRC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040/board.c
# Required include directories
BOARDINC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040
# Shared variables
ALLCSRC += $(BOARDSRC)
ALLINC += $(BOARDINC)

View File

@ -0,0 +1,12 @@
// Copyright 2022 Stefan Kerkmann
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include_next "board.h"
#undef BOARD_RP_PICO_RP2040
#define BOARD_GENERIC_RP2040
#undef BOARD_NAME
#define BOARD_NAME "Generic Raspberry Pi RP2040"

View File

@ -0,0 +1,13 @@
// Copyright 2022 Stefan Kerkmann
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#define CH_CFG_SMP_MODE TRUE
#define CH_CFG_ST_RESOLUTION 32
#define CH_CFG_ST_FREQUENCY 1000000
#define CH_CFG_INTERVALS_SIZE 32
#define CH_CFG_TIME_TYPES_SIZE 32
#define CH_CFG_ST_TIMEDELTA 20
#include_next <chconf.h>

View File

@ -0,0 +1,98 @@
/*
ChibiOS - Copyright (C) 2006..2021 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef MCUCONF_H
#define MCUCONF_H
/*
* RP2040_MCUCONF drivers configuration.
*
* IRQ priorities:
* 3...0 Lowest...Highest.
*
* DMA priorities:
* 0...1 Lowest...Highest.
*/
#define RP2040_MCUCONF
/*
* HAL driver system settings.
*/
#define RP_NO_INIT FALSE
#define RP_CORE1_START FALSE
#define RP_CORE1_VECTORS_TABLE _vectors
#define RP_CORE1_ENTRY_POINT _crt0_c1_entry
#define RP_CORE1_STACK_END __c1_main_stack_end__
/*
* IRQ system settings.
*/
#define RP_IRQ_SYSTICK_PRIORITY 2
#define RP_IRQ_TIMER_ALARM0_PRIORITY 2
#define RP_IRQ_TIMER_ALARM1_PRIORITY 2
#define RP_IRQ_TIMER_ALARM2_PRIORITY 2
#define RP_IRQ_TIMER_ALARM3_PRIORITY 2
#define RP_IRQ_UART0_PRIORITY 3
#define RP_IRQ_UART1_PRIORITY 3
#define RP_IRQ_SPI0_PRIORITY 2
#define RP_IRQ_SPI1_PRIORITY 2
#define RP_IRQ_USB0_PRIORITY 3
#define RP_IRQ_I2C0_PRIORITY 2
#define RP_IRQ_I2C1_PRIORITY 2
/*
* ADC driver system settings.
*/
#define RP_ADC_USE_ADC1 FALSE
/*
* SIO driver system settings.
*/
#define RP_SIO_USE_UART0 FALSE
#define RP_SIO_USE_UART1 FALSE
/*
* SPI driver system settings.
*/
#define RP_SPI_USE_SPI0 FALSE
#define RP_SPI_USE_SPI1 FALSE
#define RP_SPI_SPI0_RX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY
#define RP_SPI_SPI0_TX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY
#define RP_SPI_SPI1_RX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY
#define RP_SPI_SPI1_TX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY
#define RP_SPI_SPI0_DMA_PRIORITY 1
#define RP_SPI_SPI1_DMA_PRIORITY 1
#define RP_SPI_DMA_ERROR_HOOK(spip)
/*
* I2C driver system settings.
*/
#define RP_I2C_USE_I2C0 FALSE
#define RP_I2C_USE_I2C1 FALSE
#define RP_I2C_BUSY_TIMEOUT 50
#define RP_I2C_ADDRESS_MODE_10BIT FALSE
/*
* USB driver system settings.
*/
#define RP_USB_USE_USBD0 TRUE
#define RP_USB_FORCE_VBUS_DETECT TRUE
#define RP_USE_EXTERNAL_VBUS_DETECT FALSE
#define RP_USB_USE_SOF_INTR TRUE
#define RP_USB_USE_ERROR_DATA_SEQ_INTR FALSE
#endif /* MCUCONF_H */

View File

@ -0,0 +1,57 @@
// Copyright 2022 Stefan Kerkmann
// SPDX-License-Identifier: GPL-2.0-or-later
#include "quantum.h"
#include "hal.h"
#include "bootloader.h"
#include "pico/bootrom.h"
#if !defined(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED)
# define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK 0U
#else
# define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK (1U << RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED)
#endif
__attribute__((weak)) void mcu_reset(void) {
NVIC_SystemReset();
}
void bootloader_jump(void) {
reset_usb_boot(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK, 0U);
}
void enter_bootloader_mode_if_requested(void) {}
#if defined(RP2040_BOOTLOADER_DOUBLE_TAP_RESET)
# if !defined(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT)
# define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 200U
# endif
// Needs to be located in a RAM section that is never initialized on boot to
// preserve its value on reset
static volatile uint32_t __attribute__((section(".ram0.bootloader_magic"))) magic_location;
const uint32_t magic_token = 0xCAFEB0BA;
// We can not use the __early_init / enter_bootloader_mode_if_requested hook as
// we depend on an already initialized system with usable memory regions and
// populated function pointer tables to the optimized math functions in the
// bootrom. This function is called just prior to main.
void __late_init(void) {
// All clocks have to be enabled before jumping to the bootloader function,
// otherwise the bootrom will be stuck infinitely.
clocks_init();
if (magic_location != magic_token) {
magic_location = magic_token;
// ChibiOS is not initialized at this point, so sleeping is only
// possible via busy waiting. The internal timer peripheral is running
// at this point with a precision of 1us.
chSysPolledDelayX(MS2RTC(1 * MHZ, RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT));
magic_location = 0;
return;
}
magic_location = 0;
reset_usb_boot(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK, 0U);
}
#endif

View File

@ -19,6 +19,27 @@
# define SPLIT_USB_DETECT // Force this on when dedicated pin is not used
#endif
#if defined(MCU_RP)
# define CPU_CLOCK RP_CORE_CLK
# define USE_GPIOV1
# define PAL_OUTPUT_TYPE_OPENDRAIN _Static_assert(0, "RP2040 has no Open Drain GPIO configuration, setting this is not possible");
# define usb_lld_endpoint_fields
# define I2C1_SCL_PAL_MODE (PAL_MODE_ALTERNATE_I2C | PAL_RP_PAD_SLEWFAST | PAL_RP_PAD_PUE | PAL_RP_PAD_DRIVE4)
# define I2C1_SDA_PAL_MODE I2C1_SCL_PAL_MODE
# define USE_I2CV1_CONTRIB
# if !defined(I2C1_CLOCK_SPEED)
# define I2C1_CLOCK_SPEED 400000
# endif
# define SPI_SCK_PAL_MODE (PAL_MODE_ALTERNATE_SPI | PAL_RP_PAD_SLEWFAST | PAL_RP_PAD_DRIVE4)
# define SPI_MOSI_PAL_MODE SPI_SCK_PAL_MODE
# define SPI_MISO_PAL_MODE SPI_SCK_PAL_MODE
#endif
// STM32 compatibility
#if defined(MCU_STM32)
# define CPU_CLOCK STM32_SYSCLK

View File

@ -8,12 +8,12 @@
#if defined(SERIAL_USART_CONFIG)
static QMKSerialConfig serial_config = SERIAL_USART_CONFIG;
#else
#elif defined(MCU_STM32) /* STM32 MCUs */
static QMKSerialConfig serial_config = {
# if HAL_USE_SERIAL
.speed = (SERIAL_USART_SPEED), /* baudrate - mandatory */
.speed = (SERIAL_USART_SPEED),
# else
.baud = (SERIAL_USART_SPEED), /* baudrate - mandatory */
.baud = (SERIAL_USART_SPEED),
# endif
.cr1 = (SERIAL_USART_CR1),
.cr2 = (SERIAL_USART_CR2),
@ -23,6 +23,19 @@ static QMKSerialConfig serial_config = {
.cr3 = (SERIAL_USART_CR3)
# endif
};
#elif defined(MCU_RP) /* Raspberry Pi MCUs */
/* USART in 8E2 config with RX and TX FIFOs enabled. */
// clang-format off
static QMKSerialConfig serial_config = {
.baud = (SERIAL_USART_SPEED),
.UARTLCR_H = UART_UARTLCR_H_WLEN_8BITS | UART_UARTLCR_H_PEN | UART_UARTLCR_H_STP2 | UART_UARTLCR_H_FEN,
.UARTCR = 0U,
.UARTIFLS = UART_UARTIFLS_RXIFLSEL_1_8F | UART_UARTIFLS_TXIFLSEL_1_8E,
.UARTDMACR = 0U
};
// clang-format on
#else
# error MCU Familiy not supported by default, supply your own serial_config by defining SERIAL_USART_CONFIG in your keyboard files.
#endif
static QMKSerialDriver* serial_driver = (QMKSerialDriver*)&SERIAL_USART_DRIVER;
@ -156,7 +169,7 @@ inline bool serial_transport_receive_blocking(uint8_t* destination, const size_t
* @brief Initiate pins for USART peripheral. Half-duplex configuration.
*/
__attribute__((weak)) void usart_init(void) {
# if defined(MCU_STM32)
# if defined(MCU_STM32) /* STM32 MCUs */
# if defined(USE_GPIOV1)
palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_OPENDRAIN);
# else
@ -166,6 +179,8 @@ __attribute__((weak)) void usart_init(void) {
# if defined(USART_REMAP)
USART_REMAP;
# endif
# elif defined(MCU_RP) /* Raspberry Pi MCUs */
# error Half-duplex with the SIO driver is not supported due to hardware limitations on the RP2040, switch to the PIO driver which has half-duplex support.
# else
# pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files."
# endif
@ -177,7 +192,7 @@ __attribute__((weak)) void usart_init(void) {
* @brief Initiate pins for USART peripheral. Full-duplex configuration.
*/
__attribute__((weak)) void usart_init(void) {
# if defined(MCU_STM32)
# if defined(MCU_STM32) /* STM32 MCUs */
# if defined(USE_GPIOV1)
palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_PUSHPULL);
palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT);
@ -189,6 +204,9 @@ __attribute__((weak)) void usart_init(void) {
# if defined(USART_REMAP)
USART_REMAP;
# endif
# elif defined(MCU_RP) /* Raspberry Pi MCUs */
palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_UART);
palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE_UART);
# else
# pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files."
# endif

View File

@ -20,7 +20,7 @@
static pin_t currentSlavePin = NO_PIN;
#if defined(K20x) || defined(KL2x)
#if defined(K20x) || defined(KL2x) || defined(RP2040)
static SPIConfig spiConfig = {NULL, 0, 0, 0};
#else
static SPIConfig spiConfig = {false, NULL, 0, 0, 0, 0};
@ -167,7 +167,36 @@ bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {
spiConfig.SPI_CPOL = SPI_CPOL_High;
break;
}
#elif defined(MCU_RP)
if (lsbFirst) {
osalDbgAssert(lsbFirst == false, "RP2040s PrimeCell SPI implementation does not support sending LSB first.");
}
// Motorola frame format and 8bit transfer data size.
spiConfig.SSPCR0 = SPI_SSPCR0_FRF_MOTOROLA | SPI_SSPCR0_DSS_8BIT;
// Serial output clock = (ck_sys or ck_peri) / (SSPCPSR->CPSDVSR * (1 +
// SSPCR0->SCR)). SCR is always set to zero, as QMK SPI API expects the
// passed divisor to be the only value to divide the input clock by.
spiConfig.SSPCPSR = roundedDivisor; // Even number from 2 to 254
switch (mode) {
case 0:
spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPO; // Clock polarity: low
spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPH; // Clock phase: sample on first edge
break;
case 1:
spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPO; // Clock polarity: low
spiConfig.SSPCR0 |= SPI_SSPCR0_SPH; // Clock phase: sample on second edge transition
break;
case 2:
spiConfig.SSPCR0 |= SPI_SSPCR0_SPO; // Clock polarity: high
spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPH; // Clock phase: sample on first edge
break;
case 3:
spiConfig.SSPCR0 |= SPI_SSPCR0_SPO; // Clock polarity: high
spiConfig.SSPCR0 |= SPI_SSPCR0_SPH; // Clock phase: sample on second edge transition
break;
}
#else
spiConfig.cr1 = 0;

View File

@ -0,0 +1,457 @@
// Copyright 2022 Stefan Kerkmann
// SPDX-License-Identifier: GPL-2.0-or-later
#include "quantum.h"
#include "serial_usart.h"
#include "serial_protocol.h"
#include "hardware/pio.h"
#include "hardware/clocks.h"
#if !defined(MCU_RP)
# error PIO Driver is only available for Raspberry Pi 2040 MCUs!
#endif
static inline bool receive_impl(uint8_t* destination, const size_t size, sysinterval_t timeout);
static inline bool send_impl(const uint8_t* source, const size_t size);
static inline void pio_serve_interrupt(void);
#define MSG_PIO_ERROR ((msg_t)(-3))
#if defined(SERIAL_PIO_USE_PIO1)
static const PIO pio = pio1;
OSAL_IRQ_HANDLER(RP_PIO1_IRQ_0_HANDLER) {
OSAL_IRQ_PROLOGUE();
pio_serve_interrupt();
OSAL_IRQ_EPILOGUE();
}
#else
static const PIO pio = pio0;
OSAL_IRQ_HANDLER(RP_PIO0_IRQ_0_HANDLER) {
OSAL_IRQ_PROLOGUE();
pio_serve_interrupt();
OSAL_IRQ_EPILOGUE();
}
#endif
#define UART_TX_WRAP_TARGET 0
#define UART_TX_WRAP 3
// clang-format off
#if defined(SERIAL_USART_FULL_DUPLEX)
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
};
#else
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]
0x6081, // 2: out pindirs, 1
0x0642, // 3: jmp x--, 2 [6]
// .wrap
};
#endif
// clang-format on
static const pio_program_t uart_tx_program = {
.instructions = uart_tx_program_instructions,
.length = 4,
.origin = -1,
};
#define UART_RX_WRAP_TARGET 0
#define UART_RX_WRAP 8
// clang-format off
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
0xc020, // 5: irq wait 0
0x20a0, // 6: wait 1 pin, 0
0x0000, // 7: jmp 0
0x8020, // 8: push block
// .wrap
};
// clang-format on
static const pio_program_t uart_rx_program = {
.instructions = uart_rx_program_instructions,
.length = 9,
.origin = -1,
};
thread_reference_t rx_thread = NULL;
static int rx_state_machine = -1;
thread_reference_t tx_thread = NULL;
static int tx_state_machine = -1;
void pio_serve_interrupt(void) {
uint32_t irqs = pio->ints0;
// The RX FIFO is not empty any more, therefore wake any sleeping rx thread
if (irqs & (PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS << rx_state_machine)) {
// Disable rx not empty interrupt
pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, false);
osalSysLockFromISR();
osalThreadResumeI(&rx_thread, MSG_OK);
osalSysUnlockFromISR();
}
// The TX FIFO is not full any more, therefore wake any sleeping tx thread
if (irqs & (PIO_IRQ0_INTF_SM0_TXNFULL_BITS << tx_state_machine)) {
// Disable tx not full interrupt
pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, false);
osalSysLockFromISR();
osalThreadResumeI(&tx_thread, MSG_OK);
osalSysUnlockFromISR();
}
// IRQ 0 is set on framing or break errors by the rx state machine
if (pio_interrupt_get(pio, 0UL)) {
pio_interrupt_clear(pio, 0UL);
osalSysLockFromISR();
osalThreadResumeI(&rx_thread, MSG_PIO_ERROR);
osalSysUnlockFromISR();
}
}
#if !defined(SERIAL_USART_FULL_DUPLEX)
// The internal pull-ups of the RP2040 are rather weakish with a range of 50k to
// 80k, which in turn do not provide enough current to guarantee fast signal rise
// times with a parasitic capacitance of greater than 100pf. In real world
// applications, like split keyboards which might have vias in the signal path
// or long PCB traces, this prevents a successful communication. The solution
// is to temporarily augment the weak pull ups from the receiving side by
// driving the tx pin high. On the receiving side the lowest possible drive
// strength is chosen because the transmitting side must still be able to drive
// the signal low. With this configuration the rise times are fast enough and
// the generated low level with 360mV will generate a logical zero.
static inline void enter_rx_state(void) {
osalSysLock();
// Wait for the transmitting state machines FIFO to run empty. At this point
// the last byte has been pulled from the transmitting state machines FIFO
// into the output shift register. We have to wait a tiny bit more until
// this byte is transmitted, before we can turn on the receiving state
// machine again.
while (!pio_sm_is_tx_fifo_empty(pio, tx_state_machine)) {
}
// Wait for ~11 bits, 1 start bit + 8 data bits + 1 stop bit + 1 bit
// headroom.
chSysPolledDelayX(US2RTC(1 * MHZ, (1000000U * 11 / SERIAL_USART_SPEED)));
// Disable tx state machine to not interfere with our tx pin manipulation
pio_sm_set_enabled(pio, tx_state_machine, false);
gpio_set_drive_strength(SERIAL_USART_TX_PIN, GPIO_DRIVE_STRENGTH_2MA);
pio_sm_set_pins_with_mask(pio, tx_state_machine, 1U << SERIAL_USART_TX_PIN, 1U << SERIAL_USART_TX_PIN);
pio_sm_set_consecutive_pindirs(pio, tx_state_machine, SERIAL_USART_TX_PIN, 1U, false);
pio_sm_set_enabled(pio, rx_state_machine, true);
osalSysUnlock();
}
static inline void leave_rx_state(void) {
osalSysLock();
// In Half-duplex operation the tx pin dual-functions as sender and
// receiver. To not receive the data we will send, we disable the receiving
// state machine.
pio_sm_set_enabled(pio, rx_state_machine, false);
pio_sm_set_consecutive_pindirs(pio, tx_state_machine, SERIAL_USART_TX_PIN, 1U, true);
pio_sm_set_pins_with_mask(pio, tx_state_machine, 0U, 1U << SERIAL_USART_TX_PIN);
gpio_set_drive_strength(SERIAL_USART_TX_PIN, GPIO_DRIVE_STRENGTH_12MA);
pio_sm_restart(pio, tx_state_machine);
pio_sm_set_enabled(pio, tx_state_machine, true);
osalSysUnlock();
}
#else
// All this trickery is gladly not necessary for full-duplex.
static inline void enter_rx_state(void) {}
static inline void leave_rx_state(void) {}
#endif
/**
* @brief Clear the RX and TX hardware FIFOs of the state machines.
*/
inline void serial_transport_driver_clear(void) {
osalSysLock();
pio_sm_clear_fifos(pio, rx_state_machine);
pio_sm_clear_fifos(pio, tx_state_machine);
osalSysUnlock();
}
static inline msg_t sync_tx(sysinterval_t timeout) {
msg_t msg = MSG_OK;
osalSysLock();
while (pio_sm_is_tx_fifo_full(pio, tx_state_machine)) {
pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, true);
msg = osalThreadSuspendTimeoutS(&tx_thread, timeout);
if (msg < MSG_OK) {
break;
}
}
osalSysUnlock();
return msg;
}
static inline bool send_impl(const uint8_t* source, const size_t size) {
size_t send = 0;
msg_t msg;
while (send < size) {
msg = sync_tx(TIME_MS2I(SERIAL_USART_TIMEOUT));
if (msg < MSG_OK) {
return false;
}
osalSysLock();
while (send < size) {
if (pio_sm_is_tx_fifo_full(pio, tx_state_machine)) {
break;
}
if (send >= size) {
break;
}
pio_sm_put(pio, tx_state_machine, (uint32_t)(*source));
source++;
send++;
}
osalSysUnlock();
}
return send == size;
}
/**
* @brief Blocking send of buffer with timeout.
*
* @return true Send success.
* @return false Send failed.
*/
inline bool serial_transport_send(const uint8_t* source, const size_t size) {
leave_rx_state();
bool result = send_impl(source, size);
enter_rx_state();
return result;
}
static inline msg_t sync_rx(sysinterval_t timeout) {
msg_t msg = MSG_OK;
osalSysLock();
while (pio_sm_is_rx_fifo_empty(pio, rx_state_machine)) {
pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, true);
msg = osalThreadSuspendTimeoutS(&rx_thread, timeout);
if (msg < MSG_OK) {
break;
}
}
osalSysUnlock();
return msg;
}
static inline bool receive_impl(uint8_t* destination, const size_t size, sysinterval_t timeout) {
size_t read = 0U;
while (read < size) {
msg_t msg = sync_rx(timeout);
if (msg < MSG_OK) {
return false;
}
osalSysLock();
while (true) {
if (pio_sm_is_rx_fifo_empty(pio, rx_state_machine)) {
break;
}
if (read >= size) {
break;
}
*destination++ = *((uint8_t*)&pio->rxf[rx_state_machine] + 3U);
read++;
}
osalSysUnlock();
}
return read == size;
}
/**
* @brief Blocking receive of size * bytes with timeout.
*
* @return true Receive success.
* @return false Receive failed, e.g. by timeout.
*/
inline bool serial_transport_receive(uint8_t* destination, const size_t size) {
return receive_impl(destination, size, TIME_MS2I(SERIAL_USART_TIMEOUT));
}
/**
* @brief Blocking receive of size * bytes.
*
* @return true Receive success.
* @return false Receive failed.
*/
inline bool serial_transport_receive_blocking(uint8_t* destination, const size_t size) {
return receive_impl(destination, size, TIME_INFINITE);
}
static inline void pio_tx_init(pin_t tx_pin) {
uint pio_idx = pio_get_index(pio);
uint offset = pio_add_program(pio, &uart_tx_program);
#if defined(SERIAL_USART_FULL_DUPLEX)
// clang-format off
iomode_t tx_pin_mode = PAL_RP_GPIO_OE |
PAL_RP_PAD_SLEWFAST |
PAL_RP_PAD_DRIVE4 |
(pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1);
// clang-format on
pio_sm_set_pins_with_mask(pio, tx_state_machine, 1U << tx_pin, 1U << tx_pin);
pio_sm_set_consecutive_pindirs(pio, tx_state_machine, tx_pin, 1U, true);
#else
// clang-format off
iomode_t tx_pin_mode = PAL_RP_PAD_IE |
PAL_RP_GPIO_OE |
PAL_RP_PAD_SCHMITT |
PAL_RP_PAD_PUE |
PAL_RP_PAD_SLEWFAST |
PAL_RP_PAD_DRIVE12 |
PAL_RP_IOCTRL_OEOVER_DRVINVPERI |
(pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1);
// clang-format on
pio_sm_set_pins_with_mask(pio, tx_state_machine, 0U << tx_pin, 1U << tx_pin);
pio_sm_set_consecutive_pindirs(pio, tx_state_machine, tx_pin, 1U, true);
#endif
palSetLineMode(tx_pin, tx_pin_mode);
pio_sm_config config = pio_get_default_sm_config();
sm_config_set_wrap(&config, offset + UART_TX_WRAP_TARGET, offset + UART_TX_WRAP);
#if defined(SERIAL_USART_FULL_DUPLEX)
sm_config_set_sideset(&config, 2, true, false);
#else
sm_config_set_sideset(&config, 2, true, true);
#endif
// OUT shifts to right, no autopull
sm_config_set_out_shift(&config, 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(&config, tx_pin, 1);
sm_config_set_sideset_pins(&config, tx_pin);
// We only need TX, so get an 8-deep FIFO!
sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX);
// SM transmits 1 bit per 8 execution cycles.
float div = (float)clock_get_hz(clk_sys) / (8 * SERIAL_USART_SPEED);
sm_config_set_clkdiv(&config, div);
pio_sm_init(pio, tx_state_machine, offset, &config);
pio_sm_set_enabled(pio, tx_state_machine, true);
}
static inline void pio_rx_init(pin_t rx_pin) {
uint offset = pio_add_program(pio, &uart_rx_program);
#if defined(SERIAL_USART_FULL_DUPLEX)
uint pio_idx = pio_get_index(pio);
pio_sm_set_consecutive_pindirs(pio, rx_state_machine, rx_pin, 1, false);
// clang-format off
iomode_t rx_pin_mode = PAL_RP_PAD_IE |
PAL_RP_PAD_SCHMITT |
PAL_RP_PAD_PUE |
(pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1);
// clang-format on
palSetLineMode(rx_pin, rx_pin_mode);
#endif
pio_sm_config config = pio_get_default_sm_config();
sm_config_set_wrap(&config, offset + UART_RX_WRAP_TARGET, offset + UART_RX_WRAP);
sm_config_set_in_pins(&config, rx_pin); // for WAIT, IN
sm_config_set_jmp_pin(&config, rx_pin); // for JMP
// Shift to right, autopush disabled
sm_config_set_in_shift(&config, true, false, 32);
// Deeper FIFO as we're not doing any TX
sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_RX);
// SM transmits 1 bit per 8 execution cycles.
float div = (float)clock_get_hz(clk_sys) / (8 * SERIAL_USART_SPEED);
sm_config_set_clkdiv(&config, div);
pio_sm_init(pio, rx_state_machine, offset, &config);
pio_sm_set_enabled(pio, rx_state_machine, true);
}
static inline void pio_init(pin_t tx_pin, pin_t rx_pin) {
uint pio_idx = pio_get_index(pio);
/* Get PIOx peripheral out of reset state. */
hal_lld_peripheral_unreset(pio_idx == 0 ? RESETS_ALLREG_PIO0 : RESETS_ALLREG_PIO1);
tx_state_machine = pio_claim_unused_sm(pio, true);
if (tx_state_machine < 0) {
dprintln("ERROR: Failed to acquire state machine for serial transmission!");
return;
}
pio_tx_init(tx_pin);
rx_state_machine = pio_claim_unused_sm(pio, true);
if (rx_state_machine < 0) {
dprintln("ERROR: Failed to acquire state machine for serial reception!");
return;
}
pio_rx_init(rx_pin);
// Enable error flag IRQ source for rx state machine
pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, true);
pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, true);
pio_set_irq0_source_enabled(pio, pis_interrupt0, true);
// Enable PIO specific interrupt vector
#if defined(SERIAL_PIO_USE_PIO1)
nvicEnableVector(RP_PIO1_IRQ_0_NUMBER, RP_IRQ_UART0_PRIORITY);
#else
nvicEnableVector(RP_PIO0_IRQ_0_NUMBER, RP_IRQ_UART0_PRIORITY);
#endif
enter_rx_state();
}
/**
* @brief PIO driver specific initialization function for the master side.
*/
void serial_transport_driver_master_init(void) {
#if defined(SERIAL_USART_FULL_DUPLEX)
pin_t tx_pin = SERIAL_USART_TX_PIN;
pin_t rx_pin = SERIAL_USART_RX_PIN;
#else
pin_t tx_pin = SERIAL_USART_TX_PIN;
pin_t rx_pin = SERIAL_USART_TX_PIN;
#endif
#if defined(SERIAL_USART_PIN_SWAP)
pio_init(rx_pin, tx_pin);
#else
pio_init(tx_pin, rx_pin);
#endif
}
/**
* @brief PIO driver specific initialization function for the slave side.
*/
void serial_transport_driver_slave_init(void) {
#if defined(SERIAL_USART_FULL_DUPLEX)
pin_t tx_pin = SERIAL_USART_TX_PIN;
pin_t rx_pin = SERIAL_USART_RX_PIN;
#else
pin_t tx_pin = SERIAL_USART_TX_PIN;
pin_t rx_pin = SERIAL_USART_TX_PIN;
#endif
pio_init(tx_pin, rx_pin);
}

View File

@ -0,0 +1,189 @@
// Copyright 2022 Stefan Kerkmann
// SPDX-License-Identifier: GPL-2.0-or-later
#include "quantum.h"
#include "ws2812.h"
#include "hardware/pio.h"
#include "hardware/clocks.h"
#if !defined(MCU_RP)
# error PIO Driver is only available for Raspberry Pi 2040 MCUs!
#endif
#if defined(WS2812_PIO_USE_PIO1)
static const PIO pio = pio1;
#else
static const PIO pio = pio0;
#endif
#if !defined(RP_DMA_PRIORITY_WS2812)
# define RP_DMA_PRIORITY_WS2812 12
#endif
static int state_machine = -1;
#define WS2812_WRAP_TARGET 0
#define WS2812_WRAP 3
#define WS2812_T1 2
#define WS2812_T2 5
#define WS2812_T3 3
#if defined(WS2812_EXTERNAL_PULLUP)
# pragma message "The GPIOs of the RP2040 are NOT 5V tolerant! Make sure to NOT apply any voltage over 3.3V to the RGB data pin."
// clang-format off
static const uint16_t ws2812_program_instructions[] = {
// .wrap_target
0x7221, // 0: out x, 1 side 1 [2]
0x0123, // 1: jmp !x, 3 side 0 [1]
0x0400, // 2: jmp 0 side 0 [4]
0xb442, // 3: nop side 1 [4]
// .wrap
};
#else
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
};
// clang-format on
#endif
static const pio_program_t ws2812_program = {
.instructions = ws2812_program_instructions,
.length = 4,
.origin = -1,
};
static uint32_t WS2812_BUFFER[RGBLED_NUM];
static const rp_dma_channel_t* WS2812_DMA_CHANNEL;
bool ws2812_init(void) {
uint pio_idx = pio_get_index(pio);
/* Get PIOx peripheral out of reset state. */
hal_lld_peripheral_unreset(pio_idx == 0 ? RESETS_ALLREG_PIO0 : RESETS_ALLREG_PIO1);
// clang-format off
iomode_t rgb_pin_mode = PAL_RP_PAD_SLEWFAST |
PAL_RP_GPIO_OE |
(pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1);
// clang-format on
palSetLineMode(RGB_DI_PIN, rgb_pin_mode);
state_machine = pio_claim_unused_sm(pio, true);
if (state_machine < 0) {
dprintln("ERROR: Failed to acquire state machine for WS2812 output!");
return false;
}
uint offset = pio_add_program(pio, &ws2812_program);
pio_sm_set_consecutive_pindirs(pio, state_machine, RGB_DI_PIN, 1, true);
pio_sm_config config = pio_get_default_sm_config();
sm_config_set_wrap(&config, offset + WS2812_WRAP_TARGET, offset + WS2812_WRAP);
sm_config_set_sideset_pins(&config, RGB_DI_PIN);
sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX);
#if defined(WS2812_EXTERNAL_PULLUP)
/* Instruct side-set to change the pin-directions instead of outputting
* a logic level. We generate our levels the following way:
*
* 1: Set RGB data pin to high impedance input and let the pull-up drive the
* signal high.
*
* 0: Set RGB data pin to low impedance output and drive the pin low.
*/
sm_config_set_sideset(&config, 1, false, true);
#else
sm_config_set_sideset(&config, 1, false, false);
#endif
#if defined(RGBW)
sm_config_set_out_shift(&config, false, true, 32);
#else
sm_config_set_out_shift(&config, false, true, 24);
#endif
int cycles_per_bit = WS2812_T1 + WS2812_T2 + WS2812_T3;
float div = clock_get_hz(clk_sys) / (800.0f * KHZ * cycles_per_bit);
sm_config_set_clkdiv(&config, div);
pio_sm_init(pio, state_machine, offset, &config);
pio_sm_set_enabled(pio, state_machine, true);
WS2812_DMA_CHANNEL = dmaChannelAlloc(RP_DMA_CHANNEL_ID_ANY, RP_DMA_PRIORITY_WS2812, NULL, NULL);
// clang-format off
uint32_t mode = DMA_CTRL_TRIG_INCR_READ |
DMA_CTRL_TRIG_DATA_SIZE_WORD |
DMA_CTRL_TRIG_IRQ_QUIET |
DMA_CTRL_TRIG_TREQ_SEL(pio_idx == 0 ? state_machine : state_machine + 8);
// clang-format on
dmaChannelSetModeX(WS2812_DMA_CHANNEL, mode);
dmaChannelSetDestinationX(WS2812_DMA_CHANNEL, (uint32_t)&pio->txf[state_machine]);
return true;
}
/**
* @brief Convert RGBW value into WS2812 compatible 32-bit data word.
*/
__always_inline static uint32_t rgbw8888_to_u32(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) {
#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)
return ((uint32_t)green << 24) | ((uint32_t)red << 16) | ((uint32_t)blue << 8) | ((uint32_t)white);
#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)
return ((uint32_t)red << 24) | ((uint32_t)green << 16) | ((uint32_t)blue << 8) | ((uint32_t)white);
#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)
return ((uint32_t)blue << 24) | ((uint32_t)green << 16) | ((uint32_t)red << 8) | ((uint32_t)white);
#endif
}
static inline void sync_ws2812_transfer(void) {
if (unlikely(dmaChannelIsBusyX(WS2812_DMA_CHANNEL) || !pio_sm_is_tx_fifo_empty(pio, state_machine))) {
fast_timer_t start = timer_read_fast();
do {
// Abort the synchronization if we have to wait longer than the total
// count of LEDs in millisecounds. This is safely much longer than it
// would take to push all the data out.
if (unlikely(timer_elapsed_fast(start) > RGBLED_NUM)) {
dprintln("ERROR: WS2812 DMA transfer has stalled, aborting!");
dmaChannelDisableX(WS2812_DMA_CHANNEL);
return;
}
} while (dmaChannelIsBusyX(WS2812_DMA_CHANNEL) || !pio_sm_is_tx_fifo_empty(pio, state_machine));
// We wait for the WS2812 chain to reset after all data has been pushed
// out.
wait_us(WS2812_TRST_US);
}
}
void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) {
static bool is_initialized = false;
if (unlikely(!is_initialized)) {
is_initialized = ws2812_init();
}
sync_ws2812_transfer();
for (int i = 0; i < leds; i++) {
#if defined(RGBW)
WS2812_BUFFER[i] = rgbw8888_to_u32(ledarray[i].r, ledarray[i].g, ledarray[i].b, ledarray[i].w);
#else
WS2812_BUFFER[i] = rgbw8888_to_u32(ledarray[i].r, ledarray[i].g, ledarray[i].b, 0);
#endif
}
dmaChannelSetSourceX(WS2812_DMA_CHANNEL, (uint32_t)WS2812_BUFFER);
dmaChannelSetCounterX(WS2812_DMA_CHANNEL, leds);
dmaChannelEnableX(WS2812_DMA_CHANNEL);
}

View File

@ -108,6 +108,8 @@ else ifeq ($(strip $(BOOTLOADER)),kiibohd)
$(UNSYNC_OUTPUT_CMD) && $(call EXEC_DFU_UTIL)
else ifeq ($(strip $(BOOTLOADER)),tinyuf2)
$(UNSYNC_OUTPUT_CMD) && $(call EXEC_UF2_UTIL_DEPLOY)
else ifeq ($(strip $(BOOTLOADER)),rp2040)
$(UNSYNC_OUTPUT_CMD) && $(call EXEC_UF2_UTIL_DEPLOY)
else ifeq ($(strip $(MCU_FAMILY)),KINETIS)
$(UNSYNC_OUTPUT_CMD) && $(call EXEC_TEENSY)
else ifeq ($(strip $(MCU_FAMILY)),MIMXRT1062)

View File

@ -87,10 +87,10 @@ ifeq ("$(MCU_PORT_NAME)","")
MCU_PORT_NAME = $(MCU_FAMILY)
endif
ifeq ("$(wildcard $(PLATFORM_MK))","")
PLATFORM_MK = $(CHIBIOS)/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)/$(PLATFORM_NAME).mk
ifeq ("$(wildcard $(PLATFORM_MK))","")
PLATFORM_MK = $(CHIBIOS_CONTRIB)/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)/$(PLATFORM_NAME).mk
ifeq ("$(wildcard $(PLATFORM_MK))","")
PLATFORM_MK = $(CHIBIOS)/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)/$(PLATFORM_NAME).mk
endif
endif
@ -287,6 +287,17 @@ EXTRAINCDIRS += $(CHIBIOS)/os/license $(CHIBIOS)/os/oslib/include \
$(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \
$(STREAMSINC) $(CHIBIOS)/os/various $(COMMON_VPATH)
#
# QMK specific MCU family support selection.
##############################################################################
ifneq ("$(wildcard $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)/$(MCU_SERIES).mk)","")
# Either by MCU series e.g. STM32/STM32F1xx.mk or...
include $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)/$(MCU_SERIES).mk
else ifneq ("$(wildcard $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)/$(MCU_FAMILY).mk)","")
# By MCU family e.g. STM32/STM32.mk
include $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)/$(MCU_FAMILY).mk
endif
#
# ChibiOS-Contrib
##############################################################################

285
platforms/chibios/vendors/RP/RP2040.mk vendored Normal file
View File

@ -0,0 +1,285 @@
#
# Raspberry Pi RP2040 specific drivers
##############################################################################
COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/vendor/$(MCU_FAMILY)/$(MCU_SERIES)
ifeq ($(strip $(WS2812_DRIVER)), vendor)
OPT_DEFS += -DRP_DMA_REQUIRED=TRUE
endif
#
# Raspberry Pi Pico SDK Support
##############################################################################
ADEFS += -DCRT0_VTOR_INIT=1 \
-DCRT0_EXTRA_CORES_NUMBER=0
CFLAGS += -DPICO_NO_FPGA_CHECK \
-DNDEBUG
#
# Pico SDK source and header files needed by QMK and ChibiOS
##############################################################################
PICOSDKROOT := $(TOP_DIR)/lib/pico-sdk
PICOSDKSRC = $(PICOSDKROOT)/src/rp2_common/hardware_clocks/clocks.c \
$(PICOSDKROOT)/src/rp2_common/hardware_pll/pll.c \
$(PICOSDKROOT)/src/rp2_common/hardware_pio/pio.c \
$(PICOSDKROOT)/src/rp2_common/hardware_gpio/gpio.c \
$(PICOSDKROOT)/src/rp2_common/hardware_claim/claim.c \
$(PICOSDKROOT)/src/rp2_common/hardware_watchdog/watchdog.c \
$(PICOSDKROOT)/src/rp2_common/hardware_xosc/xosc.c \
$(PICOSDKROOT)/src/rp2_common/pico_bootrom/bootrom.c
PICOSDKINC = $(CHIBIOS)//os/various/pico_bindings/dumb/include \
$(PICOSDKROOT)/src/common/pico_base/include \
$(PICOSDKROOT)/src/rp2_common/pico_platform/include \
$(PICOSDKROOT)/src/rp2_common/hardware_base/include \
$(PICOSDKROOT)/src/rp2_common/hardware_clocks/include \
$(PICOSDKROOT)/src/rp2_common/hardware_claim/include \
$(PICOSDKROOT)/src/rp2_common/hardware_gpio/include \
$(PICOSDKROOT)/src/rp2_common/hardware_irq/include \
$(PICOSDKROOT)/src/rp2_common/hardware_pll/include \
$(PICOSDKROOT)/src/rp2_common/hardware_pio/include \
$(PICOSDKROOT)/src/rp2_common/hardware_sync/include \
$(PICOSDKROOT)/src/rp2_common/hardware_resets/include \
$(PICOSDKROOT)/src/rp2_common/hardware_watchdog/include \
$(PICOSDKROOT)/src/rp2_common/hardware_xosc/include \
$(PICOSDKROOT)/src/rp2040/hardware_regs/include \
$(PICOSDKROOT)/src/rp2040/hardware_structs/include \
$(PICOSDKROOT)/src/boards/include \
$(PICOSDKROOT)/src/rp2_common/pico_bootrom/include
PLATFORM_SRC += $(PICOSDKSRC)
EXTRAINCDIRS += $(PICOSDKINC)
PLATFORM_RP2040_PATH := $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)
PLATFORM_SRC += $(PLATFORM_RP2040_PATH)/stage2_bootloaders.c \
$(PLATFORM_RP2040_PATH)/pico_sdk_shims.c
EXTRAINCDIRS += $(PLATFORM_RP2040_PATH)
#
# RP2040 optimized compiler intrinsics
##############################################################################
# Enables optimized Compiler intrinsics which are located in the RP2040
# bootrom. This needs startup code and linker script support from ChibiOS,
# which is WIP. Therefore disabled by default for now.
RP2040_INTRINSICS_ENABLED ?= no
ifeq ($(strip $(RP2040_INTRINSICS_ENABLED)), yes)
PICOSDKINTRINSICSSRC = $(PICOSDKROOT)/src/rp2_common/pico_float/float_aeabi.S \
$(PICOSDKROOT)/src/rp2_common/pico_float/float_math.c \
$(PICOSDKROOT)/src/rp2_common/pico_float/float_init_rom.c \
$(PICOSDKROOT)/src/rp2_common/pico_float/float_v1_rom_shim.S \
$(PICOSDKROOT)/src/rp2_common/pico_double/double_aeabi.S \
$(PICOSDKROOT)/src/rp2_common/pico_double/double_math.c \
$(PICOSDKROOT)/src/rp2_common/pico_double/double_init_rom.c \
$(PICOSDKROOT)/src/rp2_common/pico_double/double_v1_rom_shim.S \
$(PICOSDKROOT)/src/rp2_common/pico_divider/divider.S \
$(PICOSDKROOT)/src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S \
$(PICOSDKROOT)/src/rp2_common/pico_mem_ops/mem_ops_aeabi.S \
$(PICOSDKROOT)/src/rp2_common/pico_malloc/pico_malloc.c \
$(PICOSDKROOT)/src/rp2_common/pico_bit_ops/bit_ops_aeabi.S
PICOSDKINTRINSICSINC = $(PICOSDKROOT)/src/common/pico_base/include \
$(PICOSDKROOT)/src/rp2_common/pico_platfrom/include \
$(PICOSDKROOT)/src/rp2_common/pico_bootrom/include \
$(PICOSDKROOT)/src/rp2_common/hardware_divider/include \
$(PICOSDKROOT)/src/rp2_common/pico_float/include \
$(PICOSDKROOT)/src/rp2_common/pico_double/include \
$(PICOSDKROOT)/src/rp2_common/pico_malloc/include
OPT_DEFS += -DPICO_FLOAT_SUPPORT_ROM_V1=0 -DPICO_DOUBLE_SUPPORT_ROM_V1=0
CFLAGS += -Wl,--defsym=__StackLimit=__heap_end__
CFLAGS += -Wl,--defsym=__unhandled_user_irq=_unhandled_exception
CFLAGS += -Wl,--build-id=none
# single precision floating point intrinsics
OPT_DEFS += -DPICO_FLOAT_IN_RAM=1
OPT_DEFS += -DPICO_FLOAT_PROPAGATE_NANS=0
CFLAGS += -Wl,--wrap=__aeabi_fdiv
CFLAGS += -Wl,--wrap=__aeabi_fmul
CFLAGS += -Wl,--wrap=__aeabi_frsub
CFLAGS += -Wl,--wrap=__aeabi_fsub
CFLAGS += -Wl,--wrap=__aeabi_cfcmpeq
CFLAGS += -Wl,--wrap=__aeabi_cfrcmple
CFLAGS += -Wl,--wrap=__aeabi_cfcmple
CFLAGS += -Wl,--wrap=__aeabi_fcmpeq
CFLAGS += -Wl,--wrap=__aeabi_fcmplt
CFLAGS += -Wl,--wrap=__aeabi_fcmple
CFLAGS += -Wl,--wrap=__aeabi_fcmpge
CFLAGS += -Wl,--wrap=__aeabi_fcmpgt
CFLAGS += -Wl,--wrap=__aeabi_fcmpun
CFLAGS += -Wl,--wrap=__aeabi_i2f
CFLAGS += -Wl,--wrap=__aeabi_l2f
CFLAGS += -Wl,--wrap=__aeabi_ui2f
CFLAGS += -Wl,--wrap=__aeabi_ul2f
CFLAGS += -Wl,--wrap=__aeabi_i2f
CFLAGS += -Wl,--wrap=__aeabi_f2iz
CFLAGS += -Wl,--wrap=__aeabi_f2lz
CFLAGS += -Wl,--wrap=__aeabi_f2uiz
CFLAGS += -Wl,--wrap=__aeabi_f2ulz
CFLAGS += -Wl,--wrap=__aeabi_f2d
CFLAGS += -Wl,--wrap=sqrtf
CFLAGS += -Wl,--wrap=cosf
CFLAGS += -Wl,--wrap=sinf
CFLAGS += -Wl,--wrap=tanf
CFLAGS += -Wl,--wrap=atan2f
CFLAGS += -Wl,--wrap=expf
CFLAGS += -Wl,--wrap=logf
CFLAGS += -Wl,--wrap=ldexpf
CFLAGS += -Wl,--wrap=copysignf
CFLAGS += -Wl,--wrap=truncf
CFLAGS += -Wl,--wrap=floorf
CFLAGS += -Wl,--wrap=ceilf
CFLAGS += -Wl,--wrap=roundf
CFLAGS += -Wl,--wrap=sincosf
CFLAGS += -Wl,--wrap=asinf
CFLAGS += -Wl,--wrap=acosf
CFLAGS += -Wl,--wrap=atanf
CFLAGS += -Wl,--wrap=sinhf
CFLAGS += -Wl,--wrap=coshf
CFLAGS += -Wl,--wrap=tanhf
CFLAGS += -Wl,--wrap=asinhf
CFLAGS += -Wl,--wrap=acoshf
CFLAGS += -Wl,--wrap=atanhf
CFLAGS += -Wl,--wrap=exp2f
CFLAGS += -Wl,--wrap=log2f
CFLAGS += -Wl,--wrap=exp10f
CFLAGS += -Wl,--wrap=log10f
CFLAGS += -Wl,--wrap=powf
CFLAGS += -Wl,--wrap=powintf
CFLAGS += -Wl,--wrap=hypotf
CFLAGS += -Wl,--wrap=cbrtf
CFLAGS += -Wl,--wrap=fmodf
CFLAGS += -Wl,--wrap=dremf
CFLAGS += -Wl,--wrap=remainderf
CFLAGS += -Wl,--wrap=remquof
CFLAGS += -Wl,--wrap=expm1f
CFLAGS += -Wl,--wrap=log1pf
CFLAGS += -Wl,--wrap=fmaf
# double precision floating point intrinsics
OPT_DEFS += -DPICO_DOUBLE_IN_RAM=1
OPT_DEFS += -DPICO_DOUBLE_PROPAGATE_NANS=0
CFLAGS += -Wl,--wrap=__aeabi_dadd
CFLAGS += -Wl,--wrap=__aeabi_ddiv
CFLAGS += -Wl,--wrap=__aeabi_dmul
CFLAGS += -Wl,--wrap=__aeabi_drsub
CFLAGS += -Wl,--wrap=__aeabi_dsub
CFLAGS += -Wl,--wrap=__aeabi_cdcmpeq
CFLAGS += -Wl,--wrap=__aeabi_cdrcmple
CFLAGS += -Wl,--wrap=__aeabi_cdcmple
CFLAGS += -Wl,--wrap=__aeabi_dcmpeq
CFLAGS += -Wl,--wrap=__aeabi_dcmplt
CFLAGS += -Wl,--wrap=__aeabi_dcmple
CFLAGS += -Wl,--wrap=__aeabi_dcmpge
CFLAGS += -Wl,--wrap=__aeabi_dcmpgt
CFLAGS += -Wl,--wrap=__aeabi_dcmpun
CFLAGS += -Wl,--wrap=__aeabi_i2d
CFLAGS += -Wl,--wrap=__aeabi_l2d
CFLAGS += -Wl,--wrap=__aeabi_ui2d
CFLAGS += -Wl,--wrap=__aeabi_ul2d
CFLAGS += -Wl,--wrap=__aeabi_d2iz
CFLAGS += -Wl,--wrap=__aeabi_d2lz
CFLAGS += -Wl,--wrap=__aeabi_d2uiz
CFLAGS += -Wl,--wrap=__aeabi_d2ulz
CFLAGS += -Wl,--wrap=__aeabi_d2f
CFLAGS += -Wl,--wrap=sqrt
CFLAGS += -Wl,--wrap=cos
CFLAGS += -Wl,--wrap=sin
CFLAGS += -Wl,--wrap=tan
CFLAGS += -Wl,--wrap=atan2
CFLAGS += -Wl,--wrap=exp
CFLAGS += -Wl,--wrap=log
CFLAGS += -Wl,--wrap=ldexp
CFLAGS += -Wl,--wrap=copysign
CFLAGS += -Wl,--wrap=trunc
CFLAGS += -Wl,--wrap=floor
CFLAGS += -Wl,--wrap=ceil
CFLAGS += -Wl,--wrap=round
CFLAGS += -Wl,--wrap=sincos
CFLAGS += -Wl,--wrap=asin
CFLAGS += -Wl,--wrap=acos
CFLAGS += -Wl,--wrap=atan
CFLAGS += -Wl,--wrap=sinh
CFLAGS += -Wl,--wrap=cosh
CFLAGS += -Wl,--wrap=tanh
CFLAGS += -Wl,--wrap=asinh
CFLAGS += -Wl,--wrap=acosh
CFLAGS += -Wl,--wrap=atanh
CFLAGS += -Wl,--wrap=exp2
CFLAGS += -Wl,--wrap=log2
CFLAGS += -Wl,--wrap=exp10
CFLAGS += -Wl,--wrap=log10
CFLAGS += -Wl,--wrap=pow
CFLAGS += -Wl,--wrap=powint
CFLAGS += -Wl,--wrap=hypot
CFLAGS += -Wl,--wrap=cbrt
CFLAGS += -Wl,--wrap=fmod
CFLAGS += -Wl,--wrap=drem
CFLAGS += -Wl,--wrap=remainder
CFLAGS += -Wl,--wrap=remquo
CFLAGS += -Wl,--wrap=expm1
CFLAGS += -Wl,--wrap=log1p
CFLAGS += -Wl,--wrap=fma
# bit operation intrinsics
OPT_DEFS += -DPICO_BITS_IN_RAM=1
CFLAGS += -Wl,--wrap=__clzsi2
CFLAGS += -Wl,--wrap=__clzsi2
CFLAGS += -Wl,--wrap=__clzdi2
CFLAGS += -Wl,--wrap=__ctzsi2
CFLAGS += -Wl,--wrap=__ctzdi2
CFLAGS += -Wl,--wrap=__popcountsi2
CFLAGS += -Wl,--wrap=__popcountdi2
CFLAGS += -Wl,--wrap=__clz
CFLAGS += -Wl,--wrap=__clzl
CFLAGS += -Wl,--wrap=__clzsi2
CFLAGS += -Wl,--wrap=__clzll
# integer division intrinsics
OPT_DEFS += -DPICO_DIVIDER_IN_RAM=1
OPT_DEFS += -DPICO_DIVIDER_DISABLE_INTERRUPTS=1
CFLAGS += -Wl,--wrap=__aeabi_idiv
CFLAGS += -Wl,--wrap=__aeabi_idivmod
CFLAGS += -Wl,--wrap=__aeabi_ldivmod
CFLAGS += -Wl,--wrap=__aeabi_uidiv
CFLAGS += -Wl,--wrap=__aeabi_uidivmod
CFLAGS += -Wl,--wrap=__aeabi_uldivmod
# 64bit integer intrinsics
OPT_DEFS += -DPICO_INT64_OPS_IN_RAM=1
CFLAGS += -Wl,--wrap=__aeabi_lmul
# malloc and friends functions
OPT_DEFS += -DPICO_USE_MALLOC_MUTEX=0
OPT_DEFS += -DPICO_DEBUG_MALLOC=0
OPT_DEFS ?= -DPICO_MALLOC_PANIC=0
CFLAGS += -Wl,--wrap=malloc
CFLAGS += -Wl,--wrap=calloc
CFLAGS += -Wl,--wrap=free
# memory operation intrinsics
OPT_DEFS += -DPICO_MEM_IN_RAM=1
CFLAGS += -Wl,--wrap=memcpy
CFLAGS += -Wl,--wrap=memset
CFLAGS += -Wl,--wrap=__aeabi_memcpy
CFLAGS += -Wl,--wrap=__aeabi_memset
CFLAGS += -Wl,--wrap=__aeabi_memcpy4
CFLAGS += -Wl,--wrap=__aeabi_memset4
CFLAGS += -Wl,--wrap=__aeabi_memcpy8
CFLAGS += -Wl,--wrap=__aeabi_memset8
PLATFORM_SRC += $(PICOSDKINTRINSICSSRC)
EXTRAINCDIRS += $(PICOSDKINTRINSICSINC)
endif

View File

@ -0,0 +1,37 @@
// Copyright 2022 Stefan Kerkmann
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
/* RP2040 GPIO Numbering */
#define GP0 0U
#define GP1 1U
#define GP2 2U
#define GP3 3U
#define GP4 4U
#define GP5 5U
#define GP6 6U
#define GP7 7U
#define GP8 8U
#define GP9 9U
#define GP10 10U
#define GP11 11U
#define GP12 12U
#define GP13 13U
#define GP14 14U
#define GP15 15U
#define GP16 16U
#define GP17 17U
#define GP18 18U
#define GP19 19U
#define GP20 20U
#define GP21 21U
#define GP22 22U
#define GP23 23U
#define GP24 24U
#define GP25 25U
#define GP26 26U
#define GP27 27U
#define GP28 28U
#define GP29 29U
#define GP30 30U

View File

@ -0,0 +1,9 @@
// Copyright 2022 Stefan Kerkmann
// SPDX-License-Identifier: GPL-2.0-or-later
#include <stdbool.h>
#include <ch.h>
void panic(const char *fmt, ...) {
chSysHalt(fmt);
}

View File

@ -0,0 +1,174 @@
// ----------------------------------------------------------------------------
// Pre-compiled second stage boot code for RP2040.
//
// Copyright (c) 2019-2021 Raspberry Pi (Trading) Ltd.
// SPDX-License-Identifier: BSD-3-Clause
// ----------------------------------------------------------------------------
#include <stdint.h>
#define BOOTLOADER_SECTION __attribute__ ((used, section (".boot2")))
#if defined(RP2040_FLASH_AT25SF128A)
uint8_t BOOTLOADER_SECTION BOOT2_AT25SF128A[256] = {
0x00, 0xb5, 0x31, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21,
0x88, 0x43, 0x98, 0x60, 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2d, 0x4b,
0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x01, 0x21, 0xf0, 0x22,
0x99, 0x50, 0x2a, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20,
0x00, 0xf0, 0x42, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x12, 0xd0, 0x06, 0x21,
0x19, 0x66, 0x00, 0xf0, 0x32, 0xf8, 0x19, 0x6e, 0x31, 0x21, 0x19, 0x66,
0x1a, 0x66, 0x00, 0xf0, 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e,
0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21, 0x08, 0x42, 0xf9, 0xd1,
0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60,
0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21,
0x19, 0x66, 0x20, 0x21, 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21,
0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60,
0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49,
0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5,
0x99, 0x6a, 0x04, 0x20, 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42,
0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66, 0x18, 0x66, 0xff, 0xf7,
0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40,
0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00,
0x21, 0x22, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x18, 0x22, 0x20, 0x00, 0x20,
0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xc0, 0xdd, 0xc0, 0xb5
};
#elif defined(RP2040_FLASH_GD25Q64CS)
uint8_t BOOTLOADER_SECTION BOOT2_GD25Q64CS[256] = {
0x00, 0xb5, 0x31, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21,
0x88, 0x43, 0x98, 0x60, 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2d, 0x4b,
0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x01, 0x21, 0xf0, 0x22,
0x99, 0x50, 0x2a, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20,
0x00, 0xf0, 0x42, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x12, 0xd0, 0x06, 0x21,
0x19, 0x66, 0x00, 0xf0, 0x32, 0xf8, 0x19, 0x6e, 0x31, 0x21, 0x19, 0x66,
0x1a, 0x66, 0x00, 0xf0, 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e,
0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21, 0x08, 0x42, 0xf9, 0xd1,
0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60,
0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xe7, 0x21,
0x19, 0x66, 0xa0, 0x21, 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21,
0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60,
0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49,
0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5,
0x99, 0x6a, 0x04, 0x20, 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42,
0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66, 0x18, 0x66, 0xff, 0xf7,
0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40,
0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00,
0x21, 0x12, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x18, 0x22, 0x10, 0x00, 0xa0,
0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xe2, 0xd9, 0xa2, 0xb5
};
#elif defined(RP2040_FLASH_W25X10CL)
uint8_t BOOTLOADER_SECTION BOOT2_W25X10CL[256] = {
0x00, 0xb5, 0x14, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61,
0x12, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60, 0x11, 0x49, 0x12, 0x48,
0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xbb, 0x21, 0x19, 0x66, 0x02, 0x21,
0x19, 0x66, 0x08, 0x21, 0x98, 0x6a, 0x08, 0x42, 0xfc, 0xd0, 0x00, 0x21,
0x99, 0x60, 0x0c, 0x49, 0x0a, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60,
0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x08, 0x48, 0x09, 0x49,
0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x3f, 0x00, 0x1d, 0x12, 0x00, 0x00,
0xf4, 0x00, 0x00, 0x18, 0x1e, 0x10, 0x00, 0x20, 0x00, 0x01, 0x00, 0x10,
0x08, 0xed, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7c, 0x81, 0x53, 0x9a
};
#elif defined(RP2040_FLASH_IS25LP080)
uint8_t BOOTLOADER_SECTION BOOT2_IS25LP080[256] = {
0x00, 0xb5, 0x2b, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61,
0x29, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x28, 0x48, 0x00, 0xf0,
0x42, 0xf8, 0x28, 0x4a, 0x90, 0x42, 0x12, 0xd0, 0x06, 0x21, 0x19, 0x66,
0x00, 0xf0, 0x32, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66, 0x00, 0x20,
0x1a, 0x66, 0x00, 0xf0, 0x2b, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x1f, 0x48,
0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21, 0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21,
0x99, 0x60, 0x1d, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60, 0x1c, 0x49,
0x1c, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21, 0x19, 0x66,
0xa0, 0x21, 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21, 0x99, 0x60,
0x17, 0x49, 0x16, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc,
0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x14, 0x48, 0x14, 0x49, 0x08, 0x60,
0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a,
0x04, 0x20, 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1,
0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66, 0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff,
0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
0x00, 0x00, 0x07, 0x00, 0x05, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x18,
0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0x33, 0x43, 0xb2
};
#elif defined(RP2040_FLASH_GENERIC_03H)
uint8_t BOOTLOADER_SECTION BOOT2_GENERIC_03H[256] = {
0x00, 0xb5, 0x0c, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61,
0x0a, 0x49, 0x19, 0x60, 0x0a, 0x49, 0x0b, 0x48, 0x01, 0x60, 0x00, 0x21,
0x59, 0x60, 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0,
0x00, 0x47, 0x07, 0x48, 0x07, 0x49, 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3,
0x08, 0x88, 0x08, 0x47, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x1f, 0x00,
0x18, 0x02, 0x00, 0x03, 0xf4, 0x00, 0x00, 0x18, 0x00, 0x01, 0x00, 0x10,
0x08, 0xed, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2c, 0xec, 0x21, 0x0d
};
#else
uint8_t BOOTLOADER_SECTION BOOT2_W25Q080[256] = {
0x00, 0xb5, 0x32, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21,
0x88, 0x43, 0x98, 0x60, 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2e, 0x4b,
0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x01, 0x21, 0xf0, 0x22,
0x99, 0x50, 0x2b, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20,
0x00, 0xf0, 0x44, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x14, 0xd0, 0x06, 0x21,
0x19, 0x66, 0x00, 0xf0, 0x34, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66,
0x00, 0x20, 0x18, 0x66, 0x1a, 0x66, 0x00, 0xf0, 0x2c, 0xf8, 0x19, 0x6e,
0x19, 0x6e, 0x19, 0x6e, 0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21,
0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60,
0x00, 0x21, 0x59, 0x60, 0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21,
0x99, 0x60, 0xeb, 0x21, 0x19, 0x66, 0xa0, 0x21, 0x19, 0x66, 0x00, 0xf0,
0x12, 0xf8, 0x00, 0x21, 0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60,
0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47,
0x12, 0x48, 0x13, 0x49, 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88,
0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a, 0x04, 0x20, 0x01, 0x42, 0xfb, 0xd0,
0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66,
0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd,
0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00,
0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x18,
0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x0b, 0x8f, 0xd5
};
#endif

View File

@ -46,6 +46,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "quantum_keycodes.h"
// Gross hack, remove me and change RESET keycode to QK_BOOT
#if defined(MCU_RP)
# undef RESET
#endif
// translates key to keycode
uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key);

View File

@ -32,6 +32,7 @@
#include "usb_main.h"
#include "host.h"
#include "chibios_config.h"
#include "debug.h"
#include "suspend.h"
#ifdef SLEEP_LED_ENABLE
@ -91,6 +92,13 @@ uint8_t extra_report_blank[3] = {0};
* ---------------------------------------------------------
*/
/* USB Low Level driver specific endpoint fields */
#if !defined(usb_lld_endpoint_fields)
# define usb_lld_endpoint_fields \
2, /* IN multiplier */ \
NULL, /* SETUP buffer (not a SETUP endpoint) */
#endif
/* HID specific constants */
#define HID_GET_REPORT 0x01
#define HID_GET_IDLE 0x02
@ -129,8 +137,7 @@ static const USBEndpointConfig kbd_ep_config = {
0, /* OUT maximum packet size */
&kbd_ep_state, /* IN Endpoint state */
NULL, /* OUT endpoint state */
2, /* IN multiplier */
NULL /* SETUP buffer (not a SETUP endpoint) */
usb_lld_endpoint_fields /* USB driver specific endpoint fields */
};
#endif
@ -148,8 +155,7 @@ static const USBEndpointConfig mouse_ep_config = {
0, /* OUT maximum packet size */
&mouse_ep_state, /* IN Endpoint state */
NULL, /* OUT endpoint state */
2, /* IN multiplier */
NULL /* SETUP buffer (not a SETUP endpoint) */
usb_lld_endpoint_fields /* USB driver specific endpoint fields */
};
#endif
@ -167,8 +173,7 @@ static const USBEndpointConfig shared_ep_config = {
0, /* OUT maximum packet size */
&shared_ep_state, /* IN Endpoint state */
NULL, /* OUT endpoint state */
2, /* IN multiplier */
NULL /* SETUP buffer (not a SETUP endpoint) */
usb_lld_endpoint_fields /* USB driver specific endpoint fields */
};
#endif
@ -259,8 +264,7 @@ typedef struct {
0, /* OUT maximum packet size */ \
NULL, /* IN Endpoint state */ \
NULL, /* OUT endpoint state */ \
2, /* IN multiplier */ \
NULL /* SETUP buffer (not a SETUP endpoint) */ \
usb_lld_endpoint_fields /* USB driver specific endpoint fields */ \
}, \
.out_ep_config = \
{ \
@ -272,8 +276,7 @@ typedef struct {
stream##_EPSIZE, /* OUT maximum packet size */ \
NULL, /* IN Endpoint state */ \
NULL, /* OUT endpoint state */ \
2, /* IN multiplier */ \
NULL, /* SETUP buffer (not a SETUP endpoint) */ \
usb_lld_endpoint_fields /* USB driver specific endpoint fields */ \
}, \
.int_ep_config = \
{ \
@ -285,8 +288,7 @@ typedef struct {
0, /* OUT maximum packet size */ \
NULL, /* IN Endpoint state */ \
NULL, /* OUT endpoint state */ \
2, /* IN multiplier */ \
NULL, /* SETUP buffer (not a SETUP endpoint) */ \
usb_lld_endpoint_fields /* USB driver specific endpoint fields */ \
}, \
.config = { \
.usbp = &USB_DRIVER, \