Skip to content

Commit 1b78f2d

Browse files
valeriosetticarlescufi
authored andcommitted
drivers: sensor: adding driver for STM32 quadrature encoder
This driver configures STM32's internal timers in order to use the quadrature encoder mode. Signed-off-by: Valerio Setti <[email protected]>
1 parent cce22da commit 1b78f2d

File tree

5 files changed

+183
-0
lines changed

5 files changed

+183
-0
lines changed

drivers/sensor/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ add_subdirectory_ifdef(CONFIG_OPT3001 opt3001)
7777
add_subdirectory_ifdef(CONFIG_PMS7003 pms7003)
7878
add_subdirectory_ifdef(CONFIG_QDEC_NRFX qdec_nrfx)
7979
add_subdirectory_ifdef(CONFIG_QDEC_SAM qdec_sam)
80+
add_subdirectory_ifdef(CONFIG_QDEC_STM32 qdec_stm32)
8081
add_subdirectory_ifdef(CONFIG_TEMP_NRF5 nrf5)
8182
add_subdirectory_ifdef(CONFIG_SBS_GAUGE sbs_gauge)
8283
add_subdirectory_ifdef(CONFIG_SGP40 sgp40)

drivers/sensor/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ source "drivers/sensor/qdec_nrfx/Kconfig"
196196

197197
source "drivers/sensor/qdec_sam/Kconfig"
198198

199+
source "drivers/sensor/qdec_stm32/Kconfig"
200+
199201
source "drivers/sensor/sbs_gauge/Kconfig"
200202

201203
source "drivers/sensor/sgp40/Kconfig"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright (c) 2022, Valerio Setti <[email protected]>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
zephyr_library()
6+
7+
zephyr_library_sources(qdec_stm32.c)

