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