Commit f5163e2c authored by Piotr Mienkowski's avatar Piotr Mienkowski Committed by Carles Cufi
Browse files

drivers: sensor: Add Atmel SAM QDEC (TC) Driver



Tested on Atmel SMART SAM E70 Xplained board

Origin: Original

Signed-off-by: default avatarPiotr Mienkowski <piotr.mienkowski@gmail.com>
parent d6f2ba80
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ add_subdirectory_ifdef(CONFIG_MS5837 ms5837)
add_subdirectory_ifdef(CONFIG_OPT3001		opt3001)
add_subdirectory_ifdef(CONFIG_PMS7003		pms7003)
add_subdirectory_ifdef(CONFIG_QDEC_NRFX		qdec_nrfx)
add_subdirectory_ifdef(CONFIG_QDEC_SAM		qdec_sam)
add_subdirectory_ifdef(CONFIG_TEMP_NRF5		nrf5)
add_subdirectory_ifdef(CONFIG_SHT3XD		sht3xd)
add_subdirectory_ifdef(CONFIG_SI7006		si7006)
+2 −0
Original line number Diff line number Diff line
@@ -168,6 +168,8 @@ source "drivers/sensor/pms7003/Kconfig"

source "drivers/sensor/qdec_nrfx/Kconfig"

source "drivers/sensor/qdec_sam/Kconfig"

source "drivers/sensor/sht3xd/Kconfig"

source "drivers/sensor/si7006/Kconfig"
+7 −0
Original line number Diff line number Diff line
# Copyright (c) 2021, Piotr Mienkowski
#
# SPDX-License-Identifier: Apache-2.0

zephyr_library()

zephyr_library_sources(qdec_sam.c)
+12 −0
Original line number Diff line number Diff line
# Atmel SAM MCU family Quadrature Decoder (TC) driver configuration options
#
# Copyright (c) 2021, Piotr Mienkowski
# SPDX-License-Identifier: Apache-2.0
#

config QDEC_SAM
	bool "Atmel SAM QDEC driver"
	depends on SOC_FAMILY_SAM
	default n
	help
	  Atmel SAM MCU family Quadrature Decoder (TC) driver.
+142 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2021, Piotr Mienkowski
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT atmel_sam_tc

/** @file
 * @brief Atmel SAM MCU family Quadrature Decoder (QDEC/TC) driver.
 */

#include <errno.h>
#include <sys/__assert.h>
#include <sys/util.h>
#include <device.h>
#include <init.h>
#include <soc.h>
#include <drivers/sensor.h>

#include <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 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->TC_CHANNEL[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->TC_CHANNEL[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->TC_CHANNEL[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_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, device_pm_control_nop, \
			    &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)