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