sxkbd

Firmware for RP2040-based corne split keyboard
git clone https://git.sinitax.com/sinitax/sxkbd
Log | Files | Refs | Submodules | README | LICENSE | sfeed.txt

commit 5c2cb697aa8d03eed27b45f8dc67957c45a3d722
parent 12a423434352af137bda357001ac824aa23fdf77
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sun, 20 Nov 2022 01:35:41 +0100

Onboard neopixel led support

Diffstat:
M.gitignore | 1+
MCMakeLists.txt | 23++++++++++++++---------
MMakefile | 2+-
Acompile_commands.json | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/main.c | 37++++++++++++++++++-------------------
Asrc/neopix.c | 22++++++++++++++++++++++
Asrc/neopix.h | 22++++++++++++++++++++++
Asrc/ws2812.pio | 48++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ws2812.pio.h | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 312 insertions(+), 29 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1 +1,2 @@ .build +.cache diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -2,26 +2,31 @@ cmake_minimum_required(VERSION 3.5) include(${CMAKE_CURRENT_SOURCE_DIR}/lib/tinyusb/hw/bsp/family_support.cmake) -# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) project(${PROJECT}) -# Checks this example is valid for the family and initializes the project family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) add_executable(${PROJECT}) -# Example source target_sources(${PROJECT} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c - ) + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/neopix.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c +) -# Example include target_include_directories(${PROJECT} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src - ) + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${PICO_SDK_PATH}/src/rp2_common/hardware_gpio/include + ${PICO_SDK_PATH}/src/rp2_common/hardware_pio/include +) + +target_link_libraries(${PROJECT} PRIVATE + pico_stdlib + hardware_pio + hardware_gpio +) # Configure compilation flags and libraries for the example... see the corresponding function # in hw/bsp/FAMILY/family.cmake for details. diff --git a/Makefile b/Makefile @@ -16,7 +16,7 @@ lib/picosdk: .build: mkdir $@ -upload: +flash: picotool load .build/sxkbd.uf2 .PHONY: all clean cmake upload diff --git a/compile_commands.json b/compile_commands.json @@ -0,0 +1,128 @@ +[ + { + "arguments": [ + "/usr/bin/arm-none-eabi-gcc", + "-DCFG_TUSB_MCU=OPT_MCU_RP2040", + "-DCFG_TUSB_OS=OPT_OS_PICO", + "-DLIB_PICO_BIT_OPS=1", + "-DLIB_PICO_BIT_OPS_PICO=1", + "-DLIB_PICO_BOOTSEL_VIA_DOUBLE_RESET=1", + "-DLIB_PICO_DIVIDER=1", + "-DLIB_PICO_DIVIDER_HARDWARE=1", + "-DLIB_PICO_DOUBLE=1", + "-DLIB_PICO_DOUBLE_PICO=1", + "-DLIB_PICO_FIX_RP2040_USB_DEVICE_ENUMERATION=1", + "-DLIB_PICO_FLOAT=1", + "-DLIB_PICO_FLOAT_PICO=1", + "-DLIB_PICO_INT64_OPS=1", + "-DLIB_PICO_INT64_OPS_PICO=1", + "-DLIB_PICO_MALLOC=1", + "-DLIB_PICO_MEM_OPS=1", + "-DLIB_PICO_MEM_OPS_PICO=1", + "-DLIB_PICO_PLATFORM=1", + "-DLIB_PICO_PRINTF=1", + "-DLIB_PICO_PRINTF_PICO=1", + "-DLIB_PICO_RUNTIME=1", + "-DLIB_PICO_STANDARD_LINK=1", + "-DLIB_PICO_STDIO=1", + "-DLIB_PICO_STDIO_UART=1", + "-DLIB_PICO_STDLIB=1", + "-DLIB_PICO_SYNC=1", + "-DLIB_PICO_SYNC_CORE=1", + "-DLIB_PICO_SYNC_CRITICAL_SECTION=1", + "-DLIB_PICO_SYNC_MUTEX=1", + "-DLIB_PICO_SYNC_SEM=1", + "-DLIB_PICO_TIME=1", + "-DLIB_PICO_UTIL=1", + "-DLIB_TINYUSB_BOARD=1", + "-DLIB_TINYUSB_DEVICE=1", + "-DPICO_BOARD=\"pico\"", + "-DPICO_BUILD=1", + "-DPICO_CMAKE_BUILD_TYPE=\"Release\"", + "-DPICO_COPY_TO_RAM=0", + "-DPICO_CXX_ENABLE_EXCEPTIONS=0", + "-DPICO_NO_FLASH=0", + "-DPICO_NO_HARDWARE=0", + "-DPICO_ON_DEVICE=1", + "-DPICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1", + "-DPICO_TARGET_NAME=\"sxkbd\"", + "-DPICO_USE_BLOCKED_RAM=0", + "-I/snx/dev/sxkbd/src", + "-I/usr/share/pico-sdk/src/common/pico_stdlib/include", + "-I/usr/share/pico-sdk/src/rp2_common/hardware_gpio/include", + "-I/usr/share/pico-sdk/src/common/pico_base/include", + "-I/snx/dev/sxkbd/.build/generated/pico_base", + "-I/usr/share/pico-sdk/src/boards/include", + "-I/usr/share/pico-sdk/src/rp2_common/pico_platform/include", + "-I/usr/share/pico-sdk/src/rp2040/hardware_regs/include", + "-I/usr/share/pico-sdk/src/rp2_common/hardware_base/include", + "-I/usr/share/pico-sdk/src/rp2040/hardware_structs/include", + "-I/usr/share/pico-sdk/src/rp2_common/hardware_claim/include", + "-I/usr/share/pico-sdk/src/rp2_common/hardware_sync/include", + "-I/usr/share/pico-sdk/src/rp2_common/hardware_irq/include", + "-I/usr/share/pico-sdk/src/common/pico_sync/include", + "-I/usr/share/pico-sdk/src/common/pico_time/include", + "-I/usr/share/pico-sdk/src/rp2_common/hardware_timer/include", + "-I/usr/share/pico-sdk/src/common/pico_util/include", + "-I/usr/share/pico-sdk/src/rp2_common/hardware_uart/include", + "-I/usr/share/pico-sdk/src/rp2_common/hardware_divider/include", + "-I/usr/share/pico-sdk/src/rp2_common/pico_runtime/include", + "-I/usr/share/pico-sdk/src/rp2_common/hardware_clocks/include", + "-I/usr/share/pico-sdk/src/rp2_common/hardware_resets/include", + "-I/usr/share/pico-sdk/src/rp2_common/hardware_pll/include", + "-I/usr/share/pico-sdk/src/rp2_common/hardware_vreg/include", + "-I/usr/share/pico-sdk/src/rp2_common/hardware_watchdog/include", + "-I/usr/share/pico-sdk/src/rp2_common/hardware_xosc/include", + "-I/usr/share/pico-sdk/src/rp2_common/pico_printf/include", + "-I/usr/share/pico-sdk/src/rp2_common/pico_bootrom/include", + "-I/usr/share/pico-sdk/src/common/pico_bit_ops/include", + "-I/usr/share/pico-sdk/src/common/pico_divider/include", + "-I/usr/share/pico-sdk/src/rp2_common/pico_double/include", + "-I/usr/share/pico-sdk/src/rp2_common/pico_int64_ops/include", + "-I/usr/share/pico-sdk/src/rp2_common/pico_float/include", + "-I/usr/share/pico-sdk/src/rp2_common/pico_malloc/include", + "-I/usr/share/pico-sdk/src/rp2_common/boot_stage2/include", + "-I/usr/share/pico-sdk/src/common/pico_binary_info/include", + "-I/usr/share/pico-sdk/src/rp2_common/pico_stdio/include", + "-I/usr/share/pico-sdk/src/rp2_common/pico_stdio_uart/include", + "-I/snx/dev/sxkbd/lib/tinyusb/src", + "-I/snx/dev/sxkbd/lib/tinyusb/src/common", + "-I/snx/dev/sxkbd/lib/tinyusb/hw", + "-I/usr/share/pico-sdk/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/include", + "-mcpu=cortex-m0plus", + "-mthumb", + "-O3", + "-DNDEBUG", + "-Wall", + "-Wextra", + "-Werror", + "-Wfatal-errors", + "-Wdouble-promotion", + "-Wfloat-equal", + "-Wshadow", + "-Wwrite-strings", + "-Wsign-compare", + "-Wmissing-format-attribute", + "-Wunreachable-code", + "-Wcast-align", + "-Wcast-qual", + "-Wnull-dereference", + "-Wuninitialized", + "-Wunused", + "-Wredundant-decls", + "-Wconversion", + "-Wcast-function-type", + "-Wstrict-overflow", + "-Wno-strict-aliasing", + "-ffunction-sections", + "-fdata-sections", + "-c", + "-o", + "CMakeFiles/sxkbd.dir/src/main.c.obj", + "/snx/dev/sxkbd/src/main.c" + ], + "directory": "/snx/dev/sxkbd/.build", + "file": "/snx/dev/sxkbd/src/main.c", + "output": "/snx/dev/sxkbd/.build/CMakeFiles/sxkbd.dir/src/main.c.obj" + } +] diff --git a/src/main.c b/src/main.c @@ -1,12 +1,14 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> +#include <sys/types.h> #include "bsp/board.h" #include "tusb.h" +#include "neopix.h" /* Blink pattern - * - 250 ms : device not mounted + * - 250 ms : device not mounted * - 1000 ms : device mounted * - 2500 ms : device is suspended */ @@ -18,19 +20,20 @@ enum { static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; +static struct neopix onboard_led; + void led_blinking_task(void); -/*------------- MAIN -------------*/ int main(void) { board_init(); - // init device stack on configured roothub port + neopix_init(&onboard_led, pio0, 0, 25); + tud_init(BOARD_TUD_RHPORT); - while (1) - { - tud_task(); // tinyusb device task + while (1) { + tud_task(); led_blinking_task(); } @@ -78,7 +81,6 @@ void tud_resume_cb(void) 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; @@ -93,27 +95,24 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, 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) { - // This example doesn't use multiple report and report ID (void) itf; (void) report_id; (void) report_type; - // echo back anything we received from host tud_hid_report(0, buffer, bufsize); } -//--------------------------------------------------------------------+ -// BLINKING TASK -//--------------------------------------------------------------------+ -void led_blinking_task(void) +void +led_blinking_task(void) { static uint32_t start_ms = 0; - static bool led_state = false; + static bool state = false; - // Blink every interval ms - if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time - start_ms += blink_interval_ms; + if (board_millis() - start_ms < blink_interval_ms) + return; + + neopix_put(&onboard_led, neopix_u32rgb(255 * state, 0, 0)); - board_led_write(led_state); - led_state = 1 - led_state; // toggle + start_ms += blink_interval_ms; + state ^= true; } diff --git a/src/neopix.c b/src/neopix.c @@ -0,0 +1,22 @@ +#include "hardware/pio.h" + +#include "neopix.h" +#include "ws2812.pio.h" + +void +neopix_init(struct neopix *pix, PIO pio, uint sm, uint pin) +{ + uint offset; + + pix->sm = sm; + pix->pio = pio; + pix->pin = pin; + offset = pio_add_program(pix->pio, &ws2812_program); + ws2812_program_init(pix->pio, pix->sm, offset, + pix->pin, 800000, false); +} + +void +neopix_put(struct neopix *pix, uint32_t rgb) { + pio_sm_put_blocking(pix->pio, pix->sm, rgb << 8u); +} diff --git a/src/neopix.h b/src/neopix.h @@ -0,0 +1,22 @@ +#include "hardware/pio.h" +#include "hardware/gpio.h" + +struct neopix { + PIO pio; + uint sm; + uint pin; +}; + +void neopix_init(struct neopix *pix, PIO pio, uint sm, uint pin); +void neopix_put(struct neopix *pix, uint32_t rgb); + +inline uint32_t +neopix_u32rgb(uint8_t r, uint8_t g, uint8_t b) { + uint32_t rgb; + + rgb = ((uint32_t) r) << 8; + rgb |= ((uint32_t) g) << 16; + rgb |= ((uint32_t) b) << 0; + + return rgb; +} diff --git a/src/ws2812.pio b/src/ws2812.pio @@ -0,0 +1,48 @@ +; +; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. +; +; SPDX-License-Identifier: BSD-3-Clause +; + +.program ws2812 +.side_set 1 + +.define public T1 2 +.define public T2 5 +.define public T3 3 + +.lang_opt python sideset_init = pico.PIO.OUT_HIGH +.lang_opt python out_init = pico.PIO.OUT_HIGH +.lang_opt python out_shiftdir = 1 + +.wrap_target +bitloop: + out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls + jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse +do_one: + jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse +do_zero: + nop side 0 [T2 - 1] ; Or drive low, for a short pulse +.wrap + +% c-sdk { +#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); +} +%} diff --git a/src/ws2812.pio.h b/src/ws2812.pio.h @@ -0,0 +1,58 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// -------------------------------------------------- // + +#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 = ((float) clock_get_hz(clk_sys)) / (freq * (float) cycles_per_bit); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} + +#endif