@@ -34,6 +34,10 @@ use esp_hal::{
34
34
} ;
35
35
use smart_leds_trait:: { SmartLedsWrite , RGB8 } ;
36
36
37
+ // Required RMT RAM to drive one LED.
38
+ // number of channels (r,g,b -> 3) * pulses per channel 8)
39
+ const RMT_RAM_ONE_LED : usize = 3 * 8 ;
40
+
37
41
const SK68XX_CODE_PERIOD : u32 = 1250 ; // 800kHz
38
42
const SK68XX_T0H_NS : u32 = 400 ; // 300ns per SK6812 datasheet, 400 per WS2812. Some require >350ns for T0H. Others <500ns for T0H.
39
43
const SK68XX_T0L_NS : u32 = SK68XX_CODE_PERIOD - SK68XX_T0H_NS ;
@@ -51,24 +55,90 @@ pub enum LedAdapterError {
51
55
TransmissionError ( RmtError ) ,
52
56
}
53
57
58
+ fn led_pulses_for_clock ( src_clock : u32 ) -> ( u32 , u32 ) {
59
+ (
60
+ u32:: from ( PulseCode {
61
+ level1 : true ,
62
+ length1 : ( ( SK68XX_T0H_NS * src_clock) / 1000 ) as u16 ,
63
+ level2 : false ,
64
+ length2 : ( ( SK68XX_T0L_NS * src_clock) / 1000 ) as u16 ,
65
+ } ) ,
66
+ u32:: from ( PulseCode {
67
+ level1 : true ,
68
+ length1 : ( ( SK68XX_T1H_NS * src_clock) / 1000 ) as u16 ,
69
+ level2 : false ,
70
+ length2 : ( ( SK68XX_T1L_NS * src_clock) / 1000 ) as u16 ,
71
+ } ) ,
72
+ )
73
+ }
74
+
75
+ fn led_config ( ) -> TxChannelConfig {
76
+ TxChannelConfig {
77
+ clk_divider : 1 ,
78
+ idle_output_level : false ,
79
+ carrier_modulation : false ,
80
+ idle_output : true ,
81
+
82
+ ..TxChannelConfig :: default ( )
83
+ }
84
+ }
85
+
86
+ fn convert_rgb_to_pulses (
87
+ value : RGB8 ,
88
+ mut_iter : & mut IterMut < u32 > ,
89
+ pulses : ( u32 , u32 ) ,
90
+ ) -> Result < ( ) , LedAdapterError > {
91
+ convert_rgb_channel_to_pulses ( value. g , mut_iter, pulses) ?;
92
+ convert_rgb_channel_to_pulses ( value. r , mut_iter, pulses) ?;
93
+ convert_rgb_channel_to_pulses ( value. b , mut_iter, pulses) ?;
94
+ Ok ( ( ) )
95
+ }
96
+
97
+ fn convert_rgb_channel_to_pulses (
98
+ channel_value : u8 ,
99
+ mut_iter : & mut IterMut < u32 > ,
100
+ pulses : ( u32 , u32 ) ,
101
+ ) -> Result < ( ) , LedAdapterError > {
102
+ for position in [ 128 , 64 , 32 , 16 , 8 , 4 , 2 , 1 ] {
103
+ * mut_iter. next ( ) . ok_or ( LedAdapterError :: BufferSizeExceeded ) ? =
104
+ match channel_value & position {
105
+ 0 => pulses. 0 ,
106
+ _ => pulses. 1 ,
107
+ }
108
+ }
109
+
110
+ Ok ( ( ) )
111
+ }
112
+
113
+ /// Function to calculate the required RMT buffer size for a given number of LEDs when using
114
+ /// the blocking API.
115
+ pub const fn buffer_size ( num_leds : usize ) -> usize {
116
+ // 1 additional pulse for the end delimiter
117
+ num_leds * RMT_RAM_ONE_LED + 1
118
+ }
119
+
54
120
/// Macro to allocate a buffer sized for a specific number of LEDs to be
55
121
/// addressed.
56
122
///
57
123
/// Attempting to use more LEDs that the buffer is configured for will result in
58
124
/// an `LedAdapterError:BufferSizeExceeded` error.
59
125
#[ macro_export]
126
+ macro_rules! smart_led_buffer {
127
+ ( $num_leds: expr ) => {
128
+ [ 0u32 ; $crate:: buffer_size( $num_leds) ]
129
+ } ;
130
+ }
131
+
132
+ /// Deprecated alias for [smart_led_buffer] macro.
133
+ #[ macro_export]
134
+ #[ deprecated]
60
135
macro_rules! smartLedBuffer {
61
- ( $buffer_size: literal ) => {
62
- // The size we're assigning here is calculated as following
63
- // (
64
- // Nr. of LEDs
65
- // * channels (r,g,b -> 3)
66
- // * pulses per channel 8)
67
- // ) + 1 additional pulse for the end delimiter
68
- [ 0u32 ; $buffer_size * 24 + 1 ]
136
+ ( $num_leds: expr ) => {
137
+ smart_led_buffer!( $num_leds) ;
69
138
} ;
70
139
}
71
140
141
+
72
142
/// Adapter taking an RMT channel and a specific pin and providing RGB LED
73
143
/// interaction functionality using the `smart-leds` crate
74
144
pub struct SmartLedsAdapter < TX , const BUFFER_SIZE : usize >
@@ -94,68 +164,17 @@ where
94
164
O : OutputPin + ' d ,
95
165
C : TxChannelCreator < ' d , TX , O > ,
96
166
{
97
- let config = TxChannelConfig {
98
- clk_divider : 1 ,
99
- idle_output_level : false ,
100
- carrier_modulation : false ,
101
- idle_output : true ,
102
-
103
- ..TxChannelConfig :: default ( )
104
- } ;
105
-
106
- let channel = channel. configure ( pin, config) . unwrap ( ) ;
167
+ let channel = channel. configure ( pin, led_config ( ) ) . unwrap ( ) ;
107
168
108
169
// Assume the RMT peripheral is set up to use the APB clock
109
- let clocks = Clocks :: get ( ) ;
110
- let src_clock = clocks. apb_clock . to_MHz ( ) ;
170
+ let src_clock = Clocks :: get ( ) . apb_clock . to_MHz ( ) ;
111
171
112
172
Self {
113
173
channel : Some ( channel) ,
114
174
rmt_buffer,
115
- pulses : (
116
- u32:: from ( PulseCode {
117
- level1 : true ,
118
- length1 : ( ( SK68XX_T0H_NS * src_clock) / 1000 ) as u16 ,
119
- level2 : false ,
120
- length2 : ( ( SK68XX_T0L_NS * src_clock) / 1000 ) as u16 ,
121
- } ) ,
122
- u32:: from ( PulseCode {
123
- level1 : true ,
124
- length1 : ( ( SK68XX_T1H_NS * src_clock) / 1000 ) as u16 ,
125
- level2 : false ,
126
- length2 : ( ( SK68XX_T1L_NS * src_clock) / 1000 ) as u16 ,
127
- } ) ,
128
- ) ,
175
+ pulses : led_pulses_for_clock ( src_clock) ,
129
176
}
130
177
}
131
-
132
- fn convert_rgb_to_pulse (
133
- value : RGB8 ,
134
- mut_iter : & mut IterMut < u32 > ,
135
- pulses : ( u32 , u32 ) ,
136
- ) -> Result < ( ) , LedAdapterError > {
137
- Self :: convert_rgb_channel_to_pulses ( value. g , mut_iter, pulses) ?;
138
- Self :: convert_rgb_channel_to_pulses ( value. r , mut_iter, pulses) ?;
139
- Self :: convert_rgb_channel_to_pulses ( value. b , mut_iter, pulses) ?;
140
-
141
- Ok ( ( ) )
142
- }
143
-
144
- fn convert_rgb_channel_to_pulses (
145
- channel_value : u8 ,
146
- mut_iter : & mut IterMut < u32 > ,
147
- pulses : ( u32 , u32 ) ,
148
- ) -> Result < ( ) , LedAdapterError > {
149
- for position in [ 128 , 64 , 32 , 16 , 8 , 4 , 2 , 1 ] {
150
- * mut_iter. next ( ) . ok_or ( LedAdapterError :: BufferSizeExceeded ) ? =
151
- match channel_value & position {
152
- 0 => pulses. 0 ,
153
- _ => pulses. 1 ,
154
- }
155
- }
156
-
157
- Ok ( ( ) )
158
- }
159
178
}
160
179
161
180
impl < TX , const BUFFER_SIZE : usize > SmartLedsWrite for SmartLedsAdapter < TX , BUFFER_SIZE >
@@ -180,7 +199,7 @@ where
180
199
// This will result in an `BufferSizeExceeded` error in case
181
200
// the iterator provides more elements than the buffer can take.
182
201
for item in iterator {
183
- Self :: convert_rgb_to_pulse ( item. into ( ) , & mut seq_iter, self . pulses ) ?;
202
+ convert_rgb_to_pulses ( item. into ( ) , & mut seq_iter, self . pulses ) ?;
184
203
}
185
204
186
205
// Finally, add an end element.
@@ -200,3 +219,98 @@ where
200
219
}
201
220
}
202
221
}
222
+
223
+ /// Support for asynchronous and non-blocking use of the RMT peripheral to drive smart LEDs.
224
+ pub mod asynch {
225
+ use super :: * ;
226
+ use esp_hal:: {
227
+ clock:: Clocks ,
228
+ gpio:: PeripheralOutput ,
229
+ peripheral:: Peripheral ,
230
+ rmt:: { asynch:: TxChannelAsync , TxChannelCreatorAsync } ,
231
+ } ;
232
+
233
+ /// Function to calculate the required RMT buffer size for a given number of LEDs when using
234
+ /// the asynchronous API.
235
+ pub const fn buffer_size ( num_leds : usize ) -> usize {
236
+ // 1 byte end delimiter for each transfer.
237
+ num_leds * ( RMT_RAM_ONE_LED + 1 )
238
+ }
239
+
240
+ /// Adapter taking an RMT channel and a specific pin and providing RGB LED
241
+ /// interaction functionality.
242
+ pub struct SmartLedAdapterAsync < Tx , const BUFFER_SIZE : usize > {
243
+ channel : Tx ,
244
+ rmt_buffer : [ u32 ; BUFFER_SIZE ] ,
245
+ pulses : ( u32 , u32 ) ,
246
+ }
247
+
248
+ impl < ' d , Tx : TxChannelAsync , const BUFFER_SIZE : usize > SmartLedAdapterAsync < Tx , BUFFER_SIZE > {
249
+ /// Create a new adapter object that drives the pin using the RMT channel.
250
+ pub fn new < C , O > (
251
+ channel : C ,
252
+ pin : impl Peripheral < P = O > + ' d ,
253
+ rmt_buffer : [ u32 ; BUFFER_SIZE ] ,
254
+ ) -> SmartLedAdapterAsync < Tx , BUFFER_SIZE >
255
+ where
256
+ O : PeripheralOutput + ' d ,
257
+ C : TxChannelCreatorAsync < ' d , Tx , O > ,
258
+ {
259
+ let channel = channel. configure ( pin, led_config ( ) ) . unwrap ( ) ;
260
+
261
+ // Assume the RMT peripheral is set up to use the APB clock
262
+ let src_clock = Clocks :: get ( ) . apb_clock . to_MHz ( ) ;
263
+
264
+ Self {
265
+ channel,
266
+ rmt_buffer,
267
+ pulses : led_pulses_for_clock ( src_clock) ,
268
+ }
269
+ }
270
+
271
+ /// Convert all RGB8 items of the iterator to the RMT format and
272
+ /// add them to internal buffer, then start perform all asynchronous operations based on
273
+ /// that buffer.
274
+ pub async fn write (
275
+ & mut self ,
276
+ led : impl IntoIterator < Item = RGB8 > ,
277
+ ) -> Result < ( ) , LedAdapterError > {
278
+ self . prepare_rmt_buffer ( led) ?;
279
+ for chunk in self . rmt_buffer . chunks ( RMT_RAM_ONE_LED + 1 ) {
280
+ self . channel
281
+ . transmit ( chunk)
282
+ . await
283
+ . map_err ( LedAdapterError :: TransmissionError ) ?;
284
+ }
285
+ Ok ( ( ) )
286
+ }
287
+
288
+ fn prepare_rmt_buffer (
289
+ & mut self ,
290
+ iterator : impl IntoIterator < Item = RGB8 > ,
291
+ ) -> Result < ( ) , LedAdapterError > {
292
+ // We always start from the beginning of the buffer
293
+ let mut seq_iter = self . rmt_buffer . iter_mut ( ) ;
294
+
295
+ // Add all converted iterator items to the buffer.
296
+ // This will result in an `BufferSizeExceeded` error in case
297
+ // the iterator provides more elements than the buffer can take.
298
+ for item in iterator {
299
+ Self :: convert_rgb_to_pulse ( item, & mut seq_iter, self . pulses ) ?;
300
+ }
301
+ Ok ( ( ) )
302
+ }
303
+
304
+ /// Converts a RGB value to the correspodnign pulse value.
305
+ fn convert_rgb_to_pulse (
306
+ value : RGB8 ,
307
+ mut_iter : & mut IterMut < u32 > ,
308
+ pulses : ( u32 , u32 ) ,
309
+ ) -> Result < ( ) , LedAdapterError > {
310
+ convert_rgb_to_pulses ( value, mut_iter, pulses) ?;
311
+ * mut_iter. next ( ) . ok_or ( LedAdapterError :: BufferSizeExceeded ) ? = 0 ;
312
+
313
+ Ok ( ( ) )
314
+ }
315
+ }
316
+ }
0 commit comments