blob: 3d42f2a572c4fbf16345696167878583bf0d3af1 [file]
/*
* Copyright (c) 2022 Valerio Setti <valerio.setti@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/printk.h>
#define QUAD_ENC_EMUL_ENABLED \
DT_NODE_EXISTS(DT_ALIAS(qenca)) && DT_NODE_EXISTS(DT_ALIAS(qencb))
#if QUAD_ENC_EMUL_ENABLED
#include <zephyr/drivers/gpio.h>
#define QUAD_ENC_EMUL_PERIOD 100
static const struct gpio_dt_spec phase_a =
GPIO_DT_SPEC_GET(DT_ALIAS(qenca), gpios);
static const struct gpio_dt_spec phase_b =
GPIO_DT_SPEC_GET(DT_ALIAS(qencb), gpios);
static bool toggle_a;
void qenc_emulate_work_handler(struct k_work *work)
{
toggle_a = !toggle_a;
if (toggle_a) {
gpio_pin_toggle_dt(&phase_a);
} else {
gpio_pin_toggle_dt(&phase_b);
}
}
static K_WORK_DEFINE(qenc_emulate_work, qenc_emulate_work_handler);
static void qenc_emulate_timer_handler(struct k_timer *dummy)
{
k_work_submit(&qenc_emulate_work);
}
static K_TIMER_DEFINE(qenc_emulate_timer, qenc_emulate_timer_handler, NULL);
static void qenc_emulate_init(void)
{
printk("Quadrature encoder emulator enabled with %u ms period\n",
QUAD_ENC_EMUL_PERIOD);
if (!gpio_is_ready_dt(&phase_a)) {
printk("%s: device not ready.", phase_a.port->name);
return;
}
gpio_pin_configure_dt(&phase_a, GPIO_OUTPUT);
if (!gpio_is_ready_dt(&phase_b)) {
printk("%s: device not ready.", phase_b.port->name);
return;
}
gpio_pin_configure_dt(&phase_b, GPIO_OUTPUT);
k_timer_start(&qenc_emulate_timer, K_MSEC(QUAD_ENC_EMUL_PERIOD / 2),
K_MSEC(QUAD_ENC_EMUL_PERIOD / 2));
}
#else
static void qenc_emulate_init(void) { };
#endif /* QUAD_ENC_EMUL_ENABLED */
#ifdef CONFIG_EQDC_MCUX_TRIGGER
#include <zephyr/sys/atomic.h>
/* Incremented from the trigger handler (system workqueue context). */
static atomic_t trigger_count;
static void revolution_trigger_handler(const struct device *dev,
const struct sensor_trigger *trig)
{
ARG_UNUSED(dev);
ARG_UNUSED(trig);
atomic_inc(&trigger_count);
}
/*
* Register SENSOR_TRIG_OVERFLOW on the revolution channel. With
* revolution-count-mode = modulus, the EQDC revolution counter rolls
* over/under without an INDEX signal, firing this trigger as the emulated
* encoder drives the position counter past a full revolution.
*/
static void qdec_trigger_init(const struct device *dev)
{
struct sensor_trigger trig = {
.type = SENSOR_TRIG_OVERFLOW,
.chan = SENSOR_CHAN_ENCODER_REVOLUTIONS,
};
int rc = sensor_trigger_set(dev, &trig, revolution_trigger_handler);
if (rc != 0) {
printk("Failed to set SENSOR_TRIG_OVERFLOW (%d)\n", rc);
return;
}
printk("Registered SENSOR_TRIG_OVERFLOW on SENSOR_CHAN_ENCODER_REVOLUTIONS\n");
}
#else
static void qdec_trigger_init(const struct device *dev)
{
ARG_UNUSED(dev);
};
#endif /* CONFIG_EQDC_MCUX_TRIGGER */
int main(void)
{
struct sensor_value val;
int rc;
const struct device *const dev = DEVICE_DT_GET(DT_ALIAS(qdec0));
if (!device_is_ready(dev)) {
printk("Qdec device is not ready\n");
return 0;
}
printk("Quadrature decoder sensor test\n");
qenc_emulate_init();
qdec_trigger_init(dev);
#ifndef CONFIG_COVERAGE
while (true) {
#else
for (int i = 0; i < 3; i++) {
#endif
/* sleep first to gather position from first period */
k_msleep(1000);
rc = sensor_sample_fetch(dev);
if (rc != 0) {
printk("Failed to fetch sample (%d)\n", rc);
return 0;
}
rc = sensor_channel_get(dev, SENSOR_CHAN_ROTATION, &val);
if (rc != 0) {
printk("Failed to get data (%d)\n", rc);
return 0;
}
printk("Position = %d degrees\n", val.val1);
/* Get speed (optional)*/
rc = sensor_channel_get(dev, SENSOR_CHAN_RPM, &val);
if (rc == 0) {
printk("Speed = %d RPM\n", val.val1);
}
/* Get revolutions (optional)*/
rc = sensor_channel_get(dev, SENSOR_CHAN_ENCODER_REVOLUTIONS, &val);
if (rc == 0) {
printk("Revolutions = %d\n", val.val1);
}
#ifdef CONFIG_EQDC_MCUX_TRIGGER
/*
* "triggers" is how many times the SENSOR_TRIG_OVERFLOW handler
* ran. If the trigger path works it tracks the revolution
* roll-over events; if it stays 0 while revolutions change, the
* IRQ never reaches the handler.
*/
printk("Triggers = %ld\n", (long)atomic_get(&trigger_count));
#endif
}
return 0;
}