diff --git a/.gitignore b/.gitignore index b157e34..d56581a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ build release -generated/* \ No newline at end of file +generated diff --git a/CMakeLists.txt b/CMakeLists.txt index 87e1b26..e12dde1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,8 +74,11 @@ add_executable(${NAME} webusb_task.c lcd.c i2c_peripheral.c + ws2812.c ) +pico_generate_pio_header(${NAME} ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio OUTPUT_DIR ${CMAKE_CURRENT_LIST_DIR}/generated) + if (CMAKE_BUILD_TYPE STREQUAL "Debug") message("Debug mode: using default panic handler") else () diff --git a/Makefile b/Makefile index cb6888c..c0b0226 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ INSTALL_PREFIX := $PWD BUILD_DIR := build +GENERATED_DIR := generated BL_BIN := rp2040_bootloader.bin FW_BIN := rp2040_firmware.bin @@ -16,12 +17,14 @@ all: build flash build: mkdir -p $(BUILD_DIR) + mkdir -p $(GENERATED_DIR) cd $(BUILD_DIR); cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX -DCMAKE_BUILD_TYPE=Release .. $(MAKE) -C $(BUILD_DIR) --no-print-directory all python3 genuf2.py $(BUILD_DIR)/$(BL_BIN) $(BUILD_DIR)/$(FW_BIN) $(BUILD_DIR)/$(CB_UF2) debug: mkdir -p $(BUILD_DIR) + mkdir -p $(GENERATED_DIR) cd $(BUILD_DIR); cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX -DCMAKE_BUILD_TYPE=Debug .. $(MAKE) -C $(BUILD_DIR) --no-print-directory all @@ -31,6 +34,7 @@ flash: clean: rm -rf $(BUILD_DIR) + rm -rf $(GENERATED_DIR) install_rules: cp tools/99-pico.rules /etc/udev/rules.d/ diff --git a/hardware.h b/hardware.h index 46e827b..3812305 100644 --- a/hardware.h +++ b/hardware.h @@ -63,6 +63,9 @@ #define IR_PIN 27 #define IR_PIO pio0 +// SAO WS2812 +#define WS2812_PIO pio1 + // USB virtual device numbers #define USB_CDC_ESP32 0 #define USB_CDC_FPGA 1 diff --git a/i2c_peripheral.c b/i2c_peripheral.c index f273c80..9772fcb 100644 --- a/i2c_peripheral.c +++ b/i2c_peripheral.c @@ -27,6 +27,8 @@ #include "version.h" +#include "ws2812.h" + static bool interrupt_target = false; static bool interrupt_state = false; static bool interrupt_clear = false; @@ -223,6 +225,27 @@ void i2c_handle_register_write(uint8_t reg, uint8_t value) { nec_tx_extended(IR_PIO, ir_statemachine, address, command); } break; + case I2C_REGISTER_WS2812_MODE: + switch (value) { + case 0x01: // 24-bit (RGB) mode + ws2812_enable(false); + break; + case 0x02: // 32-bit (RGBW) mode + ws2812_enable(true); + case 0x00: + default: + ws2812_disable(); + } + break; + case I2C_REGISTER_WS2812_TRIGGER: { + uint8_t length = i2c_registers.registers[I2C_REGISTER_WS2812_LENGTH]; + if (length > 10) length = 10; + for (uint8_t i = 0; i < length; i++) { + uint32_t* value = (uint32_t*) &i2c_registers.registers[I2C_REGISTER_WS2812_LED0_DATA0 + (i*4)]; + ws2812_put(*value); + } + break; + } default: break; }; diff --git a/i2c_peripheral.h b/i2c_peripheral.h index 4060d14..4573dda 100644 --- a/i2c_peripheral.h +++ b/i2c_peripheral.h @@ -156,34 +156,64 @@ enum { I2C_REGISTER_RESERVED14, // 104-111 - I2C_REGISTER_RESERVED15, - I2C_REGISTER_RESERVED16, - I2C_REGISTER_RESERVED17, - I2C_REGISTER_RESERVED18, - I2C_REGISTER_RESERVED19, - I2C_REGISTER_RESERVED20, - I2C_REGISTER_RESERVED21, - I2C_REGISTER_RESERVED22, + I2C_REGISTER_WS2812_MODE, + I2C_REGISTER_WS2812_TRIGGER, + I2C_REGISTER_WS2812_LENGTH, + I2C_REGISTER_WS2812_SPEED, + I2C_REGISTER_WS2812_LED0_DATA0, + I2C_REGISTER_WS2812_LED0_DATA1, + I2C_REGISTER_WS2812_LED0_DATA2, + I2C_REGISTER_WS2812_LED0_DATA3, // 112-119 + I2C_REGISTER_WS2812_LED1_DATA0, + I2C_REGISTER_WS2812_LED1_DATA1, + I2C_REGISTER_WS2812_LED1_DATA2, + I2C_REGISTER_WS2812_LED1_DATA3, + I2C_REGISTER_WS2812_LED2_DATA0, + I2C_REGISTER_WS2812_LED2_DATA1, + I2C_REGISTER_WS2812_LED2_DATA2, + I2C_REGISTER_WS2812_LED2_DATA3, + + // 120-127 + I2C_REGISTER_WS2812_LED3_DATA0, + I2C_REGISTER_WS2812_LED3_DATA1, + I2C_REGISTER_WS2812_LED3_DATA2, + I2C_REGISTER_WS2812_LED3_DATA3, + I2C_REGISTER_WS2812_LED4_DATA0, + I2C_REGISTER_WS2812_LED4_DATA1, + I2C_REGISTER_WS2812_LED4_DATA2, + I2C_REGISTER_WS2812_LED4_DATA3, + + // 128-135 + I2C_REGISTER_WS2812_LED5_DATA0, + I2C_REGISTER_WS2812_LED5_DATA1, + I2C_REGISTER_WS2812_LED5_DATA2, + I2C_REGISTER_WS2812_LED5_DATA3, + I2C_REGISTER_WS2812_LED6_DATA0, + I2C_REGISTER_WS2812_LED6_DATA1, + I2C_REGISTER_WS2812_LED6_DATA2, + I2C_REGISTER_WS2812_LED6_DATA3, + + // 136-143 + I2C_REGISTER_WS2812_LED7_DATA0, + I2C_REGISTER_WS2812_LED7_DATA1, + I2C_REGISTER_WS2812_LED7_DATA2, + I2C_REGISTER_WS2812_LED7_DATA3, + I2C_REGISTER_WS2812_LED8_DATA0, + I2C_REGISTER_WS2812_LED8_DATA1, + I2C_REGISTER_WS2812_LED8_DATA2, + I2C_REGISTER_WS2812_LED8_DATA3, + + // 136-143 + I2C_REGISTER_WS2812_LED9_DATA0, + I2C_REGISTER_WS2812_LED9_DATA1, + I2C_REGISTER_WS2812_LED9_DATA2, + I2C_REGISTER_WS2812_LED9_DATA3, I2C_REGISTER_RESERVED23, I2C_REGISTER_RESERVED24, I2C_REGISTER_RESERVED25, I2C_REGISTER_RESERVED26, - I2C_REGISTER_RESERVED27, - I2C_REGISTER_RESERVED28, - I2C_REGISTER_RESERVED29, - I2C_REGISTER_RESERVED30, - - // 120-127 - I2C_REGISTER_RESERVED31, - I2C_REGISTER_RESERVED32, - I2C_REGISTER_RESERVED33, - I2C_REGISTER_RESERVED34, - I2C_REGISTER_RESERVED35, - I2C_REGISTER_RESERVED36, - I2C_REGISTER_RESERVED37, - I2C_REGISTER_RESERVED38, // ... (128-255) }; diff --git a/main.c b/main.c index 0f12996..ce6b15a 100644 --- a/main.c +++ b/main.c @@ -25,6 +25,7 @@ #include "uart_task.h" #include "usb_descriptors.h" #include "webusb_task.h" +#include "ws2812.h" #ifdef PICO_PANIC_FUNCTION #define CRASH_INDICATION_MAGIC 0xFA174200 @@ -69,6 +70,8 @@ int main(void) { check_crashed(); // Populate the crash & debug state register setup_i2c_peripheral(I2C_SYSTEM, I2C_SYSTEM_SDA_PIN, I2C_SYSTEM_SCL_PIN, 0x17, 400000, i2c_slave_handler); esp32_reset(false); // Reset ESP32 to normal mode + + ws2812_setup(); while (1) { tud_task(); diff --git a/version.h b/version.h index 8372404..e6fc652 100644 --- a/version.h +++ b/version.h @@ -1,3 +1,3 @@ #pragma once -#define FW_VERSION 0x08 +#define FW_VERSION 0x09 diff --git a/ws2812.c b/ws2812.c new file mode 100644 index 0000000..4724f36 --- /dev/null +++ b/ws2812.c @@ -0,0 +1,27 @@ +#include +#include + +#include "pico/stdlib.h" +#include "hardware/pio.h" +#include "hardware/clocks.h" +#include "ws2812.pio.h" + +#include "hardware.h" + +static uint pio_offset = 0; + +void ws2812_setup() { + pio_offset = pio_add_program(WS2812_PIO, &ws2812_program); +} + +void ws2812_enable(bool rgbw) { + ws2812_program_init(WS2812_PIO, 0, pio_offset, SAO_IO0_PIN, 800000, rgbw); +} + +void ws2812_disable() { + pio_sm_set_enabled(WS2812_PIO, 0, false); +} + +void ws2812_put(uint32_t data) { + pio_sm_put_blocking(WS2812_PIO, 0, data); +} diff --git a/ws2812.h b/ws2812.h new file mode 100644 index 0000000..5d7f54c --- /dev/null +++ b/ws2812.h @@ -0,0 +1,6 @@ +#pragma once + +void ws2812_setup(); +void ws2812_enable(bool rgbw); +void ws2812_disable(); +void ws2812_put(uint32_t data); diff --git a/ws2812.pio b/ws2812.pio new file mode 100644 index 0000000..3c31fd6 --- /dev/null +++ b/ws2812.pio @@ -0,0 +1,85 @@ +; +; 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); +} +%} + +.program ws2812_parallel + +.define public T1 2 +.define public T2 5 +.define public T3 3 + +.wrap_target + out x, 32 + mov pins, !null [T1-1] + mov pins, x [T2-1] + mov pins, null [T3-2] +.wrap + +% c-sdk { +#include "hardware/clocks.h" + +static inline void ws2812_parallel_program_init(PIO pio, uint sm, uint offset, uint pin_base, uint pin_count, float freq) { + for(uint i=pin_base; i