Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
6.8 KiB

  1. /* ---------------------------- Original copyright -----------------------------
  2. *
  3. * Encoder Library, for measuring quadrature encoded signals
  4. * http://www.pjrc.com/teensy/td_libs_Encoder.html
  5. * Copyright (c) 2011,2013 PJRC.COM, LLC - Paul Stoffregen <paul@pjrc.com>
  6. *
  7. * Version 1.2 - fix -2 bug in C-only code
  8. * Version 1.1 - expand to support boards with up to 60 interrupts
  9. * Version 1.0 - initial release
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy
  12. * of this software and associated documentation files (the "Software"), to deal
  13. * in the Software without restriction, including without limitation the rights
  14. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. * copies of the Software, and to permit persons to whom the Software is
  16. * furnished to do so, subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included in
  19. * all copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  27. * THE SOFTWARE.
  28. *
  29. * -----------------------------------------------------------------------------
  30. *
  31. * Encoder.h, updated for ESP8266 use in ESPurna. Other hardware is not supported.
  32. *
  33. * - Added ESP-specific attributes to ISR handlers to place them in IRAM.
  34. * - Reduced per-encoder structure sizes - only 5 Encoders can be used on ESP8266,
  35. * and we can directly reference pin number instead of storing both register and bitmask
  36. *
  37. */
  38. #pragma once
  39. // _______ _______
  40. // Pin1 ______| |_______| |______ Pin1
  41. // negative <--- _______ _______ __ --> positive
  42. // Pin2 __| |_______| |_______| Pin2
  43. // new new old old
  44. // pin2 pin1 pin2 pin1 Result
  45. // ---- ---- ---- ---- ------
  46. // 0 0 0 0 no movement
  47. // 0 0 0 1 +1
  48. // 0 0 1 0 -1
  49. // 0 0 1 1 +2 (assume pin1 edges only)
  50. // 0 1 0 0 -1
  51. // 0 1 0 1 no movement
  52. // 0 1 1 0 -2 (assume pin1 edges only)
  53. // 0 1 1 1 +1
  54. // 1 0 0 0 +1
  55. // 1 0 0 1 -2 (assume pin1 edges only)
  56. // 1 0 1 0 no movement
  57. // 1 0 1 1 -1
  58. // 1 1 0 0 +2 (assume pin1 edges only)
  59. // 1 1 0 1 -1
  60. // 1 1 1 0 +1
  61. // 1 1 1 1 no movement
  62. namespace EncoderLibrary {
  63. typedef struct {
  64. uint8_t pin1;
  65. uint8_t pin2;
  66. uint8_t state;
  67. int32_t position;
  68. } encoder_values_t;
  69. constexpr const unsigned char ENCODERS_MAXIMUM {5u};
  70. encoder_values_t * EncoderValues[ENCODERS_MAXIMUM] = {nullptr};
  71. uint8_t _encoderFindStorage() {
  72. for (uint8_t i = 0; i < ENCODERS_MAXIMUM; i++) {
  73. if (EncoderValues[i] == nullptr) {
  74. return i;
  75. }
  76. }
  77. return ENCODERS_MAXIMUM;
  78. }
  79. void _encoderCleanStorage(uint8_t pin1, uint8_t pin2) {
  80. for (uint8_t i = 0; i < ENCODERS_MAXIMUM; i++) {
  81. if (EncoderValues[i] == nullptr) continue;
  82. if (((EncoderValues[i])->pin1 == pin1) && ((EncoderValues[i])->pin2 == pin2)) {
  83. EncoderValues[i] = nullptr;
  84. break;
  85. }
  86. }
  87. }
  88. // update() is not meant to be called from outside Encoder,
  89. // but it is public to allow static interrupt routines.
  90. void ICACHE_RAM_ATTR update(encoder_values_t *target) {
  91. uint8_t p1val = GPIP(target->pin1);
  92. uint8_t p2val = GPIP(target->pin2);
  93. uint8_t state = target->state & 3;
  94. if (p1val) state |= 4;
  95. if (p2val) state |= 8;
  96. target->state = (state >> 2);
  97. switch (state) {
  98. case 1: case 7: case 8: case 14:
  99. target->position++;
  100. return;
  101. case 2: case 4: case 11: case 13:
  102. target->position--;
  103. return;
  104. case 3: case 12:
  105. target->position += 2;
  106. return;
  107. case 6: case 9:
  108. target->position -= 2;
  109. return;
  110. }
  111. }
  112. // 2 pins per encoder, 1 isr per encoder
  113. void ICACHE_RAM_ATTR isr0() { update(EncoderValues[0]); }
  114. void ICACHE_RAM_ATTR isr1() { update(EncoderValues[1]); }
  115. void ICACHE_RAM_ATTR isr2() { update(EncoderValues[2]); }
  116. void ICACHE_RAM_ATTR isr3() { update(EncoderValues[3]); }
  117. void ICACHE_RAM_ATTR isr4() { update(EncoderValues[4]); }
  118. constexpr void (*_isr_funcs[5])() = {
  119. isr0, isr1, isr2, isr3, isr4
  120. };
  121. class Encoder {
  122. private:
  123. encoder_values_t values;
  124. public:
  125. Encoder(uint8_t pin1, uint8_t pin2) {
  126. values.pin1 = pin1;
  127. values.pin2 = pin2;
  128. pinMode(values.pin1, INPUT_PULLUP);
  129. pinMode(values.pin2, INPUT_PULLUP);
  130. values.position = 0;
  131. // allow time for a passive R-C filter to charge
  132. // through the pullup resistors, before reading
  133. // the initial state
  134. delayMicroseconds(2000);
  135. uint8_t current = 0;
  136. if (GPIP(values.pin1)) {
  137. current |= 1;
  138. }
  139. if (GPIP(values.pin2)) {
  140. current |= 2;
  141. }
  142. values.state = current;
  143. attach();
  144. }
  145. ~Encoder() {
  146. detach();
  147. }
  148. uint8_t pin1() {
  149. return values.pin1;
  150. }
  151. uint8_t pin2() {
  152. return values.pin2;
  153. }
  154. int32_t read() {
  155. noInterrupts();
  156. update(&values);
  157. int32_t ret = values.position;
  158. interrupts();
  159. return ret;
  160. }
  161. void write(int32_t position) {
  162. noInterrupts();
  163. values.position = position;
  164. interrupts();
  165. }
  166. bool attach() {
  167. uint8_t index = _encoderFindStorage();
  168. if (index >= ENCODERS_MAXIMUM) return false;
  169. EncoderValues[index] = &values;
  170. attachInterrupt(values.pin1, _isr_funcs[index], CHANGE);
  171. attachInterrupt(values.pin2, _isr_funcs[index], CHANGE);
  172. return true;
  173. }
  174. void detach() {
  175. noInterrupts();
  176. _encoderCleanStorage(values.pin1, values.pin2);
  177. detachInterrupt(values.pin1);
  178. detachInterrupt(values.pin2);
  179. interrupts();
  180. }
  181. };
  182. }
  183. using EncoderLibrary::Encoder;