@@ -33,6 +33,10 @@ use esp_hal::{
33
33
} ;
34
34
use smart_leds_trait:: { SmartLedsWrite , RGB8 } ;
35
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 ;
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.
38
42
const SK68XX_T0L_NS : u32 = SK68XX_CODE_PERIOD - SK68XX_T0H_NS ;
@@ -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
+ u32:: from ( PulseCode {
66
+ level1 : true ,
67
+ length1 : ( ( SK68XX_T0H_NS * src_clock) / 1000 ) as u16 ,
68
+ level2 : false ,
69
+ length2 : ( ( SK68XX_T0L_NS * src_clock) / 1000 ) as u16 ,
70
+ } ) ,
71
+ u32:: from ( PulseCode {
72
+ level1 : true ,
73
+ length1 : ( ( SK68XX_T1H_NS * src_clock) / 1000 ) as u16 ,
74
+ level2 : false ,
75
+ length2 : ( ( 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,67 +168,16 @@ 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
- ) ,
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
- }
179
+ pulses : led_pulses_for_clock ( src_clock) ,
160
180
}
161
-
162
- Ok ( ( ) )
163
181
}
164
182
}
165
183
@@ -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,98 @@ 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:: PeripheralOutput ,
233
+ peripheral:: Peripheral ,
234
+ rmt:: { asynch:: 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 : PeripheralOutput + ' 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
+ /// Convert all RGB8 items of the iterator to the RMT format and
276
+ /// add them to internal buffer, then start perform all asynchronous operations based on
277
+ /// that buffer.
278
+ pub async fn write (
279
+ & mut self ,
280
+ led : impl IntoIterator < Item = RGB8 > ,
281
+ ) -> Result < ( ) , LedAdapterError > {
282
+ self . prepare_rmt_buffer ( led) ?;
283
+ for chunk in self . rmt_buffer . chunks ( RMT_RAM_ONE_LED + 1 ) {
284
+ self . channel
285
+ . transmit ( chunk)
286
+ . await
287
+ . map_err ( LedAdapterError :: TransmissionError ) ?;
288
+ }
289
+ Ok ( ( ) )
290
+ }
291
+
292
+ fn prepare_rmt_buffer (
293
+ & mut self ,
294
+ iterator : impl IntoIterator < Item = RGB8 > ,
295
+ ) -> Result < ( ) , LedAdapterError > {
296
+ // We always start from the beginning of the buffer
297
+ let mut seq_iter = self . rmt_buffer . iter_mut ( ) ;
298
+
299
+ // Add all converted iterator items to the buffer.
300
+ // This will result in an `BufferSizeExceeded` error in case
301
+ // the iterator provides more elements than the buffer can take.
302
+ for item in iterator {
303
+ Self :: convert_rgb_to_pulse ( item, & mut seq_iter, self . pulses ) ?;
304
+ }
305
+ Ok ( ( ) )
306
+ }
307
+
308
+ /// Converts a RGB value to the correspodnign pulse value.
309
+ fn convert_rgb_to_pulse (
310
+ value : RGB8 ,
311
+ mut_iter : & mut IterMut < u32 > ,
312
+ pulses : ( u32 , u32 ) ,
313
+ ) -> Result < ( ) , LedAdapterError > {
314
+ convert_rgb_to_pulses ( value, mut_iter, pulses) ?;
315
+ * mut_iter. next ( ) . ok_or ( LedAdapterError :: BufferSizeExceeded ) ? = 0 ;
316
+
317
+ Ok ( ( ) )
318
+ }
319
+ }
320
+ }
0 commit comments