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