rotary.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /*
  2. * Driver for interfacing to cheap rotary switches that
  3. * have a quadrature output with an optional press button
  4. *
  5. * This sets up the relevant gpio as interrupt and then keeps track of
  6. * the position of the switch in software. Changes are enqueued to task
  7. * level and a task message posted when required. If the queue fills up
  8. * then moves are ignored, but the last press/release will be included.
  9. *
  10. * Philip Gladstone, N1DQ
  11. */
  12. #include "platform.h"
  13. #include "c_types.h"
  14. #include "../libc/c_stdlib.h"
  15. #include "../libc/c_stdio.h"
  16. #include "driver/rotary.h"
  17. #include "user_interface.h"
  18. #include "task/task.h"
  19. #include "ets_sys.h"
  20. //
  21. // Queue is empty if read == write.
  22. // However, we always want to keep the previous value
  23. // so writing is only allowed if write - read < QUEUE_SIZE - 1
  24. #define QUEUE_SIZE 8
  25. #define GET_LAST_STATUS(d) (d->queue[(d->write_offset-1) & (QUEUE_SIZE - 1)])
  26. #define GET_PREV_STATUS(d) (d->queue[(d->write_offset-2) & (QUEUE_SIZE - 1)])
  27. #define HAS_QUEUED_DATA(d) (d->read_offset < d->write_offset)
  28. #define HAS_QUEUE_SPACE(d) (d->read_offset + QUEUE_SIZE - 1 > d->write_offset)
  29. #define REPLACE_STATUS(d, x) (d->queue[(d->write_offset-1) & (QUEUE_SIZE - 1)] = (rotary_event_t) { (x), system_get_time() })
  30. #define QUEUE_STATUS(d, x) (d->queue[(d->write_offset++) & (QUEUE_SIZE - 1)] = (rotary_event_t) { (x), system_get_time() })
  31. #define GET_READ_STATUS(d) (d->queue[d->read_offset & (QUEUE_SIZE - 1)])
  32. #define ADVANCE_IF_POSSIBLE(d) if (d->read_offset < d->write_offset) { d->read_offset++; }
  33. #define STATUS_IS_PRESSED(x) ((x & 0x80000000) != 0)
  34. typedef struct {
  35. int8_t phase_a_pin;
  36. int8_t phase_b_pin;
  37. int8_t press_pin;
  38. uint32_t read_offset; // Accessed by task
  39. uint32_t write_offset; // Accessed by ISR
  40. uint32_t pin_mask;
  41. uint32_t phase_a;
  42. uint32_t phase_b;
  43. uint32_t press;
  44. uint32_t last_press_change_time;
  45. int tasknumber;
  46. rotary_event_t queue[QUEUE_SIZE];
  47. } DATA;
  48. static DATA *data[ROTARY_CHANNEL_COUNT];
  49. static uint8_t task_queued;
  50. static void set_gpio_bits(void);
  51. static void rotary_clear_pin(int pin)
  52. {
  53. if (pin >= 0) {
  54. gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[pin]), GPIO_PIN_INTR_DISABLE);
  55. platform_gpio_mode(pin, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP);
  56. }
  57. }
  58. // Just takes the channel number. Cleans up the resources used.
  59. int rotary_close(uint32_t channel)
  60. {
  61. if (channel >= sizeof(data) / sizeof(data[0])) {
  62. return -1;
  63. }
  64. DATA *d = data[channel];
  65. if (!d) {
  66. return 0;
  67. }
  68. data[channel] = NULL;
  69. rotary_clear_pin(d->phase_a_pin);
  70. rotary_clear_pin(d->phase_b_pin);
  71. rotary_clear_pin(d->press_pin);
  72. c_free(d);
  73. set_gpio_bits();
  74. return 0;
  75. }
  76. static uint32_t ICACHE_RAM_ATTR rotary_interrupt(uint32_t ret_gpio_status)
  77. {
  78. // This function really is running at interrupt level with everything
  79. // else masked off. It should take as little time as necessary.
  80. //
  81. //
  82. // This gets the set of pins which have changed status
  83. uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
  84. int i;
  85. for (i = 0; i < sizeof(data) / sizeof(data[0]); i++) {
  86. DATA *d = data[i];
  87. if (!d || (gpio_status & d->pin_mask) == 0) {
  88. continue;
  89. }
  90. GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & d->pin_mask);
  91. uint32_t bits = GPIO_REG_READ(GPIO_IN_ADDRESS);
  92. uint32_t last_status = GET_LAST_STATUS(d).pos;
  93. uint32_t now = system_get_time();
  94. uint32_t new_status;
  95. new_status = last_status & 0x80000000;
  96. // This is the debounce logic for the press switch. We ignore changes
  97. // for 10ms after a change.
  98. if (now - d->last_press_change_time > 10 * 1000) {
  99. new_status = (bits & d->press) ? 0 : 0x80000000;
  100. if (STATUS_IS_PRESSED(new_status ^ last_status)) {
  101. d->last_press_change_time = now;
  102. }
  103. }
  104. // A B
  105. // 1 1 => 0
  106. // 1 0 => 1
  107. // 0 0 => 2
  108. // 0 1 => 3
  109. int micropos = 2;
  110. if (bits & d->phase_b) {
  111. micropos = 3;
  112. }
  113. if (bits & d->phase_a) {
  114. micropos ^= 3;
  115. }
  116. int32_t rotary_pos = last_status;
  117. switch ((micropos - last_status) & 3) {
  118. case 0:
  119. // No change, nothing to do
  120. break;
  121. case 1:
  122. // Incremented by 1
  123. rotary_pos++;
  124. break;
  125. case 3:
  126. // Decremented by 1
  127. rotary_pos--;
  128. break;
  129. default:
  130. // We missed an interrupt
  131. // We will ignore... but mark it.
  132. rotary_pos += 1000000;
  133. break;
  134. }
  135. new_status |= rotary_pos & 0x7fffffff;
  136. if (last_status != new_status) {
  137. // Either we overwrite the status or we add a new one
  138. if (!HAS_QUEUED_DATA(d)
  139. || STATUS_IS_PRESSED(last_status ^ new_status)
  140. || STATUS_IS_PRESSED(last_status ^ GET_PREV_STATUS(d).pos)) {
  141. if (HAS_QUEUE_SPACE(d)) {
  142. QUEUE_STATUS(d, new_status);
  143. if (!task_queued) {
  144. if (task_post_medium(d->tasknumber, (os_param_t) &task_queued)) {
  145. task_queued = 1;
  146. }
  147. }
  148. } else {
  149. REPLACE_STATUS(d, new_status);
  150. }
  151. } else {
  152. REPLACE_STATUS(d, new_status);
  153. }
  154. }
  155. ret_gpio_status &= ~(d->pin_mask);
  156. }
  157. return ret_gpio_status;
  158. }
  159. // The pin numbers are actual platform GPIO numbers
  160. int rotary_setup(uint32_t channel, int phase_a, int phase_b, int press, task_handle_t tasknumber )
  161. {
  162. if (channel >= sizeof(data) / sizeof(data[0])) {
  163. return -1;
  164. }
  165. if (data[channel]) {
  166. if (rotary_close(channel)) {
  167. return -1;
  168. }
  169. }
  170. DATA *d = (DATA *) c_zalloc(sizeof(DATA));
  171. if (!d) {
  172. return -1;
  173. }
  174. data[channel] = d;
  175. int i;
  176. d->tasknumber = tasknumber;
  177. d->phase_a = 1 << pin_num[phase_a];
  178. platform_gpio_mode(phase_a, PLATFORM_GPIO_INT, PLATFORM_GPIO_PULLUP);
  179. gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[phase_a]), GPIO_PIN_INTR_ANYEDGE);
  180. d->phase_a_pin = phase_a;
  181. d->phase_b = 1 << pin_num[phase_b];
  182. platform_gpio_mode(phase_b, PLATFORM_GPIO_INT, PLATFORM_GPIO_PULLUP);
  183. gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[phase_b]), GPIO_PIN_INTR_ANYEDGE);
  184. d->phase_b_pin = phase_b;
  185. if (press >= 0) {
  186. d->press = 1 << pin_num[press];
  187. platform_gpio_mode(press, PLATFORM_GPIO_INT, PLATFORM_GPIO_PULLUP);
  188. gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[press]), GPIO_PIN_INTR_ANYEDGE);
  189. }
  190. d->press_pin = press;
  191. d->pin_mask = d->phase_a | d->phase_b | d->press;
  192. set_gpio_bits();
  193. return 0;
  194. }
  195. static void set_gpio_bits()
  196. {
  197. uint32_t bits = 0;
  198. for (int i = 0; i < ROTARY_CHANNEL_COUNT; i++) {
  199. DATA *d = data[i];
  200. if (d) {
  201. bits = bits | d->pin_mask;
  202. }
  203. }
  204. platform_gpio_register_intr_hook(bits, rotary_interrupt);
  205. }
  206. bool rotary_has_queued_event(uint32_t channel)
  207. {
  208. if (channel >= sizeof(data) / sizeof(data[0])) {
  209. return FALSE;
  210. }
  211. DATA *d = data[channel];
  212. if (!d) {
  213. return FALSE;
  214. }
  215. return HAS_QUEUED_DATA(d);
  216. }
  217. // Get the oldest event in the queue and remove it (if possible)
  218. bool rotary_getevent(uint32_t channel, rotary_event_t *resultp)
  219. {
  220. rotary_event_t result = { 0 };
  221. if (channel >= sizeof(data) / sizeof(data[0])) {
  222. return FALSE;
  223. }
  224. DATA *d = data[channel];
  225. if (!d) {
  226. return FALSE;
  227. }
  228. ETS_GPIO_INTR_DISABLE();
  229. bool status = FALSE;
  230. if (HAS_QUEUED_DATA(d)) {
  231. result = GET_READ_STATUS(d);
  232. d->read_offset++;
  233. status = TRUE;
  234. } else {
  235. result = GET_LAST_STATUS(d);
  236. }
  237. ETS_GPIO_INTR_ENABLE();
  238. *resultp = result;
  239. return status;
  240. }
  241. int rotary_getpos(uint32_t channel)
  242. {
  243. if (channel >= sizeof(data) / sizeof(data[0])) {
  244. return -1;
  245. }
  246. DATA *d = data[channel];
  247. if (!d) {
  248. return -1;
  249. }
  250. return GET_LAST_STATUS(d).pos;
  251. }