/* * Copyright (c) 2021, Piotr Mienkowski * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT atmel_sam_tc_qdec /** @file * @brief Atmel SAM MCU family Quadrature Decoder (QDEC/TC) driver. */ #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(qdec_sam, CONFIG_SENSOR_LOG_LEVEL); /* Device constant configuration parameters */ struct qdec_sam_dev_cfg { Tc *regs; const struct soc_gpio_pin *pin_list; uint8_t pin_list_size; uint8_t periph_id[TCCHANNEL_NUMBER]; }; /* Device run time data */ struct qdec_sam_dev_data { uint16_t position; }; #define DEV_NAME(dev) ((dev)->name) #define DEV_CFG(dev) \ ((const struct qdec_sam_dev_cfg *const)(dev)->config) #define DEV_DATA(dev) \ ((struct qdec_sam_dev_data *const)(dev)->data) static int qdec_sam_fetch(const struct device *dev, enum sensor_channel chan) { const struct qdec_sam_dev_cfg *const dev_cfg = DEV_CFG(dev); struct qdec_sam_dev_data *const dev_data = DEV_DATA(dev); Tc *const tc = dev_cfg->regs; TcChannel *tc_ch0 = &tc->TcChannel[0]; /* Read position register content */ dev_data->position = tc_ch0->TC_CV; return 0; } static int qdec_sam_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { struct qdec_sam_dev_data *const dev_data = DEV_DATA(dev); if (chan == SENSOR_CHAN_ROTATION) { val->val1 = dev_data->position; val->val2 = 0; } else { return -ENOTSUP; } return 0; } static void qdec_sam_start(Tc *const tc) { TcChannel *tc_ch0 = &tc->TcChannel[0]; /* Enable Channel 0 Clock and reset counter*/ tc_ch0->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; } static void qdec_sam_configure(const struct device *dev) { const struct qdec_sam_dev_cfg *const dev_cfg = DEV_CFG(dev); Tc *const tc = dev_cfg->regs; TcChannel *tc_ch0 = &tc->TcChannel[0]; /* Clock, Trigger Edge, Trigger and Mode Selection */ tc_ch0->TC_CMR = TC_CMR_TCCLKS_XC0 | TC_CMR_ETRGEDG_RISING | TC_CMR_ABETRG; /* Enable QDEC in Position Mode*/ tc->TC_BMR = TC_BMR_QDEN | TC_BMR_POSEN | TC_BMR_EDGPHA | TC_BMR_MAXFILT(1); qdec_sam_start(tc); } static int qdec_sam_initialize(const struct device *dev) { __ASSERT_NO_MSG(dev != NULL); const struct qdec_sam_dev_cfg *const dev_cfg = DEV_CFG(dev); /* Connect pins to the peripheral */ soc_gpio_list_configure(dev_cfg->pin_list, dev_cfg->pin_list_size); for (int i = 0; i < ARRAY_SIZE(dev_cfg->periph_id); i++) { /* Enable module's clock */ soc_pmc_peripheral_enable(dev_cfg->periph_id[i]); } qdec_sam_configure(dev); LOG_INF("Device %s initialized", DEV_NAME(dev)); return 0; } static const struct sensor_driver_api qdec_sam_driver_api = { .sample_fetch = qdec_sam_fetch, .channel_get = qdec_sam_get, }; #define QDEC_SAM_INIT(n) \ static const struct soc_gpio_pin pins_tc##n[] = ATMEL_SAM_DT_INST_PINS(n); \ \ static const struct qdec_sam_dev_cfg qdec##n##_sam_config = { \ .regs = (Tc *)DT_INST_REG_ADDR(n), \ .pin_list = pins_tc##n, \ .pin_list_size = ARRAY_SIZE(pins_tc##n), \ .periph_id = DT_INST_PROP(n, peripheral_id), \ }; \ \ static struct qdec_sam_dev_data qdec##n##_sam_data; \ \ DEVICE_DT_INST_DEFINE(n, qdec_sam_initialize, NULL, \ &qdec##n##_sam_data, &qdec##n##_sam_config, \ POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ &qdec_sam_driver_api); DT_INST_FOREACH_STATUS_OKAY(QDEC_SAM_INIT)