raspberrypi_utils/
ws2812.rs

1use raspberrypi_utils_sys::*;
2
3const WS2812_WRAP_TARGET: u32 = 0;
4const WS2812_WRAP: u32 = 3;
5
6const WS2812_T1: u32 = 3;
7const WS2812_T2: u32 = 4;
8const WS2812_T3: u32 = 3;
9const FREQ: u32 = 800_000;
10
11pub struct Ws2812 {
12    pio: *mut pio_instance,
13    sm: u32,
14    gpio_pin: u32,
15    offset: u32,
16}
17
18// extern "C" {
19//     // These match the symbols the linker was complaining about.
20//     // We declare them here to ensure Rust knows they are external.
21//     fn stdio_init_all();
22//     fn pio_claim_unused_sm(pio: *mut pio_instance, required: bool) -> i32;
23//     fn pio_add_program(pio: *mut pio_instance, program: *const pio_program) -> u32;
24// }
25
26impl Ws2812 {
27    pub fn new(gpio: u32) -> Result<Self, String> {
28        unsafe {
29            let pio0: PIO = pio_open_helper(0); // Already defined by C library
30
31            stdio_init_all();
32
33            // In some bindings, pio0 is a pointer, in others a macro.
34            // We use the pio_hw_t pointer from the lib.
35            let pio = pio0;
36
37            let sm = pio_claim_unused_sm(pio, true);
38
39            if sm < 0 {
40                return Err("No unused state machine available".to_string());
41            }
42
43            let pio_program_instructions: [u16; 4] = [
44                //     .wrap_target
45                0x6221, //  0: out    x, 1            side 0 [2]
46                0x1223, //  1: jmp    !x, 3           side 1 [2]
47                0x1300, //  2: jmp    0               side 1 [3]
48                0xa342, //  3: nop                    side 0 [3]
49                        //     .wrap
50            ];
51            let ws2812_program = pio_program {
52                instructions: pio_program_instructions.as_ptr(),
53                length: 4,
54                origin: -1,
55                pio_version: 0,
56            };
57
58            let offset = pio_add_program(pio, &ws2812_program);
59
60            println!("WS2812, using pin {:?}", gpio);
61            println!("Loaded program at {:?}, using sm {:?}", offset, sm);
62
63            // You'll need to link the ws2812.pio.h equivalent logic here
64            // Often pio programs are converted to headers with a struct
65            // For now, this is the structural equivalent of your C main()
66
67            Ok(Self {
68                pio,
69                sm: sm as u32,
70                gpio_pin: gpio,
71                offset,
72            })
73        }
74    }
75
76    fn program_get_default_config(&self) -> pio_sm_config {
77        unsafe {
78            let mut c = pio_get_default_sm_config();
79            sm_config_set_wrap(
80                &mut c,
81                self.offset + WS2812_WRAP_TARGET,
82                self.offset + WS2812_WRAP,
83            );
84            sm_config_set_sideset(&mut c, 1, false, false);
85
86            c
87        }
88    }
89
90    pub fn program_init(&self, rgbw: bool) {
91        unsafe {
92            pio_gpio_init(self.pio, self.gpio_pin);
93            pio_sm_set_consecutive_pindirs(self.pio, self.sm, self.gpio_pin, 1, true);
94            let mut c: pio_sm_config = self.program_get_default_config();
95            sm_config_set_sideset_pins(&mut c, self.gpio_pin);
96            sm_config_set_out_shift(&mut c, false, true, if rgbw { 32 } else { 24 });
97            sm_config_set_fifo_join(&mut c, pio_fifo_join_PIO_FIFO_JOIN_TX);
98            let cycles_per_bit = WS2812_T1 + WS2812_T2 + WS2812_T3;
99            let div = clock_get_hz(clock_index_clk_sys) as f32 / (FREQ * cycles_per_bit) as f32;
100            sm_config_set_clkdiv(&mut c, div);
101            pio_sm_init(self.pio, self.sm, self.offset, &mut c);
102            pio_sm_set_enabled(self.pio, self.sm, true);
103        }
104    }
105
106    fn pattern_snakes(&self, len: u32, t: u32) {
107        for i in 0..len {
108            let x = (i + (t >> 1)) % 64;
109            if x < 10 {
110                self.put_pixel(urgb_u32(0xff, 0, 0));
111            } else if x >= 15 && x < 25 {
112                self.put_pixel(urgb_u32(0, 0xff, 0));
113            } else if x >= 30 && x < 40 {
114                self.put_pixel(urgb_u32(0, 0, 0xff));
115            } else {
116                self.put_pixel(0);
117            }
118        }
119    }
120
121    pub fn program_run(&self) {
122        let num_pixels = 150;
123        let dir = 1;
124        let mut t = 0;
125        for _ in 0..100 {
126            self.pattern_snakes(num_pixels, t);
127            std::thread::sleep(std::time::Duration::from_millis(10));
128            t += dir;
129        }
130    }
131
132    pub fn put_pixel(&self, pixel_grb: u32) {
133        unsafe {
134            pio_sm_put_blocking(self.pio, self.sm, pixel_grb << 8);
135        }
136    }
137}
138
139pub fn urgb_u32(r: u8, g: u8, b: u8) -> u32 {
140    ((r as u32) << 8) | ((g as u32) << 16) | (b as u32)
141}