Skip to content

Commit 9a70bbe

Browse files
committed
Initial Commit
0 parents  commit 9a70bbe

File tree

7 files changed

+229
-0
lines changed

7 files changed

+229
-0
lines changed

LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2024 vickash
2+
3+
MIT License
4+
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
"Software"), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
12+
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# RotaryEncoderPCNT
2+
3+
This is a 2-channel rotary (quadrature) encoder implementation, for the ESP32, on the Arduino platform. It uses the ESP32's built in pulse counter (PCNT) peripheral.
4+
5+
Simply create a new `RotaryEncoderPCNT` object with the encoder's A and B pins, then call `.position()` to return the current position. The PCNT unit is set up to keep the encoder object updated in the background, handling interrupts, internal counter overflow, etc. for you.
6+
7+
Note: `.position()` returns a signed 32-bit integer. If you expect to overflow that, you need to handle it.
8+
9+
### Compatibility
10+
- Requires Arduino ESP32 Core 3.0 or higher
11+
- Incompatible with ESP32-C2 and ESP32-C3, as they don't have PCNT units.
12+
13+
### Most Basic Example
14+
```
15+
#include <RotaryEncoderPCNT.h>
16+
17+
RotaryEncoderPCNT encoder(25, 12);
18+
19+
void setup() {
20+
Serial.begin(115200);
21+
}
22+
23+
void loop(){
24+
Serial.println(encoder.position());
25+
delay(200);
26+
}
27+
```

examples/EncoderDemo/EncoderDemo.ino

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <RotaryEncoderPCNT.h>
2+
3+
// Constructor args are: pinA/Clock, pinB/Data, start position, glitch filter time in ns.
4+
// Start position and filter time are optional. Defaults are 0 and 1000ns respectively.
5+
//
6+
// RotaryEncoderPCNT encoder(25, 12, 0, 1000);
7+
// RotaryEncoderPCNT encoder(25, 12, 0);
8+
RotaryEncoderPCNT encoder(25, 12);
9+
10+
int old_position;
11+
int position;
12+
13+
void setup() {
14+
Serial.begin(115200);
15+
16+
// Change position after initialization.
17+
// encoder.setPosition(128);
18+
19+
// Show initial position.
20+
old_position = encoder.position();
21+
Serial.println(old_position);
22+
}
23+
24+
void loop() {
25+
// Show position when it changes.
26+
position = encoder.position();
27+
if(position != old_position){
28+
Serial.println(position);
29+
old_position = position;
30+
}
31+
delay(5);
32+
}

library.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "RotaryEncoderPCNT",
3+
"description": "ESP32 rotary encoder implementation, using pulse counter (PCNT) peripheral.",
4+
"keywords": "rotary, encoder, esp32, input, iot",
5+
"authors": {
6+
"name": "vickash",
7+
"url": "https://github.com/vickash/RotaryEncoderPCNT.git"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "https://github.com/vickash/RotaryEncoderPCNT.git"
12+
},
13+
"version": "1.0",
14+
"examples": "examples/*/*.ino",
15+
"frameworks": "arduino",
16+
"platforms": [
17+
"espressif32",
18+
]
19+
}

library.properties

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name=RotaryEncoderPCNT
2+
version=1.0.0
3+
author=vickash
4+
maintainer=vickash <[email protected]>
5+
sentence=ESP32 rotary encoder implementation, using pulse counter (PCNT) peripheral.
6+
paragraph=Requires Arduino ESP32 core 3.0 or higher. Not supported on ESP32-C2 or ESP32-C3 as they do not have PCNT units.
7+
category=Signal Input/Output
8+
url=https://github.com/vickash/RotaryEncoderPCNT
9+
architectures=esp32
10+
includes=RotaryEncoderPCNT.h

