1#![no_std]
2use embedded_hal::timer::CountDown;
17use fugit::{ExtU32, HertzU32, MicrosDurationU32};
18use rp2040_hal::{
19 gpio::AnyPin,
20 pio::{PIOExt, StateMachineIndex, Tx, UninitStateMachine, PIO},
21};
22use smart_leds_trait::SmartLedsWrite;
23use smart_leds_trait_0_2::SmartLedsWrite as SmartLedsWrite02;
24
25pub struct Ws2812Direct<P, SM, I>
58where
59 I: AnyPin<Function = P::PinFunction>,
60 P: PIOExt,
61 SM: StateMachineIndex,
62{
63 tx: Tx<(P, SM)>,
64 _pin: I,
65}
66
67impl<P, SM, I> Ws2812Direct<P, SM, I>
68where
69 I: AnyPin<Function = P::PinFunction>,
70 P: PIOExt,
71 SM: StateMachineIndex,
72{
73 pub fn new(
75 pin: I,
76 pio: &mut PIO<P>,
77 sm: UninitStateMachine<(P, SM)>,
78 clock_freq: fugit::HertzU32,
79 ) -> Self {
80 let side_set = pio::SideSet::new(false, 1, false);
82 let mut a = pio::Assembler::new_with_side_set(side_set);
83
84 const T1: u8 = 2; const T2: u8 = 5; const T3: u8 = 3; const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
88 const FREQ: HertzU32 = HertzU32::kHz(800);
89
90 let mut wrap_target = a.label();
91 let mut wrap_source = a.label();
92 let mut do_zero = a.label();
93 a.bind(&mut wrap_target);
94 a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
96 a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
98 a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
100 a.bind(&mut do_zero);
101 a.nop_with_delay_and_side_set(T2 - 1, 0);
103 a.bind(&mut wrap_source);
104 let program = a.assemble_with_wrap(wrap_source, wrap_target);
105
106 let installed = pio.install(&program).unwrap();
108
109 let bit_freq = FREQ * CYCLES_PER_BIT;
111 let mut int = clock_freq / bit_freq;
112 let rem = clock_freq - (int * bit_freq);
113 let frac = (rem * 256) / bit_freq;
114 assert!(
115 (1..=65536).contains(&int) && (int != 65536 || frac == 0),
116 "(System Clock / {}) must be within [1.0, 65536.0].",
117 bit_freq.to_kHz()
118 );
119
120 if int == 65536 {
122 int = 0;
123 }
124 let int: u16 = int as u16;
126 let frac: u8 = frac as u8;
127
128 let pin = pin.into();
129 let (mut sm, _, tx) = rp2040_hal::pio::PIOBuilder::from_installed_program(installed)
130 .buffers(rp2040_hal::pio::Buffers::OnlyTx)
132 .side_set_pin_base(pin.id().num)
134 .out_shift_direction(rp2040_hal::pio::ShiftDirection::Left)
136 .autopull(true)
137 .pull_threshold(24)
138 .clock_divisor_fixed_point(int, frac)
139 .build(sm);
140
141 sm.set_pindirs([(pin.id().num, rp2040_hal::pio::PinDir::Output)]);
143
144 sm.start();
145
146 Self {
147 tx,
148 _pin: I::from(pin),
149 }
150 }
151}
152
153impl<P, SM, I> SmartLedsWrite for Ws2812Direct<P, SM, I>
154where
155 I: AnyPin<Function = P::PinFunction>,
156 P: PIOExt,
157 SM: StateMachineIndex,
158{
159 type Color = smart_leds_trait::RGB8;
160 type Error = ();
161 fn write<T, J>(&mut self, iterator: T) -> Result<(), ()>
169 where
170 T: IntoIterator<Item = J>,
171 J: Into<Self::Color>,
172 {
173 for item in iterator {
174 let color: Self::Color = item.into();
175 let word =
176 (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8);
177
178 while !self.tx.write(word) {
179 cortex_m::asm::nop();
180 }
181 }
182 Ok(())
183 }
184}
185
186impl<P, SM, I> SmartLedsWrite02 for Ws2812Direct<P, SM, I>
187where
188 I: AnyPin<Function = P::PinFunction>,
189 P: PIOExt,
190 SM: StateMachineIndex,
191{
192 type Color = smart_leds_trait::RGB8;
193 type Error = ();
194 fn write<T, J>(&mut self, iterator: T) -> Result<(), ()>
202 where
203 T: Iterator<Item = J>,
204 J: Into<Self::Color>,
205 {
206 SmartLedsWrite::write(self, iterator)
207 }
208}
209
210pub struct Ws2812<P, SM, C, I>
241where
242 C: CountDown,
243 I: AnyPin<Function = P::PinFunction>,
244 P: PIOExt,
245 SM: StateMachineIndex,
246{
247 driver: Ws2812Direct<P, SM, I>,
248 cd: C,
249}
250
251impl<P, SM, C, I> Ws2812<P, SM, C, I>
252where
253 C: CountDown,
254 I: AnyPin<Function = P::PinFunction>,
255 P: PIOExt,
256 SM: StateMachineIndex,
257{
258 pub fn new(
260 pin: I,
261 pio: &mut PIO<P>,
262 sm: UninitStateMachine<(P, SM)>,
263 clock_freq: fugit::HertzU32,
264 cd: C,
265 ) -> Ws2812<P, SM, C, I> {
266 let driver = Ws2812Direct::new(pin, pio, sm, clock_freq);
267
268 Self { driver, cd }
269 }
270}
271
272impl<P, SM, I, C> SmartLedsWrite for Ws2812<P, SM, C, I>
273where
274 C: CountDown,
275 C::Time: From<MicrosDurationU32>,
276 I: AnyPin<Function = P::PinFunction>,
277 P: PIOExt,
278 SM: StateMachineIndex,
279{
280 type Color = smart_leds_trait::RGB8;
281 type Error = ();
282 fn write<T, J>(&mut self, iterator: T) -> Result<(), ()>
283 where
284 T: IntoIterator<Item = J>,
285 J: Into<Self::Color>,
286 {
287 self.driver.tx.clear_stalled_flag();
288 while !self.driver.tx.is_empty() && !self.driver.tx.has_stalled() {}
289
290 self.cd.start(60u32.micros());
291 let _ = nb::block!(self.cd.wait());
292
293 SmartLedsWrite::write(&mut self.driver, iterator)
294 }
295}
296
297impl<P, SM, I, C> SmartLedsWrite02 for Ws2812<P, SM, C, I>
298where
299 C: CountDown,
300 C::Time: From<MicrosDurationU32>,
301 I: AnyPin<Function = P::PinFunction>,
302 P: PIOExt,
303 SM: StateMachineIndex,
304{
305 type Color = smart_leds_trait::RGB8;
306 type Error = ();
307 fn write<T, J>(&mut self, iterator: T) -> Result<(), ()>
308 where
309 T: IntoIterator<Item = J>,
310 J: Into<Self::Color>,
311 {
312 SmartLedsWrite::write(self, iterator)
313 }
314}