softpwm.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. /*
  2. * This file is part of the MicroPython project, http://micropython.org/
  3. *
  4. * The MIT License (MIT)
  5. *
  6. * Copyright (c) 2016 Mark Shannon
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. */
  26. #include "py/mphal.h"
  27. #if MICROPY_PY_MACHINE_SOFT_PWM
  28. #include "stddef.h"
  29. #include "py/runtime.h"
  30. #include "py/gc.h"
  31. #include "nrf_timer.h"
  32. #include "nrf_gpio.h"
  33. #include "pin.h"
  34. #include "ticker.h"
  35. #define CYCLES_PER_MICROSECONDS 16
  36. #define MICROSECONDS_PER_TICK 16
  37. #define CYCLES_PER_TICK (CYCLES_PER_MICROSECONDS*MICROSECONDS_PER_TICK)
  38. // This must be an integer multiple of MICROSECONDS_PER_TICK
  39. #define MICROSECONDS_PER_MACRO_TICK 6000
  40. #define MILLISECONDS_PER_MACRO_TICK 6
  41. #define PWM_TICKER_INDEX 2
  42. // Default period of 20ms
  43. #define DEFAULT_PERIOD ((20*1000)/MICROSECONDS_PER_TICK)
  44. typedef struct _pwm_event {
  45. uint16_t time;
  46. uint8_t pin;
  47. uint8_t turn_on;
  48. } pwm_event;
  49. typedef struct _pwm_events {
  50. uint8_t count;
  51. uint16_t period;
  52. uint32_t all_pins;
  53. pwm_event events[1];
  54. } pwm_events;
  55. static const pwm_events OFF_EVENTS = {
  56. .count = 1,
  57. .period = DEFAULT_PERIOD,
  58. .all_pins = 0,
  59. .events = {
  60. {
  61. .time = 1024,
  62. .pin = 31,
  63. .turn_on = 0
  64. }
  65. }
  66. };
  67. #define active_events MP_STATE_PORT(pwm_active_events)
  68. #define pending_events MP_STATE_PORT(pwm_pending_events)
  69. void softpwm_init0(void) {
  70. active_events = &OFF_EVENTS;
  71. pending_events = NULL;
  72. }
  73. static uint8_t next_event = 0;
  74. static inline int32_t pwm_get_period_ticks(void) {
  75. const pwm_events *tmp = pending_events;
  76. if (tmp == NULL)
  77. tmp = active_events;
  78. return tmp->period;
  79. }
  80. #if 0
  81. void pwm_dump_events(const pwm_events *events) {
  82. printf("Count %d, period %d, all pins %d\r\n", events->count, events->period, events->all_pins);
  83. for (uint32_t i = 0; i < events->count; i++) {
  84. const pwm_event *event = &events->events[i];
  85. printf("Event. pin: %d, duty cycle: %d, turn_on: %d\r\n",
  86. event->pin, event->time, event->turn_on);
  87. }
  88. }
  89. void pwm_dump_state(void) {
  90. while(pending_events);
  91. pwm_dump_events(active_events);
  92. }
  93. #endif
  94. static const pwm_events *swap_pending(const pwm_events *in) {
  95. __disable_irq();
  96. const pwm_events *result = pending_events;
  97. pending_events = in;
  98. __enable_irq();
  99. return result;
  100. }
  101. static pwm_events *copy_events(const pwm_events *orig, uint32_t count) {
  102. pwm_events *events = m_malloc(sizeof(pwm_events) + (count-1)*sizeof(pwm_event));
  103. events->count = count;
  104. uint32_t copy = count > orig->count ? orig->count : count;
  105. for (uint32_t i = 0; i < copy; i++) {
  106. events->events[i] = orig->events[i];
  107. }
  108. return events;
  109. }
  110. static int find_pin_in_events(const pwm_events *events, uint32_t pin) {
  111. for (int i = 0; i < events->count; i++) {
  112. if (events->events[i].pin == pin)
  113. return i;
  114. }
  115. return -1;
  116. }
  117. static void sort_events(pwm_events *events) {
  118. // Insertion sort
  119. for (int32_t i = 1; i < events->count; i++) {
  120. pwm_event x = events->events[i];
  121. int32_t j;
  122. for (j = i - 1; j >= 0 && events->events[j].time > x.time; j--) {
  123. events->events[j+1] = events->events[j];
  124. }
  125. events->events[j+1] = x;
  126. }
  127. }
  128. int32_t pwm_callback(void) {
  129. int32_t tdiff;
  130. const pwm_events *events = active_events;
  131. const pwm_event *event = &events->events[next_event];
  132. int32_t tnow = (event->time*events->period)>>10;
  133. do {
  134. if (event->turn_on) {
  135. nrf_gpio_pin_set(event->pin);
  136. next_event++;
  137. } else {
  138. // TODO: Resolve port for nrf52
  139. nrf_gpio_port_out_clear(NRF_GPIO, events->all_pins);
  140. next_event = 0;
  141. tnow = 0;
  142. if (pending_events) {
  143. events = pending_events;
  144. active_events = events;
  145. pending_events = NULL;
  146. }
  147. }
  148. event = &events->events[next_event];
  149. tdiff = ((event->time*events->period)>>10) - tnow;
  150. } while (tdiff == 0);
  151. return tdiff;
  152. }
  153. void pwm_start(void) {
  154. set_ticker_callback(PWM_TICKER_INDEX, pwm_callback, 120);
  155. }
  156. void pwm_stop(void) {
  157. clear_ticker_callback(PWM_TICKER_INDEX);
  158. }
  159. static void pwm_set_period_ticks(int32_t ticks) {
  160. const pwm_events *old_events = swap_pending(NULL);
  161. if (old_events == NULL) {
  162. old_events = active_events;
  163. }
  164. pwm_events *events = copy_events(old_events, old_events->count);
  165. events->all_pins = old_events->all_pins;
  166. events->period = ticks;
  167. pending_events = events;
  168. }
  169. int pwm_set_period_us(int32_t us) {
  170. if ((us < 256) ||
  171. (us > 1000000)) {
  172. return -1;
  173. }
  174. pwm_set_period_ticks(us/MICROSECONDS_PER_TICK);
  175. return 0;
  176. }
  177. int32_t pwm_get_period_us(void) {
  178. return pwm_get_period_ticks()*MICROSECONDS_PER_TICK;
  179. }
  180. void pwm_set_duty_cycle(int32_t pin, uint32_t value) {
  181. if (value >= (1<<10)) {
  182. value = (1<<10)-1;
  183. }
  184. uint32_t turn_on_time = 1024-value;
  185. const pwm_events *old_events = swap_pending(NULL);
  186. if (old_events == NULL) {
  187. old_events = active_events;
  188. }
  189. if (((1<<pin)&old_events->all_pins) == 0) {
  190. nrf_gpio_cfg_output(pin);
  191. }
  192. int ev = find_pin_in_events(old_events, pin);
  193. pwm_events *events;
  194. if (ev < 0 && value == 0) {
  195. return;
  196. } else if (ev < 0) {
  197. events = copy_events(old_events, old_events->count+1);
  198. events->all_pins = old_events->all_pins | (1<<pin);
  199. events->events[old_events->count].time = turn_on_time;
  200. events->events[old_events->count].pin = pin;
  201. events->events[old_events->count].turn_on = 1;
  202. } else if (value == 0) {
  203. events = copy_events(old_events, old_events->count-1);
  204. events->all_pins = old_events->all_pins & ~(1<<pin);
  205. if (ev < old_events->count-1) {
  206. events->events[ev] = old_events->events[old_events->count-1];
  207. }
  208. } else {
  209. events = copy_events(old_events, old_events->count);
  210. events->all_pins = old_events->all_pins;
  211. events->events[ev].time = turn_on_time;
  212. }
  213. events->period = old_events->period;
  214. sort_events(events);
  215. pending_events = events;
  216. return;
  217. }
  218. void pwm_release(int32_t pin) {
  219. pwm_set_duty_cycle(pin, 0);
  220. const pwm_events *ev = active_events;
  221. int i = find_pin_in_events(ev, pin);
  222. if (i < 0)
  223. return;
  224. // If i >= 0 it means that `ev` is in RAM, so it safe to discard the const qualifier
  225. ((pwm_events *)ev)->events[i].pin = 31;
  226. nrf_gpio_pin_clear(pin);
  227. }
  228. #endif // MICROPY_PY_MACHINE_SOFT_PWM