raspberrypi_utils_sys/
ws2812.rs

1use crate::*;
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
18impl Ws2812 {
19    pub fn new(gpio: u32) -> Result<Self, String> {
20        unsafe {
21            let pio0: PIO = pio_open_helper(0); // Already defined by C library
22
23            stdio_init_all();
24
25            // In some bindings, pio0 is a pointer, in others a macro.
26            // We use the pio_hw_t pointer from the lib.
27            let pio = pio0;
28
29            let sm = pio_claim_unused_sm(pio, true);
30
31            if sm < 0 {
32                return Err("No unused state machine available".to_string());
33            }
34
35            let pio_program_instructions: [u16; 4] = [
36                //     .wrap_target
37                0x6221, //  0: out    x, 1            side 0 [2]
38                0x1223, //  1: jmp    !x, 3           side 1 [2]
39                0x1300, //  2: jmp    0               side 1 [3]
40                0xa342, //  3: nop                    side 0 [3]
41                        //     .wrap
42            ];
43            let ws2812_program = pio_program {
44                instructions: pio_program_instructions.as_ptr(),
45                length: 4,
46                origin: -1,
47                pio_version: 0,
48            };
49
50            let offset = pio_add_program(pio, &ws2812_program);
51
52            println!("WS2812, using GPIO {:?}", gpio);
53            println!("Loaded program at {:?}, using sm {:?}", offset, sm);
54
55            Ok(Self {
56                pio,
57                sm: sm as u32,
58                gpio_pin: gpio,
59                offset,
60            })
61        }
62    }
63
64    fn program_get_default_config(&self) -> pio_sm_config {
65        unsafe {
66            let mut c = pio_get_default_sm_config();
67            sm_config_set_wrap(
68                &mut c,
69                self.offset + WS2812_WRAP_TARGET,
70                self.offset + WS2812_WRAP,
71            );
72            sm_config_set_sideset(&mut c, 1, false, false);
73
74            c
75        }
76    }
77
78    pub fn program_init(&self, frequency: Option<u32>, is_rgbw: bool) {
79        let frequency = frequency.unwrap_or(FREQ);
80
81        unsafe {
82            pio_gpio_init(self.pio, self.gpio_pin);
83            pio_sm_set_consecutive_pindirs(self.pio, self.sm, self.gpio_pin, 1, true);
84            let mut c: pio_sm_config = self.program_get_default_config();
85            sm_config_set_sideset_pins(&mut c, self.gpio_pin);
86            sm_config_set_out_shift(&mut c, false, true, if is_rgbw { 32 } else { 24 });
87            sm_config_set_fifo_join(&mut c, pio_fifo_join_PIO_FIFO_JOIN_TX);
88            let cycles_per_bit = WS2812_T1 + WS2812_T2 + WS2812_T3;
89            let div = clock_get_hz(clock_index_clk_sys) as f32 / (frequency * cycles_per_bit) as f32;
90            sm_config_set_clkdiv(&mut c, div);
91            pio_sm_init(self.pio, self.sm, self.offset, &mut c);
92            pio_sm_set_enabled(self.pio, self.sm, true);
93        }
94    }
95
96    pub fn put_pixel(&self, pixel_grb: u32) {
97        unsafe {
98            pio_sm_put_blocking(self.pio, self.sm, pixel_grb << 8);
99        }
100    }
101}
102
103pub const fn urgb_u32(r: u8, g: u8, b: u8) -> u32 {
104    ((r as u32) << 8) | ((g as u32) << 16) | (b as u32)
105}