app_pwm.c 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  1. /**
  2. * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without modification,
  7. * are permitted provided that the following conditions are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright notice, this
  10. * list of conditions and the following disclaimer.
  11. *
  12. * 2. Redistributions in binary form, except as embedded into a Nordic
  13. * Semiconductor ASA integrated circuit in a product or a software update for
  14. * such product, must reproduce the above copyright notice, this list of
  15. * conditions and the following disclaimer in the documentation and/or other
  16. * materials provided with the distribution.
  17. *
  18. * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
  19. * contributors may be used to endorse or promote products derived from this
  20. * software without specific prior written permission.
  21. *
  22. * 4. This software, with or without modification, must only be used with a
  23. * Nordic Semiconductor ASA integrated circuit.
  24. *
  25. * 5. Any software provided in binary form under this license must not be reverse
  26. * engineered, decompiled, modified and/or disassembled.
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
  29. * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  30. * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
  31. * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
  32. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  33. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  34. * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  37. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. */
  40. #include "sdk_common.h"
  41. #if NRF_MODULE_ENABLED(APP_PWM)
  42. #include "app_pwm.h"
  43. #include "nrf_drv_timer.h"
  44. #include "nrf_drv_ppi.h"
  45. #include "nrf_drv_gpiote.h"
  46. #include "nrf_gpiote.h"
  47. #include "nrf_gpio.h"
  48. #include "app_util_platform.h"
  49. #include "nrf_assert.h"
  50. #define APP_PWM_CHANNEL_INITIALIZED 1
  51. #define APP_PWM_CHANNEL_UNINITIALIZED 0
  52. #define APP_PWM_CHANNEL_ENABLED 1
  53. #define APP_PWM_CHANNEL_DISABLED 0
  54. #define TIMER_PRESCALER_MAX 9
  55. #define TIMER_MAX_PULSEWIDTH_US_ON_16M 4095
  56. #ifndef GPIOTE_SET_CLEAR_TASKS
  57. #define APP_PWM_REQUIRED_PPI_CHANNELS_PER_INSTANCE 2
  58. #endif
  59. #define APP_PWM_REQUIRED_PPI_CHANNELS_PER_CHANNEL 2
  60. #define UNALLOCATED 0xFFFFFFFFUL
  61. #define BUSY_STATE_CHANGING 0xFE
  62. #define BUSY_STATE_IDLE 0xFF
  63. #define PWM_MAIN_CC_CHANNEL 2
  64. #define PWM_SECONDARY_CC_CHANNEL 3
  65. #ifdef GPIOTE_SET_CLEAR_TASKS
  66. static bool m_use_ppi_delay_workaround;
  67. #endif
  68. /**
  69. * @brief PWM busy status
  70. *
  71. * Stores the number of a channel being currently updated.
  72. *
  73. */
  74. static volatile uint8_t m_pwm_busy[TIMER_COUNT];
  75. /**
  76. * @brief New duty cycle value
  77. *
  78. * When the channel duty cycle reaches this value, the update process is complete.
  79. */
  80. static volatile uint32_t m_pwm_target_value[TIMER_COUNT];
  81. /**
  82. * @brief PWM ready counter
  83. *
  84. * The value in this counter is decremented in every PWM cycle after initiating the update.
  85. * If an event handler function was specified by the user, it is being called
  86. * after two cycle events (at least one full PWM cycle).
  87. */
  88. volatile uint8_t m_pwm_ready_counter[TIMER_COUNT][APP_PWM_CHANNELS_PER_INSTANCE];
  89. /**
  90. * @brief Pointers to instances
  91. *
  92. * This array connects any active timer instance number with the pointer to the PWM instance.
  93. * It is used by the interrupt runtime.
  94. */
  95. static const app_pwm_t * m_instances[TIMER_COUNT];
  96. // Macros for getting the polarity of given instance/channel.
  97. #define POLARITY_ACTIVE(INST,CH) (( ((INST)->p_cb)->channels_cb[(CH)].polarity == \
  98. APP_PWM_POLARITY_ACTIVE_LOW)?(0):(1))
  99. #define POLARITY_INACTIVE(INST,CH) (( ((INST)->p_cb)->channels_cb[(CH)].polarity == \
  100. APP_PWM_POLARITY_ACTIVE_LOW)?(1):(0))
  101. //lint -save -e534
  102. /**
  103. * @brief Workaround for PAN-73.
  104. *
  105. * @param[in] timer Timer.
  106. * @param[in] enable Enable or disable.
  107. */
  108. static void pan73_workaround(NRF_TIMER_Type * p_timer, bool enable)
  109. {
  110. #ifndef GPIOTE_SET_CLEAR_TASKS
  111. if (p_timer == NRF_TIMER0)
  112. {
  113. *(uint32_t *)0x40008C0C = (enable ? 1 : 0);
  114. }
  115. else if (p_timer == NRF_TIMER1)
  116. {
  117. *(uint32_t *)0x40009C0C = (enable ? 1 : 0);
  118. }
  119. else if (p_timer == NRF_TIMER2)
  120. {
  121. *(uint32_t *)0x4000AC0C = (enable ? 1 : 0);
  122. }
  123. #else
  124. UNUSED_PARAMETER(p_timer);
  125. UNUSED_PARAMETER(enable);
  126. #endif
  127. }
  128. bool app_pwm_busy_check(app_pwm_t const * const p_instance)
  129. {
  130. uint8_t busy_state = (m_pwm_busy[p_instance->p_timer->instance_id]);
  131. bool busy = true;
  132. if (busy_state != BUSY_STATE_IDLE)
  133. {
  134. if (busy_state != BUSY_STATE_CHANGING)
  135. {
  136. if (nrf_drv_timer_capture_get(p_instance->p_timer, (nrf_timer_cc_channel_t) busy_state)
  137. == m_pwm_target_value[p_instance->p_timer->instance_id])
  138. {
  139. m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
  140. busy = false;
  141. }
  142. }
  143. }
  144. else
  145. {
  146. busy = false;
  147. }
  148. return busy;
  149. }
  150. /**
  151. * @brief Function for enabling the IRQ for a given PWM instance.
  152. *
  153. * @param[in] p_instance PWM instance.
  154. */
  155. __STATIC_INLINE void pwm_irq_enable(app_pwm_t const * const p_instance)
  156. {
  157. nrf_drv_timer_compare_int_enable(p_instance->p_timer, PWM_MAIN_CC_CHANNEL);
  158. }
  159. /**
  160. * @brief Function for disabling the IRQ for a given PWM instance.
  161. *
  162. * @param[in] p_instance PWM instance.
  163. */
  164. __STATIC_INLINE void pwm_irq_disable(app_pwm_t const * const p_instance)
  165. {
  166. nrf_drv_timer_compare_int_disable(p_instance->p_timer, PWM_MAIN_CC_CHANNEL);
  167. }
  168. #ifndef GPIOTE_SET_CLEAR_TASKS
  169. /**
  170. * @brief Function for disabling PWM channel PPI.
  171. *
  172. * @param[in] p_instance PWM instance.
  173. */
  174. __STATIC_INLINE void pwm_channel_ppi_disable(app_pwm_t const * const p_instance, uint8_t channel)
  175. {
  176. app_pwm_cb_t * p_cb = p_instance->p_cb;
  177. nrf_drv_ppi_channel_disable(p_cb->channels_cb[channel].ppi_channels[0]);
  178. nrf_drv_ppi_channel_disable(p_cb->channels_cb[channel].ppi_channels[1]);
  179. }
  180. /**
  181. * @brief Function for disabling PWM PPI.
  182. *
  183. * @param[in] p_instance PWM instance.
  184. */
  185. __STATIC_INLINE void pwm_ppi_disable(app_pwm_t const * const p_instance)
  186. {
  187. app_pwm_cb_t * p_cb = p_instance->p_cb;
  188. nrf_drv_ppi_channel_disable(p_cb->ppi_channels[0]);
  189. nrf_drv_ppi_channel_disable(p_cb->ppi_channels[1]);
  190. }
  191. #endif
  192. /**
  193. * @brief This function is called on interrupt after duty set.
  194. *
  195. * @param[in] timer Timer used by PWM.
  196. * @param[in] timer_instance_id Timer index.
  197. */
  198. void pwm_ready_tick(nrf_timer_event_t event_type, void * p_context)
  199. {
  200. uint32_t timer_instance_id = (uint32_t)p_context;
  201. uint8_t disable = 1;
  202. for (uint8_t channel = 0; channel < APP_PWM_CHANNELS_PER_INSTANCE; ++channel)
  203. {
  204. if (m_pwm_ready_counter[timer_instance_id][channel])
  205. {
  206. --m_pwm_ready_counter[timer_instance_id][channel];
  207. if (!m_pwm_ready_counter[timer_instance_id][channel])
  208. {
  209. app_pwm_cb_t * p_cb = m_instances[timer_instance_id]->p_cb;
  210. p_cb->p_ready_callback(timer_instance_id);
  211. }
  212. else
  213. {
  214. disable = 0;
  215. }
  216. }
  217. }
  218. if (disable)
  219. {
  220. pwm_irq_disable(m_instances[timer_instance_id]);
  221. }
  222. }
  223. /**
  224. * @brief Function for resource de-allocation.
  225. *
  226. * @param[in] p_instance PWM instance.
  227. */
  228. //lint -e{650}
  229. static void pwm_dealloc(app_pwm_t const * const p_instance)
  230. {
  231. app_pwm_cb_t * p_cb = p_instance->p_cb;
  232. #ifdef GPIOTE_SET_CLEAR_TASKS
  233. nrf_drv_ppi_channel_free(p_cb->ppi_channel);
  234. #else
  235. for (uint8_t i = 0; i < APP_PWM_REQUIRED_PPI_CHANNELS_PER_INSTANCE; ++i)
  236. {
  237. if (p_cb->ppi_channels[i] != (nrf_ppi_channel_t)(uint8_t)(UNALLOCATED))
  238. {
  239. nrf_drv_ppi_channel_free(p_cb->ppi_channels[i]);
  240. }
  241. }
  242. if (p_cb->ppi_group != (nrf_ppi_channel_group_t)UNALLOCATED)
  243. {
  244. nrf_drv_ppi_group_free(p_cb->ppi_group);
  245. }
  246. #endif //GPIOTE_SET_CLEAR_TASKS
  247. for (uint8_t ch = 0; ch < APP_PWM_CHANNELS_PER_INSTANCE; ++ch)
  248. {
  249. for (uint8_t i = 0; i < APP_PWM_REQUIRED_PPI_CHANNELS_PER_CHANNEL; ++i)
  250. {
  251. if (p_cb->channels_cb[ch].ppi_channels[i] != (nrf_ppi_channel_t)UNALLOCATED)
  252. {
  253. nrf_drv_ppi_channel_free(p_cb->channels_cb[ch].ppi_channels[i]);
  254. p_cb->channels_cb[ch].ppi_channels[i] = (nrf_ppi_channel_t)UNALLOCATED;
  255. }
  256. }
  257. if (p_cb->channels_cb[ch].gpio_pin != UNALLOCATED)
  258. {
  259. nrf_drv_gpiote_out_uninit(p_cb->channels_cb[ch].gpio_pin);
  260. p_cb->channels_cb[ch].gpio_pin = UNALLOCATED;
  261. }
  262. p_cb->channels_cb[ch].initialized = APP_PWM_CHANNEL_UNINITIALIZED;
  263. }
  264. nrf_drv_timer_uninit(p_instance->p_timer);
  265. return;
  266. }
  267. #ifndef GPIOTE_SET_CLEAR_TASKS
  268. /**
  269. * @brief PWM state transition from (0%, 100%) to 0% or 100%.
  270. *
  271. * @param[in] p_instance PWM instance.
  272. * @param[in] channel PWM channel number.
  273. * @param[in] ticks Number of clock ticks.
  274. */
  275. static void pwm_transition_n_to_0or100(app_pwm_t const * const p_instance,
  276. uint8_t channel, uint16_t ticks)
  277. {
  278. app_pwm_cb_t * p_cb = p_instance->p_cb;
  279. app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
  280. nrf_ppi_channel_group_t p_ppigrp = p_cb->ppi_group;
  281. pwm_ppi_disable(p_instance);
  282. nrf_drv_ppi_group_clear(p_ppigrp);
  283. nrf_drv_ppi_channels_include_in_group(
  284. nrf_drv_ppi_channel_to_mask(p_ch_cb->ppi_channels[0]) |
  285. nrf_drv_ppi_channel_to_mask(p_ch_cb->ppi_channels[1]),
  286. p_ppigrp);
  287. if (!ticks)
  288. {
  289. nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
  290. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
  291. nrf_drv_ppi_task_addr_group_disable_get(p_ppigrp));
  292. nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, 0, false);
  293. m_pwm_target_value[p_instance->p_timer->instance_id] =
  294. nrf_drv_timer_capture_get(p_instance->p_timer, (nrf_timer_cc_channel_t) channel);
  295. nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
  296. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
  297. nrf_drv_timer_capture_task_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL));
  298. }
  299. else
  300. {
  301. ticks = p_cb->period;
  302. nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
  303. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
  304. nrf_drv_ppi_task_addr_group_disable_get(p_ppigrp));
  305. // Set secondary CC channel to non-zero value:
  306. nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, 1, false);
  307. m_pwm_target_value[p_instance->p_timer->instance_id] = 0;
  308. // The captured value will be equal to 0, because timer clear on main PWM CC channel compare is enabled.
  309. nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
  310. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
  311. nrf_drv_timer_capture_task_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL));
  312. }
  313. nrf_drv_ppi_channel_enable(p_cb->ppi_channels[0]);
  314. nrf_drv_ppi_channel_enable(p_cb->ppi_channels[1]);
  315. p_ch_cb->pulsewidth = ticks;
  316. m_pwm_busy[p_instance->p_timer->instance_id] = PWM_SECONDARY_CC_CHANNEL;
  317. }
  318. /**
  319. * @brief PWM state transition from (0%, 100%) to (0%, 100%).
  320. *
  321. * @param[in] p_instance PWM instance.
  322. * @param[in] channel PWM channel number.
  323. * @param[in] ticks Number of clock ticks.
  324. */
  325. static void pwm_transition_n_to_m(app_pwm_t const * const p_instance,
  326. uint8_t channel, uint16_t ticks)
  327. {
  328. app_pwm_cb_t * p_cb = p_instance->p_cb;
  329. app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
  330. nrf_ppi_channel_group_t p_ppigrp = p_cb->ppi_group;
  331. pwm_ppi_disable(p_instance);
  332. nrf_drv_ppi_group_clear(p_ppigrp);
  333. nrf_drv_ppi_channels_include_in_group(
  334. nrf_drv_ppi_channel_to_mask(p_cb->ppi_channels[0]) |
  335. nrf_drv_ppi_channel_to_mask(p_cb->ppi_channels[1]),
  336. p_ppigrp);
  337. nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
  338. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL),
  339. nrf_drv_timer_capture_task_address_get(p_instance->p_timer, channel));
  340. if (ticks + ((nrf_timer_frequency_get(p_instance->p_timer->p_reg) == NRF_TIMER_FREQ_16MHz) ? 1 : 0)
  341. < p_ch_cb->pulsewidth)
  342. {
  343. // For lower value, we need one more transition. Timer task delay is included.
  344. // If prescaler is disabled, one tick must be added because of 1 PCLK16M clock cycle delay.
  345. nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
  346. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL),
  347. nrf_drv_gpiote_out_task_addr_get(p_ch_cb->gpio_pin));
  348. }
  349. else
  350. {
  351. nrf_drv_ppi_channel_remove_from_group(p_cb->ppi_channels[1], p_ppigrp);
  352. }
  353. p_ch_cb->pulsewidth = ticks;
  354. nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, ticks, false);
  355. nrf_drv_ppi_group_enable(p_ppigrp);
  356. m_pwm_target_value[p_instance->p_timer->instance_id] = ticks;
  357. m_pwm_busy[p_instance->p_timer->instance_id] = channel;
  358. }
  359. /**
  360. * @brief PWM state transition from 0% or 100% to (0%, 100%).
  361. *
  362. * @param[in] p_instance PWM instance.
  363. * @param[in] channel PWM channel number.
  364. * @param[in] ticks Number of clock ticks.
  365. */
  366. static void pwm_transition_0or100_to_n(app_pwm_t const * const p_instance,
  367. uint8_t channel, uint16_t ticks)
  368. {
  369. app_pwm_cb_t * p_cb = p_instance->p_cb;
  370. app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
  371. nrf_ppi_channel_group_t p_ppigrp = p_cb->ppi_group;
  372. nrf_timer_cc_channel_t pwm_ch_cc = (nrf_timer_cc_channel_t)(channel);
  373. pwm_ppi_disable(p_instance);
  374. pwm_channel_ppi_disable(p_instance, channel);
  375. nrf_drv_timer_compare(p_instance->p_timer, pwm_ch_cc, ticks, false);
  376. nrf_drv_ppi_group_clear(p_ppigrp);
  377. nrf_drv_ppi_channels_include_in_group(
  378. nrf_drv_ppi_channel_to_mask(p_ch_cb->ppi_channels[0])|
  379. nrf_drv_ppi_channel_to_mask(p_ch_cb->ppi_channels[1]),
  380. p_ppigrp);
  381. if (!p_ch_cb->pulsewidth)
  382. {
  383. // Channel is at 0%.
  384. nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
  385. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
  386. nrf_drv_ppi_task_addr_group_enable_get(p_ppigrp));
  387. nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, 0, false);
  388. m_pwm_target_value[p_instance->p_timer->instance_id] =
  389. nrf_drv_timer_capture_get(p_instance->p_timer, (nrf_timer_cc_channel_t) channel);
  390. nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
  391. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
  392. nrf_drv_timer_capture_task_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL));
  393. }
  394. else
  395. {
  396. // Channel is at 100%.
  397. nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
  398. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
  399. nrf_drv_ppi_task_addr_group_enable_get(p_ppigrp));
  400. // Set secondary CC channel to non-zero value:
  401. nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, 1, false);
  402. m_pwm_target_value[p_instance->p_timer->instance_id] = 0;
  403. // The captured value will be equal to 0, because timer clear on main PWM CC channel compare is enabled.
  404. nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
  405. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
  406. nrf_drv_timer_capture_task_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL));
  407. }
  408. nrf_drv_ppi_channel_enable(p_cb->ppi_channels[0]);
  409. nrf_drv_ppi_channel_enable(p_cb->ppi_channels[1]);
  410. p_ch_cb->pulsewidth = ticks;
  411. m_pwm_busy[p_instance->p_timer->instance_id] = PWM_SECONDARY_CC_CHANNEL;
  412. }
  413. /**
  414. * @brief PWM state transition from 0% or 100% to 0% or 100%.
  415. *
  416. * @param[in] p_instance PWM instance.
  417. * @param[in] channel PWM channel number.
  418. * @param[in] ticks Number of clock ticks.
  419. */
  420. static void pwm_transition_0or100_to_0or100(app_pwm_t const * const p_instance,
  421. uint8_t channel, uint16_t ticks)
  422. {
  423. app_pwm_cb_t * p_cb = p_instance->p_cb;
  424. app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
  425. nrf_timer_cc_channel_t pwm_ch_cc = (nrf_timer_cc_channel_t)(channel);
  426. pwm_ppi_disable(p_instance);
  427. pwm_channel_ppi_disable(p_instance, channel);
  428. if (!ticks)
  429. {
  430. // Set to 0%.
  431. nrf_drv_gpiote_out_task_force(p_ch_cb->gpio_pin, POLARITY_INACTIVE(p_instance, channel));
  432. }
  433. else if (ticks >= p_cb->period)
  434. {
  435. // Set to 100%.
  436. ticks = p_cb->period;
  437. nrf_drv_gpiote_out_task_force(p_ch_cb->gpio_pin, POLARITY_ACTIVE(p_instance, channel));
  438. }
  439. nrf_drv_timer_compare(p_instance->p_timer, pwm_ch_cc, ticks, false);
  440. p_ch_cb->pulsewidth = ticks;
  441. m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
  442. return;
  443. }
  444. static void pwm_transition(app_pwm_t const * const p_instance,
  445. uint8_t channel, uint16_t ticks)
  446. {
  447. app_pwm_cb_t * p_cb = p_instance->p_cb;
  448. app_pwm_channel_cb_t * p_ch_cb = &p_instance->p_cb->channels_cb[channel];
  449. // Pulse width change sequence:
  450. if (!p_ch_cb->pulsewidth || p_ch_cb->pulsewidth >= p_cb->period)
  451. {
  452. // Channel is disabled (0%) or at 100%.
  453. if (!ticks || ticks >= p_cb->period)
  454. {
  455. // Set to 0 or 100%.
  456. pwm_transition_0or100_to_0or100(p_instance, channel, ticks);
  457. }
  458. else
  459. {
  460. // Other value.
  461. pwm_transition_0or100_to_n(p_instance, channel, ticks);
  462. }
  463. }
  464. else
  465. {
  466. // Channel is at other value.
  467. if (!ticks || ticks >= p_cb->period)
  468. {
  469. // Disable channel (set to 0%) or set to 100%.
  470. pwm_transition_n_to_0or100(p_instance, channel, ticks);
  471. }
  472. else
  473. {
  474. // Set to any other value.
  475. pwm_transition_n_to_m(p_instance, channel, ticks);
  476. }
  477. }
  478. }
  479. #else //GPIOTE_SET_CLEAR_TASKS
  480. /**
  481. * @brief PWM state transition.
  482. *
  483. * @param[in] p_instance PWM instance.
  484. * @param[in] channel PWM channel number.
  485. * @param[in] ticks Number of clock ticks.
  486. */
  487. static void pwm_transition(app_pwm_t const * const p_instance,
  488. uint8_t channel, uint16_t ticks)
  489. {
  490. app_pwm_cb_t * p_cb = p_instance->p_cb;
  491. app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
  492. nrf_timer_cc_channel_t pwm_ch_cc = (nrf_timer_cc_channel_t)(channel);
  493. nrf_drv_ppi_channel_disable(p_cb->ppi_channel);
  494. if (!ticks)
  495. {
  496. nrf_drv_ppi_channel_disable(p_ch_cb->ppi_channels[1]);
  497. nrf_drv_ppi_channel_enable(p_ch_cb->ppi_channels[0]);
  498. m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
  499. }
  500. else if (ticks >= p_cb->period)
  501. {
  502. ticks = p_cb->period;
  503. nrf_drv_ppi_channel_disable(p_ch_cb->ppi_channels[0]);
  504. nrf_drv_ppi_channel_enable(p_ch_cb->ppi_channels[1]);
  505. m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
  506. }
  507. else
  508. {
  509. // Set to any other value.
  510. if ((p_ch_cb->pulsewidth != p_cb->period) && (p_ch_cb->pulsewidth != 0) && (ticks < p_ch_cb->pulsewidth))
  511. {
  512. nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t)PWM_SECONDARY_CC_CHANNEL, p_ch_cb->pulsewidth, false);
  513. nrf_drv_ppi_channel_assign(p_cb->ppi_channel,
  514. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, (nrf_timer_cc_channel_t)PWM_SECONDARY_CC_CHANNEL),
  515. p_ch_cb->polarity ? nrf_drv_gpiote_clr_task_addr_get(p_ch_cb->gpio_pin) : nrf_drv_gpiote_set_task_addr_get(p_ch_cb->gpio_pin));
  516. nrf_drv_ppi_channel_enable(p_cb->ppi_channel);
  517. m_pwm_busy[p_instance->p_timer->instance_id] = channel;
  518. m_pwm_target_value[p_instance->p_timer->instance_id] = ticks;
  519. }
  520. else
  521. {
  522. m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
  523. }
  524. nrf_drv_timer_compare(p_instance->p_timer, pwm_ch_cc, ticks, false);
  525. nrf_drv_ppi_channel_enable(p_ch_cb->ppi_channels[0]);
  526. nrf_drv_ppi_channel_enable(p_ch_cb->ppi_channels[1]);
  527. }
  528. p_ch_cb->pulsewidth = ticks;
  529. return;
  530. }
  531. #endif //GPIOTE_SET_CLEAR_TASKS
  532. ret_code_t app_pwm_channel_duty_ticks_set(app_pwm_t const * const p_instance,
  533. uint8_t channel,
  534. uint16_t ticks)
  535. {
  536. app_pwm_cb_t * p_cb = p_instance->p_cb;
  537. app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
  538. ASSERT(channel < APP_PWM_CHANNELS_PER_INSTANCE);
  539. ASSERT(p_ch_cb->initialized == APP_PWM_CHANNEL_INITIALIZED);
  540. if (p_cb->state != NRFX_DRV_STATE_POWERED_ON)
  541. {
  542. return NRF_ERROR_INVALID_STATE;
  543. }
  544. if (ticks == p_ch_cb->pulsewidth)
  545. {
  546. if (p_cb->p_ready_callback)
  547. {
  548. p_cb->p_ready_callback(p_instance->p_timer->instance_id);
  549. }
  550. return NRF_SUCCESS; // No action required.
  551. }
  552. if (app_pwm_busy_check(p_instance))
  553. {
  554. return NRF_ERROR_BUSY; // PPI channels for synchronization are still in use.
  555. }
  556. m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_CHANGING;
  557. // Set new value.
  558. pwm_transition(p_instance, channel, ticks);
  559. if (p_instance->p_cb->p_ready_callback)
  560. {
  561. //PWM ready interrupt handler will be called after one full period.
  562. m_pwm_ready_counter[p_instance->p_timer->instance_id][channel] = 2;
  563. pwm_irq_enable(p_instance);
  564. }
  565. return NRF_SUCCESS;
  566. }
  567. uint16_t app_pwm_channel_duty_ticks_get(app_pwm_t const * const p_instance, uint8_t channel)
  568. {
  569. app_pwm_cb_t * p_cb = p_instance->p_cb;
  570. app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
  571. return p_ch_cb->pulsewidth;
  572. }
  573. uint16_t app_pwm_cycle_ticks_get(app_pwm_t const * const p_instance)
  574. {
  575. app_pwm_cb_t * p_cb = p_instance->p_cb;
  576. return (uint16_t)p_cb->period;
  577. }
  578. ret_code_t app_pwm_channel_duty_set(app_pwm_t const * const p_instance,
  579. uint8_t channel, app_pwm_duty_t duty)
  580. {
  581. uint32_t ticks = ((uint32_t)app_pwm_cycle_ticks_get(p_instance) * (uint32_t)duty) / 100UL;
  582. return app_pwm_channel_duty_ticks_set(p_instance, channel, ticks);
  583. }
  584. app_pwm_duty_t app_pwm_channel_duty_get(app_pwm_t const * const p_instance, uint8_t channel)
  585. {
  586. uint32_t value = ((uint32_t)app_pwm_channel_duty_ticks_get(p_instance, channel) * 100UL) \
  587. / (uint32_t)app_pwm_cycle_ticks_get(p_instance);
  588. return (app_pwm_duty_t)value;
  589. }
  590. /**
  591. * @brief Function for initializing the PWM channel.
  592. *
  593. * @param[in] p_instance PWM instance.
  594. * @param[in] channel Channel number.
  595. * @param[in] pin GPIO pin number.
  596. *
  597. * @retval NRF_SUCCESS If initialization was successful.
  598. * @retval NRF_ERROR_NO_MEM If there were not enough free resources.
  599. * @retval NRF_ERROR_INVALID_STATE If the timer is already in use or initialization failed.
  600. */
  601. static ret_code_t app_pwm_channel_init(app_pwm_t const * const p_instance, uint8_t channel,
  602. uint32_t pin, app_pwm_polarity_t polarity)
  603. {
  604. ASSERT(channel < APP_PWM_CHANNELS_PER_INSTANCE);
  605. app_pwm_cb_t * p_cb = p_instance->p_cb;
  606. app_pwm_channel_cb_t * p_channel_cb = &p_cb->channels_cb[channel];
  607. if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
  608. {
  609. return NRF_ERROR_INVALID_STATE;
  610. }
  611. p_channel_cb->pulsewidth = 0;
  612. p_channel_cb->polarity = polarity;
  613. ret_code_t err_code;
  614. /* GPIOTE setup: */
  615. nrf_drv_gpiote_out_config_t out_cfg = GPIOTE_CONFIG_OUT_TASK_TOGGLE( POLARITY_INACTIVE(p_instance, channel) );
  616. err_code = nrf_drv_gpiote_out_init((nrf_drv_gpiote_pin_t)pin,&out_cfg);
  617. if (err_code != NRF_SUCCESS)
  618. {
  619. return NRF_ERROR_NO_MEM;
  620. }
  621. p_cb->channels_cb[channel].gpio_pin = pin;
  622. // Set output to inactive state.
  623. if (polarity)
  624. {
  625. nrf_gpio_pin_clear(pin);
  626. }
  627. else
  628. {
  629. nrf_gpio_pin_set(pin);
  630. }
  631. /* PPI setup: */
  632. for (uint8_t i = 0; i < APP_PWM_REQUIRED_PPI_CHANNELS_PER_CHANNEL; ++i)
  633. {
  634. if (nrf_drv_ppi_channel_alloc(&p_channel_cb->ppi_channels[i]) != NRF_SUCCESS)
  635. {
  636. return NRF_ERROR_NO_MEM; // Resource de-allocation is done by callee.
  637. }
  638. }
  639. nrf_drv_ppi_channel_disable(p_channel_cb->ppi_channels[0]);
  640. nrf_drv_ppi_channel_disable(p_channel_cb->ppi_channels[1]);
  641. #ifdef GPIOTE_SET_CLEAR_TASKS
  642. uint32_t deactivate_task_addr = polarity ? nrf_drv_gpiote_clr_task_addr_get(p_channel_cb->gpio_pin) : nrf_drv_gpiote_set_task_addr_get(p_channel_cb->gpio_pin);
  643. uint32_t activate_task_addr = polarity ? nrf_drv_gpiote_set_task_addr_get(p_channel_cb->gpio_pin) : nrf_drv_gpiote_clr_task_addr_get(p_channel_cb->gpio_pin);
  644. nrf_drv_ppi_channel_assign(p_channel_cb->ppi_channels[0],
  645. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
  646. deactivate_task_addr);
  647. nrf_drv_ppi_channel_assign(p_channel_cb->ppi_channels[1],
  648. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
  649. activate_task_addr);
  650. #else //GPIOTE_SET_CLEAR_TASKS
  651. nrf_drv_ppi_channel_assign(p_channel_cb->ppi_channels[0],
  652. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
  653. nrf_drv_gpiote_out_task_addr_get(p_channel_cb->gpio_pin));
  654. nrf_drv_ppi_channel_assign(p_channel_cb->ppi_channels[1],
  655. nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
  656. nrf_drv_gpiote_out_task_addr_get(p_channel_cb->gpio_pin));
  657. #endif //GPIOTE_SET_CLEAR_TASKS
  658. p_channel_cb->initialized = APP_PWM_CHANNEL_INITIALIZED;
  659. m_pwm_ready_counter[p_instance->p_timer->instance_id][channel] = 0;
  660. return NRF_SUCCESS;
  661. }
  662. /**
  663. * @brief Function for calculating target timer frequency, which will allow to set given period length.
  664. *
  665. * @param[in] period_us Desired period in microseconds.
  666. *
  667. * @retval Timer frequency.
  668. */
  669. __STATIC_INLINE nrf_timer_frequency_t pwm_calculate_timer_frequency(uint32_t period_us)
  670. {
  671. uint32_t f = (uint32_t) NRF_TIMER_FREQ_16MHz;
  672. uint32_t min = (uint32_t) NRF_TIMER_FREQ_31250Hz;
  673. while ((period_us > TIMER_MAX_PULSEWIDTH_US_ON_16M) && (f < min))
  674. {
  675. period_us >>= 1;
  676. ++f;
  677. }
  678. #ifdef GPIOTE_SET_CLEAR_TASKS
  679. if ((m_use_ppi_delay_workaround) && (f == (uint32_t) NRF_TIMER_FREQ_16MHz))
  680. {
  681. f = (uint32_t) NRF_TIMER_FREQ_8MHz;
  682. }
  683. #endif // GPIOTE_SET_CLEAR_TASKS
  684. return (nrf_timer_frequency_t) f;
  685. }
  686. ret_code_t app_pwm_init(app_pwm_t const * const p_instance, app_pwm_config_t const * const p_config,
  687. app_pwm_callback_t p_ready_callback)
  688. {
  689. ASSERT(p_instance);
  690. if (!p_config)
  691. {
  692. return NRF_ERROR_INVALID_DATA;
  693. }
  694. app_pwm_cb_t * p_cb = p_instance->p_cb;
  695. if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
  696. {
  697. return NRF_ERROR_INVALID_STATE;
  698. }
  699. uint32_t err_code = nrf_drv_ppi_init();
  700. if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_MODULE_ALREADY_INITIALIZED))
  701. {
  702. return NRF_ERROR_NO_MEM;
  703. }
  704. if (!nrf_drv_gpiote_is_init())
  705. {
  706. err_code = nrf_drv_gpiote_init();
  707. if (err_code != NRF_SUCCESS)
  708. {
  709. return NRF_ERROR_INTERNAL;
  710. }
  711. }
  712. #ifdef GPIOTE_SET_CLEAR_TASKS
  713. if (((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0x30)
  714. {
  715. m_use_ppi_delay_workaround = false;
  716. }
  717. else
  718. {
  719. m_use_ppi_delay_workaround = true;
  720. }
  721. #endif
  722. // Innitialize resource status:
  723. #ifdef GPIOTE_SET_CLEAR_TASKS
  724. p_cb->ppi_channel = (nrf_ppi_channel_t)UNALLOCATED;
  725. #else
  726. p_cb->ppi_channels[0] = (nrf_ppi_channel_t)UNALLOCATED;
  727. p_cb->ppi_channels[1] = (nrf_ppi_channel_t)UNALLOCATED;
  728. p_cb->ppi_group = (nrf_ppi_channel_group_t)UNALLOCATED;
  729. #endif //GPIOTE_SET_CLEAR_TASKS
  730. for (uint8_t i = 0; i < APP_PWM_CHANNELS_PER_INSTANCE; ++i)
  731. {
  732. p_cb->channels_cb[i].initialized = APP_PWM_CHANNEL_UNINITIALIZED;
  733. p_cb->channels_cb[i].ppi_channels[0] = (nrf_ppi_channel_t)UNALLOCATED;
  734. p_cb->channels_cb[i].ppi_channels[1] = (nrf_ppi_channel_t)UNALLOCATED;
  735. p_cb->channels_cb[i].gpio_pin = UNALLOCATED;
  736. }
  737. // Allocate PPI channels and groups:
  738. #ifdef GPIOTE_SET_CLEAR_TASKS
  739. if (nrf_drv_ppi_channel_alloc(&p_cb->ppi_channel) != NRF_SUCCESS)
  740. {
  741. pwm_dealloc(p_instance);
  742. return NRF_ERROR_NO_MEM;
  743. }
  744. #else //GPIOTE_SET_CLEAR_TASKS
  745. if (nrf_drv_ppi_group_alloc(&p_cb->ppi_group) != NRF_SUCCESS)
  746. {
  747. pwm_dealloc(p_instance);
  748. return NRF_ERROR_NO_MEM;
  749. }
  750. for (uint8_t i = 0; i < APP_PWM_REQUIRED_PPI_CHANNELS_PER_INSTANCE; ++i)
  751. {
  752. if (nrf_drv_ppi_channel_alloc(&p_cb->ppi_channels[i]) != NRF_SUCCESS)
  753. {
  754. pwm_dealloc(p_instance);
  755. return NRF_ERROR_NO_MEM;
  756. }
  757. }
  758. #endif //GPIOTE_SET_CLEAR_TASKS
  759. // Initialize channels:
  760. for (uint8_t i = 0; i < APP_PWM_CHANNELS_PER_INSTANCE; ++i)
  761. {
  762. if (p_config->pins[i] != APP_PWM_NOPIN)
  763. {
  764. err_code = app_pwm_channel_init(p_instance, i, p_config->pins[i], p_config->pin_polarity[i]);
  765. if (err_code != NRF_SUCCESS)
  766. {
  767. pwm_dealloc(p_instance);
  768. return err_code;
  769. }
  770. app_pwm_channel_duty_ticks_set(p_instance, i, 0);
  771. }
  772. }
  773. // Initialize timer:
  774. nrf_timer_frequency_t timer_freq = pwm_calculate_timer_frequency(p_config->period_us);
  775. nrf_drv_timer_config_t timer_cfg = {
  776. .frequency = timer_freq,
  777. .mode = NRF_TIMER_MODE_TIMER,
  778. .bit_width = NRF_TIMER_BIT_WIDTH_16,
  779. .interrupt_priority = APP_IRQ_PRIORITY_LOWEST,
  780. .p_context = (void *) (uint32_t) p_instance->p_timer->instance_id
  781. };
  782. err_code = nrf_drv_timer_init(p_instance->p_timer, &timer_cfg,
  783. pwm_ready_tick);
  784. if (err_code != NRF_SUCCESS)
  785. {
  786. pwm_dealloc(p_instance);
  787. return err_code;
  788. }
  789. uint32_t ticks = nrf_drv_timer_us_to_ticks(p_instance->p_timer, p_config->period_us);
  790. p_cb->period = ticks;
  791. nrf_drv_timer_clear(p_instance->p_timer);
  792. nrf_drv_timer_extended_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_MAIN_CC_CHANNEL,
  793. ticks, NRF_TIMER_SHORT_COMPARE2_CLEAR_MASK, true);
  794. nrf_drv_timer_compare_int_disable(p_instance->p_timer, PWM_MAIN_CC_CHANNEL);
  795. p_cb->p_ready_callback = p_ready_callback;
  796. m_instances[p_instance->p_timer->instance_id] = p_instance;
  797. m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
  798. p_cb->state = NRFX_DRV_STATE_INITIALIZED;
  799. return NRF_SUCCESS;
  800. }
  801. void app_pwm_enable(app_pwm_t const * const p_instance)
  802. {
  803. app_pwm_cb_t * p_cb = p_instance->p_cb;
  804. ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
  805. for (uint32_t channel = 0; channel < APP_PWM_CHANNELS_PER_INSTANCE; ++channel)
  806. {
  807. app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
  808. m_pwm_ready_counter[p_instance->p_timer->instance_id][channel] = 0;
  809. if (p_ch_cb->initialized)
  810. {
  811. nrf_drv_gpiote_out_task_force(p_ch_cb->gpio_pin, POLARITY_INACTIVE(p_instance, channel));
  812. nrf_drv_gpiote_out_task_enable(p_ch_cb->gpio_pin);
  813. p_ch_cb->pulsewidth = 0;
  814. }
  815. }
  816. m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
  817. pan73_workaround(p_instance->p_timer->p_reg, true);
  818. nrf_drv_timer_clear(p_instance->p_timer);
  819. nrf_drv_timer_enable(p_instance->p_timer);
  820. p_cb->state = NRFX_DRV_STATE_POWERED_ON;
  821. return;
  822. }
  823. void app_pwm_disable(app_pwm_t const * const p_instance)
  824. {
  825. app_pwm_cb_t * p_cb = p_instance->p_cb;
  826. ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
  827. nrf_drv_timer_disable(p_instance->p_timer);
  828. pwm_irq_disable(p_instance);
  829. #ifdef GPIOTE_SET_CLEAR_TASKS
  830. nrf_drv_ppi_channel_disable(p_cb->ppi_channel);
  831. #else
  832. for (uint8_t ppi_channel = 0; ppi_channel < APP_PWM_REQUIRED_PPI_CHANNELS_PER_INSTANCE; ++ppi_channel)
  833. {
  834. nrf_drv_ppi_channel_disable(p_cb->ppi_channels[ppi_channel]);
  835. }
  836. #endif //GPIOTE_SET_CLEAR_TASKS
  837. for (uint8_t channel = 0; channel < APP_PWM_CHANNELS_PER_INSTANCE; ++channel)
  838. {
  839. app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
  840. if (p_ch_cb->initialized)
  841. {
  842. uint8_t polarity = POLARITY_INACTIVE(p_instance, channel);
  843. if (polarity)
  844. {
  845. nrf_gpio_pin_set(p_ch_cb->gpio_pin);
  846. }
  847. else
  848. {
  849. nrf_gpio_pin_clear(p_ch_cb->gpio_pin);
  850. }
  851. nrf_drv_gpiote_out_task_disable(p_ch_cb->gpio_pin);
  852. nrf_drv_ppi_channel_disable(p_ch_cb->ppi_channels[0]);
  853. nrf_drv_ppi_channel_disable(p_ch_cb->ppi_channels[1]);
  854. }
  855. }
  856. pan73_workaround(p_instance->p_timer->p_reg, false);
  857. p_cb->state = NRFX_DRV_STATE_INITIALIZED;
  858. return;
  859. }
  860. ret_code_t app_pwm_uninit(app_pwm_t const * const p_instance)
  861. {
  862. app_pwm_cb_t * p_cb = p_instance->p_cb;
  863. if (p_cb->state == NRFX_DRV_STATE_POWERED_ON)
  864. {
  865. app_pwm_disable(p_instance);
  866. }
  867. else if (p_cb->state == NRFX_DRV_STATE_UNINITIALIZED)
  868. {
  869. return NRF_ERROR_INVALID_STATE;
  870. }
  871. pwm_dealloc(p_instance);
  872. p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
  873. return NRF_SUCCESS;
  874. }
  875. //lint -restore
  876. #endif //NRF_MODULE_ENABLED(APP_PWM)