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 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 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 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 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 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 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 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 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 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. 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) 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) 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) 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) 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