|
| 1 | +PCNT - Counter and Encoder |
| 2 | +========================== |
| 3 | + |
| 4 | +The Counter and Encoder use the ESP32 Pulse Counter (PCNT) hardware peripheral, |
| 5 | +see Espressif's `ESP-IDF Pulse Counter documentation. |
| 6 | +<https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/pcnt.html>`_ |
| 7 | + |
| 8 | +For the counter not to miss any pulses, the pulse duration should be longer than one ESP32 APB_CLK cycle (1 / 80 MHz = 12.5 ns). |
| 9 | +The pulses are sampled on the edges of the APB_CLK clock and may be missed if fall between the edges. |
| 10 | +With ideal input signal maximum frequency of measured pulses is APB_CLK / 2 = 80 MHz / 2 = 40 MHz. |
| 11 | + |
| 12 | +The inputs have optional filters that can be used to discard unwanted glitches in the signal. |
| 13 | +The length of ignored pulses is provided in APB_CLK clock cycles. |
| 14 | +* Note: Filter value is a 10-bit value, so the maximum filter value should be limited to 1023. |
| 15 | +Maximum filtered glitches delay is 1023 * 12.5 ns = 12.7875 us. |
| 16 | +Big filter make cutbacks the input frequency: 1 / (12.7875 us * 2) = 39.1 kHz. |
| 17 | +* Note: Do not neglect circuitry methods to reduce noise (right powering and grounding, filtering, shielding, |
| 18 | +short conductors, twisted pair cable, differential signals, etc.). |
| 19 | + |
| 20 | +There is only one interrupt for the peripheral, and that is managed inside the module. |
| 21 | +The user has no interrupt interface, and no interrupts are generated on each pulse. |
| 22 | +Interrupts arrive when the 16-bit hardware counter buffer overflows, so this module has a tiny interrupt footprint |
| 23 | +while providing support for up to 8 simultaneous counters (Encoder or Counter objects). |
| 24 | + |
| 25 | +.. _esp32_machine.Counter: |
| 26 | + |
| 27 | +Counter |
| 28 | +======= |
| 29 | + |
| 30 | +The Pulse Counter service. |
| 31 | + |
| 32 | +Constructor |
| 33 | +----------- |
| 34 | + |
| 35 | +.. class:: Counter(id, src=None, \*, direction=Counter.UP, _src=None, edge=Counter.RISING, filter_ns=0) |
| 36 | + |
| 37 | + The Counter starts to count immediately. Filtering is disabled. |
| 38 | + |
| 39 | + - *id*. Values of *id* depend on a particular port and its hardware. |
| 40 | + Values 0, 1, etc. are commonly used to select hardware block #0, #1, etc. |
| 41 | + |
| 42 | + - *src* is the pulse input :ref:`machine.Pin <machine.Pin>` to be monitored. |
| 43 | + *src* is required in the constructor. |
| 44 | + |
| 45 | + - *direction* specifies the direction to count. Values for this include the constants |
| 46 | + |
| 47 | + - Counter.UP - (default value) and |
| 48 | + - Counter.DOWN to control the direction by software or |
| 49 | + - :ref:`machine.Pin <machine.Pin>` object to control the direction externally. If ``Pin.value()``: |
| 50 | + - 0 - count down, |
| 51 | + - 1 - count up. |
| 52 | + |
| 53 | + - *_src* is the inverse pulse input :ref:`machine.Pin <machine.Pin>` to be monitored. |
| 54 | + If the *_src* keyword is present then the *direction* keyword does not matter. |
| 55 | + *src* and *_src* count in opposite directions, one in the UP direction |
| 56 | + and the other in the DOWN direction, i.e. as an incremental/decremental counter. |
| 57 | + |
| 58 | + - *edge* specifies which edges of the input signal will be counted by Counter: |
| 59 | + |
| 60 | + - Counter.RISING : raise edges, |
| 61 | + - Counter.FALLING : fall edges, |
| 62 | + - Counter.RISING | Counter.FALLING : both edges. |
| 63 | + |
| 64 | + - *filter_ns* specifies a ns-value for the minimal time a signal has to be stable |
| 65 | + at the input to be recognized. The largest value is 12787ns (1023 * 1000000000 / APB_CLK_FREQ). |
| 66 | + The default is 0 – no filter. |
| 67 | + |
| 68 | +Methods |
| 69 | +------- |
| 70 | + |
| 71 | +.. method:: Counter.init(*, src, ...) |
| 72 | + |
| 73 | + Modify the settings of the Counter object. See the **Constructor** for details about the parameters. |
| 74 | + |
| 75 | +.. method:: Counter.deinit() |
| 76 | + |
| 77 | + Stops the Counter, disables interrupts and releases hardware resources used by the counter. |
| 78 | + A Soft Reset involve deinitializing all Encoder objects. |
| 79 | + |
| 80 | +.. method:: Counter.filter([value]) |
| 81 | + |
| 82 | + Get, and optionally set, the filter value. 0 disable filtering. |
| 83 | + |
| 84 | +.. method:: Counter.value([value]) |
| 85 | + |
| 86 | + Get, and optionally set, the counter *value* as a signed 64-bit integer. |
| 87 | + |
| 88 | +.. method:: Counter.irq(handler=None, trigger=Counter.IRQ_MATCH1 | Counter.IRQ_ZERO, value=0) |
| 89 | + |
| 90 | + -*handler* specifies a function is called when the respective *trigger* event happens. |
| 91 | + The callback function *handler* receives a single argument, which is the Counter object. |
| 92 | + All events may share the same callback or have separate callbacks. |
| 93 | + The callback will be disabled, when called with handler=None. Counter.irq() disable all callbacks. |
| 94 | + The event which triggers the callback can be identified with the ``Counter.status()`` method. |
| 95 | + The Counter object which triggers the callback can be identified with the ``Counter.id()`` method. |
| 96 | + |
| 97 | + -*trigger* events may be: |
| 98 | + |
| 99 | + - Counter.IRQ_MATCH1 triggered when the counter matches the match1 value. |
| 100 | + - Counter.IRQ_ZERO triggered when the counter matches the 0. |
| 101 | + |
| 102 | + The default is - trigger=Counter.IRQ_MATCH1 | Counter.IRQ_ZERO. |
| 103 | + The events are triggered when the counter value and match value are identical, but |
| 104 | + callbacks have always a latency. |
| 105 | + |
| 106 | + - *value* sets a counter match1 value. When the counter matches these values, |
| 107 | + a callback function can be called. They are 0 by default. |
| 108 | + |
| 109 | +Attention: ``Counter.irq()`` resets counter to 0. |
| 110 | + |
| 111 | +.. method:: Counter.status() |
| 112 | + |
| 113 | + Returns the event status flags of the recent handled Counter interrupt as a bitmap. |
| 114 | + |
| 115 | +.. method:: Counter.id() |
| 116 | + |
| 117 | + Returns id number. |
| 118 | + |
| 119 | +.. method:: Counter.pause() |
| 120 | + |
| 121 | +.. method:: Counter.resume() |
| 122 | + |
| 123 | +Constants |
| 124 | +--------- |
| 125 | + |
| 126 | +.. data:: Counter.UP |
| 127 | + Counter.DOWN |
| 128 | + |
| 129 | + Selects the counter direction. |
| 130 | + |
| 131 | +.. data:: Counter.RISING |
| 132 | + Counter.FALLING |
| 133 | + |
| 134 | + Selects the counted edges. |
| 135 | + |
| 136 | +.. data:: Counter.IRQ_MATCH1 |
| 137 | + Counter.IRQ_ZERO |
| 138 | + |
| 139 | + Selects callback triggers. |
| 140 | + |
| 141 | +:: |
| 142 | + |
| 143 | + from machine import Counter, Pin |
| 144 | + |
| 145 | + try: |
| 146 | + def irq_handler(self): |
| 147 | + print('irq_handler()', self.id(), self.status(), self.value()) |
| 148 | + |
| 149 | + cnt = Counter(0, src=Pin(17, mode=Pin.IN), direction=Pin(16, mode=Pin.IN)) |
| 150 | + |
| 151 | + cnt.pause() |
| 152 | + flt = cnt.filter() # return current filter value. |
| 153 | + cnt.filter(10_000) # filter delay is 10ms |
| 154 | + c = cnt.value(0) # get current counter value, set the counter value to 0 |
| 155 | + cnt.irq(irq_handler, Counter.IRQ_ZERO) # set irq handler |
| 156 | + cnt.resume() |
| 157 | + |
| 158 | + _c = None |
| 159 | + while True: |
| 160 | + c = cnt.value() # get the counter value |
| 161 | + if _c != c: |
| 162 | + _c = c |
| 163 | + print('Counter =', c) |
| 164 | + finally: |
| 165 | + cnt.deinit() # free the input pins and counter. |
| 166 | + |
| 167 | + |
| 168 | +.. _esp32_machine.Encoder: |
| 169 | + |
| 170 | +Encoder |
| 171 | +======= |
| 172 | + |
| 173 | +This class provides a Quadrature Incremental Encoder service. |
| 174 | +See `Quadrature encoder outputs. |
| 175 | +<https://en.wikipedia.org/wiki/Incremental_encoder#Quadrature_outputs>`_ |
| 176 | + |
| 177 | +.. image:: img/quad.png |
| 178 | + :width: 397px |
| 179 | + |
| 180 | +Constructor |
| 181 | +----------- |
| 182 | + |
| 183 | +.. class:: Encoder(id, phase_a=None, phase_b=None, \*, x124=4, filter_ns=0, match1=0) |
| 184 | + |
| 185 | + The Encoder starts to count immediately. Filtering is disabled. |
| 186 | + |
| 187 | + - *id*. Values of *id* depend on a particular port and its hardware. |
| 188 | + Values 0, 1, etc. are commonly used to select hardware block #0, #1, etc. |
| 189 | + |
| 190 | + - *phase_a*, *phase_b* are input pins :ref:`machine.Pin <machine.Pin>` for monitoring of quadrature encoder pulses. |
| 191 | + They are required in the constructor. |
| 192 | + |
| 193 | + - *x124* is a hardware multiplier, possible values is 1, 2, 4. The default value is 4. |
| 194 | + More info in `Quadrature decoder state table <https://en.wikipedia.org/wiki/Incremental_encoder#Quadrature_decoder>`_. |
| 195 | + When more Encoder resolution is needed, it is possible for the encoder to count the leading |
| 196 | + and trailing edges of the quadrature encoder’s pulse train from one channel, |
| 197 | + which doubles (x2) the number of pulses. Counting both leading and trailing edges |
| 198 | + of both channels (A and B channels) of a quadrature encoder will quadruple (x4) the number of pulses: |
| 199 | + |
| 200 | + - 1 - count the leading(or trailing) edges from one phase channel. |
| 201 | + - 2 - count the leading and trailing edges from one phase channel. |
| 202 | + - 4 - count both leading and trailing edges of both phase channels. |
| 203 | + |
| 204 | + These keywords are the same as the Counter keywords, see above: |
| 205 | + - *filter_ns* |
| 206 | + - *match1* |
| 207 | + |
| 208 | +Methods |
| 209 | +------- |
| 210 | + |
| 211 | +.. method:: Encoder.init(*, phase_a, ...) |
| 212 | + |
| 213 | + Modify the settings of the Encoder object. See the **Constructor** for details about the parameters. |
| 214 | + |
| 215 | +The Encoder has the same methods as the Counter and differs only |
| 216 | +in the constructor and internal hardware PCNT initialization. |
| 217 | + |
| 218 | +Constants |
| 219 | +--------- |
| 220 | + |
| 221 | +.. data:: Encoder.IRQ_MATCH1 |
| 222 | + Encoder.IRQ_ZERO |
| 223 | + |
| 224 | + Selects callback triggers. |
| 225 | + |
| 226 | +:: |
| 227 | + |
| 228 | + from machine import Encoder, Pin |
| 229 | + |
| 230 | + try: |
| 231 | + n = 0 |
| 232 | + def irq_handler1(self): |
| 233 | + n -= 1 |
| 234 | + print('irq_handler1()', self.id(), self.value(), n) |
| 235 | + |
| 236 | + def irq_handler2(self): |
| 237 | + n += 1 |
| 238 | + print('irq_handler2()', self.id(), self.value(), n) |
| 239 | + |
| 240 | + enc = Encoder(0, phase_a=Pin(17, mode=Pin.IN), phase_b=Pin(16, mode=Pin.IN), match1=1000) |
| 241 | + |
| 242 | + enc.pause() |
| 243 | + flt = enc.filter() # return current filter value. |
| 244 | + enc.filter(10_000) # filter delay is 10ms |
| 245 | + c = enc.value(0) # get current encoder value, set the encoder value to 0 |
| 246 | + enc.irq(irq_handler1, Encoder.IRQ_MATCH1) # set irq handler |
| 247 | + enc.resume() |
| 248 | + |
| 249 | + _c = None |
| 250 | + while True: |
| 251 | + c = enc.value() # get the encoder value |
| 252 | + if _c != c: |
| 253 | + _c = c |
| 254 | + print('Encoder =', c) |
| 255 | + finally: |
| 256 | + enc.deinit() # free the input pins and encoder. |
0 commit comments