Skip to content

Commit f154f03

Browse files
committed
Add ws2812 example (#742)
1 parent 97131a0 commit f154f03

File tree

4 files changed

+134
-1
lines changed

4 files changed

+134
-1
lines changed

examples/wch/ch32v/build.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub fn build(b: *std.Build) void {
3131
.{ .target = mb.ports.ch32v.chips.ch32v203x6, .name = "blinky_ch32v203", .file = "src/blinky.zig" },
3232
.{ .target = mb.ports.ch32v.chips.ch32v203x6, .name = "blinky_systick_ch32v203", .file = "src/blinky_systick.zig" },
3333
.{ .target = mb.ports.ch32v.boards.ch32v203.suzuduino_uno_v1b, .name = "suzuduino_blinky", .file = "src/board_blinky.zig" },
34+
.{ .target = mb.ports.ch32v.boards.ch32v203.lana_tny, .name = "lana_tny_ws2812", .file = "src/ws2812.zig" },
3435

3536
// CH32V30x
3637
.{ .target = mb.ports.ch32v.chips.ch32v303xb, .name = "empty_ch32v303", .file = "src/empty.zig" },

examples/wch/ch32v/src/ws2812.zig

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
const microzig = @import("microzig");
2+
const board = microzig.board;
3+
const hal = microzig.hal;
4+
const cpu = microzig.cpu;
5+
6+
// Taken from https://github.com/robinjanssens/WCH-Toolchain
7+
8+
pub fn main() !void {
9+
// Board brings up clocks and time
10+
board.init();
11+
12+
const pins = board.pin_config.apply();
13+
const ws2812_pin = pins.ws2812;
14+
15+
var i: u8 = 0;
16+
while (true) {
17+
const col: u32 = wheel(i);
18+
set_led(0, red(col), green(col), blue(col));
19+
write(ws2812_pin);
20+
hal.time.sleep_ms(33);
21+
// Allow overflow to wrap around
22+
i +%= 1;
23+
}
24+
}
25+
26+
// Board has a single LED
27+
const NUM_LEDS = 1;
28+
29+
fn color(r: u8, g: u8, b: u8) u32 {
30+
return (@as(u32, r) << 16) | (@as(u32, g) << 8) | b;
31+
}
32+
33+
fn red(col: u32) u8 {
34+
return @truncate((col >> 16) & 0xFF);
35+
}
36+
37+
fn green(col: u32) u8 {
38+
return @truncate((col >> 8) & 0xFF);
39+
}
40+
41+
fn blue(col: u32) u8 {
42+
return @truncate(col & 0xFF);
43+
}
44+
45+
fn wheel(wheel_pos: u8) u32 {
46+
var pos = 255 -% wheel_pos;
47+
if (pos < 85) {
48+
return color(255 -% pos *% 3, 0, pos *% 3);
49+
} else if (pos < 170) {
50+
pos -%= 85;
51+
return color(0, pos *% 3, 255 -% pos *% 3);
52+
} else {
53+
pos -%= 170;
54+
return color(pos *% 3, 255 -% pos *% 3, 0);
55+
}
56+
}
57+
58+
var rgb_array: [3 * NUM_LEDS]u8 = undefined; // Each color is 3 bytes
59+
60+
inline fn nops(comptime n: usize) void {
61+
inline for (0..n) |_| asm volatile ("nop");
62+
}
63+
64+
// Compact timing-accurate bit send using pin.put + NOP sequences
65+
fn led_send_bit(pin: anytype, bit: u1) void {
66+
if (bit != 0) {
67+
// T1H ≈ 800 ns @ 48 MHz
68+
pin.put(1);
69+
nops(34);
70+
pin.put(0);
71+
// T1L ≈ 400Ns, taken up by other functions
72+
return;
73+
}
74+
// T0H ≈ 400 ns
75+
pin.put(1);
76+
nops(14);
77+
pin.put(0);
78+
// T0L ≈ 850 ns, 400 ns is taken up by other functions
79+
nops(20);
80+
}
81+
82+
// Send a single color for a single LED
83+
// WS2812B LEDs want 24 bits per led in the string
84+
fn led_send_color(pin: anytype, r: u8, g: u8, b: u8) void {
85+
// Send the green component first (MSB)
86+
var i: i32 = 7;
87+
while (i >= 0) : (i -= 1) {
88+
led_send_bit(pin, @truncate((g >> @intCast(i)) & 1));
89+
}
90+
// Send the red component next
91+
i = 7;
92+
while (i >= 0) : (i -= 1) {
93+
led_send_bit(pin, @truncate((r >> @intCast(i)) & 1));
94+
}
95+
// Send the blue component last (LSB)
96+
i = 7;
97+
while (i >= 0) : (i -= 1) {
98+
led_send_bit(pin, @truncate((b >> @intCast(i)) & 1));
99+
}
100+
}
101+
102+
fn write(pin: anytype) void {
103+
var i: usize = 0;
104+
while (i < NUM_LEDS) : (i += 1) {
105+
led_send_color(pin, rgb_array[i * 3], rgb_array[i * 3 + 1], rgb_array[i * 3 + 2]);
106+
}
107+
hal.time.sleep_ms(1);
108+
}
109+
110+
fn set_led(i: usize, r: u8, g: u8, b: u8) void {
111+
rgb_array[i * 3] = r;
112+
rgb_array[i * 3 + 1] = g;
113+
rgb_array[i * 3 + 2] = b;
114+
}

port/wch/ch32v/src/boards/LANA_TNY.zig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ pub const pin_config = ch32v.pins.GlobalConfiguration{
1818
.PIN0 = .{
1919
.name = "ws2812",
2020
.mode = .{ .output = .general_purpose_push_pull },
21-
.speed = .max_50MHz,
2221
},
2322
},
2423
};

port/wch/ch32v/src/hals/pins.zig

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,25 @@ pub const GlobalConfiguration = struct {
185185
}
186186

187187
pub fn apply(comptime config: GlobalConfiguration) Pins(config) {
188+
// Check if we need to enable AFIO for GPIOD PD0/PD1 remapping
189+
comptime var needs_pd01_remap = false;
190+
if (config.GPIOD) |gpiod_config| {
191+
if (gpiod_config.PIN0 != null or gpiod_config.PIN1 != null) {
192+
needs_pd01_remap = true;
193+
}
194+
}
195+
196+
// Enable clocks first for PD0/PD1 remapping (must be done before remap)
197+
if (needs_pd01_remap) {
198+
// Enable AFIO and GPIOD clocks
199+
RCC.APB2PCENR.modify(.{ .AFIOEN = 1, .IOPDEN = 1 });
200+
201+
// Remap PD0/PD1 from OSCIN/OSCOUT to GPIO
202+
// On CH32V20x, AFIO.PCFR1.PD01_RM = 1 selects PD0/PD1 as GPIO pins.
203+
const AFIO = microzig.chip.peripherals.AFIO;
204+
AFIO.PCFR1.modify(.{ .PD01_RM = 1 });
205+
}
206+
188207
inline for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| {
189208
if (@field(config, port_field.name)) |port_config| {
190209
comptime var input_gpios: u16 = 0;

0 commit comments

Comments
 (0)