@@ -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 = 1200 ;
38
42
const SK68XX_T0H_NS : u32 = 320 ;
39
43
const SK68XX_T0L_NS : u32 = SK68XX_CODE_PERIOD - SK68XX_T0H_NS ;
@@ -51,24 +55,89 @@ 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
+
54
113
/// Macro to allocate a buffer sized for a specific number of LEDs to be
55
114
/// addressed.
56
115
///
57
116
/// Attempting to use more LEDs that the buffer is configured for will result in
58
117
/// an `LedAdapterError:BufferSizeExceeded` error.
59
118
#[ macro_export]
119
+ macro_rules! smart_led_buffer {
120
+ ( $num_leds: ident ) => {
121
+ [ 0u32 ; buffer_size( $num_leds) ]
122
+ } ;
123
+ }
124
+
125
+ /// Deprecated alias for [smart_led_buffer] macro.
126
+ #[ macro_export]
127
+ #[ deprecated]
60
128
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 ]
129
+ ( $num_leds: ident ) => {
130
+ smart_led_buffer!( $num_leds) ;
69
131
} ;
70
132
}
71
133
134
+ /// Function to calculate the required RMT buffer size for a given number of LEDs when using
135
+ /// the blocking API.
136
+ pub const fn buffer_size ( num_leds : usize ) -> usize {
137
+ // 1 additional pulse for the end delimiter
138
+ num_leds * RMT_RAM_ONE_LED + 1
139
+ }
140
+
72
141
/// Adapter taking an RMT channel and a specific pin and providing RGB LED
73
142
/// interaction functionality using the `smart-leds` crate
74
143
pub struct SmartLedsAdapter < TX , const BUFFER_SIZE : usize >
@@ -94,67 +163,16 @@ where
94
163
O : OutputPin + ' d ,
95
164
C : TxChannelCreator < ' d , TX , O > ,
96
165
{
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 ( ) ;
166
+ let channel = channel. configure ( pin, led_config ( ) ) . unwrap ( ) ;
107
167
108
168
// 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 ( ) ;
169
+ let src_clock = Clocks :: get ( ) . apb_clock . to_MHz ( ) ;
111
170
112
171
Self {
113
172
channel : Some ( channel) ,
114
173
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
- ) ,
129
- }
130
- }
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
- }
174
+ pulses : led_pulses_for_clock ( src_clock) ,
155
175
}
156
-
157
- Ok ( ( ) )
158
176
}
159
177
}
160
178
@@ -180,7 +198,7 @@ where
180
198
// This will result in an `BufferSizeExceeded` error in case
181
199
// the iterator provides more elements than the buffer can take.
182
200
for item in iterator {
183
- Self :: convert_rgb_to_pulse ( item. into ( ) , & mut seq_iter, self . pulses ) ?;
201
+ convert_rgb_to_pulses ( item. into ( ) , & mut seq_iter, self . pulses ) ?;
184
202
}
185
203
186
204
// Finally, add an end element.
@@ -200,3 +218,98 @@ where
200
218
}
201
219
}
202
220
}
221
+
222
+ /// Support for asynchronous and non-blocking use of the RMT peripheral to drive smart LEDs.
223
+ pub mod asynch {
224
+ use super :: * ;
225
+ use esp_hal:: {
226
+ clock:: Clocks ,
227
+ gpio:: PeripheralOutput ,
228
+ peripheral:: Peripheral ,
229
+ rmt:: { asynch:: TxChannelAsync , TxChannelCreatorAsync } ,
230
+ } ;
231
+
232
+ /// Function to calculate the required RMT buffer size for a given number of LEDs when using
233
+ /// the asynchronous API.
234
+ pub const fn buffer_size ( num_leds : usize ) -> usize {
235
+ // 1 byte end delimiter for each transfer.
236
+ num_leds * ( RMT_RAM_ONE_LED + 1 )
237
+ }
238
+
239
+ /// Adapter taking an RMT channel and a specific pin and providing RGB LED
240
+ /// interaction functionality.
241
+ pub struct SmartLedAdapterAsync < Tx , const BUFFER_SIZE : usize > {
242
+ channel : Tx ,
243
+ rmt_buffer : [ u32 ; BUFFER_SIZE ] ,
244
+ pulses : ( u32 , u32 ) ,
245
+ }
246
+
247
+ impl < ' d , Tx : TxChannelAsync , const BUFFER_SIZE : usize > SmartLedAdapterAsync < Tx , BUFFER_SIZE > {
248
+ /// Create a new adapter object that drives the pin using the RMT channel.
249
+ pub fn new < C , O > (
250
+ channel : C ,
251
+ pin : impl Peripheral < P = O > + ' d ,
252
+ rmt_buffer : [ u32 ; BUFFER_SIZE ] ,
253
+ ) -> SmartLedAdapterAsync < Tx , BUFFER_SIZE >
254
+ where
255
+ O : PeripheralOutput + ' d ,
256
+ C : TxChannelCreatorAsync < ' d , Tx , O > ,
257
+ {
258
+ let channel = channel. configure ( pin, led_config ( ) ) . unwrap ( ) ;
259
+
260
+ // Assume the RMT peripheral is set up to use the APB clock
261
+ let src_clock = Clocks :: get ( ) . apb_clock . to_MHz ( ) ;
262
+
263
+ Self {
264
+ channel,
265
+ rmt_buffer,
266
+ pulses : led_pulses_for_clock ( src_clock) ,
267
+ }
268
+ }
269
+
270
+ /// Convert all RGB8 items of the iterator to the RMT format and
271
+ /// add them to internal buffer, then start perform all asynchronous operations based on
272
+ /// that buffer.
273
+ pub async fn write (
274
+ & mut self ,
275
+ led : impl IntoIterator < Item = RGB8 > ,
276
+ ) -> Result < ( ) , LedAdapterError > {
277
+ self . prepare_rmt_buffer ( led) ?;
278
+ for chunk in self . rmt_buffer . chunks ( RMT_RAM_ONE_LED + 1 ) {
279
+ self . channel
280
+ . transmit ( chunk)
281
+ . await
282
+ . map_err ( LedAdapterError :: TransmissionError ) ?;
283
+ }
284
+ Ok ( ( ) )
285
+ }
286
+
287
+ fn prepare_rmt_buffer (
288
+ & mut self ,
289
+ iterator : impl IntoIterator < Item = RGB8 > ,
290
+ ) -> Result < ( ) , LedAdapterError > {
291
+ // We always start from the beginning of the buffer
292
+ let mut seq_iter = self . rmt_buffer . iter_mut ( ) ;
293
+
294
+ // Add all converted iterator items to the buffer.
295
+ // This will result in an `BufferSizeExceeded` error in case
296
+ // the iterator provides more elements than the buffer can take.
297
+ for item in iterator {
298
+ Self :: convert_rgb_to_pulse ( item, & mut seq_iter, self . pulses ) ?;
299
+ }
300
+ Ok ( ( ) )
301
+ }
302
+
303
+ /// Converts a RGB value to the correspodnign pulse value.
304
+ fn convert_rgb_to_pulse (
305
+ value : RGB8 ,
306
+ mut_iter : & mut IterMut < u32 > ,
307
+ pulses : ( u32 , u32 ) ,
308
+ ) -> Result < ( ) , LedAdapterError > {
309
+ convert_rgb_to_pulses ( value, mut_iter, pulses) ?;
310
+ * mut_iter. next ( ) . ok_or ( LedAdapterError :: BufferSizeExceeded ) ? = 0 ;
311
+
312
+ Ok ( ( ) )
313
+ }
314
+ }
315
+ }
0 commit comments