Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | /*
* 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 <errno.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/util.h>
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <soc.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(qdec_sam, CONFIG_SENSOR_LOG_LEVEL);
/* Device constant configuration parameters */
struct qdec_sam_dev_cfg {
Tc *regs;
const struct pinctrl_dev_config *pcfg;
uint8_t periph_id[TCCHANNEL_NUMBER];
};
/* Device run time data */
struct qdec_sam_dev_data {
uint16_t position;
};
static int qdec_sam_fetch(const struct device *dev, enum sensor_channel chan)
{
const struct qdec_sam_dev_cfg *const dev_cfg = dev->config;
struct qdec_sam_dev_data *const dev_data = dev->data;
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;
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->config;
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_NONE
| 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->config;
int retval;
/* Connect pins to the peripheral */
retval = pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (retval < 0) {
return retval;
}
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);
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) \
PINCTRL_DT_INST_DEFINE(n); \
static const struct qdec_sam_dev_cfg qdec##n##_sam_config = { \
.regs = (Tc *)DT_INST_REG_ADDR(n), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(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)
|