Skip to content

Commit

Permalink
Add WS2812 support
Browse files Browse the repository at this point in the history
  • Loading branch information
renzenicolai committed Jul 25, 2022
1 parent 0328d95 commit 9625226
Show file tree
Hide file tree
Showing 11 changed files with 208 additions and 24 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
build
release
generated/*
generated
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 ()
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

INSTALL_PREFIX := $PWD
BUILD_DIR := build
GENERATED_DIR := generated

BL_BIN := rp2040_bootloader.bin
FW_BIN := rp2040_firmware.bin
Expand All @@ -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

Expand 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/
Expand Down
3 changes: 3 additions & 0 deletions hardware.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions i2c_peripheral.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
};
Expand Down
74 changes: 52 additions & 22 deletions i2c_peripheral.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
};
3 changes: 3 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion version.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#pragma once

#define FW_VERSION 0x08
#define FW_VERSION 0x09
27 changes: 27 additions & 0 deletions ws2812.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdlib.h>

#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);
}
6 changes: 6 additions & 0 deletions ws2812.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once

void ws2812_setup();
void ws2812_enable(bool rgbw);
void ws2812_disable();
void ws2812_put(uint32_t data);
85 changes: 85 additions & 0 deletions ws2812.pio
Original file line number Diff line number Diff line change
@@ -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<pin_base+pin_count; i++) {
pio_gpio_init(pio, i);
}
pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count, true);

pio_sm_config c = ws2812_parallel_program_get_default_config(offset);
sm_config_set_out_shift(&c, true, true, 32);
sm_config_set_out_pins(&c, pin_base, pin_count);
sm_config_set_set_pins(&c, pin_base, pin_count);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);

int cycles_per_bit = ws2812_parallel_T1 + ws2812_parallel_T2 + ws2812_parallel_T3;
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
sm_config_set_clkdiv(&c, div);

pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}

0 comments on commit 9625226

Please sign in to comment.