rotary_encoder.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. /*
  2. * Copyright (c) 2019 David Antliff
  3. * Copyright 2011 Ben Buxton
  4. *
  5. * This file is part of the esp32-rotary-encoder component.
  6. *
  7. * esp32-rotary-encoder is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * esp32-rotary-encoder is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with esp32-rotary-encoder. If not, see <https://www.gnu.org/licenses/>.
  19. */
  20. /**
  21. * @file rotary_encoder.c
  22. * @brief Driver implementation for the ESP32-compatible Incremental Rotary Encoder component.
  23. *
  24. * Based on https://github.com/buxtronix/arduino/tree/master/libraries/Rotary
  25. * Original header follows:
  26. *
  27. * Rotary encoder handler for arduino. v1.1
  28. *
  29. * Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3.
  30. * Contact: bb@cactii.net
  31. *
  32. * A typical mechanical rotary encoder emits a two bit gray code
  33. * on 3 output pins. Every step in the output (often accompanied
  34. * by a physical 'click') generates a specific sequence of output
  35. * codes on the pins.
  36. *
  37. * There are 3 pins used for the rotary encoding - one common and
  38. * two 'bit' pins.
  39. *
  40. * The following is the typical sequence of code on the output when
  41. * moving from one step to the next:
  42. *
  43. * Position Bit1 Bit2
  44. * ----------------------
  45. * Step1 0 0
  46. * 1/4 1 0
  47. * 1/2 1 1
  48. * 3/4 0 1
  49. * Step2 0 0
  50. *
  51. * From this table, we can see that when moving from one 'click' to
  52. * the next, there are 4 changes in the output code.
  53. *
  54. * - From an initial 0 - 0, Bit1 goes high, Bit0 stays low.
  55. * - Then both bits are high, halfway through the step.
  56. * - Then Bit1 goes low, but Bit2 stays high.
  57. * - Finally at the end of the step, both bits return to 0.
  58. *
  59. * Detecting the direction is easy - the table simply goes in the other
  60. * direction (read up instead of down).
  61. *
  62. * To decode this, we use a simple state machine. Every time the output
  63. * code changes, it follows state, until finally a full steps worth of
  64. * code is received (in the correct order). At the final 0-0, it returns
  65. * a value indicating a step in one direction or the other.
  66. *
  67. * It's also possible to use 'half-step' mode. This just emits an event
  68. * at both the 0-0 and 1-1 positions. This might be useful for some
  69. * encoders where you want to detect all positions.
  70. *
  71. * If an invalid state happens (for example we go from '0-1' straight
  72. * to '1-0'), the state machine resets to the start until 0-0 and the
  73. * next valid codes occur.
  74. *
  75. * The biggest advantage of using a state machine over other algorithms
  76. * is that this has inherent debounce built in. Other algorithms emit spurious
  77. * output with switch bounce, but this one will simply flip between
  78. * sub-states until the bounce settles, then continue along the state
  79. * machine.
  80. * A side effect of debounce is that fast rotations can cause steps to
  81. * be skipped. By not requiring debounce, fast rotations can be accurately
  82. * measured.
  83. * Another advantage is the ability to properly handle bad state, such
  84. * as due to EMI, etc.
  85. * It is also a lot simpler than others - a static state table and less
  86. * than 10 lines of logic.
  87. */
  88. #include "rotary_encoder.h"
  89. #include "esp_log.h"
  90. #include "driver/gpio.h"
  91. #include "gpio_exp.h"
  92. #define TAG "rotary_encoder"
  93. //#define ROTARY_ENCODER_DEBUG
  94. // Use a single-item queue so that the last value can be easily overwritten by the interrupt handler
  95. #define EVENT_QUEUE_LENGTH 1
  96. #define TABLE_ROWS 7
  97. #define DIR_NONE 0x0 // No complete step yet.
  98. #define DIR_CW 0x10 // Clockwise step.
  99. #define DIR_CCW 0x20 // Anti-clockwise step.
  100. // Create the half-step state table (emits a code at 00 and 11)
  101. #define R_START 0x0
  102. #define H_CCW_BEGIN 0x1
  103. #define H_CW_BEGIN 0x2
  104. #define H_START_M 0x3
  105. #define H_CW_BEGIN_M 0x4
  106. #define H_CCW_BEGIN_M 0x5
  107. static const uint8_t _ttable_half[TABLE_ROWS][TABLE_COLS] = {
  108. // 00 01 10 11 // BA
  109. {H_START_M, H_CW_BEGIN, H_CCW_BEGIN, R_START}, // R_START (00)
  110. {H_START_M | DIR_CCW, R_START, H_CCW_BEGIN, R_START}, // H_CCW_BEGIN
  111. {H_START_M | DIR_CW, H_CW_BEGIN, R_START, R_START}, // H_CW_BEGIN
  112. {H_START_M, H_CCW_BEGIN_M, H_CW_BEGIN_M, R_START}, // H_START_M (11)
  113. {H_START_M, H_START_M, H_CW_BEGIN_M, R_START | DIR_CW}, // H_CW_BEGIN_M
  114. {H_START_M, H_CCW_BEGIN_M, H_START_M, R_START | DIR_CCW}, // H_CCW_BEGIN_M
  115. };
  116. // Create the full-step state table (emits a code at 00 only)
  117. # define F_CW_FINAL 0x1
  118. # define F_CW_BEGIN 0x2
  119. # define F_CW_NEXT 0x3
  120. # define F_CCW_BEGIN 0x4
  121. # define F_CCW_FINAL 0x5
  122. # define F_CCW_NEXT 0x6
  123. static const uint8_t _ttable_full[TABLE_ROWS][TABLE_COLS] = {
  124. // 00 01 10 11 // BA
  125. {R_START, F_CW_BEGIN, F_CCW_BEGIN, R_START}, // R_START
  126. {F_CW_NEXT, R_START, F_CW_FINAL, R_START | DIR_CW}, // F_CW_FINAL
  127. {F_CW_NEXT, F_CW_BEGIN, R_START, R_START}, // F_CW_BEGIN
  128. {F_CW_NEXT, F_CW_BEGIN, F_CW_FINAL, R_START}, // F_CW_NEXT
  129. {F_CCW_NEXT, R_START, F_CCW_BEGIN, R_START}, // F_CCW_BEGIN
  130. {F_CCW_NEXT, F_CCW_FINAL, R_START, R_START | DIR_CCW}, // F_CCW_FINAL
  131. {F_CCW_NEXT, F_CCW_FINAL, F_CCW_BEGIN, R_START}, // F_CCW_NEXT
  132. };
  133. static uint8_t _process(rotary_encoder_info_t * info)
  134. {
  135. uint8_t event = 0;
  136. if (info != NULL)
  137. {
  138. // Get state of input pins.
  139. uint8_t pin_state = (gpio_get_level_x(info->pin_b) << 1) | gpio_get_level_x(info->pin_a);
  140. // Determine new state from the pins and state table.
  141. #ifdef ROTARY_ENCODER_DEBUG
  142. uint8_t old_state = info->table_state;
  143. #endif
  144. info->table_state = info->table[info->table_state & 0xf][pin_state];
  145. // Return emit bits, i.e. the generated event.
  146. event = info->table_state & 0x30;
  147. #ifdef ROTARY_ENCODER_DEBUG
  148. ESP_EARLY_LOGD(TAG, "BA %d%d, state 0x%02x, new state 0x%02x, event 0x%02x",
  149. pin_state >> 1, pin_state & 1, old_state, info->table_state, event);
  150. #endif
  151. }
  152. return event;
  153. }
  154. static void _isr_rotenc(void * args)
  155. {
  156. rotary_encoder_info_t * info = (rotary_encoder_info_t *)args;
  157. uint8_t event = _process(info);
  158. bool send_event = false;
  159. switch (event)
  160. {
  161. case DIR_CW:
  162. ++info->state.position;
  163. info->state.direction = ROTARY_ENCODER_DIRECTION_CLOCKWISE;
  164. send_event = true;
  165. break;
  166. case DIR_CCW:
  167. --info->state.position;
  168. info->state.direction = ROTARY_ENCODER_DIRECTION_COUNTER_CLOCKWISE;
  169. send_event = true;
  170. break;
  171. default:
  172. break;
  173. }
  174. if (send_event && info->queue)
  175. {
  176. rotary_encoder_event_t queue_event =
  177. {
  178. .state =
  179. {
  180. .position = info->state.position,
  181. .direction = info->state.direction,
  182. },
  183. };
  184. if (info->pin_a < GPIO_NUM_MAX) {
  185. BaseType_t task_woken = pdFALSE;
  186. xQueueOverwriteFromISR(info->queue, &queue_event, &task_woken);
  187. if (task_woken)
  188. {
  189. portYIELD_FROM_ISR();
  190. }
  191. }
  192. else
  193. {
  194. xQueueOverwrite(info->queue, &queue_event);
  195. }
  196. }
  197. }
  198. esp_err_t rotary_encoder_init(rotary_encoder_info_t * info, gpio_num_t pin_a, gpio_num_t pin_b)
  199. {
  200. esp_err_t err = ESP_OK;
  201. if (info)
  202. {
  203. info->pin_a = pin_a;
  204. info->pin_b = pin_b;
  205. info->table = &_ttable_full[0]; //enable_half_step ? &_ttable_half[0] : &_ttable_full[0];
  206. info->table_state = R_START;
  207. info->state.position = 0;
  208. info->state.direction = ROTARY_ENCODER_DIRECTION_NOT_SET;
  209. // configure GPIOs
  210. gpio_pad_select_gpio_x(info->pin_a);
  211. gpio_set_pull_mode_x(info->pin_a, GPIO_PULLUP_ONLY);
  212. gpio_set_direction_x(info->pin_a, GPIO_MODE_INPUT);
  213. gpio_set_intr_type_x(info->pin_a, GPIO_INTR_ANYEDGE);
  214. gpio_pad_select_gpio_x(info->pin_b);
  215. gpio_set_pull_mode_x(info->pin_b, GPIO_PULLUP_ONLY);
  216. gpio_set_direction_x(info->pin_b, GPIO_MODE_INPUT);
  217. gpio_set_intr_type_x(info->pin_b, GPIO_INTR_ANYEDGE);
  218. // install interrupt handlers
  219. gpio_isr_handler_add_x(info->pin_a, _isr_rotenc, info);
  220. gpio_isr_handler_add_x(info->pin_b, _isr_rotenc, info);
  221. }
  222. else
  223. {
  224. ESP_LOGE(TAG, "info is NULL");
  225. err = ESP_ERR_INVALID_ARG;
  226. }
  227. return err;
  228. }
  229. esp_err_t rotary_encoder_enable_half_steps(rotary_encoder_info_t * info, bool enable)
  230. {
  231. esp_err_t err = ESP_OK;
  232. if (info)
  233. {
  234. info->table = enable ? &_ttable_half[0] : &_ttable_full[0];
  235. info->table_state = R_START;
  236. }
  237. else
  238. {
  239. ESP_LOGE(TAG, "info is NULL");
  240. err = ESP_ERR_INVALID_ARG;
  241. }
  242. return err;
  243. }
  244. esp_err_t rotary_encoder_flip_direction(rotary_encoder_info_t * info)
  245. {
  246. esp_err_t err = ESP_OK;
  247. if (info)
  248. {
  249. gpio_num_t temp = info->pin_a;
  250. info->pin_a = info->pin_b;
  251. info->pin_b = temp;
  252. }
  253. else
  254. {
  255. ESP_LOGE(TAG, "info is NULL");
  256. err = ESP_ERR_INVALID_ARG;
  257. }
  258. return err;
  259. }
  260. esp_err_t rotary_encoder_uninit(rotary_encoder_info_t * info)
  261. {
  262. esp_err_t err = ESP_OK;
  263. if (info)
  264. {
  265. gpio_isr_handler_remove_x(info->pin_a);
  266. gpio_isr_handler_remove_x(info->pin_b);
  267. }
  268. else
  269. {
  270. ESP_LOGE(TAG, "info is NULL");
  271. err = ESP_ERR_INVALID_ARG;
  272. }
  273. return err;
  274. }
  275. QueueHandle_t rotary_encoder_create_queue(void)
  276. {
  277. return xQueueCreate(EVENT_QUEUE_LENGTH, sizeof(rotary_encoder_event_t));
  278. }
  279. esp_err_t rotary_encoder_set_queue(rotary_encoder_info_t * info, QueueHandle_t queue)
  280. {
  281. esp_err_t err = ESP_OK;
  282. if (info)
  283. {
  284. info->queue = queue;
  285. }
  286. else
  287. {
  288. ESP_LOGE(TAG, "info is NULL");
  289. err = ESP_ERR_INVALID_ARG;
  290. }
  291. return err;
  292. }
  293. esp_err_t rotary_encoder_get_state(const rotary_encoder_info_t * info, rotary_encoder_state_t * state)
  294. {
  295. esp_err_t err = ESP_OK;
  296. if (info && state)
  297. {
  298. // make a snapshot of the state
  299. state->position = info->state.position;
  300. state->direction = info->state.direction;
  301. }
  302. else
  303. {
  304. ESP_LOGE(TAG, "info and/or state is NULL");
  305. err = ESP_ERR_INVALID_ARG;
  306. }
  307. return err;
  308. }
  309. esp_err_t rotary_encoder_reset(rotary_encoder_info_t * info)
  310. {
  311. esp_err_t err = ESP_OK;
  312. if (info)
  313. {
  314. info->state.position = 0;
  315. info->state.direction = ROTARY_ENCODER_DIRECTION_NOT_SET;
  316. }
  317. else
  318. {
  319. ESP_LOGE(TAG, "info is NULL");
  320. err = ESP_ERR_INVALID_ARG;
  321. }
  322. return err;
  323. }