1 // Copyright 2020 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/cdefs.h>
17 #include "esp_compiler.h"
18 #include "esp_log.h"
19 #include "driver/pcnt.h"
20 #include "sys/lock.h"
21 #include "hal/pcnt_hal.h"
22 #include "rotary_encoder.h"
23
24 static const char *TAG = "rotary_encoder";
25
26 #define ROTARY_CHECK(a, msg, tag, ret, ...) \
27 do { \
28 if (unlikely(!(a))) { \
29 ESP_LOGE(TAG, "%s(%d): " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
30 ret_code = ret; \
31 goto tag; \
32 } \
33 } while (0)
34
35 #define EC11_PCNT_DEFAULT_HIGH_LIMIT (100)
36 #define EC11_PCNT_DEFAULT_LOW_LIMIT (-100)
37
38 // A flag to identify if pcnt isr service has been installed.
39 static bool is_pcnt_isr_service_installed = false;
40 // A lock to avoid pcnt isr service being installed twice in multiple threads.
41 static _lock_t isr_service_install_lock;
42 #define LOCK_ACQUIRE() _lock_acquire(&isr_service_install_lock)
43 #define LOCK_RELEASE() _lock_release(&isr_service_install_lock)
44
45 typedef struct {
46 int accumu_count;
47 rotary_encoder_t parent;
48 pcnt_unit_t pcnt_unit;
49 } ec11_t;
50
ec11_set_glitch_filter(rotary_encoder_t * encoder,uint32_t max_glitch_us)51 static esp_err_t ec11_set_glitch_filter(rotary_encoder_t *encoder, uint32_t max_glitch_us)
52 {
53 esp_err_t ret_code = ESP_OK;
54 ec11_t *ec11 = __containerof(encoder, ec11_t, parent);
55
56 /* Configure and enable the input filter */
57 ROTARY_CHECK(pcnt_set_filter_value(ec11->pcnt_unit, max_glitch_us * 80) == ESP_OK, "set glitch filter failed", err, ESP_FAIL);
58
59 if (max_glitch_us) {
60 pcnt_filter_enable(ec11->pcnt_unit);
61 } else {
62 pcnt_filter_disable(ec11->pcnt_unit);
63 }
64
65 return ESP_OK;
66 err:
67 return ret_code;
68 }
69
ec11_start(rotary_encoder_t * encoder)70 static esp_err_t ec11_start(rotary_encoder_t *encoder)
71 {
72 ec11_t *ec11 = __containerof(encoder, ec11_t, parent);
73 pcnt_counter_resume(ec11->pcnt_unit);
74 return ESP_OK;
75 }
76
ec11_stop(rotary_encoder_t * encoder)77 static esp_err_t ec11_stop(rotary_encoder_t *encoder)
78 {
79 ec11_t *ec11 = __containerof(encoder, ec11_t, parent);
80 pcnt_counter_pause(ec11->pcnt_unit);
81 return ESP_OK;
82 }
83
ec11_get_counter_value(rotary_encoder_t * encoder)84 static int ec11_get_counter_value(rotary_encoder_t *encoder)
85 {
86 ec11_t *ec11 = __containerof(encoder, ec11_t, parent);
87 int16_t val = 0;
88 pcnt_get_counter_value(ec11->pcnt_unit, &val);
89 return val + ec11->accumu_count;
90 }
91
ec11_del(rotary_encoder_t * encoder)92 static esp_err_t ec11_del(rotary_encoder_t *encoder)
93 {
94 ec11_t *ec11 = __containerof(encoder, ec11_t, parent);
95 free(ec11);
96 return ESP_OK;
97 }
98
ec11_pcnt_overflow_handler(void * arg)99 static void ec11_pcnt_overflow_handler(void *arg)
100 {
101 ec11_t *ec11 = (ec11_t *)arg;
102 uint32_t status = 0;
103 pcnt_get_event_status(ec11->pcnt_unit, &status);
104
105 if (status & PCNT_EVT_H_LIM) {
106 ec11->accumu_count += EC11_PCNT_DEFAULT_HIGH_LIMIT;
107 } else if (status & PCNT_EVT_L_LIM) {
108 ec11->accumu_count += EC11_PCNT_DEFAULT_LOW_LIMIT;
109 }
110 }
111
rotary_encoder_new_ec11(const rotary_encoder_config_t * config,rotary_encoder_t ** ret_encoder)112 esp_err_t rotary_encoder_new_ec11(const rotary_encoder_config_t *config, rotary_encoder_t **ret_encoder)
113 {
114 esp_err_t ret_code = ESP_OK;
115 ec11_t *ec11 = NULL;
116
117 ROTARY_CHECK(config, "configuration can't be null", err, ESP_ERR_INVALID_ARG);
118 ROTARY_CHECK(ret_encoder, "can't assign context to null", err, ESP_ERR_INVALID_ARG);
119
120 ec11 = calloc(1, sizeof(ec11_t));
121 ROTARY_CHECK(ec11, "allocate context memory failed", err, ESP_ERR_NO_MEM);
122
123 ec11->pcnt_unit = (pcnt_unit_t)(config->dev);
124
125 // Configure channel 0
126 pcnt_config_t dev_config = {
127 .pulse_gpio_num = config->phase_a_gpio_num,
128 .ctrl_gpio_num = config->phase_b_gpio_num,
129 .channel = PCNT_CHANNEL_0,
130 .unit = ec11->pcnt_unit,
131 .pos_mode = PCNT_COUNT_DEC,
132 .neg_mode = PCNT_COUNT_INC,
133 .lctrl_mode = PCNT_MODE_REVERSE,
134 .hctrl_mode = PCNT_MODE_KEEP,
135 .counter_h_lim = EC11_PCNT_DEFAULT_HIGH_LIMIT,
136 .counter_l_lim = EC11_PCNT_DEFAULT_LOW_LIMIT,
137 };
138 ROTARY_CHECK(pcnt_unit_config(&dev_config) == ESP_OK, "config pcnt channel 0 failed", err, ESP_FAIL);
139
140 // Configure channel 1
141 dev_config.pulse_gpio_num = config->phase_b_gpio_num;
142 dev_config.ctrl_gpio_num = config->phase_a_gpio_num;
143 dev_config.channel = PCNT_CHANNEL_1;
144 dev_config.pos_mode = PCNT_COUNT_INC;
145 dev_config.neg_mode = PCNT_COUNT_DEC;
146 ROTARY_CHECK(pcnt_unit_config(&dev_config) == ESP_OK, "config pcnt channel 1 failed", err, ESP_FAIL);
147
148 // PCNT pause and reset value
149 pcnt_counter_pause(ec11->pcnt_unit);
150 pcnt_counter_clear(ec11->pcnt_unit);
151
152
153 // register interrupt handler in a thread-safe way
154 LOCK_ACQUIRE();
155 if (!is_pcnt_isr_service_installed) {
156 ROTARY_CHECK(pcnt_isr_service_install(0) == ESP_OK, "install isr service failed", err, ESP_FAIL);
157 // make sure pcnt isr service won't be installed more than one time
158 is_pcnt_isr_service_installed = true;
159 }
160 LOCK_RELEASE();
161
162 pcnt_isr_handler_add(ec11->pcnt_unit, ec11_pcnt_overflow_handler, ec11);
163
164 pcnt_event_enable(ec11->pcnt_unit, PCNT_EVT_H_LIM);
165 pcnt_event_enable(ec11->pcnt_unit, PCNT_EVT_L_LIM);
166
167 ec11->parent.del = ec11_del;
168 ec11->parent.start = ec11_start;
169 ec11->parent.stop = ec11_stop;
170 ec11->parent.set_glitch_filter = ec11_set_glitch_filter;
171 ec11->parent.get_counter_value = ec11_get_counter_value;
172
173 *ret_encoder = &(ec11->parent);
174 return ESP_OK;
175 err:
176 if (ec11) {
177 free(ec11);
178 }
179 return ret_code;
180 }
181