blob: e00baedab338be4db16180572f9d50491e76a07f [file] [log] [blame]
/*
* Copyright (c) 2022, Valerio Setti <vsetti@baylibre.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT st_stm32_qdec
/** @file
* @brief STM32 family Quadrature Decoder (QDEC) driver.
*/
#include <errno.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/util.h>
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
#include <zephyr/logging/log.h>
#include <stm32_ll_tim.h>
LOG_MODULE_REGISTER(qdec_stm32, CONFIG_SENSOR_LOG_LEVEL);
/* Device constant configuration parameters */
struct qdec_stm32_dev_cfg {
const struct pinctrl_dev_config *pin_config;
struct stm32_pclken pclken;
TIM_TypeDef *timer_inst;
bool is_input_polarity_inverted;
uint8_t input_filtering_level;
uint32_t counts_per_revolution;
};
/* Device run time data */
struct qdec_stm32_dev_data {
int32_t position;
};
static int qdec_stm32_fetch(const struct device *dev, enum sensor_channel chan)
{
struct qdec_stm32_dev_data *dev_data = dev->data;
const struct qdec_stm32_dev_cfg *dev_cfg = dev->config;
uint32_t counter_value;
if ((chan != SENSOR_CHAN_ALL) && (chan != SENSOR_CHAN_ROTATION)) {
return -ENOTSUP;
}
/* We're only interested in the remainder between the current counter value and
* counts_per_revolution. The integer part represents an entire rotation so it
* can be ignored
*/
counter_value = LL_TIM_GetCounter(dev_cfg->timer_inst) % dev_cfg->counts_per_revolution;
dev_data->position = (counter_value * 360) / dev_cfg->counts_per_revolution;
return 0;
}
static int qdec_stm32_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
struct qdec_stm32_dev_data *const dev_data = dev->data;
if (chan == SENSOR_CHAN_ROTATION) {
val->val1 = dev_data->position;
val->val2 = 0;
} else {
return -ENOTSUP;
}
return 0;
}
static int qdec_stm32_initialize(const struct device *dev)
{
const struct qdec_stm32_dev_cfg *const dev_cfg = dev->config;
int retval;
LL_TIM_ENCODER_InitTypeDef init_props;
uint32_t max_counter_value;
retval = pinctrl_apply_state(dev_cfg->pin_config, PINCTRL_STATE_DEFAULT);
if (retval < 0) {
return retval;
}
if (!device_is_ready(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE))) {
LOG_ERR("Clock control device not ready");
return -ENODEV;
}
retval = clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE),
(clock_control_subsys_t)&dev_cfg->pclken);
if (retval < 0) {
LOG_ERR("Could not initialize clock");
return retval;
}
if (dev_cfg->counts_per_revolution < 1) {
LOG_ERR("Invalid number of counts per revolution (%d)",
dev_cfg->counts_per_revolution);
return -EINVAL;
}
LL_TIM_ENCODER_StructInit(&init_props);
if (dev_cfg->is_input_polarity_inverted) {
init_props.IC1ActiveInput = LL_TIM_IC_POLARITY_FALLING;
init_props.IC2ActiveInput = LL_TIM_IC_POLARITY_FALLING;
}
init_props.IC1Filter = dev_cfg->input_filtering_level * LL_TIM_IC_FILTER_FDIV1_N2;
init_props.IC2Filter = dev_cfg->input_filtering_level * LL_TIM_IC_FILTER_FDIV1_N2;
/* Ensure that the counter will always count up to a multiple of counts_per_revolution */
if (IS_TIM_32B_COUNTER_INSTANCE(dev_cfg->timer_inst)) {
max_counter_value = UINT32_MAX - (UINT32_MAX % dev_cfg->counts_per_revolution) - 1;
} else {
max_counter_value = UINT16_MAX - (UINT16_MAX % dev_cfg->counts_per_revolution) - 1;
}
LL_TIM_SetAutoReload(dev_cfg->timer_inst, max_counter_value);
if (LL_TIM_ENCODER_Init(dev_cfg->timer_inst, &init_props) != SUCCESS) {
LOG_ERR("Initalization failed");
return -EIO;
}
LL_TIM_EnableCounter(dev_cfg->timer_inst);
return 0;
}
static const struct sensor_driver_api qdec_stm32_driver_api = {
.sample_fetch = qdec_stm32_fetch,
.channel_get = qdec_stm32_get,
};
#define QDEC_STM32_INIT(n) \
PINCTRL_DT_INST_DEFINE(n); \
static const struct qdec_stm32_dev_cfg qdec##n##_stm32_config = { \
.pin_config = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
.timer_inst = ((TIM_TypeDef *)DT_REG_ADDR(DT_INST_PARENT(n))), \
.pclken = { \
.bus = DT_CLOCKS_CELL(DT_INST_PARENT(n), bus), \
.enr = DT_CLOCKS_CELL(DT_INST_PARENT(n), bits) \
}, \
.is_input_polarity_inverted = DT_INST_PROP(n, st_input_polarity_inverted), \
.input_filtering_level = DT_INST_PROP(n, st_input_filter_level), \
.counts_per_revolution = DT_INST_PROP(n, st_counts_per_revolution), \
}; \
\
static struct qdec_stm32_dev_data qdec##n##_stm32_data; \
\
SENSOR_DEVICE_DT_INST_DEFINE(n, qdec_stm32_initialize, NULL, \
&qdec##n##_stm32_data, &qdec##n##_stm32_config, \
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
&qdec_stm32_driver_api);
DT_INST_FOREACH_STATUS_OKAY(QDEC_STM32_INIT)