drivers/sensor/qdec_stm32/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright (c) 2022, Valerio Setti <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
5+
config QDEC_STM32
6+
bool "STM32 QDEC driver"
7+
default y
8+
depends on DT_HAS_ST_STM32_QDEC_ENABLED
9+
select USE_STM32_LL_TIM
10+
help
11+
STM32 family Quadrature Decoder driver.
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright (c) 2022, Valerio Setti <[email protected]>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT st_stm32_qdec
8+
9+
/** @file
10+
* @brief STM32 family Quadrature Decoder (QDEC) driver.
11+
*/
12+
13+
#include <errno.h>
14+
15+
#include <zephyr/sys/__assert.h>
16+
#include <zephyr/sys/util.h>
17+
#include <zephyr/device.h>
18+
#include <zephyr/init.h>
19+
#include <zephyr/drivers/sensor.h>
20+
#include <zephyr/drivers/pinctrl.h>
21+
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
22+
#include <zephyr/logging/log.h>
23+
24+
#include <stm32_ll_tim.h>
25+
26+
LOG_MODULE_REGISTER(qdec_stm32, CONFIG_SENSOR_LOG_LEVEL);
27+
28+
/* Device constant configuration parameters */
29+
struct qdec_stm32_dev_cfg {
30+
const struct pinctrl_dev_config *pin_config;
31+
struct stm32_pclken pclken;
32+
TIM_TypeDef *timer_inst;
33+
bool is_input_polarity_inverted;
34+
uint8_t input_filtering_level;
35+
uint32_t counts_per_revolution;
36+
};
37+
38+
/* Device run time data */
39+
struct qdec_stm32_dev_data {
40+
int32_t position;
41+
};
42+
43+
static int qdec_stm32_fetch(const struct device *dev, enum sensor_channel chan)
44+
{
45+
struct qdec_stm32_dev_data *dev_data = dev->data;
46+
const struct qdec_stm32_dev_cfg *dev_cfg = dev->config;
47+
uint32_t counter_value;
48+
49+
if ((chan != SENSOR_CHAN_ALL) && (chan != SENSOR_CHAN_ROTATION)) {
50+
return -ENOTSUP;
51+
}
52+
53+
/* We're only interested in the remainder between the current counter value and
54+
* counts_per_revolution. The integer part represents an entire rotation so it
55+
* can be ignored
56+
*/
57+
counter_value = LL_TIM_GetCounter(dev_cfg->timer_inst) % dev_cfg->counts_per_revolution;
58+
dev_data->position = (counter_value * 360) / dev_cfg->counts_per_revolution;
59+
60+
return 0;
61+
}
62+
63+
static int qdec_stm32_get(const struct device *dev, enum sensor_channel chan,
64+
struct sensor_value *val)
65+
{
66+
struct qdec_stm32_dev_data *const dev_data = dev->data;
67+
68+
if (chan == SENSOR_CHAN_ROTATION) {
69+
val->val1 = dev_data->position;
70+
val->val2 = 0;
71+
} else {
72+
return -ENOTSUP;
73+
}
74+
75+
return 0;
76+
}
77+
78+
static int qdec_stm32_initialize(const struct device *dev)
79+
{
80+
const struct qdec_stm32_dev_cfg *const dev_cfg = dev->config;
81+
int retval;
82+
LL_TIM_ENCODER_InitTypeDef init_props;
83+
uint32_t max_counter_value;
84+
85+
retval = pinctrl_apply_state(dev_cfg->pin_config, PINCTRL_STATE_DEFAULT);
86+
if (retval < 0) {
87+
return retval;
88+
}
89+
90+
if (!device_is_ready(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE))) {
91+
LOG_ERR("Clock control device not ready");
92+
return -ENODEV;
93+
}
94+
95+
retval = clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE),
96+
(clock_control_subsys_t)&dev_cfg->pclken);
97+
if (retval < 0) {
98+
LOG_ERR("Could not initialize clock");
99+
return retval;
100+
}
101+
102+
if (dev_cfg->counts_per_revolution < 1) {
103+
LOG_ERR("Invalid number of counts per revolution (%d)",
104+
dev_cfg->counts_per_revolution);
105+
return -EINVAL;
106+
}
107+
108+
LL_TIM_ENCODER_StructInit(&init_props);
109+
110+
if (dev_cfg->is_input_polarity_inverted) {
111+
init_props.IC1ActiveInput = LL_TIM_IC_POLARITY_FALLING;
112+
init_props.IC2ActiveInput = LL_TIM_IC_POLARITY_FALLING;
113+
}
114+
115+
init_props.IC1Filter = dev_cfg->input_filtering_level * LL_TIM_IC_FILTER_FDIV1_N2;
116+
init_props.IC2Filter = dev_cfg->input_filtering_level * LL_TIM_IC_FILTER_FDIV1_N2;
117+
118+
/* Ensure that the counter will always count up to a multiple of counts_per_revolution */
119+
if (IS_TIM_32B_COUNTER_INSTANCE(dev_cfg->timer_inst)) {
120+
max_counter_value = UINT32_MAX - (UINT32_MAX % dev_cfg->counts_per_revolution) - 1;
121+
} else {
122+
max_counter_value = UINT16_MAX - (UINT16_MAX % dev_cfg->counts_per_revolution) - 1;
123+
}
124+
LL_TIM_SetAutoReload(dev_cfg->timer_inst, max_counter_value);
125+
126+
if (LL_TIM_ENCODER_Init(dev_cfg->timer_inst, &init_props) != SUCCESS) {
127+
LOG_ERR("Initalization failed");
128+
return -EIO;
129+
}
130+
131+
LL_TIM_EnableCounter(dev_cfg->timer_inst);
132+
133+
return 0;
134+
}
135+
136+
static const struct sensor_driver_api qdec_stm32_driver_api = {
137+
.sample_fetch = qdec_stm32_fetch,
138+
.channel_get = qdec_stm32_get,
139+
};
140+
141+
#define QDEC_STM32_INIT(n) \
142+
PINCTRL_DT_INST_DEFINE(n); \
143+
static const struct qdec_stm32_dev_cfg qdec##n##_stm32_config = { \
144+
.pin_config = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
145+
.timer_inst = ((TIM_TypeDef *)DT_REG_ADDR(DT_INST_PARENT(n))), \
146+
.pclken = { \
147+
.bus = DT_CLOCKS_CELL(DT_INST_PARENT(n), bus), \
148+
.enr = DT_CLOCKS_CELL(DT_INST_PARENT(n), bits) \
149+
}, \
150+
.is_input_polarity_inverted = DT_INST_PROP(n, st_input_polarity_inverted), \
151+
.input_filtering_level = DT_INST_PROP(n, st_input_filter_level), \
152+
.counts_per_revolution = DT_INST_PROP(n, st_counts_per_revolution), \
153+
}; \
154+
\
155+
static struct qdec_stm32_dev_data qdec##n##_stm32_data; \
156+
\
157+
DEVICE_DT_INST_DEFINE(n, qdec_stm32_initialize, NULL, \
158+
&qdec##n##_stm32_data, &qdec##n##_stm32_config, \
159+
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
160+
&qdec_stm32_driver_api);
161+
162+
DT_INST_FOREACH_STATUS_OKAY(QDEC_STM32_INIT)

0 commit comments

Comments
 (0)