nrf_smartled/
pwm.rs

1//! Smartleds using the PWM peripheral
2
3#[allow(unused)]
4use crate::hal;
5
6use crate::hal::{
7    gpio::{Level, Output, Pin, PushPull},
8    pac,
9};
10
11use core::sync::atomic::{compiler_fence, Ordering};
12
13use smart_leds_trait::{SmartLedsWrite, RGB8};
14
15/// Fill a buffer with the DMA representation
16///
17/// The buffer must be a slice of 24 u16s or more.
18pub fn fill_buf(color: &RGB8, buf: &mut [u16]) -> Result<(), ()> {
19    if buf.len() < 24 {
20        return Err(());
21    }
22
23    let red = color.r.reverse_bits();
24    let green = color.g.reverse_bits();
25    let blue = color.b.reverse_bits();
26
27    for g in 0..8 {
28        if ((green >> g) & 0b1) == 0b1 {
29            buf[g] = 0x8000 | 13;
30        } else {
31            buf[g] = 0x8000 | 5;
32        }
33    }
34
35    for r in 0..8 {
36        if ((red >> r) & 0b1) == 0b1 {
37            buf[8 + r] = 0x8000 | 13;
38        } else {
39            buf[8 + r] = 0x8000 | 5;
40        }
41    }
42
43    for b in 0..8 {
44        if ((blue >> b) & 0b1) == 0b1 {
45            buf[16 + b] = 0x8000 | 13;
46        } else {
47            buf[16 + b] = 0x8000 | 5;
48        }
49    }
50
51    Ok(())
52}
53
54/// A PWM peripheral driven Smartled driver
55pub struct Pwm<T: sealed::Instance> {
56    pwm: T,
57    _gpio: Pin<Output<PushPull>>,
58}
59
60impl<T> Pwm<T>
61where
62    T: sealed::Instance,
63{
64    /// Create a new Smartled driver with a given pin and PWM engine
65    pub fn new<Mode>(pwm: T, pin: Pin<Mode>) -> Pwm<T> {
66        let pin = pin.into_push_pull_output(Level::Low);
67
68        pwm.psel.out[0].write(|w| {
69            #[cfg(feature = "52840")]
70            match pin.port() {
71                hal::gpio::Port::Port0 => w.port().clear_bit(),
72                hal::gpio::Port::Port1 => w.port().set_bit(),
73            };
74            unsafe {
75                w.pin().bits(pin.pin());
76            }
77            w.connect().connected()
78        });
79
80        pwm.enable.write(|w| w.enable().enabled());
81        pwm.mode.write(|w| w.updown().up());
82        pwm.prescaler.write(|w| w.prescaler().div_1());
83        pwm.countertop.write(|w| unsafe { w.countertop().bits(20) });
84        pwm.loop_.write(|w| w.cnt().disabled());
85        pwm.decoder.write(|w| {
86            w.load().common();
87            w.mode().refresh_count()
88        });
89        pwm.seq0.refresh.write(|w| unsafe { w.bits(0) });
90        pwm.seq0.enddelay.write(|w| unsafe { w.bits(0) });
91        pwm.seq1.refresh.write(|w| unsafe { w.bits(0) });
92        pwm.seq1.enddelay.write(|w| unsafe { w.bits(0) });
93
94        Pwm { pwm, _gpio: pin }
95    }
96
97    /// Start sending raw data
98    ///
99    /// NOTE: You should probably use Pwm::send_full_buf() instead.
100    ///
101    /// SAFETY: the contents of `buf` must live and be constant until Pwm::is_done_raw()
102    /// returns true.
103    pub unsafe fn start_send_raw(&mut self, buf: *const [u16]) -> Result<(), ()> {
104        // TODO: Check maximum supported len?
105        if (*buf).is_empty() {
106            return Err(());
107        }
108
109        if (((*buf).as_ptr() as usize) < hal::target_constants::SRAM_LOWER)
110            || (((*buf).as_ptr() as usize) > hal::target_constants::SRAM_UPPER)
111        {
112            return Err(());
113        }
114
115        compiler_fence(Ordering::SeqCst);
116
117        self.pwm.seq0.ptr.write(|w| w.bits((*buf).as_ptr() as u32));
118        self.pwm.seq0.cnt.write(|w| w.bits((*buf).len() as u32));
119        self.pwm.events_seqend[0].write(|w| w.bits(0));
120        self.pwm.tasks_seqstart[0].write(|w| w.bits(1));
121
122        Ok(())
123    }
124
125    /// Set the seq[1] register's ptr and length
126    ///
127    /// SAFETY: the contents of `buf` must live and me constant until sequence 1
128    /// is completed
129    pub unsafe fn set_seq1_raw(&mut self, buf: *const [u16]) -> Result<(), ()> {
130        // TODO: Check maximum supported len?
131        if (*buf).is_empty() {
132            return Err(());
133        }
134
135        if (((*buf).as_ptr() as usize) < hal::target_constants::SRAM_LOWER)
136            || (((*buf).as_ptr() as usize) > hal::target_constants::SRAM_UPPER)
137        {
138            return Err(());
139        }
140
141        compiler_fence(Ordering::SeqCst);
142
143        self.pwm.seq1.ptr.write(|w| w.bits((*buf).as_ptr() as u32));
144        self.pwm.seq1.cnt.write(|w| w.bits((*buf).len() as u32));
145
146        Ok(())
147    }
148
149    /// Is the raw send complete?
150    ///
151    /// Note: You probably shouldn't use this function unless you
152    /// are also using Pwm::start_send_raw().
153    pub fn is_done_raw(&self) -> bool {
154        self.pwm.events_seqend[0].read().bits() == 1
155    }
156
157    /// Send a series of colors and a stop pattern, using a given scratch space
158    ///
159    /// NOTE: the size of `scratch` must be >= u16s_needed_slice(colors).
160    ///
161    /// NOTE: You can also use the SmartLedsWrite::write method to avoid the
162    /// need for a scratch space (it uses its own)
163    pub fn send_full_buf(&mut self, colors: &[RGB8], scratch: &mut [u16]) -> Result<(), ()> {
164        if scratch.len() < u16s_needed_slice(colors) {
165            return Err(());
166        }
167
168        for (color, buf) in colors.iter().zip(scratch.chunks_exact_mut(24)) {
169            fill_buf(color, buf)?;
170        }
171
172        let start = colors.len() * 24;
173        let end = start + 40;
174
175        for by in &mut scratch[start..end] {
176            *by = 0x8000;
177        }
178
179        // Disable looping, this is a one-shot
180        self.pwm.loop_.write(|w| w.cnt().disabled());
181
182        // Safety: we block until the DMA transaction is complete
183        unsafe {
184            self.start_send_raw(&scratch[..end])?;
185        }
186        while !self.is_done_raw() {}
187
188        Ok(())
189    }
190}
191
192/// How many u16s are needed to send a given slice
193///
194/// This number includes space for necessary stop patterns
195pub fn u16s_needed_slice(slice: &[RGB8]) -> usize {
196    u16s_needed_ct(slice.len())
197}
198
199/// How many u16s are needed to send a given number of RGB8s
200///
201/// This number includes space for necessary stop patterns
202pub const fn u16s_needed_ct(leds: usize) -> usize {
203    leds * 24 + 40
204}
205
206impl<T> SmartLedsWrite for Pwm<T>
207where
208    T: sealed::Instance,
209{
210    type Error = ();
211    type Color = RGB8;
212    /// Write all the items of an iterator to a ws2812 strip
213    fn write<Iter, I>(&mut self, mut iterator: Iter) -> Result<(), ()>
214    where
215        Iter: Iterator<Item = I>,
216        I: Into<Self::Color>,
217    {
218        // Two buffers to ping-pong between
219        let mut buf_a = [0u16; 24];
220        let mut buf_b = [0u16; 24];
221
222        let mut blanks_fed = 0;
223        let mut toggle = false;
224
225        match (iterator.next(), iterator.next()) {
226            (Some(a), Some(b)) => {
227                // Two pixels, fill to buffers
228                fill_buf(&a.into(), &mut buf_a)?;
229                fill_buf(&b.into(), &mut buf_b)?;
230            }
231            (Some(a), None) => {
232                // One pixel, fill the pixel and a blank
233                fill_buf(&a.into(), &mut buf_a)?;
234                buf_b.copy_from_slice(&[0x8000u16; 24]);
235                blanks_fed = 1;
236            }
237            (None, Some(_)) => {
238                // what? Intermittent iterator?
239                return Err(());
240            }
241            _ => {
242                // Empty iterator, nothing completed successfully
243                return Ok(());
244            }
245        }
246
247        unsafe {
248            // Set the back half, and set + start the front half
249            self.pwm.loop_.write(|w| w.cnt().bits(1));
250            self.pwm.events_loopsdone.write(|w| w.bits(0));
251            self.set_seq1_raw(&buf_b)?;
252            self.start_send_raw(&buf_a)?;
253        }
254
255        #[derive(Copy, Clone)]
256        enum Data {
257            Pixel(RGB8),
258            Blank,
259        }
260
261        // Create a new iterator that can contain pixels and blanks.
262        // We include three blanks to ensure that we always have
263        // enough to do a full A/B cycle, even if there are an
264        // odd number of LEDs.
265        //
266        // TODO: In the future, we could have slightly more complex
267        // code to skip the "extra" sequence by setting the loop
268        // count to zero
269        let new_iter = iterator
270            .map(|seq| Data::Pixel(seq.into()))
271            .chain([Data::Blank; 3].iter().cloned());
272
273        // Begin filling the rest of the LEDs, or any remaining
274        // blanks needed
275        for seq in new_iter {
276            if !toggle {
277                // We're currently on the "A" side, about to start the "B" side,
278                // and refill the "A" side data.
279
280                // wait until seq_end[0] to refill the data
281                while !self.is_done_raw() {}
282
283                // refill seq[0] data
284                match seq {
285                    Data::Pixel(p) => {
286                        fill_buf(&p, &mut buf_a)?;
287                    }
288                    Data::Blank => {
289                        buf_a.copy_from_slice(&[0x8000u16; 24]);
290                        blanks_fed += 1;
291                    }
292                }
293
294                compiler_fence(Ordering::SeqCst);
295            } else {
296                // We're currently on the "B" side, waiting to restart the
297                // sequence, then refill the "B" side data
298
299                // If we are **here**, this means:
300                // 0 blanks: We haven't filled ANY blanks, might still
301                //   have more LEDs or not
302                // 1 blanks: We've filled the A side with the first blank.
303                //   we still need to start the seq and fill the B side
304                //   with a blank.
305                // 2 blanks: We've sent a blank in B before, and loaded
306                //   one into the pending "A" side, but we still
307                //   need to launch A to fire the last blank.
308                // > 2 blanks: Shouldn't happen.
309
310                // wait until the A+B loop is done
311                while self.pwm.events_loopsdone.read().bits() == 0 {}
312
313                // start seq[0] as quickly as possible
314                unsafe {
315                    self.pwm.tasks_seqstart[0].write(|w| w.bits(1));
316                    self.pwm.events_seqend[0].write(|w| w.bits(0));
317                    self.pwm.events_loopsdone.write(|w| w.bits(0));
318                }
319
320                compiler_fence(Ordering::SeqCst);
321
322                // refill seq[1] data
323                match seq {
324                    Data::Pixel(p) => {
325                        fill_buf(&p, &mut buf_b)?;
326                    }
327                    Data::Blank => {
328                        buf_b.copy_from_slice(&[0x8000u16; 24]);
329                        blanks_fed += 1;
330                    }
331                }
332                compiler_fence(Ordering::SeqCst);
333
334                // We've filled at least two blanks
335                if blanks_fed >= 2 {
336                    break;
337                }
338            }
339            toggle = !toggle;
340        }
341
342        // Wait until the last loop is done
343        while self.pwm.events_loopsdone.read().bits() == 0 {}
344
345        Ok(())
346    }
347}
348
349// As of now, there is no mainline PWM driver. Implement the parts
350// we need to support various configurations
351
352mod sealed {
353    use core::ops::Deref;
354    pub trait Instance: Deref<Target = crate::hal::pac::pwm0::RegisterBlock> {}
355}
356
357impl sealed::Instance for pac::PWM0 {}
358
359#[cfg(not(any(feature = "52810")))]
360impl sealed::Instance for pac::PWM1 {}
361
362#[cfg(not(any(feature = "52810")))]
363impl sealed::Instance for pac::PWM2 {}
364
365#[cfg(not(any(feature = "52810", feature = "52832")))]
366impl sealed::Instance for pac::PWM3 {}