rpi_ws281x_c/
lib.rs

1#![no_std]
2
3use core::{fmt, slice, mem, ptr};
4
5pub use rpi_ws281x_sys as bindings;
6pub use rpi_ws281x_sys::ws2811_return_t as ErrorCode;
7
8#[repr(transparent)]
9///Raspberry PI information
10pub struct PiInfo(&'static bindings::rpi_hw_t);
11
12impl PiInfo {
13    #[inline]
14    ///Checks running Hardware, returning if it is supported raspberry pi.
15    pub fn detect() -> Option<Self> {
16        //This is static data of C library so safe to cast with 'static lifetime
17        unsafe {
18            bindings::rpi_hw_detect().as_ref().map(PiInfo)
19        }
20    }
21
22    #[inline(always)]
23    ///Returns hardware revision
24    pub const fn revision(&self) -> u32 {
25        self.0.hwver
26    }
27
28    #[inline(always)]
29    ///Peripheral base address
30    pub const fn periph_base(&self) -> u32 {
31        self.0.periph_base
32    }
33
34    #[inline(always)]
35    ///Video core base address
36    pub const fn video_core_base(&self) -> u32 {
37        self.0.videocore_base
38    }
39
40    #[inline(always)]
41    ///Returns descriptive name
42    ///
43    ///Empty string on utf-8 error.
44    pub fn description(&self) -> &'static str {
45        let bytes = unsafe {
46            let len = libc::strlen(self.0.desc);
47            core::slice::from_raw_parts(self.0.desc as *const u8, len as usize)
48        };
49
50        match core::str::from_utf8(bytes) {
51            Ok(result) => result,
52            Err(_) => "",
53        }
54    }
55}
56
57impl fmt::Debug for PiInfo {
58    #[inline]
59    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
60        fmt.debug_struct("PiInfo")
61           .field("revision", &self.revision())
62           .field("description", &self.description())
63           .field("peripheral base", &self.periph_base())
64           .field("video core base", &self.video_core_base())
65           .finish()
66    }
67}
68
69#[repr(u32)]
70///PWM pins
71pub enum PwmPin {
72    ///PWM0 on GPIO 12
73    Pwm0_1 = 12,
74    ///PWM0 on GPIO 18
75    Pwm0_2 = 18,
76    ///PWM1 on GPIO 13
77    Pwm1_1 = 13,
78    ///PWM1 on GPIO 19
79    Pwm1_2 = 19,
80}
81
82#[repr(u32)]
83///SPI pins
84pub enum SpiPin {
85    ///SPI0 available on GPIO 10
86    Spi0 = 10,
87}
88
89#[repr(u32)]
90///PCM pins
91pub enum PcmPin {
92    ///PCM_DOUT is only available on GPIO 21.
93    Board = 21,
94    ///P5 header provides GPIO 31 with PCM_DOUT function, not available for all boards.
95    P5 = 31,
96}
97
98#[repr(transparent)]
99///Driver's channel
100pub struct Channel(bindings::ws2811_channel_t);
101
102impl Channel {
103    ///Creates unused channel.
104    pub const fn disabled() -> Self {
105        Self(bindings::ws2811_channel_t {
106            gpionum: 0,
107            invert: 0,
108            count: 0,
109            strip_type: bindings::WS2811_STRIP_RGB as _,
110            leds: ptr::null_mut(),
111            brightness: 0,
112            wshift: 0,
113            rshift: 0,
114            gshift: 0,
115            bshift: 0,
116            gamma: ptr::null_mut(),
117        })
118    }
119
120    #[inline]
121    ///Creates PWM channel
122    pub const fn set_pwm(pin: PwmPin) -> Self {
123        let mut ch = Channel::disabled();
124        ch.0.gpionum = pin as _;
125        ch
126    }
127
128    #[inline]
129    ///Creates PCM channel
130    pub const fn set_pcm(pin: PcmPin) -> Self {
131        let mut ch = Channel::disabled();
132        ch.0.gpionum = pin as _;
133        ch
134    }
135
136    #[inline]
137    ///Creates SPI channel
138    pub const fn set_spi(pin: SpiPin) -> Self {
139        let mut ch = Channel::disabled();
140        ch.0.gpionum = pin as _;
141        ch
142    }
143
144    #[inline]
145    ///Sets brightness.
146    pub const fn set_led_count(mut self, led_count: u16) -> Self {
147        debug_assert!(led_count > 0);
148        self.0.count = led_count as _;
149        self
150    }
151
152    #[inline]
153    ///Sets brightness.
154    pub const fn set_brightness(mut self, brightness: u8) -> Self {
155        self.0.brightness = brightness;
156        self
157    }
158
159    #[inline]
160    ///Get channel's brightness.
161    pub fn get_brightness(&self) -> u8 {
162        self.0.brightness
163    }
164
165    #[inline]
166    ///Changes channel's brightness.
167    pub fn change_brightness(&mut self, brightness: u8) {
168        self.0.brightness = brightness;
169    }
170
171    #[inline]
172    const fn set_strip(mut self, strip: u32) -> Self {
173        self.0.strip_type = strip as _;
174        self
175    }
176
177    #[inline]
178    ///Sets strip type to `WS2811_STRIP_RGB`
179    pub const fn set_strip_rgb(self) -> Self {
180        self.set_strip(bindings::WS2811_STRIP_RGB)
181    }
182
183    #[inline]
184    ///Sets strip type to `WS2811_STRIP_RBG`
185    pub const fn set_strip_rbg(self) -> Self {
186        self.set_strip(bindings::WS2811_STRIP_RBG)
187    }
188
189    #[inline]
190    ///Sets strip type to `WS2811_STRIP_GRB`
191    pub const fn set_strip_grb(self) -> Self {
192        self.set_strip(bindings::WS2811_STRIP_GRB)
193    }
194
195    #[inline]
196    ///Sets strip type to `WS2811_STRIP_GBR`
197    pub const fn set_strip_gbr(self) -> Self {
198        self.set_strip(bindings::WS2811_STRIP_GBR)
199    }
200
201    #[inline]
202    ///Sets strip type to `WS2811_STRIP_BGR`
203    pub const fn set_strip_bgr(self) -> Self {
204        self.set_strip(bindings::WS2811_STRIP_BGR)
205    }
206
207    #[inline]
208    ///Sets strip type to `WS2811_STRIP_BRG`
209    pub const fn set_strip_brg(self) -> Self {
210        self.set_strip(bindings::WS2811_STRIP_BRG)
211    }
212
213    #[inline]
214    ///Access LEDs array.
215    pub fn leds(&self) -> &[bindings::ws2811_led_t] {
216        debug_assert!(!self.0.leds.is_null());
217
218        unsafe {
219            slice::from_raw_parts(self.0.leds, self.0.count as usize)
220        }
221    }
222
223    #[inline]
224    ///Mutable access LEDs array.
225    pub fn leds_mut(&mut self) -> &mut [bindings::ws2811_led_t] {
226        debug_assert!(!self.0.leds.is_null());
227
228        unsafe {
229            slice::from_raw_parts_mut(self.0.leds, self.0.count as usize)
230        }
231    }
232}
233
234#[derive(Copy, Clone)]
235///LED driver builder
236///
237///Default values:
238///
239///- `freq` - `WS2811_TARGET_FREQ` constant (800000);
240///- `strip` - `WS2811_STRIP_RGB`
241///- `dma_channel` - 10;
242pub struct DriverBuilder {
243    render_wait_time: u64,
244    freq: u32,
245    dma_channel: u8,
246    channel: [bindings::ws2811_channel_t; 2],
247}
248
249impl DriverBuilder {
250    #[inline]
251    ///Creates default configuration:
252    pub const fn new() -> Self {
253        Self {
254            render_wait_time: 0,
255            freq: bindings::WS2811_TARGET_FREQ,
256            dma_channel: 10,
257            channel: [Channel::disabled().0; 2],
258        }
259    }
260
261    #[inline]
262    ///Sets waiting time for rendering function.
263    pub const fn render_wait_time(mut self, render_wait_time: u64) -> Self {
264        self.render_wait_time = render_wait_time;
265        self
266    }
267
268    #[inline]
269    ///Sets frequency.
270    pub const fn freq(mut self, freq: u32) -> Self {
271        debug_assert!(freq > 0);
272        self.freq = freq;
273        self
274    }
275
276    #[inline]
277    ///Sets DMA channel number to use
278    pub const fn dma(mut self, dma_num: u8) -> Self {
279        self.dma_channel = dma_num;
280        self
281    }
282
283
284    ///Sets first channel
285    pub const fn channel1(mut self, channel: Channel) -> Self {
286        //PWM1 must be used as channel 2 only
287        debug_assert!(channel.0.gpionum != PwmPin::Pwm0_1 as i32);
288        debug_assert!(channel.0.gpionum != PwmPin::Pwm0_2 as i32);
289
290        self.channel[0] = channel.0;
291        self
292    }
293
294    ///Sets second channel
295    pub const fn channel2(mut self, channel: Channel) -> Self {
296        //PWM1 must be used as channel 2 only
297        debug_assert!(channel.0.gpionum == PwmPin::Pwm1_1 as i32 || channel.0.gpionum == PwmPin::Pwm1_2 as i32);
298
299        self.channel[1] = channel.0;
300        self
301    }
302
303    #[inline]
304    pub fn build(self) -> Result<Driver, ErrorCode> {
305        let mut inner = bindings::ws2811_t {
306            render_wait_time: 0,
307            device: core::ptr::null_mut(),
308            rpi_hw: core::ptr::null(),
309            freq: self.freq,
310            dmanum: self.dma_channel as _,
311            channel: self.channel,
312        };
313
314        let result = unsafe {
315            bindings::ws2811_init(&mut inner)
316        };
317
318        match result {
319            ErrorCode::WS2811_SUCCESS => Ok(Driver {
320                inner,
321            }),
322            error => Err(error),
323        }
324    }
325}
326
327#[repr(transparent)]
328///rpi_ws281x driver wrapper.
329pub struct Driver {
330    inner: bindings::ws2811_t,
331}
332
333impl Driver {
334    #[inline]
335    ///Starts building driver.
336    pub const fn builder() -> DriverBuilder {
337        DriverBuilder::new()
338    }
339
340    #[inline]
341    ///Accesses first channel
342    pub const fn channel1(&self) -> &'_ Channel {
343        unsafe {
344            mem::transmute(&self.inner.channel[0])
345        }
346    }
347
348    #[inline]
349    ///Accesses first channel
350    pub fn channel1_mut(&mut self) -> &'_ mut Channel {
351        unsafe {
352            mem::transmute(&mut self.inner.channel[0])
353        }
354    }
355
356    #[inline]
357    ///Accesses second channel
358    ///
359    ///Only usable when PWM1 is set
360    pub const fn channel2(&self) -> &'_ Channel {
361        unsafe {
362            mem::transmute(&self.inner.channel[1])
363        }
364    }
365
366    #[inline]
367    ///Accesses second channel
368    pub fn channel2_mut(&mut self) -> &'_ mut Channel {
369        unsafe {
370            mem::transmute(&mut self.inner.channel[1])
371        }
372    }
373
374    #[inline]
375    ///Renders LEDs, awaiting previous render completion and performing necessary data transfer.
376    pub fn render(&mut self) -> Result<(), ErrorCode> {
377        let result = unsafe {
378            bindings::ws2811_render(&mut self.inner)
379        };
380
381        match result {
382            ErrorCode::WS2811_SUCCESS => Ok(()),
383            error => Err(error),
384        }
385    }
386
387    #[inline]
388    ///Waits for ongoing data transfer to complete
389    ///
390    ///Waiting is not needed for SPI channel.
391    pub fn wait(&mut self) -> Result<(), ErrorCode> {
392        let result = unsafe {
393            bindings::ws2811_wait(&mut self.inner)
394        };
395
396        match result {
397            ErrorCode::WS2811_SUCCESS => Ok(()),
398            error => Err(error),
399        }
400    }
401
402    #[inline]
403    ///Sets custom gamma correction using factor value.
404    pub fn set_gamma_factor(&mut self, factor: f64) {
405        unsafe {
406            bindings::ws2811_set_custom_gamma_factor(&mut self.inner, factor)
407        }
408    }
409
410    #[inline]
411    ///Shuts down HW, cleans up memory.
412    ///
413    ///After that driver can no longer be used.
414    ///
415    ///It is recommended to always do proper shutdown by dropping this struct or calling `stop`.
416    ///In order to guarantee that one can install signal handler that will make sure to call
417    ///`stop`.
418    pub fn stop(&mut self) {
419        if !self.inner.device.is_null() {
420            //all dynamic memory is freed and NULLed after this
421            //so just don't call it again if it is already null.
422            unsafe {
423                bindings::ws2811_fini(&mut self.inner)
424            }
425        }
426    }
427
428    ///Accesses underlying driver structure
429    pub const fn as_inner(&self) -> &bindings::ws2811_t {
430        &self.inner
431    }
432}
433
434impl Drop for Driver {
435    #[inline]
436    fn drop(&mut self) {
437        self.stop()
438    }
439}