low_power_pwm.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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(LOW_POWER_PWM)
  42. #include <string.h>
  43. #include "low_power_pwm.h"
  44. #include "nrf_gpio.h"
  45. #include "app_timer.h"
  46. #include "nrf_assert.h"
  47. /**
  48. * @brief Function for turning on pins.
  49. *
  50. * Sets the pin high state according to active_high parameter.
  51. *
  52. * @param[in] p_pwm_instance Pointer to instance of low-power PWM.
  53. */
  54. __STATIC_INLINE void pin_on(low_power_pwm_t * p_pwm_instance)
  55. {
  56. if (p_pwm_instance->active_high)
  57. {
  58. nrf_gpio_port_out_set(p_pwm_instance->p_port, p_pwm_instance->bit_mask_toggle);
  59. }
  60. else
  61. {
  62. nrf_gpio_port_out_clear(p_pwm_instance->p_port, p_pwm_instance->bit_mask_toggle);
  63. }
  64. p_pwm_instance->pin_is_on = true;
  65. }
  66. /**
  67. * @brief Function for turning off pins.
  68. *
  69. * Sets the pin low state according to active_high parameter.
  70. *
  71. * @param[in] p_pwm_instance Pointer to instance of low-power PWM.
  72. */
  73. __STATIC_INLINE void pin_off(low_power_pwm_t * p_pwm_instance)
  74. {
  75. if (p_pwm_instance->active_high)
  76. {
  77. nrf_gpio_port_out_clear(p_pwm_instance->p_port, p_pwm_instance->bit_mask_toggle);
  78. }
  79. else
  80. {
  81. nrf_gpio_port_out_set(p_pwm_instance->p_port, p_pwm_instance->bit_mask_toggle);
  82. }
  83. p_pwm_instance->pin_is_on = false;
  84. }
  85. /**
  86. * @brief Timer event handler for PWM.
  87. *
  88. * @param[in] p_context General purpose pointer. Will be passed to the time-out handler
  89. * when the timer expires.
  90. *
  91. */
  92. static void pwm_timeout_handler(void * p_context)
  93. {
  94. ret_code_t err_code;
  95. uint8_t duty_cycle;
  96. low_power_pwm_t * p_pwm_instance = (low_power_pwm_t *)p_context;
  97. if (p_pwm_instance->evt_type == LOW_POWER_PWM_EVENT_PERIOD)
  98. {
  99. if (p_pwm_instance->handler)
  100. {
  101. p_pwm_instance->handler(p_pwm_instance);
  102. if (p_pwm_instance->pwm_state != NRFX_DRV_STATE_POWERED_ON)
  103. {
  104. return;
  105. }
  106. }
  107. duty_cycle = p_pwm_instance->duty_cycle;
  108. if (duty_cycle == p_pwm_instance->period) // Process duty cycle 100%
  109. {
  110. pin_on(p_pwm_instance);
  111. p_pwm_instance->timeout_ticks = p_pwm_instance->period + APP_TIMER_MIN_TIMEOUT_TICKS;
  112. }
  113. else if (duty_cycle == 0) // Process duty cycle 0%
  114. {
  115. pin_off(p_pwm_instance);
  116. p_pwm_instance->timeout_ticks = p_pwm_instance->period + APP_TIMER_MIN_TIMEOUT_TICKS;
  117. }
  118. else // Process any other duty cycle than 0 or 100%
  119. {
  120. pin_on(p_pwm_instance);
  121. p_pwm_instance->timeout_ticks = ((duty_cycle * p_pwm_instance->period)>>8) +
  122. APP_TIMER_MIN_TIMEOUT_TICKS;
  123. // setting next state
  124. p_pwm_instance->evt_type = LOW_POWER_PWM_EVENT_DUTY_CYCLE;
  125. }
  126. }
  127. else
  128. {
  129. pin_off(p_pwm_instance);
  130. p_pwm_instance->evt_type = LOW_POWER_PWM_EVENT_PERIOD;
  131. p_pwm_instance->timeout_ticks = (((p_pwm_instance->period - p_pwm_instance->duty_cycle) *
  132. p_pwm_instance->period)>>8) + APP_TIMER_MIN_TIMEOUT_TICKS;
  133. }
  134. if (p_pwm_instance->pwm_state == NRFX_DRV_STATE_POWERED_ON)
  135. {
  136. err_code = app_timer_start(*p_pwm_instance->p_timer_id, p_pwm_instance->timeout_ticks, p_pwm_instance);
  137. APP_ERROR_CHECK(err_code);
  138. }
  139. }
  140. ret_code_t low_power_pwm_init(low_power_pwm_t * p_pwm_instance,
  141. low_power_pwm_config_t const * p_pwm_config,
  142. app_timer_timeout_handler_t handler)
  143. {
  144. ASSERT(p_pwm_instance->pwm_state == NRFX_DRV_STATE_UNINITIALIZED);
  145. ASSERT(p_pwm_config->bit_mask != 0);
  146. ASSERT(p_pwm_config->p_port != NULL);
  147. ASSERT(p_pwm_config->period != 0);
  148. ret_code_t err_code;
  149. uint32_t bit_mask;
  150. uint32_t pin_number = 0;
  151. p_pwm_instance->handler = handler;
  152. bit_mask = p_pwm_config->bit_mask;
  153. p_pwm_instance->active_high = p_pwm_config->active_high;
  154. p_pwm_instance->bit_mask = p_pwm_config->bit_mask;
  155. p_pwm_instance->bit_mask_toggle = p_pwm_config->bit_mask;
  156. p_pwm_instance->p_port = p_pwm_config->p_port;
  157. p_pwm_instance->period = p_pwm_config->period;
  158. p_pwm_instance->p_timer_id = p_pwm_config->p_timer_id;
  159. err_code = app_timer_create(p_pwm_instance->p_timer_id, APP_TIMER_MODE_SINGLE_SHOT, pwm_timeout_handler);
  160. if (err_code != NRF_SUCCESS)
  161. {
  162. return err_code;
  163. }
  164. while (bit_mask)
  165. {
  166. if (bit_mask & 0x1UL)
  167. {
  168. nrf_gpio_cfg_output(pin_number);
  169. }
  170. pin_number++;
  171. bit_mask >>= 1UL;
  172. }
  173. pin_off(p_pwm_instance);
  174. p_pwm_instance->pwm_state = NRFX_DRV_STATE_INITIALIZED;
  175. return NRF_SUCCESS;
  176. }
  177. ret_code_t low_power_pwm_start(low_power_pwm_t * p_pwm_instance,
  178. uint32_t pin_bit_mask)
  179. {
  180. ASSERT(p_pwm_instance->pwm_state != NRFX_DRV_STATE_UNINITIALIZED);
  181. ASSERT(((p_pwm_instance->bit_mask) & pin_bit_mask) != 0x00);
  182. p_pwm_instance->pwm_state = NRFX_DRV_STATE_POWERED_ON;
  183. p_pwm_instance->bit_mask_toggle = pin_bit_mask;
  184. pin_off(p_pwm_instance);
  185. p_pwm_instance->bit_mask |= pin_bit_mask;
  186. p_pwm_instance->evt_type = LOW_POWER_PWM_EVENT_PERIOD;
  187. app_timer_timeout_handler_t handler = p_pwm_instance->handler;
  188. p_pwm_instance->handler = NULL;
  189. pwm_timeout_handler(p_pwm_instance);
  190. p_pwm_instance->handler = handler;
  191. return NRF_SUCCESS;
  192. }
  193. ret_code_t low_power_pwm_stop(low_power_pwm_t * p_pwm_instance)
  194. {
  195. ASSERT(p_pwm_instance->pwm_state == NRFX_DRV_STATE_POWERED_ON);
  196. ret_code_t err_code;
  197. err_code = app_timer_stop(*p_pwm_instance->p_timer_id);
  198. pin_off(p_pwm_instance);
  199. if (err_code != NRF_SUCCESS)
  200. {
  201. return err_code;
  202. }
  203. p_pwm_instance->pwm_state = NRFX_DRV_STATE_INITIALIZED;
  204. return NRF_SUCCESS;
  205. }
  206. ret_code_t low_power_pwm_duty_set(low_power_pwm_t * p_pwm_instance, uint8_t duty_cycle)
  207. {
  208. if ( p_pwm_instance->period < duty_cycle)
  209. {
  210. return NRF_ERROR_INVALID_PARAM;
  211. }
  212. p_pwm_instance->duty_cycle = duty_cycle;
  213. return NRF_SUCCESS;
  214. }
  215. #endif //NRF_MODULE_ENABLED(LOW_POWER_PWM)