src/RotaryEncoderPCNT.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#include "RotaryEncoderPCNT.h"
2+
3+
RotaryEncoderPCNT::RotaryEncoderPCNT(int a, int b, int start_pos, uint16_t glitch_ns){
4+
install(a, b, start_pos, glitch_ns);
5+
}
6+
7+
RotaryEncoderPCNT::RotaryEncoderPCNT(int a, int b, int start_pos){
8+
install(a, b, start_pos, GLITCH_NS_DEFAULT);
9+
}
10+
11+
RotaryEncoderPCNT::RotaryEncoderPCNT(int a, int b){
12+
install(a, b, START_POS_DEFAULT, GLITCH_NS_DEFAULT);
13+
}
14+
15+
void RotaryEncoderPCNT::install(int a, int b, int start_pos, uint16_t glitch_ns){
16+
offset = start_pos;
17+
pin_a = a;
18+
pin_b = b;
19+
20+
// Unit config
21+
pcnt_unit_config_t unit_config = {
22+
.low_limit = low_limit,
23+
.high_limit = high_limit,
24+
};
25+
unit_config.flags.accum_count = true;
26+
27+
// Create unit
28+
ESP_ERROR_CHECK(pcnt_new_unit(&unit_config, &unit));
29+
30+
// Set watch points at low and high limits to auto-accumulate overflows.
31+
pcnt_unit_add_watch_point(unit, low_limit);
32+
pcnt_unit_add_watch_point(unit, high_limit);
33+
34+
// Glitch filter setup
35+
pcnt_glitch_filter_config_t filter_config = {
36+
.max_glitch_ns = glitch_ns,
37+
};
38+
ESP_ERROR_CHECK(pcnt_unit_set_glitch_filter(unit, &filter_config));
39+
40+
// Channel A setup
41+
pcnt_chan_config_t chan_a_config = {
42+
.edge_gpio_num = pin_a,
43+
.level_gpio_num = pin_b,
44+
};
45+
ESP_ERROR_CHECK(pcnt_new_channel(unit, &chan_a_config, &chan_a));
46+
47+
// Channel B setup
48+
pcnt_chan_config_t chan_b_config = {
49+
.edge_gpio_num = pin_b,
50+
.level_gpio_num = pin_a,
51+
};
52+
ESP_ERROR_CHECK(pcnt_new_channel(unit, &chan_b_config, &chan_b));
53+
54+
// Set edge and level actions for both channels
55+
ESP_ERROR_CHECK(pcnt_channel_set_edge_action(chan_a, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE));
56+
ESP_ERROR_CHECK(pcnt_channel_set_level_action(chan_a, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));
57+
ESP_ERROR_CHECK(pcnt_channel_set_edge_action(chan_b, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_DECREASE));
58+
ESP_ERROR_CHECK(pcnt_channel_set_level_action(chan_b, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));
59+
60+
// Enable, clear and start the PCNT unit.
61+
ESP_ERROR_CHECK(pcnt_unit_enable(unit));
62+
ESP_ERROR_CHECK(pcnt_unit_clear_count(unit));
63+
ESP_ERROR_CHECK(pcnt_unit_start(unit));
64+
}
65+
66+
RotaryEncoderPCNT::~RotaryEncoderPCNT(){
67+
// Free PCNT resources when destroyed.
68+
pcnt_unit_disable(unit);
69+
pcnt_del_channel(chan_a);
70+
pcnt_del_channel(chan_b);
71+
pcnt_del_unit(unit);
72+
}
73+
74+
int RotaryEncoderPCNT::position(){
75+
pcnt_unit_get_count(unit, &count);
76+
return (count + offset);
77+
}
78+
79+
void RotaryEncoderPCNT::setPosition(int pos){
80+
offset = pos;
81+
pcnt_unit_get_count(unit, &count);
82+
zero();
83+
}
84+
85+
void RotaryEncoderPCNT::zero(){
86+
pcnt_unit_clear_count(unit);
87+
}

src/RotaryEncoderPCNT.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#ifndef ROTARY_ENCODER_PCNT_H
2+
#define ROTARY_ENCODER_PCNT_H
3+
4+
#include "driver/pulse_cnt.h"
5+
6+
#define START_POS_DEFAULT 0
7+
#define GLITCH_NS_DEFAULT 1000
8+
9+
class RotaryEncoderPCNT {
10+
public:
11+
RotaryEncoderPCNT(int a, int b, int start_pos, uint16_t glitch_ns);
12+
RotaryEncoderPCNT(int a, int b, int start_pos);
13+
RotaryEncoderPCNT(int a, int b);
14+
~RotaryEncoderPCNT();
15+
int position();
16+
void setPosition(int pos);
17+
void zero();
18+
19+
private:
20+
void install(int a, int b, int start_pos, uint16_t glitch_ns);
21+
22+
pcnt_unit_handle_t unit;
23+
pcnt_channel_handle_t chan_a;
24+
pcnt_channel_handle_t chan_b;
25+
uint8_t pin_a;
26+
uint8_t pin_b;
27+
int count = 0;
28+
int offset = 0;
29+
int16_t low_limit = INT16_MIN;
30+
int16_t high_limit = INT16_MAX;
31+
};
32+
#endif

0 commit comments

Comments
 (0)