rotary_encoder.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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. #define TAG "rotary_encoder"
  92. //#define ROTARY_ENCODER_DEBUG
  93. // Use a single-item queue so that the last value can be easily overwritten by the interrupt handler
  94. #define EVENT_QUEUE_LENGTH 1
  95. #define TABLE_ROWS 7
  96. #define DIR_NONE 0x0 // No complete step yet.
  97. #define DIR_CW 0x10 // Clockwise step.
  98. #define DIR_CCW 0x20 // Anti-clockwise step.
  99. // Create the half-step state table (emits a code at 00 and 11)
  100. #define R_START 0x0
  101. #define H_CCW_BEGIN 0x1
  102. #define H_CW_BEGIN 0x2
  103. #define H_START_M 0x3
  104. #define H_CW_BEGIN_M 0x4
  105. #define H_CCW_BEGIN_M 0x5
  106. static const uint8_t _ttable_half[TABLE_ROWS][TABLE_COLS] = {
  107. // 00 01 10 11 // BA
  108. {H_START_M, H_CW_BEGIN, H_CCW_BEGIN, R_START}, // R_START (00)
  109. {H_START_M | DIR_CCW, R_START, H_CCW_BEGIN, R_START}, // H_CCW_BEGIN
  110. {H_START_M | DIR_CW, H_CW_BEGIN, R_START, R_START}, // H_CW_BEGIN
  111. {H_START_M, H_CCW_BEGIN_M, H_CW_BEGIN_M, R_START}, // H_START_M (11)
  112. {H_START_M, H_START_M, H_CW_BEGIN_M, R_START | DIR_CW}, // H_CW_BEGIN_M
  113. {H_START_M, H_CCW_BEGIN_M, H_START_M, R_START | DIR_CCW}, // H_CCW_BEGIN_M
  114. };
  115. // Create the full-step state table (emits a code at 00 only)
  116. # define F_CW_FINAL 0x1
  117. # define F_CW_BEGIN 0x2
  118. # define F_CW_NEXT 0x3
  119. # define F_CCW_BEGIN 0x4
  120. # define F_CCW_FINAL 0x5
  121. # define F_CCW_NEXT 0x6
  122. static const uint8_t _ttable_full[TABLE_ROWS][TABLE_COLS] = {
  123. // 00 01 10 11 // BA
  124. {R_START, F_CW_BEGIN, F_CCW_BEGIN, R_START}, // R_START
  125. {F_CW_NEXT, R_START, F_CW_FINAL, R_START | DIR_CW}, // F_CW_FINAL
  126. {F_CW_NEXT, F_CW_BEGIN, R_START, R_START}, // F_CW_BEGIN
  127. {F_CW_NEXT, F_CW_BEGIN, F_CW_FINAL, R_START}, // F_CW_NEXT
  128. {F_CCW_NEXT, R_START, F_CCW_BEGIN, R_START}, // F_CCW_BEGIN
  129. {F_CCW_NEXT, F_CCW_FINAL, R_START, R_START | DIR_CCW}, // F_CCW_FINAL
  130. {F_CCW_NEXT, F_CCW_FINAL, F_CCW_BEGIN, R_START}, // F_CCW_NEXT
  131. };
  132. static uint8_t _process(rotary_encoder_info_t * info)
  133. {
  134. uint8_t event = 0;
  135. if (info != NULL)
  136. {
  137. // Get state of input pins.
  138. uint8_t pin_state = (gpio_get_level(info->pin_b) << 1) | gpio_get_level(info->pin_a);
  139. // Determine new state from the pins and state table.
  140. #ifdef ROTARY_ENCODER_DEBUG
  141. uint8_t old_state = info->table_state;
  142. #endif
  143. info->table_state = info->table[info->table_state & 0xf][pin_state];
  144. // Return emit bits, i.e. the generated event.
  145. event = info->table_state & 0x30;
  146. #ifdef ROTARY_ENCODER_DEBUG
  147. ESP_EARLY_LOGD(TAG, "BA %d%d, state 0x%02x, new state 0x%02x, event 0x%02x",
  148. pin_state >> 1, pin_state & 1, old_state, info->table_state, event);
  149. #endif
  150. }
  151. return event;
  152. }
  153. static void _isr_rotenc(void * args)
  154. {
  155. rotary_encoder_info_t * info = (rotary_encoder_info_t *)args;
  156. uint8_t event = _process(info);
  157. bool send_event = false;
  158. switch (event)
  159. {
  160. case DIR_CW:
  161. ++info->state.position;
  162. info->state.direction = ROTARY_ENCODER_DIRECTION_CLOCKWISE;
  163. send_event = true;
  164. break;
  165. case DIR_CCW:
  166. --info->state.position;
  167. info->state.direction = ROTARY_ENCODER_DIRECTION_COUNTER_CLOCKWISE;
  168. send_event = true;
  169. break;
  170. default:
  171. break;
  172. }
  173. if (send_event && info->queue)
  174. {
  175. rotary_encoder_event_t queue_event =
  176. {
  177. .state =
  178. {
  179. .position = info->state.position,
  180. .direction = info->state.direction,
  181. },
  182. };
  183. BaseType_t task_woken = pdFALSE;
  184. xQueueOverwriteFromISR(info->queue, &queue_event, &task_woken);
  185. if (task_woken)
  186. {
  187. portYIELD_FROM_ISR();
  188. }
  189. }
  190. }
  191. esp_err_t rotary_encoder_init(rotary_encoder_info_t * info, gpio_num_t pin_a, gpio_num_t pin_b)
  192. {
  193. esp_err_t err = ESP_OK;
  194. if (info)
  195. {
  196. info->pin_a = pin_a;
  197. info->pin_b = pin_b;
  198. info->table = &_ttable_full[0]; //enable_half_step ? &_ttable_half[0] : &_ttable_full[0];
  199. info->table_state = R_START;
  200. info->state.position = 0;
  201. info->state.direction = ROTARY_ENCODER_DIRECTION_NOT_SET;
  202. // configure GPIOs
  203. gpio_pad_select_gpio(info->pin_a);
  204. gpio_set_pull_mode(info->pin_a, GPIO_PULLUP_ONLY);
  205. gpio_set_direction(info->pin_a, GPIO_MODE_INPUT);
  206. gpio_set_intr_type(info->pin_a, GPIO_INTR_ANYEDGE);
  207. gpio_pad_select_gpio(info->pin_b);
  208. gpio_set_pull_mode(info->pin_b, GPIO_PULLUP_ONLY);
  209. gpio_set_direction(info->pin_b, GPIO_MODE_INPUT);
  210. gpio_set_intr_type(info->pin_b, GPIO_INTR_ANYEDGE);
  211. // install interrupt handlers
  212. gpio_isr_handler_add(info->pin_a, _isr_rotenc, info);
  213. gpio_isr_handler_add(info->pin_b, _isr_rotenc, info);
  214. }
  215. else
  216. {
  217. ESP_LOGE(TAG, "info is NULL");
  218. err = ESP_ERR_INVALID_ARG;
  219. }
  220. return err;
  221. }
  222. esp_err_t rotary_encoder_enable_half_steps(rotary_encoder_info_t * info, bool enable)
  223. {
  224. esp_err_t err = ESP_OK;
  225. if (info)
  226. {
  227. info->table = enable ? &_ttable_half[0] : &_ttable_full[0];
  228. info->table_state = R_START;
  229. }
  230. else
  231. {
  232. ESP_LOGE(TAG, "info is NULL");
  233. err = ESP_ERR_INVALID_ARG;
  234. }
  235. return err;
  236. }
  237. esp_err_t rotary_encoder_flip_direction(rotary_encoder_info_t * info)
  238. {
  239. esp_err_t err = ESP_OK;
  240. if (info)
  241. {
  242. gpio_num_t temp = info->pin_a;
  243. info->pin_a = info->pin_b;
  244. info->pin_b = temp;
  245. }
  246. else
  247. {
  248. ESP_LOGE(TAG, "info is NULL");
  249. err = ESP_ERR_INVALID_ARG;
  250. }
  251. return err;
  252. }
  253. esp_err_t rotary_encoder_uninit(rotary_encoder_info_t * info)
  254. {
  255. esp_err_t err = ESP_OK;
  256. if (info)
  257. {
  258. gpio_isr_handler_remove(info->pin_a);
  259. gpio_isr_handler_remove(info->pin_b);
  260. }
  261. else
  262. {
  263. ESP_LOGE(TAG, "info is NULL");
  264. err = ESP_ERR_INVALID_ARG;
  265. }
  266. return err;
  267. }
  268. QueueHandle_t rotary_encoder_create_queue(void)
  269. {
  270. return xQueueCreate(EVENT_QUEUE_LENGTH, sizeof(rotary_encoder_event_t));
  271. }
  272. esp_err_t rotary_encoder_set_queue(rotary_encoder_info_t * info, QueueHandle_t queue)
  273. {
  274. esp_err_t err = ESP_OK;
  275. if (info)
  276. {
  277. info->queue = queue;
  278. }
  279. else
  280. {
  281. ESP_LOGE(TAG, "info is NULL");
  282. err = ESP_ERR_INVALID_ARG;
  283. }
  284. return err;
  285. }
  286. esp_err_t rotary_encoder_get_state(const rotary_encoder_info_t * info, rotary_encoder_state_t * state)
  287. {
  288. esp_err_t err = ESP_OK;
  289. if (info && state)
  290. {
  291. // make a snapshot of the state
  292. state->position = info->state.position;
  293. state->direction = info->state.direction;
  294. }
  295. else
  296. {
  297. ESP_LOGE(TAG, "info and/or state is NULL");
  298. err = ESP_ERR_INVALID_ARG;
  299. }
  300. return err;
  301. }
  302. esp_err_t rotary_encoder_reset(rotary_encoder_info_t * info)
  303. {
  304. esp_err_t err = ESP_OK;
  305. if (info)
  306. {
  307. info->state.position = 0;
  308. info->state.direction = ROTARY_ENCODER_DIRECTION_NOT_SET;
  309. }
  310. else
  311. {
  312. ESP_LOGE(TAG, "info is NULL");
  313. err = ESP_ERR_INVALID_ARG;
  314. }
  315. return err;
  316. }