raspberrypi_utils/
ws2812.rs1use 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
18impl Ws2812 {
27 pub fn new(gpio: u32) -> Result<Self, String> {
28 unsafe {
29 let pio0: PIO = pio_open_helper(0); stdio_init_all();
32
33 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 0x6221, 0x1223, 0x1300, 0xa342, ];
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 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}