xref: /nrf52832-nimble/nordic/nrfx/drivers/src/nrfx_pwm.c (revision 150812a83cab50279bd772ef6db1bfaf255f2c5b)
1 /*
2  * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this
9  *    list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its
16  *    contributors may be used to endorse or promote products derived from this
17  *    software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <nrfx.h>
33 
34 #if NRFX_CHECK(NRFX_PWM_ENABLED)
35 
36 #if !(NRFX_CHECK(NRFX_PWM0_ENABLED) || NRFX_CHECK(NRFX_PWM1_ENABLED) || \
37       NRFX_CHECK(NRFX_PWM2_ENABLED) || NRFX_CHECK(NRFX_PWM3_ENABLED))
38 #error "No enabled PWM instances. Check <nrfx_config.h>."
39 #endif
40 
41 #include <nrfx_pwm.h>
42 #include <hal/nrf_gpio.h>
43 
44 #define NRFX_LOG_MODULE PWM
45 #include <nrfx_log.h>
46 
47 #if NRFX_CHECK(NRFX_PWM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
48 // The workaround uses interrupts to wake up the CPU and ensure it is active
49 // when PWM is about to start a DMA transfer. For initial transfer, done when
50 // a playback is started via PPI, a specific EGU instance is used to generate
51 // an interrupt. During the playback, the PWM interrupt triggered on SEQEND
52 // event of a preceding sequence is used to protect the transfer done for
53 // the next sequence to be played.
54 #include <hal/nrf_egu.h>
55 #define USE_DMA_ISSUE_WORKAROUND
56 #endif
57 #if defined(USE_DMA_ISSUE_WORKAROUND)
58 #define EGU_IRQn(i)         EGU_IRQn_(i)
59 #define EGU_IRQn_(i)        SWI##i##_EGU##i##_IRQn
60 #define EGU_IRQHandler(i)   EGU_IRQHandler_(i)
61 #define EGU_IRQHandler_(i)  nrfx_swi_##i##_irq_handler
62 #define DMA_ISSUE_EGU_IDX           NRFX_PWM_NRF52_ANOMALY_109_EGU_INSTANCE
63 #define DMA_ISSUE_EGU               NRFX_CONCAT_2(NRF_EGU, DMA_ISSUE_EGU_IDX)
64 #define DMA_ISSUE_EGU_IRQn          EGU_IRQn(DMA_ISSUE_EGU_IDX)
65 #define DMA_ISSUE_EGU_IRQHandler    EGU_IRQHandler(DMA_ISSUE_EGU_IDX)
66 #endif
67 
68 // Control block - driver instance local data.
69 typedef struct
70 {
71 #if defined(USE_DMA_ISSUE_WORKAROUND)
72     uint32_t                  starting_task_address;
73 #endif
74     nrfx_pwm_handler_t        handler;
75     nrfx_drv_state_t volatile state;
76     uint8_t                   flags;
77 } pwm_control_block_t;
78 static pwm_control_block_t m_cb[NRFX_PWM_ENABLED_COUNT];
79 
configure_pins(nrfx_pwm_t const * const p_instance,nrfx_pwm_config_t const * p_config)80 static void configure_pins(nrfx_pwm_t const * const p_instance,
81                            nrfx_pwm_config_t const * p_config)
82 {
83     uint32_t out_pins[NRF_PWM_CHANNEL_COUNT];
84     uint8_t i;
85 
86     for (i = 0; i < NRF_PWM_CHANNEL_COUNT; ++i)
87     {
88         uint8_t output_pin = p_config->output_pins[i];
89         if (output_pin != NRFX_PWM_PIN_NOT_USED)
90         {
91             bool inverted = output_pin &  NRFX_PWM_PIN_INVERTED;
92             out_pins[i]   = output_pin & ~NRFX_PWM_PIN_INVERTED;
93 
94             if (inverted)
95             {
96                 nrf_gpio_pin_set(out_pins[i]);
97             }
98             else
99             {
100                 nrf_gpio_pin_clear(out_pins[i]);
101             }
102 
103             nrf_gpio_cfg_output(out_pins[i]);
104         }
105         else
106         {
107             out_pins[i] = NRF_PWM_PIN_NOT_CONNECTED;
108         }
109     }
110 
111     nrf_pwm_pins_set(p_instance->p_registers, out_pins);
112 }
113 
114 
nrfx_pwm_init(nrfx_pwm_t const * const p_instance,nrfx_pwm_config_t const * p_config,nrfx_pwm_handler_t handler)115 nrfx_err_t nrfx_pwm_init(nrfx_pwm_t const * const p_instance,
116                          nrfx_pwm_config_t const * p_config,
117                          nrfx_pwm_handler_t        handler)
118 {
119     NRFX_ASSERT(p_config);
120 
121     nrfx_err_t err_code;
122 
123     pwm_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];
124 
125     if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
126     {
127         err_code = NRFX_ERROR_INVALID_STATE;
128         NRFX_LOG_WARNING("Function: %s, error code: %s.",
129                          __func__,
130                          NRFX_LOG_ERROR_STRING_GET(err_code));
131         return err_code;
132     }
133 
134     p_cb->handler = handler;
135 
136     configure_pins(p_instance, p_config);
137 
138     nrf_pwm_enable(p_instance->p_registers);
139     nrf_pwm_configure(p_instance->p_registers,
140         p_config->base_clock, p_config->count_mode, p_config->top_value);
141     nrf_pwm_decoder_set(p_instance->p_registers,
142         p_config->load_mode, p_config->step_mode);
143 
144     nrf_pwm_shorts_set(p_instance->p_registers, 0);
145     nrf_pwm_int_set(p_instance->p_registers, 0);
146     nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_LOOPSDONE);
147     nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_SEQEND0);
148     nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_SEQEND1);
149     nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_STOPPED);
150 
151     // The workaround for nRF52 Anomaly 109 "protects" DMA transfers by handling
152     // interrupts generated on SEQEND0 and SEQEND1 events (this ensures that
153     // the 64 MHz clock is ready when data for the next sequence to be played
154     // is read). Therefore, the PWM interrupt must be enabled even if the event
155     // handler is not used.
156 #if defined(USE_DMA_ISSUE_WORKAROUND)
157     NRFX_IRQ_PRIORITY_SET(DMA_ISSUE_EGU_IRQn, p_config->irq_priority);
158     NRFX_IRQ_ENABLE(DMA_ISSUE_EGU_IRQn);
159 #else
160     if (p_cb->handler)
161 #endif
162     {
163         NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_registers),
164             p_config->irq_priority);
165         NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_registers));
166     }
167 
168     p_cb->state = NRFX_DRV_STATE_INITIALIZED;
169 
170     err_code = NRFX_SUCCESS;
171     NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
172     return err_code;
173 }
174 
175 
nrfx_pwm_uninit(nrfx_pwm_t const * const p_instance)176 void nrfx_pwm_uninit(nrfx_pwm_t const * const p_instance)
177 {
178     pwm_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];
179     NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
180 
181     NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_registers));
182 #if defined(USE_DMA_ISSUE_WORKAROUND)
183     NRFX_IRQ_DISABLE(DMA_ISSUE_EGU_IRQn);
184 #endif
185 
186     nrf_pwm_disable(p_instance->p_registers);
187 
188     p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
189 }
190 
191 
start_playback(nrfx_pwm_t const * const p_instance,pwm_control_block_t * p_cb,uint8_t flags,nrf_pwm_task_t starting_task)192 static uint32_t start_playback(nrfx_pwm_t const * const p_instance,
193                                pwm_control_block_t * p_cb,
194                                uint8_t               flags,
195                                nrf_pwm_task_t        starting_task)
196 {
197     p_cb->state = NRFX_DRV_STATE_POWERED_ON;
198     p_cb->flags = flags;
199 
200     if (p_cb->handler)
201     {
202         // The notification about finished playback is by default enabled,
203         // but this can be suppressed.
204         // The notification that the peripheral has stopped is always enabled.
205         uint32_t int_mask = NRF_PWM_INT_LOOPSDONE_MASK |
206                             NRF_PWM_INT_STOPPED_MASK;
207 
208         // The workaround for nRF52 Anomaly 109 "protects" DMA transfers by
209         // handling interrupts generated on SEQEND0 and SEQEND1 events (see
210         // 'nrfx_pwm_init'), hence these events must be always enabled
211         // to generate interrupts.
212         // However, the user handler is called for them only when requested
213         // (see 'irq_handler').
214 #if defined(USE_DMA_ISSUE_WORKAROUND)
215         int_mask |= NRF_PWM_INT_SEQEND0_MASK | NRF_PWM_INT_SEQEND1_MASK;
216 #else
217         if (flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ0)
218         {
219             int_mask |= NRF_PWM_INT_SEQEND0_MASK;
220         }
221         if (flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ1)
222         {
223             int_mask |= NRF_PWM_INT_SEQEND1_MASK;
224         }
225 #endif
226         if (flags & NRFX_PWM_FLAG_NO_EVT_FINISHED)
227         {
228             int_mask &= ~NRF_PWM_INT_LOOPSDONE_MASK;
229         }
230 
231         nrf_pwm_int_set(p_instance->p_registers, int_mask);
232     }
233 #if defined(USE_DMA_ISSUE_WORKAROUND)
234     else
235     {
236         nrf_pwm_int_set(p_instance->p_registers,
237             NRF_PWM_INT_SEQEND0_MASK | NRF_PWM_INT_SEQEND1_MASK);
238     }
239 #endif
240 
241     nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_STOPPED);
242 
243     if (flags & NRFX_PWM_FLAG_START_VIA_TASK)
244     {
245         uint32_t starting_task_address =
246             nrf_pwm_task_address_get(p_instance->p_registers, starting_task);
247 
248 #if defined(USE_DMA_ISSUE_WORKAROUND)
249         // To "protect" the initial DMA transfer it is required to start
250         // the PWM by triggering the proper task from EGU interrupt handler,
251         // it is not safe to do it directly via PPI.
252         p_cb->starting_task_address = starting_task_address;
253         nrf_egu_int_enable(DMA_ISSUE_EGU,
254             nrf_egu_int_get(DMA_ISSUE_EGU, p_instance->drv_inst_idx));
255         return (uint32_t)nrf_egu_task_trigger_address_get(DMA_ISSUE_EGU,
256             p_instance->drv_inst_idx);
257 #else
258         return starting_task_address;
259 #endif
260     }
261 
262     nrf_pwm_task_trigger(p_instance->p_registers, starting_task);
263     return 0;
264 }
265 
266 
nrfx_pwm_simple_playback(nrfx_pwm_t const * const p_instance,nrf_pwm_sequence_t const * p_sequence,uint16_t playback_count,uint32_t flags)267 uint32_t nrfx_pwm_simple_playback(nrfx_pwm_t const * const p_instance,
268                                   nrf_pwm_sequence_t const * p_sequence,
269                                   uint16_t                   playback_count,
270                                   uint32_t                   flags)
271 {
272     pwm_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];
273     NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
274     NRFX_ASSERT(playback_count > 0);
275     NRFX_ASSERT(nrfx_is_in_ram(p_sequence->values.p_raw));
276 
277     // To take advantage of the looping mechanism, we need to use both sequences
278     // (single sequence can be played back only once).
279     nrf_pwm_sequence_set(p_instance->p_registers, 0, p_sequence);
280     nrf_pwm_sequence_set(p_instance->p_registers, 1, p_sequence);
281     bool odd = (playback_count & 1);
282     nrf_pwm_loop_set(p_instance->p_registers,
283         (playback_count / 2) + (odd ? 1 : 0));
284 
285     uint32_t shorts_mask;
286     if (flags & NRFX_PWM_FLAG_STOP)
287     {
288         shorts_mask = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;
289     }
290     else if (flags & NRFX_PWM_FLAG_LOOP)
291     {
292         shorts_mask = odd ? NRF_PWM_SHORT_LOOPSDONE_SEQSTART1_MASK
293                           : NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK;
294     }
295     else
296     {
297         shorts_mask = 0;
298     }
299     nrf_pwm_shorts_set(p_instance->p_registers, shorts_mask);
300 
301     NRFX_LOG_INFO("Function: %s, sequence length: %d.",
302                   __func__,
303                   p_sequence->length);
304     NRFX_LOG_DEBUG("Sequence data:");
305     NRFX_LOG_HEXDUMP_DEBUG((uint8_t *)p_sequence->values.p_raw,
306                            p_sequence->length * sizeof(uint16_t));
307     return start_playback(p_instance, p_cb, flags,
308         odd ? NRF_PWM_TASK_SEQSTART1 : NRF_PWM_TASK_SEQSTART0);
309 }
310 
311 
nrfx_pwm_complex_playback(nrfx_pwm_t const * 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)312 uint32_t nrfx_pwm_complex_playback(nrfx_pwm_t const * const p_instance,
313                                    nrf_pwm_sequence_t const * p_sequence_0,
314                                    nrf_pwm_sequence_t const * p_sequence_1,
315                                    uint16_t                   playback_count,
316                                    uint32_t                   flags)
317 {
318     pwm_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];
319     NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
320     NRFX_ASSERT(playback_count > 0);
321     NRFX_ASSERT(nrfx_is_in_ram(p_sequence_0->values.p_raw));
322     NRFX_ASSERT(nrfx_is_in_ram(p_sequence_1->values.p_raw));
323 
324     nrf_pwm_sequence_set(p_instance->p_registers, 0, p_sequence_0);
325     nrf_pwm_sequence_set(p_instance->p_registers, 1, p_sequence_1);
326     nrf_pwm_loop_set(p_instance->p_registers, playback_count);
327 
328     uint32_t shorts_mask;
329     if (flags & NRFX_PWM_FLAG_STOP)
330     {
331         shorts_mask = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;
332     }
333     else if (flags & NRFX_PWM_FLAG_LOOP)
334     {
335         shorts_mask = NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK;
336     }
337     else
338     {
339         shorts_mask = 0;
340     }
341     nrf_pwm_shorts_set(p_instance->p_registers, shorts_mask);
342 
343     NRFX_LOG_INFO("Function: %s, sequence 0 length: %d.",
344                   __func__,
345                   p_sequence_0->length);
346     NRFX_LOG_INFO("Function: %s, sequence 1 length: %d.",
347                   __func__,
348                   p_sequence_1->length);
349     NRFX_LOG_DEBUG("Sequence 0 data:");
350     NRFX_LOG_HEXDUMP_DEBUG(p_sequence_0->values.p_raw,
351                            p_sequence_0->length * sizeof(uint16_t));
352     NRFX_LOG_DEBUG("Sequence 1 data:");
353     NRFX_LOG_HEXDUMP_DEBUG(p_sequence_1->values.p_raw,
354                            p_sequence_1->length * sizeof(uint16_t));
355     return start_playback(p_instance, p_cb, flags, NRF_PWM_TASK_SEQSTART0);
356 }
357 
358 
nrfx_pwm_stop(nrfx_pwm_t const * const p_instance,bool wait_until_stopped)359 bool nrfx_pwm_stop(nrfx_pwm_t const * const p_instance,
360                    bool wait_until_stopped)
361 {
362     NRFX_ASSERT(m_cb[p_instance->drv_inst_idx].state != NRFX_DRV_STATE_UNINITIALIZED);
363 
364     bool ret_val = false;
365 
366     if (nrfx_pwm_is_stopped(p_instance))
367     {
368         ret_val = true;
369     }
370     else
371     {
372         nrf_pwm_task_trigger(p_instance->p_registers, NRF_PWM_TASK_STOP);
373 
374         do {
375             if (nrfx_pwm_is_stopped(p_instance))
376             {
377                 ret_val = true;
378                 break;
379             }
380         } while (wait_until_stopped);
381     }
382 
383     NRFX_LOG_INFO("%s returned %d.", __func__, ret_val);
384     return ret_val;
385 }
386 
387 
nrfx_pwm_is_stopped(nrfx_pwm_t const * const p_instance)388 bool nrfx_pwm_is_stopped(nrfx_pwm_t const * const p_instance)
389 {
390     pwm_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];
391     NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
392 
393     bool ret_val = false;
394 
395     // If the event handler is used (interrupts are enabled), the state will
396     // be changed in interrupt handler when the STOPPED event occurs.
397     if (p_cb->state != NRFX_DRV_STATE_POWERED_ON)
398     {
399         ret_val = true;
400     }
401     // If interrupts are disabled, we must check the STOPPED event here.
402     if (nrf_pwm_event_check(p_instance->p_registers, NRF_PWM_EVENT_STOPPED))
403     {
404         p_cb->state = NRFX_DRV_STATE_INITIALIZED;
405         NRFX_LOG_INFO("Disabled.");
406         ret_val = true;
407     }
408 
409     NRFX_LOG_INFO("%s returned %d.", __func__, ret_val);
410     return ret_val;
411 }
412 
413 
irq_handler(NRF_PWM_Type * p_pwm,pwm_control_block_t * p_cb)414 static void irq_handler(NRF_PWM_Type * p_pwm, pwm_control_block_t * p_cb)
415 {
416     // The user handler is called for SEQEND0 and SEQEND1 events only when the
417     // user asks for it (by setting proper flags when starting the playback).
418     if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_SEQEND0))
419     {
420         nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_SEQEND0);
421         if ((p_cb->flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ0) && p_cb->handler)
422         {
423             p_cb->handler(NRFX_PWM_EVT_END_SEQ0);
424         }
425     }
426     if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_SEQEND1))
427     {
428         nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_SEQEND1);
429         if ((p_cb->flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ1) && p_cb->handler)
430         {
431             p_cb->handler(NRFX_PWM_EVT_END_SEQ1);
432         }
433     }
434     // For LOOPSDONE the handler is called by default, but the user can disable
435     // this (via flags).
436     if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_LOOPSDONE))
437     {
438         nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_LOOPSDONE);
439         if (!(p_cb->flags & NRFX_PWM_FLAG_NO_EVT_FINISHED) && p_cb->handler)
440         {
441             p_cb->handler(NRFX_PWM_EVT_FINISHED);
442         }
443     }
444 
445     // The STOPPED event is always propagated to the user handler.
446     if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_STOPPED))
447     {
448         nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_STOPPED);
449 
450         p_cb->state = NRFX_DRV_STATE_INITIALIZED;
451         if (p_cb->handler)
452         {
453             p_cb->handler(NRFX_PWM_EVT_STOPPED);
454         }
455     }
456 }
457 
458 
459 #if defined(USE_DMA_ISSUE_WORKAROUND)
460 // See 'start_playback' why this is needed.
DMA_ISSUE_EGU_IRQHandler(void)461 void DMA_ISSUE_EGU_IRQHandler(void)
462 {
463     int i;
464     for (i = 0; i < NRFX_PWM_ENABLED_COUNT; ++i)
465     {
466         volatile uint32_t * p_event_reg =
467             nrf_egu_event_triggered_address_get(DMA_ISSUE_EGU, i);
468         if (*p_event_reg)
469         {
470             *p_event_reg = 0;
471             *(volatile uint32_t *)(m_cb[i].starting_task_address) = 1;
472         }
473     }
474 }
475 #endif
476 
477 
478 #if NRFX_CHECK(NRFX_PWM0_ENABLED)
nrfx_pwm_0_irq_handler(void)479 void nrfx_pwm_0_irq_handler(void)
480 {
481     irq_handler(NRF_PWM0, &m_cb[NRFX_PWM0_INST_IDX]);
482 }
483 #endif
484 
485 #if NRFX_CHECK(NRFX_PWM1_ENABLED)
nrfx_pwm_1_irq_handler(void)486 void nrfx_pwm_1_irq_handler(void)
487 {
488     irq_handler(NRF_PWM1, &m_cb[NRFX_PWM1_INST_IDX]);
489 }
490 #endif
491 
492 #if NRFX_CHECK(NRFX_PWM2_ENABLED)
nrfx_pwm_2_irq_handler(void)493 void nrfx_pwm_2_irq_handler(void)
494 {
495     irq_handler(NRF_PWM2, &m_cb[NRFX_PWM2_INST_IDX]);
496 }
497 #endif
498 
499 #if NRFX_CHECK(NRFX_PWM3_ENABLED)
nrfx_pwm_3_irq_handler(void)500 void nrfx_pwm_3_irq_handler(void)
501 {
502     irq_handler(NRF_PWM3, &m_cb[NRFX_PWM3_INST_IDX]);
503 }
504 #endif
505 
506 #endif // NRFX_CHECK(NRFX_PWM_ENABLED)
507