1 /*
2  * Copyright (c) 2022, Valerio Setti <vsetti@baylibre.com>
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 	uint32_t encoder_mode;
34 	bool is_input_polarity_inverted;
35 	uint8_t input_filtering_level;
36 	uint32_t counts_per_revolution;
37 };
38 
39 /* Device run time data */
40 struct qdec_stm32_dev_data {
41 	int32_t position;
42 };
43 
qdec_stm32_fetch(const struct device * dev,enum sensor_channel chan)44 static int qdec_stm32_fetch(const struct device *dev, enum sensor_channel chan)
45 {
46 	struct qdec_stm32_dev_data *dev_data = dev->data;
47 	const struct qdec_stm32_dev_cfg *dev_cfg = dev->config;
48 	uint32_t counter_value;
49 
50 	if ((chan != SENSOR_CHAN_ALL) && (chan != SENSOR_CHAN_ROTATION)) {
51 		return -ENOTSUP;
52 	}
53 
54 	/* We're only interested in the remainder between the current counter value and
55 	 * counts_per_revolution. The integer part represents an entire rotation so it
56 	 * can be ignored
57 	 */
58 	counter_value = LL_TIM_GetCounter(dev_cfg->timer_inst) % dev_cfg->counts_per_revolution;
59 	dev_data->position = (counter_value * 360) / dev_cfg->counts_per_revolution;
60 
61 	return 0;
62 }
63 
qdec_stm32_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)64 static int qdec_stm32_get(const struct device *dev, enum sensor_channel chan,
65 			  struct sensor_value *val)
66 {
67 	struct qdec_stm32_dev_data *const dev_data = dev->data;
68 
69 	if (chan == SENSOR_CHAN_ROTATION) {
70 		val->val1 = dev_data->position;
71 		val->val2 = 0;
72 	} else {
73 		return -ENOTSUP;
74 	}
75 
76 	return 0;
77 }
78 
qdec_stm32_initialize(const struct device * dev)79 static int qdec_stm32_initialize(const struct device *dev)
80 {
81 	const struct qdec_stm32_dev_cfg *const dev_cfg = dev->config;
82 	int retval;
83 	LL_TIM_ENCODER_InitTypeDef init_props;
84 	uint32_t max_counter_value;
85 
86 	retval = pinctrl_apply_state(dev_cfg->pin_config, PINCTRL_STATE_DEFAULT);
87 	if (retval < 0) {
88 		return retval;
89 	}
90 
91 	if (!device_is_ready(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE))) {
92 		LOG_ERR("Clock control device not ready");
93 		return -ENODEV;
94 	}
95 
96 	retval = clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE),
97 				  (clock_control_subsys_t)&dev_cfg->pclken);
98 	if (retval < 0) {
99 		LOG_ERR("Could not initialize clock");
100 		return retval;
101 	}
102 
103 	if (dev_cfg->counts_per_revolution < 1) {
104 		LOG_ERR("Invalid number of counts per revolution (%d)",
105 			dev_cfg->counts_per_revolution);
106 		return -EINVAL;
107 	}
108 
109 	LL_TIM_ENCODER_StructInit(&init_props);
110 
111 	init_props.EncoderMode = dev_cfg->encoder_mode;
112 
113 	if (dev_cfg->is_input_polarity_inverted) {
114 		init_props.IC1Polarity = LL_TIM_IC_POLARITY_FALLING;
115 		init_props.IC2Polarity = LL_TIM_IC_POLARITY_FALLING;
116 	}
117 
118 	init_props.IC1Filter = dev_cfg->input_filtering_level * LL_TIM_IC_FILTER_FDIV1_N2;
119 	init_props.IC2Filter = dev_cfg->input_filtering_level * LL_TIM_IC_FILTER_FDIV1_N2;
120 
121 	/* Ensure that the counter will always count up to a multiple of counts_per_revolution */
122 	if (IS_TIM_32B_COUNTER_INSTANCE(dev_cfg->timer_inst)) {
123 		max_counter_value = UINT32_MAX - (UINT32_MAX % dev_cfg->counts_per_revolution) - 1;
124 	} else {
125 		max_counter_value = UINT16_MAX - (UINT16_MAX % dev_cfg->counts_per_revolution) - 1;
126 	}
127 	LL_TIM_SetAutoReload(dev_cfg->timer_inst, max_counter_value);
128 
129 	if (LL_TIM_ENCODER_Init(dev_cfg->timer_inst, &init_props) != SUCCESS) {
130 		LOG_ERR("Initalization failed");
131 		return -EIO;
132 	}
133 
134 	LL_TIM_EnableCounter(dev_cfg->timer_inst);
135 
136 	return 0;
137 }
138 
139 static DEVICE_API(sensor, qdec_stm32_driver_api) = {
140 	.sample_fetch = qdec_stm32_fetch,
141 	.channel_get = qdec_stm32_get,
142 };
143 
144 #define QDEC_STM32_INIT(n)                                                                         \
145 	BUILD_ASSERT(!(DT_INST_PROP(n, st_encoder_mode) & ~TIM_SMCR_SMS),                          \
146 		     "Encoder mode is not supported by this MCU");                                 \
147                                                                                                    \
148 	PINCTRL_DT_INST_DEFINE(n);                                                                 \
149 	static const struct qdec_stm32_dev_cfg qdec##n##_stm32_config = {                          \
150 		.pin_config = PINCTRL_DT_INST_DEV_CONFIG_GET(n),                                   \
151 		.timer_inst = ((TIM_TypeDef *)DT_REG_ADDR(DT_INST_PARENT(n))),                     \
152 		.pclken = {.bus = DT_CLOCKS_CELL(DT_INST_PARENT(n), bus),                          \
153 			   .enr = DT_CLOCKS_CELL(DT_INST_PARENT(n), bits)},                        \
154 		.encoder_mode = DT_INST_PROP(n, st_encoder_mode),                                  \
155 		.is_input_polarity_inverted = DT_INST_PROP(n, st_input_polarity_inverted),         \
156 		.input_filtering_level = DT_INST_PROP(n, st_input_filter_level),                   \
157 		.counts_per_revolution = DT_INST_PROP(n, st_counts_per_revolution),                \
158 	};                                                                                         \
159                                                                                                    \
160 	static struct qdec_stm32_dev_data qdec##n##_stm32_data;                                    \
161                                                                                                    \
162 	SENSOR_DEVICE_DT_INST_DEFINE(n, qdec_stm32_initialize, NULL, &qdec##n##_stm32_data,        \
163 				     &qdec##n##_stm32_config, POST_KERNEL,                         \
164 				     CONFIG_SENSOR_INIT_PRIORITY, &qdec_stm32_driver_api);
165 
166 DT_INST_FOREACH_STATUS_OKAY(QDEC_STM32_INIT)
167