1 /*
2 * Copyright (c) 2015 - 2025, Nordic Semiconductor ASA
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include <nrfx.h>
35
36 #if NRFX_CHECK(NRFX_PWM_ENABLED)
37
38 #if !NRFX_FEATURE_PRESENT(NRFX_PWM, _ENABLED)
39 #error "No enabled PWM instances. Check <nrfx_config.h>."
40 #endif
41
42 #include <nrfx_pwm.h>
43 #include <haly/nrfy_gpio.h>
44
45 #define NRFX_LOG_MODULE PWM
46 #include <nrfx_log.h>
47
48 #if NRFX_CHECK(NRFX_PWM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
49 // The workaround uses interrupts to wake up the CPU and ensure it is active
50 // when PWM is about to start a DMA transfer. For initial transfer, done when
51 // a playback is started via PPI, a specific EGU instance is used to generate
52 // an interrupt. During the playback, the PWM interrupt triggered on SEQEND
53 // event of a preceding sequence is used to protect the transfer done for
54 // the next sequence to be played.
55 #include <hal/nrf_egu.h>
56 #define USE_DMA_ISSUE_WORKAROUND
57 #endif
58 #if defined(USE_DMA_ISSUE_WORKAROUND)
59 #define EGU_IRQn(i) EGU_IRQn_(i)
60 #define EGU_IRQn_(i) SWI##i##_EGU##i##_IRQn
61 #define EGU_IRQHandler(i) EGU_IRQHandler_(i)
62 #define EGU_IRQHandler_(i) nrfx_egu_##i##_irq_handler
63 #define DMA_ISSUE_EGU_IDX NRFX_PWM_NRF52_ANOMALY_109_EGU_INSTANCE
64 #define DMA_ISSUE_EGU NRFX_CONCAT_2(NRF_EGU, DMA_ISSUE_EGU_IDX)
65 #define DMA_ISSUE_EGU_IRQn EGU_IRQn(DMA_ISSUE_EGU_IDX)
66 #define DMA_ISSUE_EGU_IRQHandler EGU_IRQHandler(DMA_ISSUE_EGU_IDX)
67 #endif
68
69 // Control block - driver instance local data.
70 typedef struct
71 {
72 #if defined(USE_DMA_ISSUE_WORKAROUND)
73 uint32_t starting_task_address;
74 #endif
75 nrfx_pwm_handler_t handler;
76 void * p_context;
77 nrfx_drv_state_t volatile state;
78 uint32_t flags;
79 bool skip_gpio_cfg;
80 } pwm_control_block_t;
81 static pwm_control_block_t m_cb[NRFX_PWM_ENABLED_COUNT];
82
pins_configure(nrfx_pwm_config_t const * p_config)83 static void pins_configure(nrfx_pwm_config_t const * p_config)
84 {
85 // Nothing to do here if both GPIO configuration and pin selection are
86 // to be skipped (the pin numbers may be then even not specified).
87 if (p_config->skip_gpio_cfg && p_config->skip_psel_cfg)
88 {
89 return;
90 }
91
92 for (uint8_t i = 0; i < NRF_PWM_CHANNEL_COUNT; ++i)
93 {
94 uint32_t output_pin = p_config->output_pins[i];
95 if (output_pin != NRF_PWM_PIN_NOT_CONNECTED)
96 {
97 if (p_config->pin_inverted[i])
98 {
99 nrfy_gpio_pin_set(output_pin);
100 }
101 else
102 {
103 nrfy_gpio_pin_clear(output_pin);
104 }
105
106 nrfy_gpio_cfg_output(output_pin);
107 }
108 }
109 }
110
pins_deconfigure(nrfx_pwm_t const * p_instance)111 static void pins_deconfigure(nrfx_pwm_t const * p_instance)
112 {
113 for (uint8_t ch_idx = 0; ch_idx < NRF_PWM_CHANNEL_COUNT; ch_idx++)
114 {
115 uint32_t pin = nrfy_pwm_pin_get(p_instance->p_reg, ch_idx);
116 if (pin != NRF_PWM_PIN_NOT_CONNECTED)
117 {
118 nrfy_gpio_cfg_default(pin);
119 }
120 }
121 }
122
123 #if defined(USE_DMA_ISSUE_WORKAROUND)
apply_errata_109(nrfx_pwm_config_t const * p_config)124 static void apply_errata_109(nrfx_pwm_config_t const * p_config)
125 {
126 /* The workaround for nRF52 Anomaly 109 "protects" DMA transfers by handling
127 * interrupts generated on SEQEND0 and SEQEND1 events (this ensures that
128 * the 64 MHz clock is ready when data for the next sequence to be played
129 * is read). Therefore, the PWM interrupt must be enabled even if the event
130 * handler is not used.
131 */
132 NRFY_IRQ_PRIORITY_SET(DMA_ISSUE_EGU_IRQn, p_config->irq_priority);
133 NRFY_IRQ_ENABLE(DMA_ISSUE_EGU_IRQn);
134 }
135 #endif
136
137 #define PWM_PIN_INIT(i, field) field[i]
138
pwm_configure(nrfx_pwm_t const * p_instance,nrfx_pwm_config_t const * p_config)139 static void pwm_configure(nrfx_pwm_t const * p_instance, nrfx_pwm_config_t const * p_config)
140 {
141 if (!p_config->skip_gpio_cfg)
142 {
143 pins_configure(p_config);
144 }
145
146 nrfy_pwm_config_t nrfy_config =
147 {
148 .output_pins =
149 {
150 NRFX_LISTIFY(NRF_PWM_CHANNEL_COUNT, PWM_PIN_INIT, (,), p_config->output_pins)
151 },
152 .top_value = p_config->top_value,
153 .base_clock = p_config->base_clock,
154 .count_mode = p_config->count_mode,
155 .load_mode = p_config->load_mode,
156 .step_mode = p_config->step_mode,
157 .skip_psel_cfg = p_config->skip_psel_cfg
158 };
159
160 nrfy_pwm_periph_configure(p_instance->p_reg, &nrfy_config);
161 uint32_t to_clear = NRF_PWM_EVENT_LOOPSDONE | NRF_PWM_EVENT_SEQEND0 |
162 NRF_PWM_EVENT_SEQEND1 | NRF_PWM_EVENT_STOPPED;
163 nrfy_pwm_int_init(p_instance->p_reg, to_clear, p_config->irq_priority, false);
164
165 #if NRF_PWM_HAS_IDLEOUT
166 for (uint8_t channel = 0; channel < NRF_PWM_CHANNEL_COUNT; ++channel)
167 {
168 nrfy_pwm_channel_idle_set(p_instance->p_reg, channel,
169 p_config->count_mode == NRF_PWM_MODE_UP ?
170 !p_config->pin_inverted[channel] :
171 p_config->pin_inverted[channel]);
172 }
173 #endif
174
175 #if defined(USE_DMA_ISSUE_WORKAROUND)
176 apply_errata_109(p_config);
177 #endif
178 }
179
pwm_stopped_check(nrfx_pwm_t const * p_instance)180 static bool pwm_stopped_check(nrfx_pwm_t const * p_instance)
181 {
182 pwm_control_block_t * p_cb = &m_cb[p_instance->instance_id];
183
184 if (!p_cb->handler)
185 {
186 if (nrfy_pwm_events_process(p_instance->p_reg,
187 NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_STOPPED)))
188 {
189 p_cb->state = NRFX_DRV_STATE_INITIALIZED;
190 }
191 }
192
193 return p_cb->state != NRFX_DRV_STATE_POWERED_ON;
194 }
195
nrfx_pwm_init(nrfx_pwm_t const * p_instance,nrfx_pwm_config_t const * p_config,nrfx_pwm_handler_t handler,void * p_context)196 nrfx_err_t nrfx_pwm_init(nrfx_pwm_t const * p_instance,
197 nrfx_pwm_config_t const * p_config,
198 nrfx_pwm_handler_t handler,
199 void * p_context)
200 {
201 nrfx_err_t err_code;
202
203 pwm_control_block_t * p_cb = &m_cb[p_instance->instance_id];
204
205 if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
206 {
207 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
208 err_code = NRFX_ERROR_ALREADY;
209 #else
210 err_code = NRFX_ERROR_INVALID_STATE;
211 #endif
212 NRFX_LOG_WARNING("Function: %s, error code: %s.",
213 __func__,
214 NRFX_LOG_ERROR_STRING_GET(err_code));
215 return err_code;
216 }
217
218 p_cb->handler = handler;
219 p_cb->p_context = p_context;
220
221 if (p_config)
222 {
223 p_cb->skip_gpio_cfg = p_config->skip_gpio_cfg;
224 pwm_configure(p_instance, p_config);
225 }
226
227 p_cb->state = NRFX_DRV_STATE_INITIALIZED;
228
229 err_code = NRFX_SUCCESS;
230 NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
231 return err_code;
232 }
233
nrfx_pwm_reconfigure(nrfx_pwm_t const * p_instance,nrfx_pwm_config_t const * p_config)234 nrfx_err_t nrfx_pwm_reconfigure(nrfx_pwm_t const * p_instance, nrfx_pwm_config_t const * p_config)
235 {
236 pwm_control_block_t * p_cb = &m_cb[p_instance->instance_id];
237
238 NRFX_ASSERT(p_config);
239
240 if (p_cb->state == NRFX_DRV_STATE_UNINITIALIZED)
241 {
242 return NRFX_ERROR_INVALID_STATE;
243 }
244 if (p_cb->state == NRFX_DRV_STATE_POWERED_ON)
245 {
246 return NRFX_ERROR_BUSY;
247 }
248
249 pwm_configure(p_instance, p_config);
250
251 return NRFX_SUCCESS;
252 }
253
nrfx_pwm_uninit(nrfx_pwm_t const * p_instance)254 void nrfx_pwm_uninit(nrfx_pwm_t const * p_instance)
255 {
256 pwm_control_block_t * p_cb = &m_cb[p_instance->instance_id];
257
258 NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
259
260 nrfy_pwm_int_uninit(p_instance->p_reg);
261 #if defined(USE_DMA_ISSUE_WORKAROUND)
262 NRFY_IRQ_DISABLE(DMA_ISSUE_EGU_IRQn);
263 #endif
264
265 nrfy_pwm_disable(p_instance->p_reg);
266
267 if (!p_cb->skip_gpio_cfg)
268 {
269 pins_deconfigure(p_instance);
270 }
271
272 p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
273 NRFX_LOG_INFO("Uninitialized.");
274 }
275
nrfx_pwm_init_check(nrfx_pwm_t const * p_instance)276 bool nrfx_pwm_init_check(nrfx_pwm_t const * p_instance)
277 {
278 pwm_control_block_t * p_cb = &m_cb[p_instance->instance_id];
279
280 return (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
281 }
282
start_playback(nrfx_pwm_t const * p_instance,pwm_control_block_t * p_cb,uint32_t flags,uint8_t seq_id)283 static uint32_t start_playback(nrfx_pwm_t const * p_instance,
284 pwm_control_block_t * p_cb,
285 uint32_t flags,
286 uint8_t seq_id)
287 {
288 p_cb->state = NRFX_DRV_STATE_POWERED_ON;
289 p_cb->flags = flags;
290
291 nrfy_pwm_enable(p_instance->p_reg);
292
293 if (p_cb->handler)
294 {
295 // The notification about finished playback is by default enabled,
296 // but this can be suppressed.
297 // The notification that the peripheral has stopped is always enabled.
298 uint32_t int_mask = NRF_PWM_INT_LOOPSDONE_MASK |
299 NRF_PWM_INT_STOPPED_MASK;
300
301 // The workaround for nRF52 Anomaly 109 "protects" DMA transfers by
302 // handling interrupts generated on SEQEND0 and SEQEND1 events (see
303 // 'nrfx_pwm_init'), hence these events must be always enabled
304 // to generate interrupts.
305 // However, the user handler is called for them only when requested
306 // (see 'irq_handler').
307 #if defined(USE_DMA_ISSUE_WORKAROUND)
308 int_mask |= NRF_PWM_INT_SEQEND0_MASK | NRF_PWM_INT_SEQEND1_MASK;
309 #else
310 if (flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ0)
311 {
312 int_mask |= NRF_PWM_INT_SEQEND0_MASK;
313 }
314 if (flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ1)
315 {
316 int_mask |= NRF_PWM_INT_SEQEND1_MASK;
317 }
318 #endif
319 if (flags & NRFX_PWM_FLAG_NO_EVT_FINISHED)
320 {
321 int_mask &= (uint32_t)~NRF_PWM_INT_LOOPSDONE_MASK;
322 }
323
324 nrfy_pwm_int_set(p_instance->p_reg, int_mask);
325 }
326 #if defined(USE_DMA_ISSUE_WORKAROUND)
327 else
328 {
329 nrfy_pwm_int_set(p_instance->p_reg,
330 NRF_PWM_INT_SEQEND0_MASK | NRF_PWM_INT_SEQEND1_MASK);
331 }
332 #endif
333 if (flags & NRFX_PWM_FLAG_START_VIA_TASK)
334 {
335 uint32_t starting_task_address =
336 nrfy_pwm_task_address_get(p_instance->p_reg, nrfy_pwm_seqstart_task_get(seq_id));
337
338 #if defined(USE_DMA_ISSUE_WORKAROUND)
339 // To "protect" the initial DMA transfer it is required to start
340 // the PWM by triggering the proper task from EGU interrupt handler,
341 // it is not safe to do it directly via PPI.
342 p_cb->starting_task_address = starting_task_address;
343 nrf_egu_int_enable(DMA_ISSUE_EGU, nrf_egu_channel_int_get(p_instance->instance_id));
344 return nrf_egu_task_address_get(DMA_ISSUE_EGU,
345 nrf_egu_trigger_task_get(p_instance->instance_id));
346 #else
347 return starting_task_address;
348 #endif
349 }
350
351 nrfy_pwm_start(p_instance->p_reg, seq_id, false);
352 return 0;
353 }
354
nrfx_pwm_simple_playback(nrfx_pwm_t const * p_instance,nrf_pwm_sequence_t const * p_sequence,uint16_t playback_count,uint32_t flags)355 uint32_t nrfx_pwm_simple_playback(nrfx_pwm_t const * p_instance,
356 nrf_pwm_sequence_t const * p_sequence,
357 uint16_t playback_count,
358 uint32_t flags)
359 {
360 pwm_control_block_t * p_cb = &m_cb[p_instance->instance_id];
361
362 NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
363 NRFX_ASSERT(playback_count > 0);
364 NRFX_ASSERT(nrfx_is_in_ram(p_sequence->values.p_raw));
365
366 // To take advantage of the looping mechanism, we need to use both sequences
367 // (single sequence can be played back only once).
368 nrfy_pwm_sequence_set(p_instance->p_reg, 0, p_sequence);
369 nrfy_pwm_sequence_set(p_instance->p_reg, 1, p_sequence);
370 bool odd = (playback_count & 1);
371 nrfy_pwm_loop_set(p_instance->p_reg,
372 (uint16_t)((playback_count / 2UL) + (odd ? 1UL : 0UL)));
373
374 uint32_t shorts_mask = 0;
375 if (flags & NRFX_PWM_FLAG_STOP)
376 {
377 shorts_mask = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;
378 }
379 else if (flags & NRFX_PWM_FLAG_LOOP)
380 {
381 #if NRF_PWM_HAS_SHORT_LOOPSDONE_SEQSTART
382 shorts_mask = odd ? NRF_PWM_SHORT_LOOPSDONE_SEQSTART1_MASK
383 : NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK;
384 #else
385 NRFX_ASSERT(0);
386 #endif
387 }
388 nrfy_pwm_shorts_set(p_instance->p_reg, shorts_mask);
389
390 NRFX_LOG_INFO("Function: %s, sequence length: %d.",
391 __func__,
392 p_sequence->length);
393 NRFX_LOG_DEBUG("Sequence data:");
394 NRFX_LOG_HEXDUMP_DEBUG((uint8_t *)p_sequence->values.p_raw,
395 p_sequence->length * sizeof(uint16_t));
396 return start_playback(p_instance, p_cb, flags, odd ? 1 : 0);
397 }
398
nrfx_pwm_complex_playback(nrfx_pwm_t const * p_instance,nrf_pwm_sequence_t const * p_sequence_0,nrf_pwm_sequence_t const * p_sequence_1,uint16_t playback_count,uint32_t flags)399 uint32_t nrfx_pwm_complex_playback(nrfx_pwm_t const * p_instance,
400 nrf_pwm_sequence_t const * p_sequence_0,
401 nrf_pwm_sequence_t const * p_sequence_1,
402 uint16_t playback_count,
403 uint32_t flags)
404 {
405 pwm_control_block_t * p_cb = &m_cb[p_instance->instance_id];
406
407 NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
408 NRFX_ASSERT(playback_count > 0);
409 NRFX_ASSERT(nrfx_is_in_ram(p_sequence_0->values.p_raw));
410 NRFX_ASSERT(nrfx_is_in_ram(p_sequence_1->values.p_raw));
411
412 nrfy_pwm_sequence_set(p_instance->p_reg, 0, p_sequence_0);
413 nrfy_pwm_sequence_set(p_instance->p_reg, 1, p_sequence_1);
414 nrfy_pwm_loop_set(p_instance->p_reg, playback_count);
415
416 uint32_t shorts_mask = 0;
417 if (flags & NRFX_PWM_FLAG_STOP)
418 {
419 shorts_mask = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;
420 }
421 else if (flags & NRFX_PWM_FLAG_LOOP)
422 {
423 #if NRF_PWM_HAS_SHORT_LOOPSDONE_SEQSTART
424 shorts_mask = NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK;
425 #else
426 NRFX_ASSERT(0);
427 #endif
428 }
429 nrfy_pwm_shorts_set(p_instance->p_reg, shorts_mask);
430
431 NRFX_LOG_INFO("Function: %s, sequence 0 length: %d.",
432 __func__,
433 p_sequence_0->length);
434 NRFX_LOG_INFO("Function: %s, sequence 1 length: %d.",
435 __func__,
436 p_sequence_1->length);
437 NRFX_LOG_DEBUG("Sequence 0 data:");
438 NRFX_LOG_HEXDUMP_DEBUG(p_sequence_0->values.p_raw,
439 p_sequence_0->length * sizeof(uint16_t));
440 NRFX_LOG_DEBUG("Sequence 1 data:");
441 NRFX_LOG_HEXDUMP_DEBUG(p_sequence_1->values.p_raw,
442 p_sequence_1->length * sizeof(uint16_t));
443 return start_playback(p_instance, p_cb, flags, 0);
444 }
445
nrfx_pwm_stop(nrfx_pwm_t const * p_instance,bool wait_until_stopped)446 bool nrfx_pwm_stop(nrfx_pwm_t const * p_instance, bool wait_until_stopped)
447 {
448 pwm_control_block_t * p_cb = &m_cb[p_instance->instance_id];
449
450 NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
451
452 bool ret_val = false;
453
454 // Deactivate shortcuts before triggering the STOP task, otherwise the PWM
455 // could be immediately started again if the LOOPSDONE event occurred in
456 // the same peripheral clock cycle as the STOP task was triggered.
457 nrfy_pwm_shorts_set(p_instance->p_reg, 0);
458
459 // Trigger the STOP task even if the PWM appears to be already stopped.
460 // It won't harm, but it could help if for some strange reason the stopped
461 // status was not reported correctly.
462 nrfy_pwm_abort(p_instance->p_reg, false);
463
464 if (wait_until_stopped)
465 {
466 while (!pwm_stopped_check(p_instance))
467 {}
468 nrfy_pwm_disable(p_instance->p_reg);
469 p_cb->state = NRFX_DRV_STATE_INITIALIZED;
470 }
471
472 ret_val = pwm_stopped_check(p_instance);
473
474 NRFX_LOG_INFO("%s returned %d.", __func__, ret_val);
475 return ret_val;
476 }
477
nrfx_pwm_stopped_check(nrfx_pwm_t const * p_instance)478 bool nrfx_pwm_stopped_check(nrfx_pwm_t const * p_instance)
479 {
480 NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
481
482 bool ret_val = pwm_stopped_check(p_instance);
483
484 NRFX_LOG_INFO("%s returned %d.", __func__, ret_val);
485 return ret_val;
486 }
487
irq_handler(NRF_PWM_Type * p_pwm,pwm_control_block_t * p_cb)488 static void irq_handler(NRF_PWM_Type * p_pwm, pwm_control_block_t * p_cb)
489 {
490 uint32_t evt_mask = nrfy_pwm_events_process(p_pwm,
491 NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_SEQEND0) |
492 NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_SEQEND1) |
493 NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_LOOPSDONE) |
494 NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_STOPPED));
495
496 // The user handler is called for SEQEND0 and SEQEND1 events only when the
497 // user asks for it (by setting proper flags when starting the playback).
498 if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_SEQEND0))
499 {
500 if ((p_cb->flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ0) && p_cb->handler)
501 {
502 p_cb->handler(NRFX_PWM_EVT_END_SEQ0, p_cb->p_context);
503 }
504 }
505 if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_SEQEND1))
506 {
507 if ((p_cb->flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ1) && p_cb->handler)
508 {
509 p_cb->handler(NRFX_PWM_EVT_END_SEQ1, p_cb->p_context);
510 }
511 }
512 // For LOOPSDONE the handler is called by default, but the user can disable
513 // this (via flags).
514 if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_LOOPSDONE))
515 {
516 if (!(p_cb->flags & NRFX_PWM_FLAG_NO_EVT_FINISHED) && p_cb->handler)
517 {
518 p_cb->handler(NRFX_PWM_EVT_FINISHED, p_cb->p_context);
519 }
520 }
521
522 // The STOPPED event is always propagated to the user handler.
523 if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_STOPPED))
524 {
525 nrfy_pwm_disable(p_pwm);
526 p_cb->state = NRFX_DRV_STATE_INITIALIZED;
527 if (p_cb->handler)
528 {
529 p_cb->handler(NRFX_PWM_EVT_STOPPED, p_cb->p_context);
530 }
531 }
532 }
533
534 #if defined(USE_DMA_ISSUE_WORKAROUND)
535 // See 'start_playback' why this is needed.
DMA_ISSUE_EGU_IRQHandler(void)536 void DMA_ISSUE_EGU_IRQHandler(void)
537 {
538 for (uint8_t i = 0; i < NRFX_PWM_ENABLED_COUNT; i++)
539 {
540 nrf_egu_event_t event = nrf_egu_triggered_event_get(i);
541 if (nrf_egu_event_check(DMA_ISSUE_EGU, event))
542 {
543 nrf_egu_event_clear(DMA_ISSUE_EGU, event);
544 *(volatile uint32_t *)(m_cb[i].starting_task_address) = 1;
545 }
546 }
547 }
548 #endif
549
550 NRFX_INSTANCE_IRQ_HANDLERS(PWM, pwm)
551
552 #endif // NRFX_CHECK(NRFX_PWM_ENABLED)
553