4
4
* The MIT License (MIT)
5
5
*
6
6
* Copyright (c) 2016-2018 Glenn Ruben Bakke
7
+ * Copyright (c) 2023 Robert Hammelrath
7
8
*
8
9
* Permission is hereby granted, free of charge, to any person obtaining a copy
9
10
* of this software and associated documentation files (the "Software"), to deal
47
48
#define PWM_MAX_PERIOD (32768)
48
49
49
50
typedef enum {
50
- MODE_HIGH_LOW ,
51
+ MODE_HIGH_LOW = 0 ,
51
52
MODE_LOW_HIGH
52
53
} pwm_mode_t ;
53
54
54
55
typedef enum {
55
- DUTY_NOT_SET ,
56
+ DUTY_NOT_SET = 0 ,
56
57
DUTY_PERCENT ,
57
58
DUTY_U16 ,
58
59
DUTY_NS
59
60
} pwm_duty_t ;
60
61
62
+ typedef enum {
63
+ FREE = 0 ,
64
+ STOPPED ,
65
+ RUNNING
66
+ } pwm_run_t ;
67
+
61
68
typedef struct {
62
- uint8_t pwm_pin ;
63
- uint8_t duty_mode ;
64
- int8_t freq_div ;
69
+ uint8_t pwm_pin [NRF_PWM_CHANNEL_COUNT ];
70
+ pwm_mode_t mode [NRF_PWM_CHANNEL_COUNT ];
71
+ pwm_duty_t duty_mode [NRF_PWM_CHANNEL_COUNT ];
72
+ uint32_t duty [NRF_PWM_CHANNEL_COUNT ];
73
+ pwm_run_t active ;
65
74
bool defer_start ;
66
- uint32_t duty ;
75
+ int8_t freq_div ;
67
76
uint32_t freq ;
68
- bool mode ;
69
77
} machine_pwm_config_t ;
70
78
71
79
typedef struct _machine_pwm_obj_t {
72
80
mp_obj_base_t base ;
73
81
const nrfx_pwm_t * p_pwm ;
74
82
machine_pwm_config_t * p_config ;
83
+ uint8_t id ;
84
+ uint8_t channel ;
75
85
} machine_pwm_obj_t ;
76
86
77
87
STATIC const nrfx_pwm_t machine_hard_pwm_instances [] = {
@@ -86,46 +96,57 @@ STATIC const nrfx_pwm_t machine_hard_pwm_instances[] = {
86
96
};
87
97
88
98
STATIC machine_pwm_config_t hard_configs [MP_ARRAY_SIZE (machine_hard_pwm_instances )];
89
- STATIC uint8_t pwm_used [MP_ARRAY_SIZE (machine_hard_pwm_instances )];
90
99
91
100
STATIC const machine_pwm_obj_t machine_hard_pwm_obj [] = {
92
101
#if defined(NRF52_SERIES )
93
- {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [0 ], .p_config = & hard_configs [0 ]},
94
- {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [1 ], .p_config = & hard_configs [1 ]},
95
- {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [2 ], .p_config = & hard_configs [2 ]},
102
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [0 ], .p_config = & hard_configs [0 ], 0 , 0 },
103
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [0 ], .p_config = & hard_configs [0 ], 0 , 1 },
104
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [0 ], .p_config = & hard_configs [0 ], 0 , 2 },
105
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [0 ], .p_config = & hard_configs [0 ], 0 , 3 },
106
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [1 ], .p_config = & hard_configs [1 ], 1 , 0 },
107
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [1 ], .p_config = & hard_configs [1 ], 1 , 1 },
108
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [1 ], .p_config = & hard_configs [1 ], 1 , 2 },
109
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [1 ], .p_config = & hard_configs [1 ], 1 , 3 },
110
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [2 ], .p_config = & hard_configs [2 ], 2 , 0 },
111
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [2 ], .p_config = & hard_configs [2 ], 2 , 1 },
112
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [2 ], .p_config = & hard_configs [2 ], 2 , 2 },
113
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [2 ], .p_config = & hard_configs [2 ], 2 , 3 },
96
114
#if NRF52840
97
- {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [3 ], .p_config = & hard_configs [3 ]},
115
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [3 ], .p_config = & hard_configs [3 ], 3 , 0 },
116
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [3 ], .p_config = & hard_configs [3 ], 3 , 1 },
117
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [3 ], .p_config = & hard_configs [3 ], 3 , 2 },
118
+ {{& machine_pwm_type }, .p_pwm = & machine_hard_pwm_instances [3 ], .p_config = & hard_configs [3 ], 3 , 3 },
98
119
#endif
99
120
#endif
100
121
};
101
122
102
123
void pwm_init0 (void ) {
124
+ for (int i = 0 ; i < MP_ARRAY_SIZE (hard_configs ); i ++ ) {
125
+ hard_configs [i ].active = FREE ;
126
+ hard_configs [i ].freq_div = -1 ;
127
+ hard_configs [i ].freq = 0 ;
128
+ memset (hard_configs [i ].duty_mode , DUTY_NOT_SET , NRF_PWM_CHANNEL_COUNT );
129
+ }
103
130
}
104
131
105
- // Find a free PWM
106
- STATIC int hard_pwm_find (int pin ) {
107
- // check, if a PWM object can be reused .
108
- for (int i = 0 ; i < MP_ARRAY_SIZE (machine_hard_pwm_obj ); i ++ ) {
109
- if (machine_hard_pwm_obj [ i ]. p_config -> pwm_pin == pin ) {
110
- return i ;
132
+ // Find a free PWM object
133
+ STATIC int hard_pwm_find () {
134
+ // look for a free module .
135
+ for (int j = 0 ; j < MP_ARRAY_SIZE (hard_configs ); j ++ ) {
136
+ if (hard_configs [ j ]. active == FREE ) {
137
+ return j * NRF_PWM_CHANNEL_COUNT ;
111
138
}
112
139
}
113
- // if not, look for a free object.
114
- for (int i = 0 ; i < MP_ARRAY_SIZE (machine_hard_pwm_obj ); i ++ ) {
115
- if (pwm_used [i ] == 0 ) {
116
- return i ;
117
- }
118
- }
119
- mp_raise_ValueError (MP_ERROR_TEXT ("no free PWM id" ));
140
+ mp_raise_ValueError (MP_ERROR_TEXT ("no free PWM object" ));
120
141
}
121
142
122
143
STATIC void mp_machine_pwm_print (const mp_print_t * print , mp_obj_t self_in , mp_print_kind_t kind ) {
123
144
machine_pwm_obj_t * self = self_in ;
124
145
static char * duty_suffix [] = { "" , "" , "_u16" , "_ns" };
125
- mp_printf (print , "<PWM: Pin=%u freq=%dHz duty%s=%d invert=%d id=%u>" ,
126
- self -> p_config -> pwm_pin , self -> p_config -> freq ,
127
- duty_suffix [self -> p_config -> duty_mode ] , self -> p_config -> duty ,
128
- self -> p_config -> mode , self -> p_pwm -> drv_inst_idx );
146
+ mp_printf (print , "<PWM: Pin=%u freq=%dHz duty%s=%d invert=%d id=%u channel=%u >" ,
147
+ self -> p_config -> pwm_pin [ self -> channel ] , self -> p_config -> freq ,
148
+ duty_suffix [self -> p_config -> duty_mode [ self -> channel ]] , self -> p_config -> duty [ self -> channel ] ,
149
+ self -> p_config -> mode [ self -> channel ] , self -> id , self -> channel );
129
150
}
130
151
131
152
/******************************************************************************/
@@ -146,10 +167,11 @@ static const mp_arg_t allowed_args[] = {
146
167
{ MP_QSTR_duty_ns , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = -1 } },
147
168
{ MP_QSTR_invert , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = -1 } },
148
169
{ MP_QSTR_id , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = -1 } },
170
+ { MP_QSTR_channel , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = -1 } },
149
171
};
150
172
151
173
STATIC void mp_machine_pwm_init_helper (const machine_pwm_obj_t * self , size_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
152
- enum { ARG_pin , ARG_freq , ARG_duty , ARG_duty_u16 , ARG_duty_ns , ARG_invert , ARG_id };
174
+ enum { ARG_pin , ARG_freq , ARG_duty , ARG_duty_u16 , ARG_duty_ns , ARG_invert , ARG_id , ARG_channel };
153
175
154
176
mp_arg_val_t args [MP_ARRAY_SIZE (allowed_args )];
155
177
mp_arg_parse_all (n_args , pos_args , kw_args , MP_ARRAY_SIZE (allowed_args ), allowed_args , args );
@@ -168,7 +190,7 @@ STATIC void mp_machine_pwm_init_helper(const machine_pwm_obj_t *self, size_t n_a
168
190
mp_machine_pwm_duty_set_ns (self , args [ARG_duty_ns ].u_int );
169
191
}
170
192
if (args [ARG_invert ].u_int != -1 ) {
171
- self -> p_config -> mode = args [ARG_invert ].u_int ? MODE_LOW_HIGH : MODE_HIGH_LOW ;
193
+ self -> p_config -> mode [ self -> channel ] = args [ARG_invert ].u_int ? MODE_LOW_HIGH : MODE_HIGH_LOW ;
172
194
}
173
195
self -> p_config -> defer_start = false;
174
196
@@ -177,7 +199,7 @@ STATIC void mp_machine_pwm_init_helper(const machine_pwm_obj_t *self, size_t n_a
177
199
178
200
179
201
STATIC mp_obj_t mp_machine_pwm_make_new (const mp_obj_type_t * type , size_t n_args , size_t n_kw , const mp_obj_t * all_args ) {
180
- enum { ARG_pin , ARG_freq , ARG_duty , ARG_duty_u16 , ARG_duty_ns , ARG_invert , ARG_id };
202
+ enum { ARG_pin , ARG_freq , ARG_duty , ARG_duty_u16 , ARG_duty_ns , ARG_invert , ARG_id , ARG_channel };
181
203
182
204
// parse args
183
205
mp_arg_val_t args [MP_ARRAY_SIZE (allowed_args )];
@@ -191,26 +213,33 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args
191
213
mp_raise_ValueError (MP_ERROR_TEXT ("Pin missing" ));
192
214
}
193
215
216
+ // Get the PWM object number
217
+ // If just the ID is given, use channel 0
218
+ // If none is given, attempt to find an unused object.
194
219
int pwm_id = -1 ;
195
220
if (args [ARG_id ].u_int != -1 ) {
196
- // get static peripheral object
197
- if (args [ARG_id ].u_int >= 0 && args [ARG_id ].u_int < MP_ARRAY_SIZE (machine_hard_pwm_obj )) {
198
- pwm_id = args [ARG_id ].u_int ;
221
+ if (args [ARG_id ].u_int >= 0 && args [ARG_id ].u_int < MP_ARRAY_SIZE (machine_hard_pwm_instances )) {
222
+ pwm_id = args [ARG_id ].u_int * NRF_PWM_CHANNEL_COUNT ;
223
+ if (args [ARG_channel ].u_int != -1 ) {
224
+ if (args [ARG_channel ].u_int >= 0 && args [ARG_channel ].u_int < NRF_PWM_CHANNEL_COUNT ) {
225
+ pwm_id += args [ARG_channel ].u_int ;
226
+ }
227
+ }
199
228
}
200
229
} else {
201
- pwm_id = hard_pwm_find (pwm_pin );
230
+ // no ID given, search for a free ID.
231
+ pwm_id = hard_pwm_find ();
202
232
}
203
233
if (pwm_id < 0 ) {
204
234
mp_raise_ValueError (MP_ERROR_TEXT ("invalid PWM id" ));
205
235
}
206
236
const machine_pwm_obj_t * self = & machine_hard_pwm_obj [pwm_id ];
207
- self -> p_config -> pwm_pin = pwm_pin ;
237
+ int pwm_channel = pwm_id % NRF_PWM_CHANNEL_COUNT ;
238
+ self -> p_config -> pwm_pin [pwm_channel ] = pwm_pin ;
239
+ self -> p_config -> duty_mode [pwm_channel ] = DUTY_NOT_SET ;
240
+ self -> p_config -> duty [pwm_channel ] = 0 ;
241
+ self -> p_config -> mode [pwm_channel ] = MODE_HIGH_LOW ;
208
242
self -> p_config -> defer_start = false;
209
- self -> p_config -> duty_mode = DUTY_NOT_SET ;
210
- self -> p_config -> duty = 0 ;
211
- self -> p_config -> freq = 0 ;
212
- self -> p_config -> freq_div = -1 ;
213
- self -> p_config -> mode = MODE_HIGH_LOW ;
214
243
215
244
// start the PWM running for this channel
216
245
mp_map_t kw_args ;
@@ -220,14 +249,17 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args
220
249
return MP_OBJ_FROM_PTR (self );
221
250
}
222
251
252
+ // Stop all PWM modules and release them
223
253
void pwm_deinit_all (void ) {
224
- for (int i = 0 ; i < MP_ARRAY_SIZE (machine_hard_pwm_obj ); i ++ ) {
225
- mp_machine_pwm_deinit (& machine_hard_pwm_obj [i ]);
254
+ for (int i = 0 ; i < MP_ARRAY_SIZE (machine_hard_pwm_instances ); i ++ ) {
255
+ mp_machine_pwm_deinit (& machine_hard_pwm_obj [i * NRF_PWM_CHANNEL_COUNT ]);
226
256
}
257
+ pwm_init0 ();
227
258
}
228
259
260
+ // Stop the PWM module, but do not release it.
229
261
STATIC void mp_machine_pwm_deinit (const machine_pwm_obj_t * self ) {
230
- pwm_used [ self -> p_pwm -> drv_inst_idx ] = 0 ;
262
+ self -> p_config -> active = STOPPED ;
231
263
nrfx_pwm_stop (self -> p_pwm , true);
232
264
nrfx_pwm_uninit (self -> p_pwm );
233
265
}
@@ -253,48 +285,48 @@ STATIC void mp_machine_pwm_freq_set(const machine_pwm_obj_t *self, mp_int_t freq
253
285
}
254
286
255
287
STATIC mp_obj_t mp_machine_pwm_duty_get (const machine_pwm_obj_t * self ) {
256
- if (self -> p_config -> duty_mode == DUTY_PERCENT ) {
257
- return MP_OBJ_NEW_SMALL_INT (self -> p_config -> duty );
258
- } else if (self -> p_config -> duty_mode == DUTY_U16 ) {
259
- return MP_OBJ_NEW_SMALL_INT (self -> p_config -> duty * 100 / 65536 );
288
+ if (self -> p_config -> duty_mode [ self -> channel ] == DUTY_PERCENT ) {
289
+ return MP_OBJ_NEW_SMALL_INT (self -> p_config -> duty [ self -> channel ] );
290
+ } else if (self -> p_config -> duty_mode [ self -> channel ] == DUTY_U16 ) {
291
+ return MP_OBJ_NEW_SMALL_INT (self -> p_config -> duty [ self -> channel ] * 100 / 65536 );
260
292
} else {
261
293
return MP_OBJ_NEW_SMALL_INT (-1 );
262
294
}
263
295
}
264
296
265
297
STATIC void mp_machine_pwm_duty_set (const machine_pwm_obj_t * self , mp_int_t duty ) {
266
- self -> p_config -> duty = duty ;
267
- self -> p_config -> duty_mode = DUTY_PERCENT ;
298
+ self -> p_config -> duty [ self -> channel ] = duty ;
299
+ self -> p_config -> duty_mode [ self -> channel ] = DUTY_PERCENT ;
268
300
machine_hard_pwm_start (self );
269
301
}
270
302
271
303
STATIC mp_obj_t mp_machine_pwm_duty_get_u16 (const machine_pwm_obj_t * self ) {
272
- if (self -> p_config -> duty_mode == DUTY_U16 ) {
273
- return MP_OBJ_NEW_SMALL_INT (self -> p_config -> duty );
274
- } else if (self -> p_config -> duty_mode == DUTY_PERCENT ) {
275
- return MP_OBJ_NEW_SMALL_INT (self -> p_config -> duty * 65536 / 100 );
304
+ if (self -> p_config -> duty_mode [ self -> channel ] == DUTY_U16 ) {
305
+ return MP_OBJ_NEW_SMALL_INT (self -> p_config -> duty [ self -> channel ] );
306
+ } else if (self -> p_config -> duty_mode [ self -> channel ] == DUTY_PERCENT ) {
307
+ return MP_OBJ_NEW_SMALL_INT (self -> p_config -> duty [ self -> channel ] * 65536 / 100 );
276
308
} else {
277
309
return MP_OBJ_NEW_SMALL_INT (-1 );
278
310
}
279
311
}
280
312
281
313
STATIC void mp_machine_pwm_duty_set_u16 (const machine_pwm_obj_t * self , mp_int_t duty ) {
282
- self -> p_config -> duty = duty ;
283
- self -> p_config -> duty_mode = DUTY_U16 ;
314
+ self -> p_config -> duty [ self -> channel ] = duty ;
315
+ self -> p_config -> duty_mode [ self -> channel ] = DUTY_U16 ;
284
316
machine_hard_pwm_start (self );
285
317
}
286
318
287
319
STATIC mp_obj_t mp_machine_pwm_duty_get_ns (const machine_pwm_obj_t * self ) {
288
- if (self -> p_config -> duty_mode == DUTY_NS ) {
289
- return MP_OBJ_NEW_SMALL_INT (self -> p_config -> duty );
320
+ if (self -> p_config -> duty_mode [ self -> channel ] == DUTY_NS ) {
321
+ return MP_OBJ_NEW_SMALL_INT (self -> p_config -> duty [ self -> channel ] );
290
322
} else {
291
323
return MP_OBJ_NEW_SMALL_INT (-1 );
292
324
}
293
325
}
294
326
295
327
STATIC void mp_machine_pwm_duty_set_ns (const machine_pwm_obj_t * self , mp_int_t duty ) {
296
- self -> p_config -> duty = duty ;
297
- self -> p_config -> duty_mode = DUTY_NS ;
328
+ self -> p_config -> duty [ self -> channel ] = duty ;
329
+ self -> p_config -> duty_mode [ self -> channel ] = DUTY_NS ;
298
330
machine_hard_pwm_start (self );
299
331
}
300
332
@@ -305,15 +337,16 @@ STATIC void machine_hard_pwm_start(const machine_pwm_obj_t *self) {
305
337
nrfx_pwm_config_t config ;
306
338
307
339
// check if ready to go
308
- if (self -> p_config -> defer_start == true || self -> p_config -> freq_div < 0 || self -> p_config -> duty_mode == DUTY_NOT_SET ) {
340
+ if (self -> p_config -> defer_start == true || self -> p_config -> freq_div < 0 || self -> p_config -> duty_mode [ self -> channel ] == DUTY_NOT_SET ) {
309
341
return ; // Not ready yet.
310
342
}
311
- pwm_used [self -> p_pwm -> drv_inst_idx ] = 1 ;
312
343
313
- config .output_pins [0 ] = self -> p_config -> pwm_pin ;
314
- config .output_pins [1 ] = NRFX_PWM_PIN_NOT_USED ;
315
- config .output_pins [2 ] = NRFX_PWM_PIN_NOT_USED ;
316
- config .output_pins [3 ] = NRFX_PWM_PIN_NOT_USED ;
344
+ self -> p_config -> active = RUNNING ;
345
+
346
+ config .output_pins [0 ] = self -> p_config -> duty_mode [0 ] != DUTY_NOT_SET ? self -> p_config -> pwm_pin [0 ] : NRFX_PWM_PIN_NOT_USED ;
347
+ config .output_pins [1 ] = self -> p_config -> duty_mode [1 ] != DUTY_NOT_SET ? self -> p_config -> pwm_pin [1 ] : NRFX_PWM_PIN_NOT_USED ;
348
+ config .output_pins [2 ] = self -> p_config -> duty_mode [2 ] != DUTY_NOT_SET ? self -> p_config -> pwm_pin [2 ] : NRFX_PWM_PIN_NOT_USED ;
349
+ config .output_pins [3 ] = self -> p_config -> duty_mode [3 ] != DUTY_NOT_SET ? self -> p_config -> pwm_pin [3 ] : NRFX_PWM_PIN_NOT_USED ;
317
350
318
351
uint32_t tick_freq = PWM_MAX_BASE_FREQ / (1 << self -> p_config -> freq_div );
319
352
uint32_t period = tick_freq / self -> p_config -> freq ;
@@ -330,29 +363,25 @@ STATIC void machine_hard_pwm_start(const machine_pwm_obj_t *self) {
330
363
331
364
nrfx_pwm_init (self -> p_pwm , & config , NULL , NULL );
332
365
333
- uint16_t pulse_width ;
334
- if (self -> p_config -> duty_mode == DUTY_PERCENT ) {
335
- pulse_width = ((period * self -> p_config -> duty ) / 100 );
336
- } else if (self -> p_config -> duty_mode == DUTY_U16 ) {
337
- pulse_width = ((period * self -> p_config -> duty ) / 65536 );
338
- }
339
- if (self -> p_config -> duty_mode == DUTY_NS ) {
340
- pulse_width = (uint64_t )self -> p_config -> duty * tick_freq / 1000000000ULL ;
341
- }
342
-
343
- // TODO: Move DMA buffer to global memory.
344
366
volatile static uint16_t pwm_seq [4 ];
345
367
346
- if (self -> p_config -> mode == MODE_HIGH_LOW ) {
347
- pwm_seq [0 ] = 0x8000 | pulse_width ;
348
- } else {
349
- pwm_seq [0 ] = pulse_width ;
350
- }
368
+ for (int i = 0 ; i < NRF_PWM_CHANNEL_COUNT ; i ++ ) {
369
+ uint16_t pulse_width = 0 ;
370
+ if (self -> p_config -> duty_mode [i ] == DUTY_PERCENT ) {
371
+ pulse_width = ((period * self -> p_config -> duty [i ]) / 100 );
372
+ } else if (self -> p_config -> duty_mode [i ] == DUTY_U16 ) {
373
+ pulse_width = ((period * self -> p_config -> duty [i ]) / 65536 );
374
+ }
375
+ if (self -> p_config -> duty_mode [i ] == DUTY_NS ) {
376
+ pulse_width = (uint64_t )self -> p_config -> duty [i ] * tick_freq / 1000000000ULL ;
377
+ }
351
378
352
- // Outputs 1..3 are not used for now
353
- // pwm_seq[1] = 0x8000 | pulse_width;
354
- // pwm_seq[2] = 0x8000 | pulse_width;
355
- // pwm_seq[3] = 0x8000 | pulse_width;
379
+ if (self -> p_config -> mode [i ] == MODE_HIGH_LOW ) {
380
+ pwm_seq [i ] = 0x8000 | pulse_width ;
381
+ } else {
382
+ pwm_seq [i ] = pulse_width ;
383
+ }
384
+ }
356
385
357
386
const nrf_pwm_sequence_t pwm_sequence = {
358
387
.values .p_raw = (const uint16_t * )& pwm_seq ,
0 commit comments