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