From de1ebb144749abb975a9dc751670119dc8141631 Mon Sep 17 00:00:00 2001 From: LeonardHerbst Date: Fri, 17 Oct 2025 10:03:51 +0200 Subject: [PATCH 01/15] drivers: added support for a generic hall effect driver --- drivers/Kconfig | 1 + drivers/hall_effect/Kconfig | 24 ++++ drivers/hall_effect/Makefile | 1 + drivers/hall_effect/Makefile.dep | 3 + drivers/hall_effect/Makefile.include | 2 + drivers/hall_effect/hall_effect.c | 129 ++++++++++++++++++ drivers/hall_effect/hall_effect_saul.c | 60 ++++++++ .../hall_effect/include/hall_effect_params.h | 95 +++++++++++++ drivers/include/hall_effect.h | 93 +++++++++++++ drivers/include/saul.h | 3 + .../saul/init_devs/auto_init_hall_effect.c | 65 +++++++++ drivers/saul/init_devs/init.c | 4 + drivers/saul/saul_str.c | 2 + examples/basic/saul/Makefile | 2 + sys/include/phydat.h | 1 + sys/phydat/phydat_str.c | 2 + sys/senml/phydat.c | 1 + tests/drivers/hall_effect/Makefile | 6 + tests/drivers/hall_effect/Makefile.ci | 1 + tests/drivers/hall_effect/README.md | 56 ++++++++ tests/drivers/hall_effect/main.c | 53 +++++++ 21 files changed, 604 insertions(+) create mode 100644 drivers/hall_effect/Kconfig create mode 100644 drivers/hall_effect/Makefile create mode 100644 drivers/hall_effect/Makefile.dep create mode 100644 drivers/hall_effect/Makefile.include create mode 100644 drivers/hall_effect/hall_effect.c create mode 100644 drivers/hall_effect/hall_effect_saul.c create mode 100644 drivers/hall_effect/include/hall_effect_params.h create mode 100644 drivers/include/hall_effect.h create mode 100644 drivers/saul/init_devs/auto_init_hall_effect.c create mode 100644 tests/drivers/hall_effect/Makefile create mode 100644 tests/drivers/hall_effect/Makefile.ci create mode 100644 tests/drivers/hall_effect/README.md create mode 100644 tests/drivers/hall_effect/main.c diff --git a/drivers/Kconfig b/drivers/Kconfig index a2f3ee786768..16b1ae0eaec8 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -33,6 +33,7 @@ rsource "ads101x/Kconfig" rsource "bmx055/Kconfig" rsource "fxos8700/Kconfig" rsource "gp2y10xx/Kconfig" +rsource "hall_effect/Kconfig" rsource "hdc1000/Kconfig" rsource "hm330x/Kconfig" rsource "hsc/Kconfig" diff --git a/drivers/hall_effect/Kconfig b/drivers/hall_effect/Kconfig new file mode 100644 index 000000000000..5f35c0fc5f48 --- /dev/null +++ b/drivers/hall_effect/Kconfig @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2025 TU Dresden +# SPDX-License-Identifier: LGPL-2.1-only + +config MODULE_HALL_EFFECT + bool "Hall Effect RPM Sensor" + depends on TEST_KCONFIG + + +menu "Hall Effect RPM Driver" + depends on USEMODULE_HALL_EFFECT + + config HALL_EFFECT_GEAR_RED_RATIO + int "gear reduction ratio" + default 204 + help + Defines the gear reduction ratio. + + config HALL_EFFECT_PPR + int "Pulses per rotation" + default 13 + help + Number of high flanks per rotation. + +endmenu # Hall Effect Driver diff --git a/drivers/hall_effect/Makefile b/drivers/hall_effect/Makefile new file mode 100644 index 000000000000..7131c0443214 --- /dev/null +++ b/drivers/hall_effect/Makefile @@ -0,0 +1 @@ +include $(RIOTMAKE)/driver_with_saul.mk diff --git a/drivers/hall_effect/Makefile.dep b/drivers/hall_effect/Makefile.dep new file mode 100644 index 000000000000..619b63a72282 --- /dev/null +++ b/drivers/hall_effect/Makefile.dep @@ -0,0 +1,3 @@ +FEATURES_REQUIRED += periph_gpio +FEATURES_REQUIRED += periph_gpio_irq +USEMODULE += ztimer_usec \ No newline at end of file diff --git a/drivers/hall_effect/Makefile.include b/drivers/hall_effect/Makefile.include new file mode 100644 index 000000000000..da4103ccc765 --- /dev/null +++ b/drivers/hall_effect/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE_INCLUDES_hall_effect := $(LAST_MAKEFILEDIR)/include +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_hall_effect) diff --git a/drivers/hall_effect/hall_effect.c b/drivers/hall_effect/hall_effect.c new file mode 100644 index 000000000000..b5280c899d69 --- /dev/null +++ b/drivers/hall_effect/hall_effect.c @@ -0,0 +1,129 @@ +/* + * SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup drivers_hall_effect + * @{ + * + * @file + * @brief Device driver implementation for a generic hall effect sensor + * + * @author Leonard Herbst + * + * @} + */ + +#include "hall_effect.h" +#include "hall_effect_params.h" + +#include +#include "log.h" +#include "ztimer.h" +#include "time_units.h" + +static void _pulse_callback(void *arg); +static void read_reset_pulse_counter(hall_effect_t *dev, int32_t *pulse_counter); +static bool _read_delta_t_direction(hall_effect_t *dev, uint32_t *delta_t, bool *clock_wise); + +/* Public API */ + +int hall_effect_init(hall_effect_t *dev, const hall_effect_params_t *params) +{ + dev->params = *params; + + if (gpio_init(dev->params.direction, GPIO_IN)) { + LOG_ERROR("[hall_effect] Failed configuring the direction pin as an input!\n"); + return -EIO; + } + + dev->delta_t = 0; + dev->clock_wise = false; + dev->stale = false; + dev->pulse_counter = 0; + dev->last_read_time = ztimer_now(ZTIMER_USEC); + + if (gpio_init_int(dev->params.interrupt, GPIO_IN, GPIO_RISING, _pulse_callback, (void *) dev)) { + LOG_ERROR("[hall_effect] Failed configuring the interrupt pin!\n"); + return -EIO; + } + + return 0; +} + +int hall_effect_read_rpm(hall_effect_t *dev, int32_t *rpm) +{ + uint32_t delta_t; + bool clock_wise; + if(!_read_delta_t_direction(dev, &delta_t, &clock_wise)) { + *rpm = 0; + return 0; + } + + /* delta_t represents the number of micro seconds since last readout. + * Invert and devide by the number of micro seconds per minute + * to obtain the rpm. Apply scaling factors like gear reduction + * or pulses per rotation. + */ + *rpm = SEC_PER_MIN * US_PER_SEC * 10 + / (delta_t * CONFIG_HALL_EFFECT_PPR * CONFIG_HALL_EFFECT_GEAR_RED_RATIO); + if (!clock_wise) { + *rpm *= -1; + } + return 0; +} + +int hall_effect_read_reset_revolutions_hundreths(hall_effect_t *dev, int32_t *pulse_counter) +{ + read_reset_pulse_counter(dev, pulse_counter); + *pulse_counter *= 1000; + *pulse_counter /= CONFIG_HALL_EFFECT_PPR; + *pulse_counter /= CONFIG_HALL_EFFECT_GEAR_RED_RATIO; + return 0; +} + +/* Private API */ + +static void _pulse_callback(void *arg) +{ + hall_effect_t *dev = (hall_effect_t *) arg; + + uint32_t now = ztimer_now(ZTIMER_USEC); + + dev->clock_wise = gpio_read(dev->params.direction); + dev->delta_t = now - dev->last_read_time; + dev->last_read_time = now; + dev->pulse_counter += dev->clock_wise ? 1 : -1; + dev->stale= false; +} + +static void read_reset_pulse_counter(hall_effect_t *dev, int32_t *pulse_counter) +{ + int irq_state = irq_disable(); + *pulse_counter = dev->pulse_counter; + dev->pulse_counter = 0; + irq_restore(irq_state); +} + +static bool _read_delta_t_direction(hall_effect_t *dev, uint32_t *delta_t, bool *clock_wise) +{ + int irq_state = irq_disable(); + /* If the last data we read is stale we can just return false*/ + if (dev->stale) { + irq_restore(irq_state); + return false; + } + uint32_t now = ztimer_now(ZTIMER_USEC); + if (now - dev->last_read_time >= dev->delta_t) { + /* we have been waiting for an update longer than the last delta t + * the rotation stopped we canmark the data as stale for optimizations */ + *delta_t = now - dev->last_read_time; + dev->stale= true; + } else { + *delta_t = dev->delta_t; + } + *clock_wise = dev->clock_wise; + irq_restore(irq_state); + return true; +} \ No newline at end of file diff --git a/drivers/hall_effect/hall_effect_saul.c b/drivers/hall_effect/hall_effect_saul.c new file mode 100644 index 000000000000..b7fa2031f399 --- /dev/null +++ b/drivers/hall_effect/hall_effect_saul.c @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup drivers_hall_effect + * @{ + * + * @file + * @brief Hall effect sensor adaption to the RIOT actuator/sensor interface + * + * @author Leonard Herbst + * + * @} + */ + +#include +#include + +#include "saul.h" +#include "hall_effect.h" + +static int read_rpm(const void *dev, phydat_t *res) { + hall_effect_t *d = (hall_effect_t *) dev; + int32_t rpm; + if (hall_effect_read_rpm(d, &rpm)) { + /* Read failure */ + return -ECANCELED; + } + res->val[0] = (uint16_t) rpm; + res->unit = UNIT_RPM; + res->scale = 0; + return 1; +} + +static int read_reset_pulse_counter(const void *dev, phydat_t *res) { + hall_effect_t *d = (hall_effect_t *)dev; + int32_t counter; + if (hall_effect_read_reset_revolutions_hundreths(d, &counter)) { + /* Read failure */ + return -ECANCELED; + } + res->val[0] = (int16_t) counter; + res->unit = UNIT_CTS; + res->scale = -2; + return 1; +} + +const saul_driver_t hall_effect_rpm_saul_driver = { + .read = read_rpm, + .write = saul_write_notsup, + .type = SAUL_SENSE_SPEED, +}; + +const saul_driver_t hall_pulse_count_saul_driver = { + .read = read_reset_pulse_counter, + .write = saul_write_notsup, + .type = SAUL_SENSE_COUNT, +}; diff --git a/drivers/hall_effect/include/hall_effect_params.h b/drivers/hall_effect/include/hall_effect_params.h new file mode 100644 index 000000000000..5143c3529163 --- /dev/null +++ b/drivers/hall_effect/include/hall_effect_params.h @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @ingroup drivers_hall_effect + * + * @{ + * @file + * @brief Default configuration for a generic hall effect sensor. + * + * @author Leonard Herbst + */ + +#include "board.h" +#include "hall_effect.h" +#include "saul_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Default gear reduction ratio + */ +#ifndef CONFIG_HALL_EFFECT_GEAR_RED_RATIO +# define CONFIG_HALL_EFFECT_GEAR_RED_RATIO 204 +#endif + +/** + * @brief Default number of pulses per revolution + */ +#ifndef CONFIG_HALL_EFFECT_PPR +# define CONFIG_HALL_EFFECT_PPR 13 +#endif + +/** + * @name default configuration parameters for a generic hall effect sensor + * @{ + */ +/** + * @brief Default pin of the first phase used to trigger the interrupt + */ +#ifndef HALL_EFFECT_INTERRUPT +# define HALL_EFFECT_INTERRUPT GPIO_PIN(1, 1) +#endif + +/** + * @brief Default pin of the second (offset) phase used to determine the direction + */ +#ifndef HALL_EFFECT_DIRECTION +# define HALL_EFFECT_DIRECTION GPIO_PIN(1, 2) +#endif + +/** + * @brief Default parameters + */ +#ifndef HALL_EFFECT_PARAMS +# define HALL_EFFECT_PARAMS { .interrupt = HALL_EFFECT_INTERRUPT, \ + .direction = HALL_EFFECT_DIRECTION } +#endif +/**@}*/ + +/** + * @brief SAUL info for the rpm and pulse count driver + */ +#ifndef HALL_EFFECT_SAUL_INFO +# define HALL_EFFECT_SAUL_INFO { { .name = "Hall Effect RPM Sensor" }, \ + { .name = "Hall Effect Pulse Count Sensor" } } +#endif + +/** + * @brief Hall effect sensor configuraiton + */ +static const hall_effect_params_t hall_effect_params[] = +{ + HALL_EFFECT_PARAMS +}; + +/** + * @brief Additional meta information to keep in the SAUL registry + */ +static const saul_reg_info_t hall_effect_saul_info[][2] = +{ + HALL_EFFECT_SAUL_INFO +}; + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/drivers/include/hall_effect.h b/drivers/include/hall_effect.h new file mode 100644 index 000000000000..f0d05a8d1150 --- /dev/null +++ b/drivers/include/hall_effect.h @@ -0,0 +1,93 @@ +/* + * SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @defgroup drivers_hall_effect Generic Hall Effect Sensor + * @ingroup drivers_sensors + * @brief Generic hall effect sensor to measur rpm and rotation count. + * + * ## Description + * This is a driver for a generic Hall effect sensor. + * The sensor produces a fixed number of pulses per rotation on two output pins. + * The phases are slightly offset, allowing the detection of rotation direction. + * + * This driver provides functions to read the current RPM + * and the total number of rotations (in hundredths) since the last measurement. + * + * Configuration options are available via Kconfig to specify + * the number of pulses per rotation and the gear reduction ratio. + * @{ + * + * @file + * + * @author Leonard Herbst + */ + +#include "periph/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Device initialization parameters + */ +typedef struct { + gpio_t interrupt; /**< Interrupt pin */ + gpio_t direction; /**< Pin used to determine the direction */ +} hall_effect_params_t; + +/** + * @brief Device descriptor for the driver + */ +typedef struct { + hall_effect_params_t params; /**< configuration parameters */ + uint32_t delta_t; /**< time delta since the last read */ + int32_t pulse_counter; /**< number of pulses since last read */ + uint32_t last_read_time; /**< time of the last read */ + bool clock_wise; /**< was the rotation clock wise */ + bool stale; /**< if data is stale we can just return 0 rpm */ +} hall_effect_t; + +/** + * @brief Initialize the given device + * + * @param[inout] dev Device descriptor of hall effect sensor + * @param[in] params Initialization parameters + * + * @retval 0 on success + * @retval -EIO on failure setting up the pins + */ +int hall_effect_init(hall_effect_t *dev, const hall_effect_params_t *params); + +/** + * @brief Read the current rpm of the motor. + * + * @param[in] dev Device descriptor of hall effect sensor + * @param[out] rpm Revolutions per minute. + * Negative rpm responds to counter clock wise rotation. + * + * @return 0 on success + */ +int hall_effect_read_rpm(hall_effect_t *dev, int32_t *rpm); + +/** + * @brief Read number of revolutions since the last read in hundredths. + * + * @param[in] dev Device descriptor of hall effect sensor + * @param[out] rpm Number of rotation since the last read in hundredths. + * Negative rotation means counter clock wise rotation. + * + * @return 0 on success + */ +int hall_effect_read_reset_revolutions_hundreths(hall_effect_t *dev, int32_t *pulse_counter); + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/drivers/include/saul.h b/drivers/include/saul.h index 824b45f4f266..2cc578652488 100644 --- a/drivers/include/saul.h +++ b/drivers/include/saul.h @@ -140,6 +140,7 @@ enum { SAUL_SENSE_ID_PH, /**< sensor: pH */ SAUL_SENSE_ID_POWER, /**< sensor: power */ SAUL_SENSE_ID_SIZE, /**< sensor: size */ + SAUL_SENSE_ID_SPEED, /**< sensor: speed */ SAUL_SENSE_NUMOF /**< Number of actuators supported */ /* Extend this list as needed, but keep SAUL_SENSE_ID_ANY the first and * SAUL_SENSE_NUMOF the last entry @@ -223,6 +224,8 @@ enum { SAUL_SENSE_POWER = SAUL_CAT_SENSE | SAUL_SENSE_ID_POWER, /** sensor: size */ SAUL_SENSE_SIZE = SAUL_CAT_SENSE | SAUL_SENSE_ID_SIZE, + /** sensor: speed */ + SAUL_SENSE_SPEED = SAUL_CAT_SENSE | SAUL_SENSE_ID_SPEED, /** any device - wildcard */ SAUL_CLASS_ANY = 0xff /* extend this list as needed... */ diff --git a/drivers/saul/init_devs/auto_init_hall_effect.c b/drivers/saul/init_devs/auto_init_hall_effect.c new file mode 100644 index 000000000000..238bc53a9693 --- /dev/null +++ b/drivers/saul/init_devs/auto_init_hall_effect.c @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + + +/** + * @ingroup drivers_hall_effect + * @{ + * + * @file + * @brief SAUL adaption for generic hall effect sensor + * to measure the number as well as rate of rotation. + * + * @author Leonard Herbst + * @} + */ + +#include "log.h" +#include "saul_reg.h" +#include "hall_effect_params.h" + +/** + * @brief Define the number of configured hall effect sensors + */ +#define HALL_EFFECT_NUM ARRAY_SIZE(hall_effect_params) + +/** + * @brief Allocate memory for the device descriptors + */ +static hall_effect_t hall_effect_devs[HALL_EFFECT_NUM]; + +/** + * @brief Memory for the SAUL registry entries + */ +static saul_reg_t saul_entries[HALL_EFFECT_NUM * 2]; + +extern saul_driver_t hall_effect_rpm_saul_driver; +extern saul_driver_t hall_pulse_count_saul_driver; + +void auto_init_hall_effect(void) +{ + for (unsigned i = 0; i < HALL_EFFECT_NUM; i++) { + LOG_DEBUG("[auto_init_saul] initializing hall_effect #%u\n", i); + + int res = hall_effect_init(&hall_effect_devs[i], &hall_effect_params[i]); + if (res != 0) { + LOG_ERROR("[auto_init_saul] error initializing hall_effect #%u\n", i); + continue; + } + + /* RPM */ + saul_entries[(i * 2)].dev = &(hall_effect_devs[i]); + saul_entries[(i * 2)].name = hall_effect_saul_info[i][0].name; + saul_entries[(i * 2)].driver = &hall_effect_rpm_saul_driver; + + /* Pulse Count */ + saul_entries[(i * 2) + 1].dev = &(hall_effect_devs[i]); + saul_entries[(i * 2) + 1].name = hall_effect_saul_info[i][1].name; + saul_entries[(i * 2) + 1].driver = &hall_pulse_count_saul_driver; + + saul_reg_add(&(saul_entries[(i * 2)])); + saul_reg_add(&(saul_entries[(i * 2) + 1])); + } +} diff --git a/drivers/saul/init_devs/init.c b/drivers/saul/init_devs/init.c index c6cdad13db6b..92f5e721d790 100644 --- a/drivers/saul/init_devs/init.c +++ b/drivers/saul/init_devs/init.c @@ -123,6 +123,10 @@ void saul_init_devs(void) extern void auto_init_grove_ledbar(void); auto_init_grove_ledbar(); } + if (IS_USED(MODULE_HALL_EFFECT)) { + extern void auto_init_hall_effect(void); + auto_init_hall_effect(); + } if (IS_USED(MODULE_HMC5883L)) { extern void auto_init_hmc5883l(void); auto_init_hmc5883l(); diff --git a/drivers/saul/saul_str.c b/drivers/saul/saul_str.c index d9ea6abe913b..a33273e9c552 100644 --- a/drivers/saul/saul_str.c +++ b/drivers/saul/saul_str.c @@ -71,6 +71,7 @@ static FLASH_ATTR const char _sense_voltage[] = "SENSE_VOLTAGE"; static FLASH_ATTR const char _sense_ph[] = "SENSE_PH"; static FLASH_ATTR const char _sense_power[] = "SENSE_POWER"; static FLASH_ATTR const char _sense_size[] = "SENSE_SIZE"; +static FLASH_ATTR const char _sense_speed[] = "SENSE_SPEED"; static FLASH_ATTR const char * FLASH_ATTR const sensors[] = { [SAUL_SENSE_ID_ANY] = _sense_any, @@ -102,6 +103,7 @@ static FLASH_ATTR const char * FLASH_ATTR const sensors[] = { [SAUL_SENSE_ID_PH] = _sense_ph, [SAUL_SENSE_ID_POWER] = _sense_power, [SAUL_SENSE_ID_SIZE] = _sense_size, + [SAUL_SENSE_ID_SPEED] = _sense_speed, }; static FLASH_ATTR const char _class_undef[] = "CLASS_UNDEF"; diff --git a/examples/basic/saul/Makefile b/examples/basic/saul/Makefile index be40c8ca57fe..f1bc9c7713f5 100644 --- a/examples/basic/saul/Makefile +++ b/examples/basic/saul/Makefile @@ -14,6 +14,8 @@ USEMODULE += shell_cmds_default # additional modules for debugging: USEMODULE += ps +USEMODULE += hall_effect + # Comment this out to disable code in RIOT that does safety checking # which is not needed in a production environment but helps in the # development process: diff --git a/sys/include/phydat.h b/sys/include/phydat.h index 166a95b7ca01..c2a895ac86c6 100644 --- a/sys/include/phydat.h +++ b/sys/include/phydat.h @@ -91,6 +91,7 @@ enum { UNIT_G_FORCE, /**< gravitational force equivalent */ UNIT_G = UNIT_G_FORCE, /**< @deprecated, use UNIT_G_FORCE instead */ UNIT_DPS, /**< degree per second */ + UNIT_RPM, /**< revolutions per minute */ /* weight */ UNIT_GRAM, /**< grams - not using the SI unit (kg) here to make scale * handling simpler */ diff --git a/sys/phydat/phydat_str.c b/sys/phydat/phydat_str.c index d22c2e35eb05..d695a9579dda 100644 --- a/sys/phydat/phydat_str.c +++ b/sys/phydat/phydat_str.c @@ -111,6 +111,7 @@ static FLASH_ATTR const char _unit_square_metre[] = "m^2"; static FLASH_ATTR const char _unit_cubic_metre[] = "m^3"; static FLASH_ATTR const char _unit_g_force[] = "gₙ"; static FLASH_ATTR const char _unit_degree_per_second[] = "dps"; +static FLASH_ATTR const char _unit_revolutions_per_minute[] = "rpm"; static FLASH_ATTR const char _unit_gram[] = "g"; static FLASH_ATTR const char _unit_ampere[] = "A"; static FLASH_ATTR const char _unit_volt[] = "V"; @@ -147,6 +148,7 @@ static FLASH_ATTR const char * FLASH_ATTR const _unit_to_str[] = { [UNIT_M3] = _unit_cubic_metre, [UNIT_G_FORCE] = _unit_g_force, [UNIT_DPS] = _unit_degree_per_second, + [UNIT_RPM] = _unit_revolutions_per_minute, [UNIT_GRAM] = _unit_gram, [UNIT_A] = _unit_ampere, [UNIT_V] = _unit_volt, diff --git a/sys/senml/phydat.c b/sys/senml/phydat.c index a8265b7a8950..fe5daab07aac 100644 --- a/sys/senml/phydat.c +++ b/sys/senml/phydat.c @@ -33,6 +33,7 @@ static uint8_t phydat_unit_to_senml_unit(uint8_t unit) case UNIT_PH: return SENML_UNIT_PH; case UNIT_PA: return SENML_UNIT_PASCAL; case UNIT_CD: return SENML_UNIT_CANDELA; + case UNIT_RPM: return SENML_UNIT_RPM; /* Compatible Secondary units */ case UNIT_DBM: return SENML_UNIT_DECIBEL_MILLIWATT; diff --git a/tests/drivers/hall_effect/Makefile b/tests/drivers/hall_effect/Makefile new file mode 100644 index 000000000000..bcd89948aab3 --- /dev/null +++ b/tests/drivers/hall_effect/Makefile @@ -0,0 +1,6 @@ +include ../Makefile.drivers_common + +USEMODULE += hall_effect +USEMODULE += ztimer_sec + +include $(RIOTBASE)/Makefile.include diff --git a/tests/drivers/hall_effect/Makefile.ci b/tests/drivers/hall_effect/Makefile.ci new file mode 100644 index 000000000000..89a09fdb8c99 --- /dev/null +++ b/tests/drivers/hall_effect/Makefile.ci @@ -0,0 +1 @@ +BOARD_INSUFFICIENT_MEMORY := diff --git a/tests/drivers/hall_effect/README.md b/tests/drivers/hall_effect/README.md new file mode 100644 index 000000000000..0a6c802fcc66 --- /dev/null +++ b/tests/drivers/hall_effect/README.md @@ -0,0 +1,56 @@ +# About +This is a manual test application for the PIR motion sensor driver. + +In order to build this application, you need to add the board to the +Makefile's `WHITELIST` first and define a pin mapping (see below). + + +# Usage +There are two ways to test this. You can either actively poll the sensor +state, or you can register a thread which receives messages for state +changes. + +## Interrupt driven +Connect the sensor's "out" pin to a GPIO of your board that can be +configured to create interrupts. +Compile and flash this test application like: + + CFLAGS="-DPIR_PARAM_GPIO=name_of_your_pin -DPIR_PARAM_ACTIVE_HIGH=if_gpio_pin_is_high_or_low_when_pir_is_active" make BOARD=your_board clean all-interrupt flash + +Ideally, the above configuration passed via CFLAGS should be in "board.h" + +The output should look like: + + kernel_init(): jumping into first task... + + PIR motion sensor test application + + Initializing PIR sensor at GPIO_8... [OK] + + Registering PIR handler thread... [OK] + PIR handler got a message: the movement has ceased. + PIR handler got a message: something started moving. + PIR handler got a message: the movement has ceased. + + +## Polling Mode +Connect the sensor's "out" pin to any GPIO pin of you board. +Compile and flash this test application like: + + CFLAGS="-DPIR_PARAM_GPIO=name_of_your_pin -DPIR_PARAM_ACTIVE_HIGH=if_gpio_pin_is_high_or_low_when_pir_is_active" make BOARD=your_board make clean all-polling flash + +Ideally, the above configuration passed via CFLAGS should be in "board.h" + +The output should look like this: + + kernel_init(): jumping into first task... + PIR motion sensor test application + + Initializing PIR sensor at GPIO_10... [OK] + + Printing sensor state every second. + Status: lo + ... + Status: lo + Status: hi + ... diff --git a/tests/drivers/hall_effect/main.c b/tests/drivers/hall_effect/main.c new file mode 100644 index 000000000000..1cd0b72257c6 --- /dev/null +++ b/tests/drivers/hall_effect/main.c @@ -0,0 +1,53 @@ +/* +* SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for a generic hall effect sensor + * + * @author Leonard Herbst + * + * @} + */ + +#include + +#include "hall_effect.h" +#include "hall_effect_params.h" +#include "ztimer.h" + +static hall_effect_t dev; + +int main(void) +{ + puts("Generic hall effect sensor test application\n"); + printf("Initializing hall effect sensor with the interrupt pin at " + "GPIO_%d and the direction pin at GPIO_%d...\n", + HALL_EFFECT_INTERRUPT, HALL_EFFECT_DIRECTION); + if (hall_effect_init(&dev, &hall_effect_params[0]) == 0) { + puts("[OK]\n"); + } + else { + puts("[Failed]"); + return 1; + } + + puts("Printing sensor state every second."); + while (1) { + int32_t rpm; + int32_t pulses; + if (hall_effect_read_rpm(&dev, &rpm) + || hall_effect_read_pulse_counter_hundreths(&dev, &pulses)) { + puts("[Failed]"); + return 1; + } + printf("SENSOR DATA:\n\tRPM: %ld\n\tPULSES: %ld\n", rpm, pulses); + ztimer_sleep(ZTIMER_SEC, 1); + } +} From c6a88391deeabf9d91520b4295c3895f54dbf14d Mon Sep 17 00:00:00 2001 From: LeonardHerbst Date: Fri, 17 Oct 2025 13:55:23 +0200 Subject: [PATCH 02/15] Clean up and documentation. --- drivers/hall_effect/Kconfig | 9 +-- drivers/hall_effect/hall_effect.c | 65 ++++++++++++------- drivers/hall_effect/hall_effect_saul.c | 2 +- .../hall_effect/include/hall_effect_params.h | 2 +- drivers/include/hall_effect.h | 42 +++++++----- .../saul/init_devs/auto_init_hall_effect.c | 3 +- examples/basic/saul/Makefile | 2 - tests/drivers/hall_effect/README.md | 57 ++-------------- tests/drivers/hall_effect/main.c | 3 +- 9 files changed, 79 insertions(+), 106 deletions(-) diff --git a/drivers/hall_effect/Kconfig b/drivers/hall_effect/Kconfig index 5f35c0fc5f48..b42dc614f323 100644 --- a/drivers/hall_effect/Kconfig +++ b/drivers/hall_effect/Kconfig @@ -10,15 +10,16 @@ menu "Hall Effect RPM Driver" depends on USEMODULE_HALL_EFFECT config HALL_EFFECT_GEAR_RED_RATIO - int "gear reduction ratio" + int "gear reduction ratio (in tenths)" default 204 help - Defines the gear reduction ratio. + Defines the gear reduction ratio. For example a gear reduction ratio + of 1:20.4 would result in a value of 204. config HALL_EFFECT_PPR - int "Pulses per rotation" + int "Pulses per revolution" default 13 help - Number of high flanks per rotation. + Number of high flanks per revolution. endmenu # Hall Effect Driver diff --git a/drivers/hall_effect/hall_effect.c b/drivers/hall_effect/hall_effect.c index b5280c899d69..3f5d4e0c4ace 100644 --- a/drivers/hall_effect/hall_effect.c +++ b/drivers/hall_effect/hall_effect.c @@ -23,9 +23,11 @@ #include "ztimer.h" #include "time_units.h" +/* Prototypes */ + static void _pulse_callback(void *arg); -static void read_reset_pulse_counter(hall_effect_t *dev, int32_t *pulse_counter); -static bool _read_delta_t_direction(hall_effect_t *dev, uint32_t *delta_t, bool *clock_wise); +static void _read_reset_pulse_counter(hall_effect_t *dev, int32_t *pulse_counter); +static bool _read_delta_t_direction(hall_effect_t *dev, uint32_t *delta_t, bool *ccw); /* Public API */ @@ -39,7 +41,7 @@ int hall_effect_init(hall_effect_t *dev, const hall_effect_params_t *params) } dev->delta_t = 0; - dev->clock_wise = false; + dev->ccw = false; dev->stale = false; dev->pulse_counter = 0; dev->last_read_time = ztimer_now(ZTIMER_USEC); @@ -55,28 +57,28 @@ int hall_effect_init(hall_effect_t *dev, const hall_effect_params_t *params) int hall_effect_read_rpm(hall_effect_t *dev, int32_t *rpm) { uint32_t delta_t; - bool clock_wise; - if(!_read_delta_t_direction(dev, &delta_t, &clock_wise)) { + bool ccw; + if (!_read_delta_t_direction(dev, &delta_t, &ccw)) { *rpm = 0; return 0; } - /* delta_t represents the number of micro seconds since last readout. - * Invert and devide by the number of micro seconds per minute + /* delta_t represents the number of micro seconds since the last pulse. + * Invert and divide by the number of micro seconds per minute * to obtain the rpm. Apply scaling factors like gear reduction - * or pulses per rotation. + * or pulses per revolution. */ *rpm = SEC_PER_MIN * US_PER_SEC * 10 / (delta_t * CONFIG_HALL_EFFECT_PPR * CONFIG_HALL_EFFECT_GEAR_RED_RATIO); - if (!clock_wise) { + if (ccw) { *rpm *= -1; } return 0; } -int hall_effect_read_reset_revolutions_hundreths(hall_effect_t *dev, int32_t *pulse_counter) +int hall_effect_read_reset_ceti_revs(hall_effect_t *dev, int32_t *pulse_counter) { - read_reset_pulse_counter(dev, pulse_counter); + _read_reset_pulse_counter(dev, pulse_counter); *pulse_counter *= 1000; *pulse_counter /= CONFIG_HALL_EFFECT_PPR; *pulse_counter /= CONFIG_HALL_EFFECT_GEAR_RED_RATIO; @@ -85,20 +87,28 @@ int hall_effect_read_reset_revolutions_hundreths(hall_effect_t *dev, int32_t *pu /* Private API */ +/* Triggered on the high flank of a pulse */ static void _pulse_callback(void *arg) { hall_effect_t *dev = (hall_effect_t *) arg; uint32_t now = ztimer_now(ZTIMER_USEC); - dev->clock_wise = gpio_read(dev->params.direction); - dev->delta_t = now - dev->last_read_time; + /* Reading the shifted phase: low -> cw, high -> ccw */ + dev->ccw = gpio_read(dev->params.direction); + if (now < dev->last_read_time) { + /* timer had an overflow */ + dev->delta_t = UINT32_MAX - dev->last_read_time + now + 1; + } else { + dev->delta_t = now - dev->last_read_time; + } dev->last_read_time = now; - dev->pulse_counter += dev->clock_wise ? 1 : -1; + dev->pulse_counter += dev->ccw ? -1 : 1; + /* data is no longer stale */ dev->stale= false; } -static void read_reset_pulse_counter(hall_effect_t *dev, int32_t *pulse_counter) +static void _read_reset_pulse_counter(hall_effect_t *dev, int32_t *pulse_counter) { int irq_state = irq_disable(); *pulse_counter = dev->pulse_counter; @@ -106,24 +116,33 @@ static void read_reset_pulse_counter(hall_effect_t *dev, int32_t *pulse_counter) irq_restore(irq_state); } -static bool _read_delta_t_direction(hall_effect_t *dev, uint32_t *delta_t, bool *clock_wise) +static bool _read_delta_t_direction(hall_effect_t *dev, uint32_t *delta_t, bool *ccw) { int irq_state = irq_disable(); - /* If the last data we read is stale we can just return false*/ + /* There have been no pulses for a while -> rotation probably stopped. */ if (dev->stale) { irq_restore(irq_state); return false; } uint32_t now = ztimer_now(ZTIMER_USEC); - if (now - dev->last_read_time >= dev->delta_t) { - /* we have been waiting for an update longer than the last delta t - * the rotation stopped we canmark the data as stale for optimizations */ - *delta_t = now - dev->last_read_time; + uint32_t pulse_age; + + if (now < dev->last_read_time) { + /* the timer had an overflow */ + pulse_age = UINT32_MAX - dev->last_read_time + now + 1; + } else { + pulse_age = now - dev->last_read_time; + } + if (pulse_age >= dev->delta_t) { + /* Data is stale if the time elapsed since the last pulse + * is longer than delta_t */ + *delta_t = pulse_age; dev->stale= true; } else { *delta_t = dev->delta_t; } - *clock_wise = dev->clock_wise; + *ccw = dev->ccw; + irq_restore(irq_state); return true; -} \ No newline at end of file +} diff --git a/drivers/hall_effect/hall_effect_saul.c b/drivers/hall_effect/hall_effect_saul.c index b7fa2031f399..a091c503c791 100644 --- a/drivers/hall_effect/hall_effect_saul.c +++ b/drivers/hall_effect/hall_effect_saul.c @@ -37,7 +37,7 @@ static int read_rpm(const void *dev, phydat_t *res) { static int read_reset_pulse_counter(const void *dev, phydat_t *res) { hall_effect_t *d = (hall_effect_t *)dev; int32_t counter; - if (hall_effect_read_reset_revolutions_hundreths(d, &counter)) { + if (hall_effect_read_reset_ceti_revs(d, &counter)) { /* Read failure */ return -ECANCELED; } diff --git a/drivers/hall_effect/include/hall_effect_params.h b/drivers/hall_effect/include/hall_effect_params.h index 5143c3529163..e3f63bdc93a6 100644 --- a/drivers/hall_effect/include/hall_effect_params.h +++ b/drivers/hall_effect/include/hall_effect_params.h @@ -73,7 +73,7 @@ extern "C" { #endif /** - * @brief Hall effect sensor configuraiton + * @brief Hall effect sensor configuration */ static const hall_effect_params_t hall_effect_params[] = { diff --git a/drivers/include/hall_effect.h b/drivers/include/hall_effect.h index f0d05a8d1150..2ca9cd0c0860 100644 --- a/drivers/include/hall_effect.h +++ b/drivers/include/hall_effect.h @@ -11,15 +11,21 @@ * @brief Generic hall effect sensor to measur rpm and rotation count. * * ## Description - * This is a driver for a generic Hall effect sensor. - * The sensor produces a fixed number of pulses per rotation on two output pins. - * The phases are slightly offset, allowing the detection of rotation direction. + * This is a driver for a generic Hall effect sensor. These sensors are most often + * used with motors. + * The sensor generates a fixed number of pulses per rotation on two output pins. + * These signals are phase-shifted slightly, enabling the detection of rotation direction. * - * This driver provides functions to read the current RPM - * and the total number of rotations (in hundredths) since the last measurement. + * The driver provides functions to read the current RPM + * and the total number of revolutions (in hundredths) since the last measurement. * * Configuration options are available via Kconfig to specify - * the number of pulses per rotation and the gear reduction ratio. + * the number of pulses per rotation and the gear reduction ratio (in tenths). + * + * @note After approximately 327 revolutions without reading and resetting + * the revolution counter, the `phydat` value will overflow. + * Use the regular driver interface instead of SAUL if necessary. + * * @{ * * @file @@ -37,20 +43,20 @@ extern "C" { * @brief Device initialization parameters */ typedef struct { - gpio_t interrupt; /**< Interrupt pin */ - gpio_t direction; /**< Pin used to determine the direction */ + gpio_t interrupt; /**< Interrupt pin (first phase) */ + gpio_t direction; /**< Pin used to determine the direction (shifted phase) */ } hall_effect_params_t; /** * @brief Device descriptor for the driver */ typedef struct { - hall_effect_params_t params; /**< configuration parameters */ - uint32_t delta_t; /**< time delta since the last read */ - int32_t pulse_counter; /**< number of pulses since last read */ - uint32_t last_read_time; /**< time of the last read */ - bool clock_wise; /**< was the rotation clock wise */ - bool stale; /**< if data is stale we can just return 0 rpm */ + hall_effect_params_t params; /**< configuration parameters */ + uint32_t delta_t; /**< time delta since the last read */ + int32_t pulse_counter; /**< number of pulses since last read */ + uint32_t last_read_time; /**< time of the last read */ + bool ccw; /**< counter clock wise rotation */ + bool stale; /**< indicates that there is no new data to be read */ } hall_effect_t; /** @@ -76,15 +82,15 @@ int hall_effect_init(hall_effect_t *dev, const hall_effect_params_t *params); int hall_effect_read_rpm(hall_effect_t *dev, int32_t *rpm); /** - * @brief Read number of revolutions since the last read in hundredths. + * @brief Read and reset number of revolutions since the last readout in hundredths. * * @param[in] dev Device descriptor of hall effect sensor - * @param[out] rpm Number of rotation since the last read in hundredths. - * Negative rotation means counter clock wise rotation. + * @param[out] rpm Number of revolutions since the last read in hundredths. + * Negative revolutions signal counter clock wise rotations. * * @return 0 on success */ -int hall_effect_read_reset_revolutions_hundreths(hall_effect_t *dev, int32_t *pulse_counter); +int hall_effect_read_reset_ceti_revs(hall_effect_t *dev, int32_t *pulse_counter); #ifdef __cplusplus } diff --git a/drivers/saul/init_devs/auto_init_hall_effect.c b/drivers/saul/init_devs/auto_init_hall_effect.c index 238bc53a9693..0b961cb9b150 100644 --- a/drivers/saul/init_devs/auto_init_hall_effect.c +++ b/drivers/saul/init_devs/auto_init_hall_effect.c @@ -3,7 +3,6 @@ * SPDX-License-Identifier: LGPL-2.1-only */ - /** * @ingroup drivers_hall_effect * @{ @@ -26,7 +25,7 @@ #define HALL_EFFECT_NUM ARRAY_SIZE(hall_effect_params) /** - * @brief Allocate memory for the device descriptors + * @brief Memory for the device descriptors */ static hall_effect_t hall_effect_devs[HALL_EFFECT_NUM]; diff --git a/examples/basic/saul/Makefile b/examples/basic/saul/Makefile index f1bc9c7713f5..be40c8ca57fe 100644 --- a/examples/basic/saul/Makefile +++ b/examples/basic/saul/Makefile @@ -14,8 +14,6 @@ USEMODULE += shell_cmds_default # additional modules for debugging: USEMODULE += ps -USEMODULE += hall_effect - # Comment this out to disable code in RIOT that does safety checking # which is not needed in a production environment but helps in the # development process: diff --git a/tests/drivers/hall_effect/README.md b/tests/drivers/hall_effect/README.md index 0a6c802fcc66..62181fd51c60 100644 --- a/tests/drivers/hall_effect/README.md +++ b/tests/drivers/hall_effect/README.md @@ -1,56 +1,7 @@ # About -This is a manual test application for the PIR motion sensor driver. - -In order to build this application, you need to add the board to the -Makefile's `WHITELIST` first and define a pin mapping (see below). - +This is a manual test application for a generic hall effect sensor. # Usage -There are two ways to test this. You can either actively poll the sensor -state, or you can register a thread which receives messages for state -changes. - -## Interrupt driven -Connect the sensor's "out" pin to a GPIO of your board that can be -configured to create interrupts. -Compile and flash this test application like: - - CFLAGS="-DPIR_PARAM_GPIO=name_of_your_pin -DPIR_PARAM_ACTIVE_HIGH=if_gpio_pin_is_high_or_low_when_pir_is_active" make BOARD=your_board clean all-interrupt flash - -Ideally, the above configuration passed via CFLAGS should be in "board.h" - -The output should look like: - - kernel_init(): jumping into first task... - - PIR motion sensor test application - - Initializing PIR sensor at GPIO_8... [OK] - - Registering PIR handler thread... [OK] - PIR handler got a message: the movement has ceased. - PIR handler got a message: something started moving. - PIR handler got a message: the movement has ceased. - - -## Polling Mode -Connect the sensor's "out" pin to any GPIO pin of you board. -Compile and flash this test application like: - - CFLAGS="-DPIR_PARAM_GPIO=name_of_your_pin -DPIR_PARAM_ACTIVE_HIGH=if_gpio_pin_is_high_or_low_when_pir_is_active" make BOARD=your_board make clean all-polling flash - -Ideally, the above configuration passed via CFLAGS should be in "board.h" - -The output should look like this: - - kernel_init(): jumping into first task... - PIR motion sensor test application - - Initializing PIR sensor at GPIO_10... [OK] - - Printing sensor state every second. - Status: lo - ... - Status: lo - Status: hi - ... +Connect a sensor - like a magnetic rotary encoder found on some dc motors - to your mcu. +The test will periodically print the measured RPM and number of revolutions +since the last readout. \ No newline at end of file diff --git a/tests/drivers/hall_effect/main.c b/tests/drivers/hall_effect/main.c index 1cd0b72257c6..0cd4cf4b09a7 100644 --- a/tests/drivers/hall_effect/main.c +++ b/tests/drivers/hall_effect/main.c @@ -3,7 +3,6 @@ * SPDX-License-Identifier: LGPL-2.1-only */ - /** * @ingroup tests * @{ @@ -43,7 +42,7 @@ int main(void) int32_t rpm; int32_t pulses; if (hall_effect_read_rpm(&dev, &rpm) - || hall_effect_read_pulse_counter_hundreths(&dev, &pulses)) { + || hall_effect_read_reset_ceti_revs(&dev, &pulses)) { puts("[Failed]"); return 1; } From 4a1a9cf1c687e42969fd5e2c124ce481a286a7d9 Mon Sep 17 00:00:00 2001 From: LeonardHerbst Date: Fri, 17 Oct 2025 13:55:58 +0200 Subject: [PATCH 03/15] tailing whitespace --- tests/drivers/hall_effect/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/drivers/hall_effect/README.md b/tests/drivers/hall_effect/README.md index 62181fd51c60..a7a254a1e220 100644 --- a/tests/drivers/hall_effect/README.md +++ b/tests/drivers/hall_effect/README.md @@ -3,5 +3,5 @@ This is a manual test application for a generic hall effect sensor. # Usage Connect a sensor - like a magnetic rotary encoder found on some dc motors - to your mcu. -The test will periodically print the measured RPM and number of revolutions +The test will periodically print the measured RPM and number of revolutions since the last readout. \ No newline at end of file From 64d76eb8fa6a59476eb279a97765159b8cd3ae2e Mon Sep 17 00:00:00 2001 From: LeonardHerbst Date: Fri, 17 Oct 2025 14:56:18 +0200 Subject: [PATCH 04/15] Introduced a macro to avoid using integer literals --- drivers/hall_effect/hall_effect.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/hall_effect/hall_effect.c b/drivers/hall_effect/hall_effect.c index 3f5d4e0c4ace..fe9be3bb0a60 100644 --- a/drivers/hall_effect/hall_effect.c +++ b/drivers/hall_effect/hall_effect.c @@ -23,6 +23,11 @@ #include "ztimer.h" #include "time_units.h" +/** + * @brief Scaling factor to apply to adjust for the gear reduction ratio being in tenths. + */ +#define GEAR_RED_RATIO_SCALE 10 + /* Prototypes */ static void _pulse_callback(void *arg); @@ -68,7 +73,7 @@ int hall_effect_read_rpm(hall_effect_t *dev, int32_t *rpm) * to obtain the rpm. Apply scaling factors like gear reduction * or pulses per revolution. */ - *rpm = SEC_PER_MIN * US_PER_SEC * 10 + *rpm = SEC_PER_MIN * US_PER_SEC * GEAR_RED_RATIO_SCALE / (delta_t * CONFIG_HALL_EFFECT_PPR * CONFIG_HALL_EFFECT_GEAR_RED_RATIO); if (ccw) { *rpm *= -1; @@ -79,7 +84,7 @@ int hall_effect_read_rpm(hall_effect_t *dev, int32_t *rpm) int hall_effect_read_reset_ceti_revs(hall_effect_t *dev, int32_t *pulse_counter) { _read_reset_pulse_counter(dev, pulse_counter); - *pulse_counter *= 1000; + *pulse_counter *= 100 * GEAR_RED_RATIO_SCALE; *pulse_counter /= CONFIG_HALL_EFFECT_PPR; *pulse_counter /= CONFIG_HALL_EFFECT_GEAR_RED_RATIO; return 0; From 96291be1c5394a47babee97a5ebad5e063e46e85 Mon Sep 17 00:00:00 2001 From: LeonardHerbst Date: Fri, 17 Oct 2025 16:49:22 +0200 Subject: [PATCH 05/15] Fixup --- drivers/hall_effect/Kconfig | 6 +++--- drivers/hall_effect/hall_effect.c | 17 ++++++++++------- .../hall_effect/include/hall_effect_params.h | 2 +- drivers/include/hall_effect.h | 9 ++++++--- sys/phydat/phydat_str.c | 2 +- tests/drivers/hall_effect/main.c | 6 ++---- 6 files changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/hall_effect/Kconfig b/drivers/hall_effect/Kconfig index b42dc614f323..2b0b4cbed41d 100644 --- a/drivers/hall_effect/Kconfig +++ b/drivers/hall_effect/Kconfig @@ -10,16 +10,16 @@ menu "Hall Effect RPM Driver" depends on USEMODULE_HALL_EFFECT config HALL_EFFECT_GEAR_RED_RATIO - int "gear reduction ratio (in tenths)" + int "Gear Reduction Ratio (in tenths)" default 204 help Defines the gear reduction ratio. For example a gear reduction ratio of 1:20.4 would result in a value of 204. config HALL_EFFECT_PPR - int "Pulses per revolution" + int "Pulses per Revolution" default 13 help - Number of high flanks per revolution. + Number of Rising Edges per Revolution. endmenu # Hall Effect Driver diff --git a/drivers/hall_effect/hall_effect.c b/drivers/hall_effect/hall_effect.c index fe9be3bb0a60..c4a530a60b49 100644 --- a/drivers/hall_effect/hall_effect.c +++ b/drivers/hall_effect/hall_effect.c @@ -47,7 +47,7 @@ int hall_effect_init(hall_effect_t *dev, const hall_effect_params_t *params) dev->delta_t = 0; dev->ccw = false; - dev->stale = false; + dev->stale = true; dev->pulse_counter = 0; dev->last_read_time = ztimer_now(ZTIMER_USEC); @@ -68,9 +68,9 @@ int hall_effect_read_rpm(hall_effect_t *dev, int32_t *rpm) return 0; } - /* delta_t represents the number of micro seconds since the last pulse. + /* delta_t represents the number of microseconds since the last pulse. * Invert and divide by the number of micro seconds per minute - * to obtain the rpm. Apply scaling factors like gear reduction + * to obtain the RPM. Apply scaling factors like gear reduction * or pulses per revolution. */ *rpm = SEC_PER_MIN * US_PER_SEC * GEAR_RED_RATIO_SCALE @@ -92,7 +92,7 @@ int hall_effect_read_reset_ceti_revs(hall_effect_t *dev, int32_t *pulse_counter) /* Private API */ -/* Triggered on the high flank of a pulse */ +/* Triggered on the rising edge of a pulse */ static void _pulse_callback(void *arg) { hall_effect_t *dev = (hall_effect_t *) arg; @@ -104,7 +104,8 @@ static void _pulse_callback(void *arg) if (now < dev->last_read_time) { /* timer had an overflow */ dev->delta_t = UINT32_MAX - dev->last_read_time + now + 1; - } else { + } + else { dev->delta_t = now - dev->last_read_time; } dev->last_read_time = now; @@ -135,7 +136,8 @@ static bool _read_delta_t_direction(hall_effect_t *dev, uint32_t *delta_t, bool if (now < dev->last_read_time) { /* the timer had an overflow */ pulse_age = UINT32_MAX - dev->last_read_time + now + 1; - } else { + } + else { pulse_age = now - dev->last_read_time; } if (pulse_age >= dev->delta_t) { @@ -143,7 +145,8 @@ static bool _read_delta_t_direction(hall_effect_t *dev, uint32_t *delta_t, bool * is longer than delta_t */ *delta_t = pulse_age; dev->stale= true; - } else { + } + else { *delta_t = dev->delta_t; } *ccw = dev->ccw; diff --git a/drivers/hall_effect/include/hall_effect_params.h b/drivers/hall_effect/include/hall_effect_params.h index e3f63bdc93a6..0d5da111a039 100644 --- a/drivers/hall_effect/include/hall_effect_params.h +++ b/drivers/hall_effect/include/hall_effect_params.h @@ -65,7 +65,7 @@ extern "C" { /**@}*/ /** - * @brief SAUL info for the rpm and pulse count driver + * @brief SAUL info for the RPM and pulse count driver */ #ifndef HALL_EFFECT_SAUL_INFO # define HALL_EFFECT_SAUL_INFO { { .name = "Hall Effect RPM Sensor" }, \ diff --git a/drivers/include/hall_effect.h b/drivers/include/hall_effect.h index 2ca9cd0c0860..7067bd62f3c9 100644 --- a/drivers/include/hall_effect.h +++ b/drivers/include/hall_effect.h @@ -8,7 +8,7 @@ /** * @defgroup drivers_hall_effect Generic Hall Effect Sensor * @ingroup drivers_sensors - * @brief Generic hall effect sensor to measur rpm and rotation count. + * @brief Generic hall effect sensor to measur RPM and rotation count. * * ## Description * This is a driver for a generic Hall effect sensor. These sensors are most often @@ -33,7 +33,10 @@ * @author Leonard Herbst */ +#include + #include "periph/gpio.h" +#include "irq.h" #ifdef __cplusplus extern "C" { @@ -71,11 +74,11 @@ typedef struct { int hall_effect_init(hall_effect_t *dev, const hall_effect_params_t *params); /** - * @brief Read the current rpm of the motor. + * @brief Read the current RPM of the motor. * * @param[in] dev Device descriptor of hall effect sensor * @param[out] rpm Revolutions per minute. - * Negative rpm responds to counter clock wise rotation. + * Negative RPM responds to counter clock wise rotation. * * @return 0 on success */ diff --git a/sys/phydat/phydat_str.c b/sys/phydat/phydat_str.c index d695a9579dda..15d6fb496c5d 100644 --- a/sys/phydat/phydat_str.c +++ b/sys/phydat/phydat_str.c @@ -111,7 +111,7 @@ static FLASH_ATTR const char _unit_square_metre[] = "m^2"; static FLASH_ATTR const char _unit_cubic_metre[] = "m^3"; static FLASH_ATTR const char _unit_g_force[] = "gₙ"; static FLASH_ATTR const char _unit_degree_per_second[] = "dps"; -static FLASH_ATTR const char _unit_revolutions_per_minute[] = "rpm"; +static FLASH_ATTR const char _unit_revolutions_per_minute[] = "RPM"; static FLASH_ATTR const char _unit_gram[] = "g"; static FLASH_ATTR const char _unit_ampere[] = "A"; static FLASH_ATTR const char _unit_volt[] = "V"; diff --git a/tests/drivers/hall_effect/main.c b/tests/drivers/hall_effect/main.c index 0cd4cf4b09a7..95c61b4894f2 100644 --- a/tests/drivers/hall_effect/main.c +++ b/tests/drivers/hall_effect/main.c @@ -16,6 +16,7 @@ */ #include +#include #include "hall_effect.h" #include "hall_effect_params.h" @@ -26,9 +27,6 @@ static hall_effect_t dev; int main(void) { puts("Generic hall effect sensor test application\n"); - printf("Initializing hall effect sensor with the interrupt pin at " - "GPIO_%d and the direction pin at GPIO_%d...\n", - HALL_EFFECT_INTERRUPT, HALL_EFFECT_DIRECTION); if (hall_effect_init(&dev, &hall_effect_params[0]) == 0) { puts("[OK]\n"); } @@ -46,7 +44,7 @@ int main(void) puts("[Failed]"); return 1; } - printf("SENSOR DATA:\n\tRPM: %ld\n\tPULSES: %ld\n", rpm, pulses); + printf("SENSOR DATA:\n\tRPM: %ld\n\tPULSES: %ld\n", (long) rpm, (long) pulses); ztimer_sleep(ZTIMER_SEC, 1); } } From 41dfe5aa80e29afdb7a2ad3209072b11a109b1dc Mon Sep 17 00:00:00 2001 From: LeonardHerbst Date: Fri, 17 Oct 2025 16:54:05 +0200 Subject: [PATCH 06/15] Fixup --- drivers/hall_effect/hall_effect_saul.c | 6 ++++-- drivers/include/hall_effect.h | 24 ++++++++++++------------ tests/drivers/hall_effect/main.c | 2 +- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/hall_effect/hall_effect_saul.c b/drivers/hall_effect/hall_effect_saul.c index a091c503c791..77dfb6274349 100644 --- a/drivers/hall_effect/hall_effect_saul.c +++ b/drivers/hall_effect/hall_effect_saul.c @@ -21,7 +21,8 @@ #include "saul.h" #include "hall_effect.h" -static int read_rpm(const void *dev, phydat_t *res) { +static int read_rpm(const void *dev, phydat_t *res) +{ hall_effect_t *d = (hall_effect_t *) dev; int32_t rpm; if (hall_effect_read_rpm(d, &rpm)) { @@ -34,7 +35,8 @@ static int read_rpm(const void *dev, phydat_t *res) { return 1; } -static int read_reset_pulse_counter(const void *dev, phydat_t *res) { +static int read_reset_pulse_counter(const void *dev, phydat_t *res) +{ hall_effect_t *d = (hall_effect_t *)dev; int32_t counter; if (hall_effect_read_reset_ceti_revs(d, &counter)) { diff --git a/drivers/include/hall_effect.h b/drivers/include/hall_effect.h index 7067bd62f3c9..61fac97052d3 100644 --- a/drivers/include/hall_effect.h +++ b/drivers/include/hall_effect.h @@ -65,33 +65,33 @@ typedef struct { /** * @brief Initialize the given device * - * @param[inout] dev Device descriptor of hall effect sensor - * @param[in] params Initialization parameters + * @param[inout] dev Device descriptor of hall effect sensor + * @param[in] params Initialization parameters * - * @retval 0 on success - * @retval -EIO on failure setting up the pins + * @retval 0 on success + * @retval -EIO on failure setting up the pins */ int hall_effect_init(hall_effect_t *dev, const hall_effect_params_t *params); /** * @brief Read the current RPM of the motor. * - * @param[in] dev Device descriptor of hall effect sensor - * @param[out] rpm Revolutions per minute. - * Negative RPM responds to counter clock wise rotation. + * @param[in] dev Device descriptor of hall effect sensor + * @param[out] rpm Revolutions per minute. + * Negative RPM responds to counter clock wise rotation. * - * @return 0 on success + * @return 0 on success */ int hall_effect_read_rpm(hall_effect_t *dev, int32_t *rpm); /** * @brief Read and reset number of revolutions since the last readout in hundredths. * - * @param[in] dev Device descriptor of hall effect sensor - * @param[out] rpm Number of revolutions since the last read in hundredths. - * Negative revolutions signal counter clock wise rotations. + * @param[in] dev Device descriptor of hall effect sensor + * @param[out] pulse_counter Number of revolutions since the last read in hundredths. + * Negative revolutions signal counter clock wise rotations. * - * @return 0 on success + * @return 0 on success */ int hall_effect_read_reset_ceti_revs(hall_effect_t *dev, int32_t *pulse_counter); diff --git a/tests/drivers/hall_effect/main.c b/tests/drivers/hall_effect/main.c index 95c61b4894f2..b08e47128471 100644 --- a/tests/drivers/hall_effect/main.c +++ b/tests/drivers/hall_effect/main.c @@ -1,5 +1,5 @@ /* -* SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-FileCopyrightText: 2025 TU Dresden * SPDX-License-Identifier: LGPL-2.1-only */ From 0fe05588cb79800377741dcfd7b3637ada12dddf Mon Sep 17 00:00:00 2001 From: LeonardHerbst Date: Fri, 17 Oct 2025 17:11:30 +0200 Subject: [PATCH 07/15] Fixup --- drivers/include/hall_effect.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/include/hall_effect.h b/drivers/include/hall_effect.h index 61fac97052d3..348903df000c 100644 --- a/drivers/include/hall_effect.h +++ b/drivers/include/hall_effect.h @@ -16,6 +16,20 @@ * The sensor generates a fixed number of pulses per rotation on two output pins. * These signals are phase-shifted slightly, enabling the detection of rotation direction. * + * These encoders typically have four wires: + * - GNR (Ground) + * - VCC (Power) + * - Phase A + * - Phase B + * + * Phase A should be connected to the pin configured as the interrupt pin, + * while Phase B (the shifted signal) should be connected to the direction pin. + * + * If the Phase A and Phase B connections are swapped, the detected rotation direction will be reversed. + * When only Phase A is connected, the encoder will still report movement, + * but there will be no distinction between counter clock wise and clock wise rotation. + * An example for such an encoder can be found [here](https://www.dfrobot.com/product-1617.html) + * * The driver provides functions to read the current RPM * and the total number of revolutions (in hundredths) since the last measurement. * From 4f869b0f953927eda3027d18c7fe6ee33f03f34a Mon Sep 17 00:00:00 2001 From: LeonardHerbst Date: Mon, 20 Oct 2025 09:47:15 +0200 Subject: [PATCH 08/15] Renamed the driver. --- drivers/Kconfig | 2 +- drivers/hall_effect/Makefile.include | 2 - .../hall_effect/include/hall_effect_params.h | 95 ------------------- drivers/{hall_effect => inc_encoder}/Kconfig | 14 +-- drivers/{hall_effect => inc_encoder}/Makefile | 0 .../{hall_effect => inc_encoder}/Makefile.dep | 0 drivers/inc_encoder/Makefile.include | 2 + .../inc_encoder.c} | 34 +++---- .../inc_encoder_saul.c} | 18 ++-- .../inc_encoder/include/inc_encoder_params.h | 95 +++++++++++++++++++ .../include/{hall_effect.h => inc_encoder.h} | 50 +++++----- .../saul/init_devs/auto_init_hall_effect.c | 64 ------------- .../saul/init_devs/auto_init_inc_encoder.c | 64 +++++++++++++ drivers/saul/init_devs/init.c | 6 +- .../{hall_effect => inc_encoder}/Makefile | 2 +- .../{hall_effect => inc_encoder}/Makefile.ci | 0 .../{hall_effect => inc_encoder}/README.md | 2 +- .../{hall_effect => inc_encoder}/main.c | 16 ++-- 18 files changed, 233 insertions(+), 233 deletions(-) delete mode 100644 drivers/hall_effect/Makefile.include delete mode 100644 drivers/hall_effect/include/hall_effect_params.h rename drivers/{hall_effect => inc_encoder}/Kconfig (64%) rename drivers/{hall_effect => inc_encoder}/Makefile (100%) rename drivers/{hall_effect => inc_encoder}/Makefile.dep (100%) create mode 100644 drivers/inc_encoder/Makefile.include rename drivers/{hall_effect/hall_effect.c => inc_encoder/inc_encoder.c} (76%) rename drivers/{hall_effect/hall_effect_saul.c => inc_encoder/inc_encoder_saul.c} (66%) create mode 100644 drivers/inc_encoder/include/inc_encoder_params.h rename drivers/include/{hall_effect.h => inc_encoder.h} (59%) delete mode 100644 drivers/saul/init_devs/auto_init_hall_effect.c create mode 100644 drivers/saul/init_devs/auto_init_inc_encoder.c rename tests/drivers/{hall_effect => inc_encoder}/Makefile (79%) rename tests/drivers/{hall_effect => inc_encoder}/Makefile.ci (100%) rename tests/drivers/{hall_effect => inc_encoder}/README.md (72%) rename tests/drivers/{hall_effect => inc_encoder}/main.c (63%) diff --git a/drivers/Kconfig b/drivers/Kconfig index 16b1ae0eaec8..89c5a0213841 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -33,7 +33,7 @@ rsource "ads101x/Kconfig" rsource "bmx055/Kconfig" rsource "fxos8700/Kconfig" rsource "gp2y10xx/Kconfig" -rsource "hall_effect/Kconfig" +rsource "rotary_encoder/Kconfig" rsource "hdc1000/Kconfig" rsource "hm330x/Kconfig" rsource "hsc/Kconfig" diff --git a/drivers/hall_effect/Makefile.include b/drivers/hall_effect/Makefile.include deleted file mode 100644 index da4103ccc765..000000000000 --- a/drivers/hall_effect/Makefile.include +++ /dev/null @@ -1,2 +0,0 @@ -USEMODULE_INCLUDES_hall_effect := $(LAST_MAKEFILEDIR)/include -USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_hall_effect) diff --git a/drivers/hall_effect/include/hall_effect_params.h b/drivers/hall_effect/include/hall_effect_params.h deleted file mode 100644 index 0d5da111a039..000000000000 --- a/drivers/hall_effect/include/hall_effect_params.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 TU Dresden - * SPDX-License-Identifier: LGPL-2.1-only - */ - -#pragma once - -/** - * @ingroup drivers_hall_effect - * - * @{ - * @file - * @brief Default configuration for a generic hall effect sensor. - * - * @author Leonard Herbst - */ - -#include "board.h" -#include "hall_effect.h" -#include "saul_reg.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Default gear reduction ratio - */ -#ifndef CONFIG_HALL_EFFECT_GEAR_RED_RATIO -# define CONFIG_HALL_EFFECT_GEAR_RED_RATIO 204 -#endif - -/** - * @brief Default number of pulses per revolution - */ -#ifndef CONFIG_HALL_EFFECT_PPR -# define CONFIG_HALL_EFFECT_PPR 13 -#endif - -/** - * @name default configuration parameters for a generic hall effect sensor - * @{ - */ -/** - * @brief Default pin of the first phase used to trigger the interrupt - */ -#ifndef HALL_EFFECT_INTERRUPT -# define HALL_EFFECT_INTERRUPT GPIO_PIN(1, 1) -#endif - -/** - * @brief Default pin of the second (offset) phase used to determine the direction - */ -#ifndef HALL_EFFECT_DIRECTION -# define HALL_EFFECT_DIRECTION GPIO_PIN(1, 2) -#endif - -/** - * @brief Default parameters - */ -#ifndef HALL_EFFECT_PARAMS -# define HALL_EFFECT_PARAMS { .interrupt = HALL_EFFECT_INTERRUPT, \ - .direction = HALL_EFFECT_DIRECTION } -#endif -/**@}*/ - -/** - * @brief SAUL info for the RPM and pulse count driver - */ -#ifndef HALL_EFFECT_SAUL_INFO -# define HALL_EFFECT_SAUL_INFO { { .name = "Hall Effect RPM Sensor" }, \ - { .name = "Hall Effect Pulse Count Sensor" } } -#endif - -/** - * @brief Hall effect sensor configuration - */ -static const hall_effect_params_t hall_effect_params[] = -{ - HALL_EFFECT_PARAMS -}; - -/** - * @brief Additional meta information to keep in the SAUL registry - */ -static const saul_reg_info_t hall_effect_saul_info[][2] = -{ - HALL_EFFECT_SAUL_INFO -}; - -#ifdef __cplusplus -} -#endif - -/** @} */ diff --git a/drivers/hall_effect/Kconfig b/drivers/inc_encoder/Kconfig similarity index 64% rename from drivers/hall_effect/Kconfig rename to drivers/inc_encoder/Kconfig index 2b0b4cbed41d..23d0ddd30903 100644 --- a/drivers/hall_effect/Kconfig +++ b/drivers/inc_encoder/Kconfig @@ -1,25 +1,25 @@ # SPDX-FileCopyrightText: 2025 TU Dresden # SPDX-License-Identifier: LGPL-2.1-only -config MODULE_HALL_EFFECT - bool "Hall Effect RPM Sensor" +config MODULE_INC_ENCODER + bool "Incremental Rotary Encoder" depends on TEST_KCONFIG -menu "Hall Effect RPM Driver" - depends on USEMODULE_HALL_EFFECT +menu "Incremental Rotary Encoder Driver" + depends on USEMODULE_INC_ENCODER - config HALL_EFFECT_GEAR_RED_RATIO + config INC_ENCODER_GEAR_RED_RATIO int "Gear Reduction Ratio (in tenths)" default 204 help Defines the gear reduction ratio. For example a gear reduction ratio of 1:20.4 would result in a value of 204. - config HALL_EFFECT_PPR + config INC_ENCODER_PPR int "Pulses per Revolution" default 13 help Number of Rising Edges per Revolution. -endmenu # Hall Effect Driver +endmenu # Incremental Encoder Driver diff --git a/drivers/hall_effect/Makefile b/drivers/inc_encoder/Makefile similarity index 100% rename from drivers/hall_effect/Makefile rename to drivers/inc_encoder/Makefile diff --git a/drivers/hall_effect/Makefile.dep b/drivers/inc_encoder/Makefile.dep similarity index 100% rename from drivers/hall_effect/Makefile.dep rename to drivers/inc_encoder/Makefile.dep diff --git a/drivers/inc_encoder/Makefile.include b/drivers/inc_encoder/Makefile.include new file mode 100644 index 000000000000..ddf65e7c3a39 --- /dev/null +++ b/drivers/inc_encoder/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE_INCLUDES_inc_encoder := $(LAST_MAKEFILEDIR)/include +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_inc_encoder) diff --git a/drivers/hall_effect/hall_effect.c b/drivers/inc_encoder/inc_encoder.c similarity index 76% rename from drivers/hall_effect/hall_effect.c rename to drivers/inc_encoder/inc_encoder.c index c4a530a60b49..5a8581851817 100644 --- a/drivers/hall_effect/hall_effect.c +++ b/drivers/inc_encoder/inc_encoder.c @@ -4,19 +4,19 @@ */ /** - * @ingroup drivers_hall_effect + * @ingroup drivers_inc_encoder * @{ * * @file - * @brief Device driver implementation for a generic hall effect sensor + * @brief Device driver implementation for a generic incremental rotary encoder * * @author Leonard Herbst * * @} */ -#include "hall_effect.h" -#include "hall_effect_params.h" +#include "inc_encoder.h" +#include "inc_encoder_params.h" #include #include "log.h" @@ -31,17 +31,17 @@ /* Prototypes */ static void _pulse_callback(void *arg); -static void _read_reset_pulse_counter(hall_effect_t *dev, int32_t *pulse_counter); -static bool _read_delta_t_direction(hall_effect_t *dev, uint32_t *delta_t, bool *ccw); +static void _read_reset_pulse_counter(inc_encoder_t *dev, int32_t *pulse_counter); +static bool _read_delta_t_direction(inc_encoder_t *dev, uint32_t *delta_t, bool *ccw); /* Public API */ -int hall_effect_init(hall_effect_t *dev, const hall_effect_params_t *params) +int inc_encoder_init(inc_encoder_t *dev, const inc_encoder_params_t *params) { dev->params = *params; if (gpio_init(dev->params.direction, GPIO_IN)) { - LOG_ERROR("[hall_effect] Failed configuring the direction pin as an input!\n"); + LOG_ERROR("[inc_encoder] Failed configuring the direction pin as an input!\n"); return -EIO; } @@ -52,14 +52,14 @@ int hall_effect_init(hall_effect_t *dev, const hall_effect_params_t *params) dev->last_read_time = ztimer_now(ZTIMER_USEC); if (gpio_init_int(dev->params.interrupt, GPIO_IN, GPIO_RISING, _pulse_callback, (void *) dev)) { - LOG_ERROR("[hall_effect] Failed configuring the interrupt pin!\n"); + LOG_ERROR("[inc_encoder] Failed configuring the interrupt pin!\n"); return -EIO; } return 0; } -int hall_effect_read_rpm(hall_effect_t *dev, int32_t *rpm) +int inc_encoder_read_rpm(inc_encoder_t *dev, int32_t *rpm) { uint32_t delta_t; bool ccw; @@ -74,19 +74,19 @@ int hall_effect_read_rpm(hall_effect_t *dev, int32_t *rpm) * or pulses per revolution. */ *rpm = SEC_PER_MIN * US_PER_SEC * GEAR_RED_RATIO_SCALE - / (delta_t * CONFIG_HALL_EFFECT_PPR * CONFIG_HALL_EFFECT_GEAR_RED_RATIO); + / (delta_t * CONFIG_INC_ENCODER_PPR * CONFIG_INC_ENCODER_GEAR_RED_RATIO); if (ccw) { *rpm *= -1; } return 0; } -int hall_effect_read_reset_ceti_revs(hall_effect_t *dev, int32_t *pulse_counter) +int inc_encoder_read_reset_ceti_revs(inc_encoder_t *dev, int32_t *pulse_counter) { _read_reset_pulse_counter(dev, pulse_counter); *pulse_counter *= 100 * GEAR_RED_RATIO_SCALE; - *pulse_counter /= CONFIG_HALL_EFFECT_PPR; - *pulse_counter /= CONFIG_HALL_EFFECT_GEAR_RED_RATIO; + *pulse_counter /= CONFIG_INC_ENCODER_PPR; + *pulse_counter /= CONFIG_INC_ENCODER_GEAR_RED_RATIO; return 0; } @@ -95,7 +95,7 @@ int hall_effect_read_reset_ceti_revs(hall_effect_t *dev, int32_t *pulse_counter) /* Triggered on the rising edge of a pulse */ static void _pulse_callback(void *arg) { - hall_effect_t *dev = (hall_effect_t *) arg; + inc_encoder_t *dev = (inc_encoder_t *) arg; uint32_t now = ztimer_now(ZTIMER_USEC); @@ -114,7 +114,7 @@ static void _pulse_callback(void *arg) dev->stale= false; } -static void _read_reset_pulse_counter(hall_effect_t *dev, int32_t *pulse_counter) +static void _read_reset_pulse_counter(inc_encoder_t *dev, int32_t *pulse_counter) { int irq_state = irq_disable(); *pulse_counter = dev->pulse_counter; @@ -122,7 +122,7 @@ static void _read_reset_pulse_counter(hall_effect_t *dev, int32_t *pulse_counter irq_restore(irq_state); } -static bool _read_delta_t_direction(hall_effect_t *dev, uint32_t *delta_t, bool *ccw) +static bool _read_delta_t_direction(inc_encoder_t *dev, uint32_t *delta_t, bool *ccw) { int irq_state = irq_disable(); /* There have been no pulses for a while -> rotation probably stopped. */ diff --git a/drivers/hall_effect/hall_effect_saul.c b/drivers/inc_encoder/inc_encoder_saul.c similarity index 66% rename from drivers/hall_effect/hall_effect_saul.c rename to drivers/inc_encoder/inc_encoder_saul.c index 77dfb6274349..b956c18ac964 100644 --- a/drivers/hall_effect/hall_effect_saul.c +++ b/drivers/inc_encoder/inc_encoder_saul.c @@ -4,11 +4,11 @@ */ /** - * @ingroup drivers_hall_effect + * @ingroup drivers_inc_encoder * @{ * * @file - * @brief Hall effect sensor adaption to the RIOT actuator/sensor interface + * @brief Generic incremental rotary encoder adaption to the RIOT actuator/sensor interface * * @author Leonard Herbst * @@ -19,13 +19,13 @@ #include #include "saul.h" -#include "hall_effect.h" +#include "inc_encoder.h" static int read_rpm(const void *dev, phydat_t *res) { - hall_effect_t *d = (hall_effect_t *) dev; + inc_encoder_t *d = (inc_encoder_t *) dev; int32_t rpm; - if (hall_effect_read_rpm(d, &rpm)) { + if (inc_encoder_read_rpm(d, &rpm)) { /* Read failure */ return -ECANCELED; } @@ -37,9 +37,9 @@ static int read_rpm(const void *dev, phydat_t *res) static int read_reset_pulse_counter(const void *dev, phydat_t *res) { - hall_effect_t *d = (hall_effect_t *)dev; + inc_encoder_t *d = (inc_encoder_t *)dev; int32_t counter; - if (hall_effect_read_reset_ceti_revs(d, &counter)) { + if (inc_encoder_read_reset_ceti_revs(d, &counter)) { /* Read failure */ return -ECANCELED; } @@ -49,13 +49,13 @@ static int read_reset_pulse_counter(const void *dev, phydat_t *res) return 1; } -const saul_driver_t hall_effect_rpm_saul_driver = { +const saul_driver_t inc_encoder_rpm_saul_driver = { .read = read_rpm, .write = saul_write_notsup, .type = SAUL_SENSE_SPEED, }; -const saul_driver_t hall_pulse_count_saul_driver = { +const saul_driver_t inc_encoder_pulse_count_saul_driver = { .read = read_reset_pulse_counter, .write = saul_write_notsup, .type = SAUL_SENSE_COUNT, diff --git a/drivers/inc_encoder/include/inc_encoder_params.h b/drivers/inc_encoder/include/inc_encoder_params.h new file mode 100644 index 000000000000..077be4a045ee --- /dev/null +++ b/drivers/inc_encoder/include/inc_encoder_params.h @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @ingroup drivers_inc_encoder + * + * @{ + * @file + * @brief Default configuration for a generic incremental rotary encoder. + * + * @author Leonard Herbst + */ + +#include "board.h" +#include "inc_encoder.h" +#include "saul_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Default gear reduction ratio + */ +#ifndef CONFIG_INC_ENCODER_GEAR_RED_RATIO +# define CONFIG_INC_ENCODER_GEAR_RED_RATIO 204 +#endif + +/** + * @brief Default number of pulses per revolution + */ +#ifndef CONFIG_INC_ENCODER_PPR +# define CONFIG_INC_ENCODER_PPR 13 +#endif + +/** + * @name default configuration parameters for a generic incremental rotary encoder + * @{ + */ +/** + * @brief Default pin of the first phase used to trigger the interrupt + */ +#ifndef INC_ENCODER_INTERRUPT +# define INC_ENCODER_INTERRUPT GPIO_PIN(1, 1) +#endif + +/** + * @brief Default pin of the second (shifted) phase used to determine the direction + */ +#ifndef INC_ENCODER_DIRECTION +# define INC_ENCODER_DIRECTION GPIO_PIN(1, 2) +#endif + +/** + * @brief Default parameters + */ +#ifndef INC_ENCODER_PARAMS +# define INC_ENCODER_PARAMS { .interrupt = INC_ENCODER_INTERRUPT, \ + .direction = INC_ENCODER_DIRECTION } +#endif +/**@}*/ + +/** + * @brief SAUL info for the RPM and pulse count driver + */ +#ifndef INC_ENCODER_SAUL_INFO +# define INC_ENCODER_SAUL_INFO { { .name = "Incremental Rotary Encoder RPM Sensor" }, \ + { .name = "Incremental Rotary Encoder Pulse Count Sensor" } } +#endif + +/** + * @brief Incremental rotary encoder configuration + */ +static const inc_encoder_params_t inc_encoder_params[] = +{ + INC_ENCODER_PARAMS +}; + +/** + * @brief Additional meta information to keep in the SAUL registry + */ +static const saul_reg_info_t inc_encoder_saul_info[][2] = +{ + INC_ENCODER_SAUL_INFO +}; + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/drivers/include/hall_effect.h b/drivers/include/inc_encoder.h similarity index 59% rename from drivers/include/hall_effect.h rename to drivers/include/inc_encoder.h index 348903df000c..352ddd36922b 100644 --- a/drivers/include/hall_effect.h +++ b/drivers/include/inc_encoder.h @@ -6,12 +6,12 @@ #pragma once /** - * @defgroup drivers_hall_effect Generic Hall Effect Sensor + * @defgroup drivers_inc_encoder Generic Incremental Rotary Encoder * @ingroup drivers_sensors - * @brief Generic hall effect sensor to measur RPM and rotation count. + * @brief Generic incremental rotary encoder to measur RPM and rotation count. * * ## Description - * This is a driver for a generic Hall effect sensor. These sensors are most often + * This is a driver for a generic incremental rotary encoder. These sensors are most often * used with motors. * The sensor generates a fixed number of pulses per rotation on two output pins. * These signals are phase-shifted slightly, enabling the detection of rotation direction. @@ -36,7 +36,7 @@ * Configuration options are available via Kconfig to specify * the number of pulses per rotation and the gear reduction ratio (in tenths). * - * @note After approximately 327 revolutions without reading and resetting + * @note After 327 revolutions without reading and resetting * the revolution counter, the `phydat` value will overflow. * Use the regular driver interface instead of SAUL if necessary. * @@ -62,52 +62,52 @@ extern "C" { typedef struct { gpio_t interrupt; /**< Interrupt pin (first phase) */ gpio_t direction; /**< Pin used to determine the direction (shifted phase) */ -} hall_effect_params_t; +} inc_encoder_params_t; /** * @brief Device descriptor for the driver */ typedef struct { - hall_effect_params_t params; /**< configuration parameters */ + inc_encoder_params_t params; /**< configuration parameters */ uint32_t delta_t; /**< time delta since the last read */ int32_t pulse_counter; /**< number of pulses since last read */ uint32_t last_read_time; /**< time of the last read */ bool ccw; /**< counter clock wise rotation */ bool stale; /**< indicates that there is no new data to be read */ -} hall_effect_t; +} inc_encoder_t; /** - * @brief Initialize the given device + * @brief Initialize the given device * - * @param[inout] dev Device descriptor of hall effect sensor - * @param[in] params Initialization parameters + * @param[in, out] dev Device descriptor of incremental rotary encoder + * @param[in] params Initialization parameters * - * @retval 0 on success - * @retval -EIO on failure setting up the pins + * @retval 0 on success + * @retval -EIO on failure setting up the pins */ -int hall_effect_init(hall_effect_t *dev, const hall_effect_params_t *params); +int inc_encoder_init(inc_encoder_t *dev, const inc_encoder_params_t *params); /** - * @brief Read the current RPM of the motor. + * @brief Read the current RPM of the motor. * - * @param[in] dev Device descriptor of hall effect sensor - * @param[out] rpm Revolutions per minute. - * Negative RPM responds to counter clock wise rotation. + * @param[in] dev Device descriptor of incremental rotary encoder + * @param[out] rpm Revolutions per minute. + * Negative RPM responds to counter clock wise rotation. * - * @return 0 on success + * @return 0 on success */ -int hall_effect_read_rpm(hall_effect_t *dev, int32_t *rpm); +int inc_encoder_read_rpm(inc_encoder_t *dev, int32_t *rpm); /** - * @brief Read and reset number of revolutions since the last readout in hundredths. + * @brief Read and reset number of revolutions since the last readout in hundredths. * - * @param[in] dev Device descriptor of hall effect sensor - * @param[out] pulse_counter Number of revolutions since the last read in hundredths. - * Negative revolutions signal counter clock wise rotations. + * @param[in] dev Device descriptor of incremental rotary encoder + * @param[out] pulse_counter Number of revolutions since the last read in hundredths. + * Negative revolutions signal counter clock wise rotations. * - * @return 0 on success + * @return 0 on success */ -int hall_effect_read_reset_ceti_revs(hall_effect_t *dev, int32_t *pulse_counter); +int inc_encoder_read_reset_ceti_revs(inc_encoder_t *dev, int32_t *pulse_counter); #ifdef __cplusplus } diff --git a/drivers/saul/init_devs/auto_init_hall_effect.c b/drivers/saul/init_devs/auto_init_hall_effect.c deleted file mode 100644 index 0b961cb9b150..000000000000 --- a/drivers/saul/init_devs/auto_init_hall_effect.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 TU Dresden - * SPDX-License-Identifier: LGPL-2.1-only - */ - -/** - * @ingroup drivers_hall_effect - * @{ - * - * @file - * @brief SAUL adaption for generic hall effect sensor - * to measure the number as well as rate of rotation. - * - * @author Leonard Herbst - * @} - */ - -#include "log.h" -#include "saul_reg.h" -#include "hall_effect_params.h" - -/** - * @brief Define the number of configured hall effect sensors - */ -#define HALL_EFFECT_NUM ARRAY_SIZE(hall_effect_params) - -/** - * @brief Memory for the device descriptors - */ -static hall_effect_t hall_effect_devs[HALL_EFFECT_NUM]; - -/** - * @brief Memory for the SAUL registry entries - */ -static saul_reg_t saul_entries[HALL_EFFECT_NUM * 2]; - -extern saul_driver_t hall_effect_rpm_saul_driver; -extern saul_driver_t hall_pulse_count_saul_driver; - -void auto_init_hall_effect(void) -{ - for (unsigned i = 0; i < HALL_EFFECT_NUM; i++) { - LOG_DEBUG("[auto_init_saul] initializing hall_effect #%u\n", i); - - int res = hall_effect_init(&hall_effect_devs[i], &hall_effect_params[i]); - if (res != 0) { - LOG_ERROR("[auto_init_saul] error initializing hall_effect #%u\n", i); - continue; - } - - /* RPM */ - saul_entries[(i * 2)].dev = &(hall_effect_devs[i]); - saul_entries[(i * 2)].name = hall_effect_saul_info[i][0].name; - saul_entries[(i * 2)].driver = &hall_effect_rpm_saul_driver; - - /* Pulse Count */ - saul_entries[(i * 2) + 1].dev = &(hall_effect_devs[i]); - saul_entries[(i * 2) + 1].name = hall_effect_saul_info[i][1].name; - saul_entries[(i * 2) + 1].driver = &hall_pulse_count_saul_driver; - - saul_reg_add(&(saul_entries[(i * 2)])); - saul_reg_add(&(saul_entries[(i * 2) + 1])); - } -} diff --git a/drivers/saul/init_devs/auto_init_inc_encoder.c b/drivers/saul/init_devs/auto_init_inc_encoder.c new file mode 100644 index 000000000000..1b5695aa879a --- /dev/null +++ b/drivers/saul/init_devs/auto_init_inc_encoder.c @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup drivers_inc_encoder + * @{ + * + * @file + * @brief SAUL adaption for generic incremental rotary encoder + * to measure the number as well as rate of rotation. + * + * @author Leonard Herbst + * @} + */ + +#include "log.h" +#include "saul_reg.h" +#include "inc_encoder_params.h" + +/** + * @brief Define the number of configured incremental rotary encoders + */ +#define INC_ENCODER_NUM ARRAY_SIZE(inc_encoder_params) + +/** + * @brief Memory for the device descriptors + */ +static inc_encoder_t inc_encoder_devs[INC_ENCODER_NUM]; + +/** + * @brief Memory for the SAUL registry entries + */ +static saul_reg_t saul_entries[INC_ENCODER_NUM * 2]; + +extern saul_driver_t inc_encoder_rpm_saul_driver; +extern saul_driver_t inc_encoder_pulse_count_saul_driver; + +void auto_init_inc_encoder(void) +{ + for (unsigned i = 0; i < INC_ENCODER_NUM; i++) { + LOG_DEBUG("[auto_init_saul] initializing inc_encoder #%u\n", i); + + int res = inc_encoder_init(&inc_encoder_devs[i], &inc_encoder_params[i]); + if (res != 0) { + LOG_ERROR("[auto_init_saul] error initializing inc_encoder #%u\n", i); + continue; + } + + /* RPM */ + saul_entries[(i * 2)].dev = &(inc_encoder_devs[i]); + saul_entries[(i * 2)].name = inc_encoder_saul_info[i][0].name; + saul_entries[(i * 2)].driver = &inc_encoder_rpm_saul_driver; + + /* Pulse Count */ + saul_entries[(i * 2) + 1].dev = &(inc_encoder_devs[i]); + saul_entries[(i * 2) + 1].name = inc_encoder_saul_info[i][1].name; + saul_entries[(i * 2) + 1].driver = &inc_encoder_pulse_count_saul_driver; + + saul_reg_add(&(saul_entries[(i * 2)])); + saul_reg_add(&(saul_entries[(i * 2) + 1])); + } +} diff --git a/drivers/saul/init_devs/init.c b/drivers/saul/init_devs/init.c index 92f5e721d790..c91ac0c8cf3b 100644 --- a/drivers/saul/init_devs/init.c +++ b/drivers/saul/init_devs/init.c @@ -123,9 +123,9 @@ void saul_init_devs(void) extern void auto_init_grove_ledbar(void); auto_init_grove_ledbar(); } - if (IS_USED(MODULE_HALL_EFFECT)) { - extern void auto_init_hall_effect(void); - auto_init_hall_effect(); + if (IS_USED(MODULE_INC_ENCODER)) { + extern void auto_init_inc_encoder(void); + auto_init_inc_encoder(); } if (IS_USED(MODULE_HMC5883L)) { extern void auto_init_hmc5883l(void); diff --git a/tests/drivers/hall_effect/Makefile b/tests/drivers/inc_encoder/Makefile similarity index 79% rename from tests/drivers/hall_effect/Makefile rename to tests/drivers/inc_encoder/Makefile index bcd89948aab3..ef01c7eeec3a 100644 --- a/tests/drivers/hall_effect/Makefile +++ b/tests/drivers/inc_encoder/Makefile @@ -1,6 +1,6 @@ include ../Makefile.drivers_common -USEMODULE += hall_effect +USEMODULE += inc_encoder USEMODULE += ztimer_sec include $(RIOTBASE)/Makefile.include diff --git a/tests/drivers/hall_effect/Makefile.ci b/tests/drivers/inc_encoder/Makefile.ci similarity index 100% rename from tests/drivers/hall_effect/Makefile.ci rename to tests/drivers/inc_encoder/Makefile.ci diff --git a/tests/drivers/hall_effect/README.md b/tests/drivers/inc_encoder/README.md similarity index 72% rename from tests/drivers/hall_effect/README.md rename to tests/drivers/inc_encoder/README.md index a7a254a1e220..72d68153095d 100644 --- a/tests/drivers/hall_effect/README.md +++ b/tests/drivers/inc_encoder/README.md @@ -1,5 +1,5 @@ # About -This is a manual test application for a generic hall effect sensor. +This is a manual test application for a generic incremental rotary encoder. # Usage Connect a sensor - like a magnetic rotary encoder found on some dc motors - to your mcu. diff --git a/tests/drivers/hall_effect/main.c b/tests/drivers/inc_encoder/main.c similarity index 63% rename from tests/drivers/hall_effect/main.c rename to tests/drivers/inc_encoder/main.c index b08e47128471..91e78202c76c 100644 --- a/tests/drivers/hall_effect/main.c +++ b/tests/drivers/inc_encoder/main.c @@ -8,7 +8,7 @@ * @{ * * @file - * @brief Test application for a generic hall effect sensor + * @brief Test application for a generic incremental rotary encoder * * @author Leonard Herbst * @@ -18,16 +18,16 @@ #include #include -#include "hall_effect.h" -#include "hall_effect_params.h" +#include "inc_encoder.h" +#include "inc_encoder_params.h" #include "ztimer.h" -static hall_effect_t dev; +static inc_encoder_t dev; int main(void) { - puts("Generic hall effect sensor test application\n"); - if (hall_effect_init(&dev, &hall_effect_params[0]) == 0) { + puts("Generic incremental rotary encoder test application\n"); + if (inc_encoder_init(&dev, &inc_encoder_params[0]) == 0) { puts("[OK]\n"); } else { @@ -39,8 +39,8 @@ int main(void) while (1) { int32_t rpm; int32_t pulses; - if (hall_effect_read_rpm(&dev, &rpm) - || hall_effect_read_reset_ceti_revs(&dev, &pulses)) { + if (inc_encoder_read_rpm(&dev, &rpm) + || inc_encoder_read_reset_ceti_revs(&dev, &pulses)) { puts("[Failed]"); return 1; } From 40f4bdc03edaeb680a25edfa6eafefd74fb49c38 Mon Sep 17 00:00:00 2001 From: LeonardHerbst Date: Mon, 20 Oct 2025 09:50:21 +0200 Subject: [PATCH 09/15] split a long line --- drivers/include/inc_encoder.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/include/inc_encoder.h b/drivers/include/inc_encoder.h index 352ddd36922b..4980eef99b92 100644 --- a/drivers/include/inc_encoder.h +++ b/drivers/include/inc_encoder.h @@ -25,7 +25,8 @@ * Phase A should be connected to the pin configured as the interrupt pin, * while Phase B (the shifted signal) should be connected to the direction pin. * - * If the Phase A and Phase B connections are swapped, the detected rotation direction will be reversed. + * If the Phase A and Phase B connections are swapped, + * the detected rotation direction will be reversed. * When only Phase A is connected, the encoder will still report movement, * but there will be no distinction between counter clock wise and clock wise rotation. * An example for such an encoder can be found [here](https://www.dfrobot.com/product-1617.html) From 9df6e4ac4358988af4c2cc79685a1c88ad9a3229 Mon Sep 17 00:00:00 2001 From: LeonardHerbst Date: Mon, 20 Oct 2025 10:18:57 +0200 Subject: [PATCH 10/15] Fixed typo --- drivers/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/Kconfig b/drivers/Kconfig index 89c5a0213841..842defd04474 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -33,7 +33,7 @@ rsource "ads101x/Kconfig" rsource "bmx055/Kconfig" rsource "fxos8700/Kconfig" rsource "gp2y10xx/Kconfig" -rsource "rotary_encoder/Kconfig" +rsource "inc_encoder/Kconfig" rsource "hdc1000/Kconfig" rsource "hm330x/Kconfig" rsource "hsc/Kconfig" From 4f4fd33cab18c332a3079b9392009023aef2db0d Mon Sep 17 00:00:00 2001 From: LeonardHerbst Date: Wed, 29 Oct 2025 14:46:58 +0100 Subject: [PATCH 11/15] Added as an optional backend. --- drivers/inc_encoder/Kconfig | 13 ++ drivers/inc_encoder/Makefile | 8 ++ drivers/inc_encoder/Makefile.dep | 11 +- drivers/inc_encoder/Makefile.include | 5 + .../inc_encoder/backends/hardware/Makefile | 9 ++ .../backends/hardware/inc_encoder_hardware.c | 135 ++++++++++++++++++ .../inc_encoder/backends/software/Makefile | 9 ++ .../software/inc_encoder_software.c} | 84 +++++------ .../include/inc_encoder_constants.h | 35 +++++ .../inc_encoder/include/inc_encoder_params.h | 49 +++++-- drivers/include/inc_encoder.h | 72 ++++++++-- tests/drivers/inc_encoder/Makefile | 8 ++ tests/drivers/inc_encoder/README.md | 6 +- .../external_boards/nrf52840dk_qdec/Kconfig | 13 ++ .../external_boards/nrf52840dk_qdec/Makefile | 6 + .../nrf52840dk_qdec/Makefile.dep | 4 + .../nrf52840dk_qdec/Makefile.features | 3 + .../nrf52840dk_qdec/Makefile.include | 1 + .../nrf52840dk_qdec/include/board.h | 1 + .../nrf52840dk_qdec/include/periph_conf.h | 75 ++++++++++ tests/drivers/inc_encoder/main.c | 2 +- 21 files changed, 484 insertions(+), 65 deletions(-) create mode 100644 drivers/inc_encoder/backends/hardware/Makefile create mode 100644 drivers/inc_encoder/backends/hardware/inc_encoder_hardware.c create mode 100644 drivers/inc_encoder/backends/software/Makefile rename drivers/inc_encoder/{inc_encoder.c => backends/software/inc_encoder_software.c} (67%) create mode 100644 drivers/inc_encoder/include/inc_encoder_constants.h create mode 100644 tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Kconfig create mode 100644 tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile create mode 100644 tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.dep create mode 100644 tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.features create mode 100644 tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.include create mode 120000 tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/board.h create mode 100644 tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/periph_conf.h diff --git a/drivers/inc_encoder/Kconfig b/drivers/inc_encoder/Kconfig index 23d0ddd30903..a1ea46e7492a 100644 --- a/drivers/inc_encoder/Kconfig +++ b/drivers/inc_encoder/Kconfig @@ -9,6 +9,12 @@ config MODULE_INC_ENCODER menu "Incremental Rotary Encoder Driver" depends on USEMODULE_INC_ENCODER + config INC_ENCODER_MAX_RPM + int "Maximum RPM" + default 210 + help + Defines the maximum RPM the encoder is expected to handle. + config INC_ENCODER_GEAR_RED_RATIO int "Gear Reduction Ratio (in tenths)" default 204 @@ -22,4 +28,11 @@ menu "Incremental Rotary Encoder Driver" help Number of Rising Edges per Revolution. + config INC_ENCODER_HARDWARE_PERIOD_MS + int "RPM Calculation Period (in ms)" + depends on USEMODULE_INC_ENCODER_HARDWARE + default 200 + help + Time period in milliseconds for RPM calculation. + endmenu # Incremental Encoder Driver diff --git a/drivers/inc_encoder/Makefile b/drivers/inc_encoder/Makefile index 7131c0443214..e4cbfcef0ce8 100644 --- a/drivers/inc_encoder/Makefile +++ b/drivers/inc_encoder/Makefile @@ -1 +1,9 @@ +ifneq (,$(filter inc_encoder_hardware,$(USEMODULE))) + DIRS += backends/hardware +endif + +ifneq (,$(filter inc_encoder_software,$(USEMODULE))) + DIRS += backends/software +endif + include $(RIOTMAKE)/driver_with_saul.mk diff --git a/drivers/inc_encoder/Makefile.dep b/drivers/inc_encoder/Makefile.dep index 619b63a72282..1faa089c48f0 100644 --- a/drivers/inc_encoder/Makefile.dep +++ b/drivers/inc_encoder/Makefile.dep @@ -1,3 +1,10 @@ -FEATURES_REQUIRED += periph_gpio -FEATURES_REQUIRED += periph_gpio_irq +ifneq (,$(filter inc_encoder_hardware,$(USEMODULE))) + FEATURES_REQUIRED += periph_qdec +endif + +ifneq (,$(filter inc_encoder_software,$(USEMODULE))) + FEATURES_REQUIRED += periph_gpio + FEATURES_REQUIRED += periph_gpio_irq +endif + USEMODULE += ztimer_usec \ No newline at end of file diff --git a/drivers/inc_encoder/Makefile.include b/drivers/inc_encoder/Makefile.include index ddf65e7c3a39..a7fcf101021d 100644 --- a/drivers/inc_encoder/Makefile.include +++ b/drivers/inc_encoder/Makefile.include @@ -1,2 +1,7 @@ +ifneq (1,$(words $(filter inc_encoder_%,$(USEMODULE)))) + $(error "Please specify exactly one inc_encoder backend: \ + inc_encoder_hardware or inc_encoder_software!") +endif + USEMODULE_INCLUDES_inc_encoder := $(LAST_MAKEFILEDIR)/include USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_inc_encoder) diff --git a/drivers/inc_encoder/backends/hardware/Makefile b/drivers/inc_encoder/backends/hardware/Makefile new file mode 100644 index 000000000000..f9ae123319d7 --- /dev/null +++ b/drivers/inc_encoder/backends/hardware/Makefile @@ -0,0 +1,9 @@ +MODULE := inc_encoder_hardware +BASE_MODULE := inc_encoder + +SRC := inc_encoder_hardware.c + +# enable submodules +SUBMODULES := 1 + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/inc_encoder/backends/hardware/inc_encoder_hardware.c b/drivers/inc_encoder/backends/hardware/inc_encoder_hardware.c new file mode 100644 index 000000000000..328815aee77d --- /dev/null +++ b/drivers/inc_encoder/backends/hardware/inc_encoder_hardware.c @@ -0,0 +1,135 @@ +/* + * SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup drivers_inc_encoder + * @{ + * + * @file + * @brief Device driver implementation for a generic incremental rotary encoder + * + * @author Leonard Herbst + * + * @} + */ + +#include "inc_encoder.h" +#include "inc_encoder_params.h" +#include "inc_encoder_constants.h" + +#include +#include "log.h" +#include "ztimer.h" +#include "time_units.h" + +/* The maximum delta_count that does not cause an overflow in the RPM calculation */ +#define DELTA_COUNT_MAX (INT32_MAX / (SEC_PER_MIN * MS_PER_SEC * GEAR_RED_RATIO_SCALE)) + +/* Maximum RPM before we accumulate more than DELTA_COUNT_MAX pulses per calculation period, + * which would cause an overflow. */ +#define MAX_RPM ((DELTA_COUNT_MAX * SEC_PER_MIN * MS_PER_SEC * GEAR_RED_RATIO_SCALE) \ + / (CONFIG_INC_ENCODER_PPR \ + * CONFIG_INC_ENCODER_GEAR_RED_RATIO \ + * CONFIG_INC_ENCODER_HARDWARE_PERIOD_MS \ + * 4)) + +#if (MAX_RPM < CONFIG_INC_ENCODER_MAX_RPM) +# error With the current configuration the RPM calculation can overflow. \ + Please reduce the period, pulses per revolution, gear reduction ratio, or the max RPM. +#endif + +/* Prototypes */ +static bool _rpm_calc_timer_cb(void *arg); +static void _acc_overflow_cb(void *args); + +/* Public API */ +int inc_encoder_init(inc_encoder_t *dev, const inc_encoder_params_t *params) +{ + dev->params = *params; + + if (qdec_init(dev->params.qdec_dev, QDEC_X4, _acc_overflow_cb, (void *) dev)) { + LOG_ERROR("[inc_encoder] Qdec mode not supported!\n"); + return -EINVAL; + } + + dev->extended_count = 0; + dev->prev_count = 0; + dev->leftover_count = 0; + dev->last_rpm = 0; + + /* Task to periodically calculate RPM */ + ztimer_periodic_init(ZTIMER_USEC, &dev->rpm_timer, _rpm_calc_timer_cb, (void *) dev, + CONFIG_INC_ENCODER_HARDWARE_PERIOD_MS * US_PER_MS); + + ztimer_periodic_start(&dev->rpm_timer); + + return 0; +} + +int inc_encoder_read_rpm(inc_encoder_t *dev, int32_t *rpm) +{ + int irq_state = irq_disable(); + *rpm = dev->last_rpm; + irq_restore(irq_state); + return 0; +} + +int inc_encoder_read_reset_ceti_revs(inc_encoder_t *dev, int32_t *pulse_counter) +{ + int32_t total_count; + int32_t delta_count; + + int irq_state = irq_disable(); + total_count = qdec_read_and_reset(dev->params.qdec_dev); + total_count += dev->extended_count; + delta_count = total_count - dev->prev_count; + + /* We reset the counter but we need to keep the number + * of pulses since last read for the RPM calculation */ + dev->leftover_count = delta_count; + dev->extended_count = 0; + dev->prev_count = 0; + irq_restore(irq_state); + + /* The 4X mode counts all falling and rising edges */ + *pulse_counter = (int32_t) total_count / 4; + + *pulse_counter *= UNIT_SCALE * GEAR_RED_RATIO_SCALE; + *pulse_counter /= CONFIG_INC_ENCODER_PPR; + *pulse_counter /= CONFIG_INC_ENCODER_GEAR_RED_RATIO; + return 0; +} + +/* Private API */ +static bool _rpm_calc_timer_cb(void *arg) +{ + inc_encoder_t *dev = (inc_encoder_t *) arg; + int32_t delta_count; + int32_t rpm; + int32_t total_count; + + total_count = dev->extended_count + qdec_read(dev->params.qdec_dev); + delta_count = total_count - dev->prev_count; + if (dev->leftover_count != 0) { + /* Add leftover count from last reset */ + delta_count += dev->leftover_count; + dev->leftover_count = 0; + } + dev->prev_count = total_count; + + rpm = (int32_t)(SEC_PER_MIN * MS_PER_SEC * GEAR_RED_RATIO_SCALE * delta_count) / + (int32_t)(CONFIG_INC_ENCODER_PPR * CONFIG_INC_ENCODER_GEAR_RED_RATIO + * CONFIG_INC_ENCODER_HARDWARE_PERIOD_MS * 4); /* 4X mode counts all edges */ + + dev->last_rpm = rpm; + return true; +} + +static void _acc_overflow_cb(void *args) +{ + inc_encoder_t *dev = (inc_encoder_t *) args; + + dev->extended_count += qdec_read_and_reset(dev->params.qdec_dev); +} diff --git a/drivers/inc_encoder/backends/software/Makefile b/drivers/inc_encoder/backends/software/Makefile new file mode 100644 index 000000000000..7a9c4cee0264 --- /dev/null +++ b/drivers/inc_encoder/backends/software/Makefile @@ -0,0 +1,9 @@ +MODULE := inc_encoder_software +BASE_MODULE := inc_encoder + +SRC := inc_encoder_software.c + +# enable submodules +SUBMODULES := 1 + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/inc_encoder/inc_encoder.c b/drivers/inc_encoder/backends/software/inc_encoder_software.c similarity index 67% rename from drivers/inc_encoder/inc_encoder.c rename to drivers/inc_encoder/backends/software/inc_encoder_software.c index 5a8581851817..8e128e8aefe1 100644 --- a/drivers/inc_encoder/inc_encoder.c +++ b/drivers/inc_encoder/backends/software/inc_encoder_software.c @@ -17,41 +17,46 @@ #include "inc_encoder.h" #include "inc_encoder_params.h" +#include "inc_encoder_constants.h" #include #include "log.h" -#include "ztimer.h" #include "time_units.h" -/** - * @brief Scaling factor to apply to adjust for the gear reduction ratio being in tenths. +/* If delta_t exceeds this threshold, the calculated RPM will be less than one + * and will be truncated to zero. + * When delta_t is larger than this threshold, we directly return zero + * and prevent potential overflows in the RPM calculation. + * + * An overflow would occur when delta_t > INT32_MAX / (PPR * GEAR_RED_RATIO), + * but our threshold is always lower than that because: + * + * INT32_MAX > SEC_PER_MIN * US_PER_SEC * GEAR_RED_RATIO_SCALE */ -#define GEAR_RED_RATIO_SCALE 10 +#define DELTA_T_THRESHOLD ((SEC_PER_MIN * US_PER_SEC * GEAR_RED_RATIO_SCALE) \ + / (CONFIG_INC_ENCODER_PPR * CONFIG_INC_ENCODER_GEAR_RED_RATIO)) /* Prototypes */ - -static void _pulse_callback(void *arg); -static void _read_reset_pulse_counter(inc_encoder_t *dev, int32_t *pulse_counter); -static bool _read_delta_t_direction(inc_encoder_t *dev, uint32_t *delta_t, bool *ccw); +static void _pulse_cb(void *arg); +static bool _read_delta_t_direction(inc_encoder_t *dev, uint32_t *delta_t, bool *cw); /* Public API */ int inc_encoder_init(inc_encoder_t *dev, const inc_encoder_params_t *params) { dev->params = *params; - if (gpio_init(dev->params.direction, GPIO_IN)) { LOG_ERROR("[inc_encoder] Failed configuring the direction pin as an input!\n"); return -EIO; } - dev->delta_t = 0; - dev->ccw = false; - dev->stale = true; - dev->pulse_counter = 0; + dev->delta_t = 0; + dev->pulse_counter = 0; + dev->cw = false; + dev->stale = true; dev->last_read_time = ztimer_now(ZTIMER_USEC); - if (gpio_init_int(dev->params.interrupt, GPIO_IN, GPIO_RISING, _pulse_callback, (void *) dev)) { + if (gpio_init_int(dev->params.interrupt, GPIO_IN, GPIO_RISING, _pulse_cb, (void *) dev)) { LOG_ERROR("[inc_encoder] Failed configuring the interrupt pin!\n"); return -EIO; } @@ -62,8 +67,8 @@ int inc_encoder_init(inc_encoder_t *dev, const inc_encoder_params_t *params) int inc_encoder_read_rpm(inc_encoder_t *dev, int32_t *rpm) { uint32_t delta_t; - bool ccw; - if (!_read_delta_t_direction(dev, &delta_t, &ccw)) { + bool cw; + if (!_read_delta_t_direction(dev, &delta_t, &cw) || (delta_t >= DELTA_T_THRESHOLD)) { *rpm = 0; return 0; } @@ -71,20 +76,24 @@ int inc_encoder_read_rpm(inc_encoder_t *dev, int32_t *rpm) /* delta_t represents the number of microseconds since the last pulse. * Invert and divide by the number of micro seconds per minute * to obtain the RPM. Apply scaling factors like gear reduction - * or pulses per revolution. - */ + * or pulses per revolution. */ *rpm = SEC_PER_MIN * US_PER_SEC * GEAR_RED_RATIO_SCALE / (delta_t * CONFIG_INC_ENCODER_PPR * CONFIG_INC_ENCODER_GEAR_RED_RATIO); - if (ccw) { + if (!cw) { *rpm *= -1; } + return 0; } int inc_encoder_read_reset_ceti_revs(inc_encoder_t *dev, int32_t *pulse_counter) { - _read_reset_pulse_counter(dev, pulse_counter); - *pulse_counter *= 100 * GEAR_RED_RATIO_SCALE; + int irq_state = irq_disable(); + *pulse_counter = dev->pulse_counter; + dev->pulse_counter = 0; + irq_restore(irq_state); + + *pulse_counter *= UNIT_SCALE * GEAR_RED_RATIO_SCALE; *pulse_counter /= CONFIG_INC_ENCODER_PPR; *pulse_counter /= CONFIG_INC_ENCODER_GEAR_RED_RATIO; return 0; @@ -93,53 +102,48 @@ int inc_encoder_read_reset_ceti_revs(inc_encoder_t *dev, int32_t *pulse_counter) /* Private API */ /* Triggered on the rising edge of a pulse */ -static void _pulse_callback(void *arg) +static void _pulse_cb(void *arg) { inc_encoder_t *dev = (inc_encoder_t *) arg; - uint32_t now = ztimer_now(ZTIMER_USEC); - /* Reading the shifted phase: low -> cw, high -> ccw */ - dev->ccw = gpio_read(dev->params.direction); + /* Reading the shifted phase: high -> cw, low -> ccw */ + dev->cw = gpio_read(dev->params.direction); + if (now < dev->last_read_time) { - /* timer had an overflow */ dev->delta_t = UINT32_MAX - dev->last_read_time + now + 1; } else { dev->delta_t = now - dev->last_read_time; } + dev->last_read_time = now; - dev->pulse_counter += dev->ccw ? -1 : 1; + dev->pulse_counter += dev->cw ? 1 : -1; /* data is no longer stale */ dev->stale= false; } -static void _read_reset_pulse_counter(inc_encoder_t *dev, int32_t *pulse_counter) +static bool _read_delta_t_direction(inc_encoder_t *dev, uint32_t *delta_t, bool *cw) { + uint32_t now; + uint32_t pulse_age; int irq_state = irq_disable(); - *pulse_counter = dev->pulse_counter; - dev->pulse_counter = 0; - irq_restore(irq_state); -} -static bool _read_delta_t_direction(inc_encoder_t *dev, uint32_t *delta_t, bool *ccw) -{ - int irq_state = irq_disable(); - /* There have been no pulses for a while -> rotation probably stopped. */ if (dev->stale) { + /* Rotation stopped */ irq_restore(irq_state); return false; } - uint32_t now = ztimer_now(ZTIMER_USEC); - uint32_t pulse_age; + now = ztimer_now(ZTIMER_USEC); + /* Handle potential overflows */ if (now < dev->last_read_time) { - /* the timer had an overflow */ pulse_age = UINT32_MAX - dev->last_read_time + now + 1; } else { pulse_age = now - dev->last_read_time; } + if (pulse_age >= dev->delta_t) { /* Data is stale if the time elapsed since the last pulse * is longer than delta_t */ @@ -149,7 +153,7 @@ static bool _read_delta_t_direction(inc_encoder_t *dev, uint32_t *delta_t, bool else { *delta_t = dev->delta_t; } - *ccw = dev->ccw; + *cw = dev->cw; irq_restore(irq_state); return true; diff --git a/drivers/inc_encoder/include/inc_encoder_constants.h b/drivers/inc_encoder/include/inc_encoder_constants.h new file mode 100644 index 000000000000..907ba347719d --- /dev/null +++ b/drivers/inc_encoder/include/inc_encoder_constants.h @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @ingroup drivers_inc_encoder + * @{ + * + * @file + * @brief Constants used in the incremental rotary encoder driver + * + * @author Leonard Herbst + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Scaling factor to apply to adjust for the gear reduction ratio being in tenths. + */ +#define GEAR_RED_RATIO_SCALE 10 + +/** + * @brief Scaling factor to convert revolutions per minute to hundredths of revolutions per minute. + */ +#define UNIT_SCALE 100 + +#ifdef __cplusplus +} +#endif +/** @} */ diff --git a/drivers/inc_encoder/include/inc_encoder_params.h b/drivers/inc_encoder/include/inc_encoder_params.h index 077be4a045ee..c750c5b0f9b8 100644 --- a/drivers/inc_encoder/include/inc_encoder_params.h +++ b/drivers/inc_encoder/include/inc_encoder_params.h @@ -37,30 +37,59 @@ extern "C" { # define CONFIG_INC_ENCODER_PPR 13 #endif +/** + * @brief Default number of pulses per revolution + */ +#if IS_USED(MODULE_INC_ENCODER_HARDWARE) +# ifndef CONFIG_INC_ENCODER_HARDWARE_PERIOD_MS +# define CONFIG_INC_ENCODER_HARDWARE_PERIOD_MS 200 +# endif +#endif + /** * @name default configuration parameters for a generic incremental rotary encoder * @{ */ +#if IS_USED(MODULE_INC_ENCODER_SOFTWARE) /** - * @brief Default pin of the first phase used to trigger the interrupt + * @brief Default pin of the first phase used to trigger the interrupt for software decoding */ -#ifndef INC_ENCODER_INTERRUPT -# define INC_ENCODER_INTERRUPT GPIO_PIN(1, 1) -#endif +# ifndef INC_ENCODER_INTERRUPT +# define INC_ENCODER_INTERRUPT GPIO_PIN(1, 10) +# endif + +/** + * @brief Default pin of the second (shifted) phase used to determine + * the direction for software decoding + */ +# ifndef INC_ENCODER_DIRECTION +# define INC_ENCODER_DIRECTION GPIO_PIN(1, 11) +# endif +#endif /* IS_USED(MODULE_INC_ENCODER_SOFTWARE) */ +#if IS_USED(MODULE_INC_ENCODER_HARDWARE) /** - * @brief Default pin of the second (shifted) phase used to determine the direction + * @brief Default QDEC device used for hardware decoding */ -#ifndef INC_ENCODER_DIRECTION -# define INC_ENCODER_DIRECTION GPIO_PIN(1, 2) +#ifndef INC_ENCODER_QDEC_DEV +# define INC_ENCODER_QDEC_DEV QDEC_DEV(0) #endif +#endif /* IS_USED(MODULE_INC_ENCODER_HARDWARE) */ /** * @brief Default parameters */ -#ifndef INC_ENCODER_PARAMS -# define INC_ENCODER_PARAMS { .interrupt = INC_ENCODER_INTERRUPT, \ - .direction = INC_ENCODER_DIRECTION } +#if IS_USED(MODULE_INC_ENCODER_SOFTWARE) +# ifndef INC_ENCODER_PARAMS +# define INC_ENCODER_PARAMS { .interrupt = INC_ENCODER_INTERRUPT, \ + .direction = INC_ENCODER_DIRECTION } +# endif +#elif IS_USED(MODULE_INC_ENCODER_HARDWARE) +# ifndef INC_ENCODER_PARAMS +# define INC_ENCODER_PARAMS { .qdec_dev = INC_ENCODER_QDEC_DEV } +# endif +#else +# define INC_ENCODER_PARAMS {} #endif /**@}*/ diff --git a/drivers/include/inc_encoder.h b/drivers/include/inc_encoder.h index 4980eef99b92..913096eb460a 100644 --- a/drivers/include/inc_encoder.h +++ b/drivers/include/inc_encoder.h @@ -13,8 +13,10 @@ * ## Description * This is a driver for a generic incremental rotary encoder. These sensors are most often * used with motors. + * An example of such an encoder can be found [here](https://www.dfrobot.com/product-1617.html) + * * The sensor generates a fixed number of pulses per rotation on two output pins. - * These signals are phase-shifted slightly, enabling the detection of rotation direction. + * These signals are phase-shifted by 90 degrees, enabling the detection of rotation direction. * * These encoders typically have four wires: * - GNR (Ground) @@ -22,20 +24,51 @@ * - Phase A * - Phase B * - * Phase A should be connected to the pin configured as the interrupt pin, - * while Phase B (the shifted signal) should be connected to the direction pin. - * + * Phase A should be connected to the pin configured as the interrupt pin or the QDEC A pin, + * while Phase B (the shifted signal) should be connected to the direction pin or QDEC B pin. * If the Phase A and Phase B connections are swapped, * the detected rotation direction will be reversed. - * When only Phase A is connected, the encoder will still report movement, - * but there will be no distinction between counter clock wise and clock wise rotation. - * An example for such an encoder can be found [here](https://www.dfrobot.com/product-1617.html) * * The driver provides functions to read the current RPM * and the total number of revolutions (in hundredths) since the last measurement. * * Configuration options are available via Kconfig to specify - * the number of pulses per rotation and the gear reduction ratio (in tenths). + * the number of pulses per rotation, the maximum RPM, and the gear reduction ratio (in tenths). + * + * ## Backends + * This driver supports two backends for reading the encoder signals: + * - Hardware accelerated using the QDEC peripheral + * - Software based using GPIO interrupts + * + * To use either of the backends add the corresponding module to your application Makefile: + * ``` + * USEMODULE += inc_encoder_hardware + * ``` + * or + * ``` + * USEMODULE += inc_encoder_software + * ``` + * + * ### GPIO Interrupt Backend + * The GPIO interrupt backend uses interrupts to count the number of rising edges on Phase A and + * determines the rotation direction based on the state of Phase B. + * The RPM calculation is done based on the delta time between the last two rising edges. + * If only one phase is connected, the driver will still count the pulses, + * but there will be no direction detection. + * + * ### QDEC Backend + * The QDEC backend uses the microcontroller's Quadrature Decoder peripheral to handle the counting + * and direction detection in hardware. + * The RPM calculation is done periodically based on the + * pulse count provided by the QDEC peripheral. + * If only one phase is connected, the QDEC peripheral will not count any pulses. + * The period for RPM calculation can be configured via Kconfig. + * + * ## SAUL Interface + * This driver implements the SAUL sensor interface. + * It provides two SAUL devices: + * - One for reading the current RPM + * - One for reading the total number of revolutions in hundredths since the last read. * * @note After 327 revolutions without reading and resetting * the revolution counter, the `phydat` value will overflow. @@ -52,6 +85,10 @@ #include "periph/gpio.h" #include "irq.h" +#include "periph/qdec.h" +#include "ztimer.h" +#include "ztimer/periodic.h" +#include "kernel_defines.h" #ifdef __cplusplus extern "C" { @@ -61,20 +98,32 @@ extern "C" { * @brief Device initialization parameters */ typedef struct { +#if IS_USED(MODULE_INC_ENCODER_HARDWARE) + qdec_t qdec_dev; /**< QDEC device used for hardware decoding */ +#elif IS_USED(MODULE_INC_ENCODER_SOFTWARE) gpio_t interrupt; /**< Interrupt pin (first phase) */ gpio_t direction; /**< Pin used to determine the direction (shifted phase) */ +#endif } inc_encoder_params_t; /** * @brief Device descriptor for the driver */ typedef struct { - inc_encoder_params_t params; /**< configuration parameters */ - uint32_t delta_t; /**< time delta since the last read */ + inc_encoder_params_t params; /**< configuration parameters */ +#if IS_USED(MODULE_INC_ENCODER_SOFTWARE) + uint32_t delta_t; /**< time delta since the last pulse */ int32_t pulse_counter; /**< number of pulses since last read */ uint32_t last_read_time; /**< time of the last read */ - bool ccw; /**< counter clock wise rotation */ + bool cw; /**< clock wise rotation */ bool stale; /**< indicates that there is no new data to be read */ +#elif IS_USED(MODULE_INC_ENCODER_HARDWARE) + int32_t extended_count; /**< accumulated count of pulse overflows */ + int32_t prev_count; /**< number of pulses at last rpm calculation */ + int32_t leftover_count; /**< leftover from last reset */ + int32_t last_rpm; /**< last calculated RPM value */ + ztimer_periodic_t rpm_timer; /**< timer used to periodically calculate RPM */ +#endif } inc_encoder_t; /** @@ -85,6 +134,7 @@ typedef struct { * * @retval 0 on success * @retval -EIO on failure setting up the pins + * @retval -EINVAL on invalid qdec configuration */ int inc_encoder_init(inc_encoder_t *dev, const inc_encoder_params_t *params); diff --git a/tests/drivers/inc_encoder/Makefile b/tests/drivers/inc_encoder/Makefile index ef01c7eeec3a..10c716ae3c78 100644 --- a/tests/drivers/inc_encoder/Makefile +++ b/tests/drivers/inc_encoder/Makefile @@ -1,6 +1,14 @@ include ../Makefile.drivers_common +EXTERNAL_BOARD_DIRS = $(CURDIR)/external_boards + + +BOARD ?= nrf52840dk_qdec + + USEMODULE += inc_encoder +#USEMODULE += inc_encoder_software +USEMODULE += inc_encoder_hardware USEMODULE += ztimer_sec include $(RIOTBASE)/Makefile.include diff --git a/tests/drivers/inc_encoder/README.md b/tests/drivers/inc_encoder/README.md index 72d68153095d..708a6e0d9933 100644 --- a/tests/drivers/inc_encoder/README.md +++ b/tests/drivers/inc_encoder/README.md @@ -4,4 +4,8 @@ This is a manual test application for a generic incremental rotary encoder. # Usage Connect a sensor - like a magnetic rotary encoder found on some dc motors - to your mcu. The test will periodically print the measured RPM and number of revolutions -since the last readout. \ No newline at end of file +since the last readout. +See [drivers/inc_encoder/include/inc_encoder.h][inc_encoder] +for details on how to connect your sensor. + +[inc_encoder]: ../../drivers/inc_encoder/include/inc_encoder.h \ No newline at end of file diff --git a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Kconfig b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Kconfig new file mode 100644 index 000000000000..fee8fd1d2573 --- /dev/null +++ b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Kconfig @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2025 TU Dresden +# SPDX-License-Identifier: LGPL-2.1-only + +config BOARD + default "nrf52840dk" if BOARD_NRF52840DK + +config BOARD_NRF52840DK + bool + default y + select BOARDS_COMMON_NRF52XXXDK + select CPU_MODEL_NRF52840XXAA + +source "$(RIOTBOARD)/common/nrf52xxxdk/Kconfig" diff --git a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile new file mode 100644 index 000000000000..33d5c8624bd6 --- /dev/null +++ b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile @@ -0,0 +1,6 @@ +# This must be a different name than 'board' as it is implemented by 'native' +MODULE = board_nrf52840dk_qdec + +DIRS += $(RIOTBOARD)/nrf52840dk + +include $(RIOTBASE)/Makefile.base diff --git a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.dep b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.dep new file mode 100644 index 000000000000..760b5488a6a8 --- /dev/null +++ b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.dep @@ -0,0 +1,4 @@ +# This must be a different name than 'board' as it is implemented by 'native' +USEMODULE += board_nrf52840dk_qdec + +include $(RIOTBOARD)/nrf52840dk/Makefile.dep diff --git a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.features b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.features new file mode 100644 index 000000000000..069f6d7f6978 --- /dev/null +++ b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.features @@ -0,0 +1,3 @@ +FEATURES_PROVIDED += periph_qdec + +include $(RIOTBOARD)/nrf52840dk/Makefile.features diff --git a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.include b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.include new file mode 100644 index 000000000000..b4f1ce6b4032 --- /dev/null +++ b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.include @@ -0,0 +1 @@ +include $(RIOTBOARD)/nrf52840dk/Makefile.include diff --git a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/board.h b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/board.h new file mode 120000 index 000000000000..2dc6f87ec4a8 --- /dev/null +++ b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/board.h @@ -0,0 +1 @@ +../../../../../../boards/nrf52840dk/include/board.h \ No newline at end of file diff --git a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/periph_conf.h b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/periph_conf.h new file mode 100644 index 000000000000..a3df3d43f209 --- /dev/null +++ b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/periph_conf.h @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @ingroup boards_nrf52840dk_qdec + * @{ + * + * @file + * @brief Minimal peripheral set for the nRF52840 DK with two quadrature + * decoders enabled + * + * @author Leonard Herbst Date: Thu, 30 Oct 2025 10:45:50 +0100 Subject: [PATCH 12/15] Fixed typos --- drivers/inc_encoder/backends/software/inc_encoder_software.c | 2 +- drivers/include/inc_encoder.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/inc_encoder/backends/software/inc_encoder_software.c b/drivers/inc_encoder/backends/software/inc_encoder_software.c index 8e128e8aefe1..ed0b9683f107 100644 --- a/drivers/inc_encoder/backends/software/inc_encoder_software.c +++ b/drivers/inc_encoder/backends/software/inc_encoder_software.c @@ -74,7 +74,7 @@ int inc_encoder_read_rpm(inc_encoder_t *dev, int32_t *rpm) } /* delta_t represents the number of microseconds since the last pulse. - * Invert and divide by the number of micro seconds per minute + * Invert and divide by the number of microseconds per minute * to obtain the RPM. Apply scaling factors like gear reduction * or pulses per revolution. */ *rpm = SEC_PER_MIN * US_PER_SEC * GEAR_RED_RATIO_SCALE diff --git a/drivers/include/inc_encoder.h b/drivers/include/inc_encoder.h index 913096eb460a..99252f260858 100644 --- a/drivers/include/inc_encoder.h +++ b/drivers/include/inc_encoder.h @@ -119,7 +119,7 @@ typedef struct { bool stale; /**< indicates that there is no new data to be read */ #elif IS_USED(MODULE_INC_ENCODER_HARDWARE) int32_t extended_count; /**< accumulated count of pulse overflows */ - int32_t prev_count; /**< number of pulses at last rpm calculation */ + int32_t prev_count; /**< number of pulses at last RPM calculation */ int32_t leftover_count; /**< leftover from last reset */ int32_t last_rpm; /**< last calculated RPM value */ ztimer_periodic_t rpm_timer; /**< timer used to periodically calculate RPM */ From 5749d856e5490f34cdcdd66394a25ed2f7cdb71c Mon Sep 17 00:00:00 2001 From: LeonardHerbst Date: Thu, 30 Oct 2025 13:08:35 +0100 Subject: [PATCH 13/15] Removed external board since RIOT already has other boards that support qdec --- tests/drivers/inc_encoder/Makefile | 6 +- .../external_boards/nrf52840dk_qdec/Kconfig | 13 ---- .../external_boards/nrf52840dk_qdec/Makefile | 6 -- .../nrf52840dk_qdec/Makefile.dep | 4 - .../nrf52840dk_qdec/Makefile.features | 3 - .../nrf52840dk_qdec/Makefile.include | 1 - .../nrf52840dk_qdec/include/board.h | 1 - .../nrf52840dk_qdec/include/periph_conf.h | 75 ------------------- 8 files changed, 1 insertion(+), 108 deletions(-) delete mode 100644 tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Kconfig delete mode 100644 tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile delete mode 100644 tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.dep delete mode 100644 tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.features delete mode 100644 tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.include delete mode 120000 tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/board.h delete mode 100644 tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/periph_conf.h diff --git a/tests/drivers/inc_encoder/Makefile b/tests/drivers/inc_encoder/Makefile index 10c716ae3c78..aa54e63b7743 100644 --- a/tests/drivers/inc_encoder/Makefile +++ b/tests/drivers/inc_encoder/Makefile @@ -1,10 +1,6 @@ include ../Makefile.drivers_common -EXTERNAL_BOARD_DIRS = $(CURDIR)/external_boards - - -BOARD ?= nrf52840dk_qdec - +BOARD ?= nucleo-f401re USEMODULE += inc_encoder #USEMODULE += inc_encoder_software diff --git a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Kconfig b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Kconfig deleted file mode 100644 index fee8fd1d2573..000000000000 --- a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-FileCopyrightText: 2025 TU Dresden -# SPDX-License-Identifier: LGPL-2.1-only - -config BOARD - default "nrf52840dk" if BOARD_NRF52840DK - -config BOARD_NRF52840DK - bool - default y - select BOARDS_COMMON_NRF52XXXDK - select CPU_MODEL_NRF52840XXAA - -source "$(RIOTBOARD)/common/nrf52xxxdk/Kconfig" diff --git a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile deleted file mode 100644 index 33d5c8624bd6..000000000000 --- a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# This must be a different name than 'board' as it is implemented by 'native' -MODULE = board_nrf52840dk_qdec - -DIRS += $(RIOTBOARD)/nrf52840dk - -include $(RIOTBASE)/Makefile.base diff --git a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.dep b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.dep deleted file mode 100644 index 760b5488a6a8..000000000000 --- a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.dep +++ /dev/null @@ -1,4 +0,0 @@ -# This must be a different name than 'board' as it is implemented by 'native' -USEMODULE += board_nrf52840dk_qdec - -include $(RIOTBOARD)/nrf52840dk/Makefile.dep diff --git a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.features b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.features deleted file mode 100644 index 069f6d7f6978..000000000000 --- a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.features +++ /dev/null @@ -1,3 +0,0 @@ -FEATURES_PROVIDED += periph_qdec - -include $(RIOTBOARD)/nrf52840dk/Makefile.features diff --git a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.include b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.include deleted file mode 100644 index b4f1ce6b4032..000000000000 --- a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/Makefile.include +++ /dev/null @@ -1 +0,0 @@ -include $(RIOTBOARD)/nrf52840dk/Makefile.include diff --git a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/board.h b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/board.h deleted file mode 120000 index 2dc6f87ec4a8..000000000000 --- a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/board.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../boards/nrf52840dk/include/board.h \ No newline at end of file diff --git a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/periph_conf.h b/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/periph_conf.h deleted file mode 100644 index a3df3d43f209..000000000000 --- a/tests/drivers/inc_encoder/external_boards/nrf52840dk_qdec/include/periph_conf.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 TU Dresden - * SPDX-License-Identifier: LGPL-2.1-only - */ - -#pragma once - -/** - * @ingroup boards_nrf52840dk_qdec - * @{ - * - * @file - * @brief Minimal peripheral set for the nRF52840 DK with two quadrature - * decoders enabled - * - * @author Leonard Herbst Date: Thu, 30 Oct 2025 14:02:44 +0100 Subject: [PATCH 14/15] Added a inc_encoder backend to the saul driver test --- tests/drivers/saul_drivers/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/drivers/saul_drivers/Makefile b/tests/drivers/saul_drivers/Makefile index bb31d7b40472..983665e46992 100644 --- a/tests/drivers/saul_drivers/Makefile +++ b/tests/drivers/saul_drivers/Makefile @@ -28,6 +28,9 @@ endif ifneq (,$(filter bme680,$(DRIVERS))) USEMODULE += bme680_i2c endif +ifneq (,$(filter inc_encoder,$(DRIVERS))) + USEMODULE += inc_encoder_software +endif ifneq (,$(filter lm75,$(DRIVERS))) USEMODULE += tmp1075 endif From 3710cb816952f3b27db66bb9a503b036c1cfecf1 Mon Sep 17 00:00:00 2001 From: LeonardHerbst Date: Fri, 14 Nov 2025 09:00:14 +0100 Subject: [PATCH 15/15] Integrated feedback. Also realized the backends using the RIOT submodule system. --- drivers/inc_encoder/Makefile | 16 ++++++++++------ drivers/inc_encoder/Makefile.dep | 6 +++--- drivers/inc_encoder/Makefile.include | 5 ++++- drivers/inc_encoder/backends/hardware/Makefile | 9 --------- drivers/inc_encoder/backends/software/Makefile | 9 --------- .../inc_encoder_hardware.c => hardware.c} | 14 +++++++------- drivers/inc_encoder/inc_encoder_saul.c | 14 +++++++------- .../inc_encoder/include/inc_encoder_constants.h | 4 ++-- drivers/inc_encoder/include/inc_encoder_params.h | 3 +-- .../inc_encoder_software.c => software.c} | 10 +++++----- drivers/include/inc_encoder.h | 12 +++++++----- drivers/include/saul.h | 4 ++-- drivers/saul/init_devs/auto_init_inc_encoder.c | 4 ++-- tests/drivers/inc_encoder/main.c | 6 +++--- 14 files changed, 53 insertions(+), 63 deletions(-) delete mode 100644 drivers/inc_encoder/backends/hardware/Makefile delete mode 100644 drivers/inc_encoder/backends/software/Makefile rename drivers/inc_encoder/{backends/hardware/inc_encoder_hardware.c => hardware.c} (89%) rename drivers/inc_encoder/{backends/software/inc_encoder_software.c => software.c} (93%) diff --git a/drivers/inc_encoder/Makefile b/drivers/inc_encoder/Makefile index e4cbfcef0ce8..dba907dfc50c 100644 --- a/drivers/inc_encoder/Makefile +++ b/drivers/inc_encoder/Makefile @@ -1,9 +1,13 @@ -ifneq (,$(filter inc_encoder_hardware,$(USEMODULE))) - DIRS += backends/hardware -endif +SUBMODULES := 1 + +# Since we have submodules we need to manually handle saul instead of +# including driver_with_saul.mk +MODULE ?= $(shell basename $(CURDIR)) +SAUL_INTERFACE ?= $(MODULE)_saul.c -ifneq (,$(filter inc_encoder_software,$(USEMODULE))) - DIRS += backends/software +# only include _saul.c if saul module is used +ifneq (,$(filter saul,$(USEMODULE))) + SRC += $(SAUL_INTERFACE) endif -include $(RIOTMAKE)/driver_with_saul.mk +include $(RIOTBASE)/Makefile.base diff --git a/drivers/inc_encoder/Makefile.dep b/drivers/inc_encoder/Makefile.dep index 1faa089c48f0..ecda619f258c 100644 --- a/drivers/inc_encoder/Makefile.dep +++ b/drivers/inc_encoder/Makefile.dep @@ -1,10 +1,10 @@ ifneq (,$(filter inc_encoder_hardware,$(USEMODULE))) FEATURES_REQUIRED += periph_qdec + USEMODULE += ztimer_msec endif ifneq (,$(filter inc_encoder_software,$(USEMODULE))) FEATURES_REQUIRED += periph_gpio FEATURES_REQUIRED += periph_gpio_irq -endif - -USEMODULE += ztimer_usec \ No newline at end of file + USEMODULE += ztimer_usec +endif \ No newline at end of file diff --git a/drivers/inc_encoder/Makefile.include b/drivers/inc_encoder/Makefile.include index a7fcf101021d..8c97e567af14 100644 --- a/drivers/inc_encoder/Makefile.include +++ b/drivers/inc_encoder/Makefile.include @@ -1,4 +1,7 @@ -ifneq (1,$(words $(filter inc_encoder_%,$(USEMODULE)))) +PSEUDOMODULES += inc_encoder_hardware +PSEUDOMODULES += inc_encoder_software + +ifneq (1,$(words $(filter inc_encoder_hardware inc_encoder_software,$(USEMODULE)))) $(error "Please specify exactly one inc_encoder backend: \ inc_encoder_hardware or inc_encoder_software!") endif diff --git a/drivers/inc_encoder/backends/hardware/Makefile b/drivers/inc_encoder/backends/hardware/Makefile deleted file mode 100644 index f9ae123319d7..000000000000 --- a/drivers/inc_encoder/backends/hardware/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -MODULE := inc_encoder_hardware -BASE_MODULE := inc_encoder - -SRC := inc_encoder_hardware.c - -# enable submodules -SUBMODULES := 1 - -include $(RIOTBASE)/Makefile.base diff --git a/drivers/inc_encoder/backends/software/Makefile b/drivers/inc_encoder/backends/software/Makefile deleted file mode 100644 index 7a9c4cee0264..000000000000 --- a/drivers/inc_encoder/backends/software/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -MODULE := inc_encoder_software -BASE_MODULE := inc_encoder - -SRC := inc_encoder_software.c - -# enable submodules -SUBMODULES := 1 - -include $(RIOTBASE)/Makefile.base diff --git a/drivers/inc_encoder/backends/hardware/inc_encoder_hardware.c b/drivers/inc_encoder/hardware.c similarity index 89% rename from drivers/inc_encoder/backends/hardware/inc_encoder_hardware.c rename to drivers/inc_encoder/hardware.c index 328815aee77d..f7a1bdc52944 100644 --- a/drivers/inc_encoder/backends/hardware/inc_encoder_hardware.c +++ b/drivers/inc_encoder/hardware.c @@ -60,8 +60,8 @@ int inc_encoder_init(inc_encoder_t *dev, const inc_encoder_params_t *params) dev->last_rpm = 0; /* Task to periodically calculate RPM */ - ztimer_periodic_init(ZTIMER_USEC, &dev->rpm_timer, _rpm_calc_timer_cb, (void *) dev, - CONFIG_INC_ENCODER_HARDWARE_PERIOD_MS * US_PER_MS); + ztimer_periodic_init(ZTIMER_MSEC, &dev->rpm_timer, _rpm_calc_timer_cb, (void *) dev, + CONFIG_INC_ENCODER_HARDWARE_PERIOD_MS); ztimer_periodic_start(&dev->rpm_timer); @@ -76,7 +76,7 @@ int inc_encoder_read_rpm(inc_encoder_t *dev, int32_t *rpm) return 0; } -int inc_encoder_read_reset_ceti_revs(inc_encoder_t *dev, int32_t *pulse_counter) +int inc_encoder_read_reset_milli_revs(inc_encoder_t *dev, int32_t *rev_counter) { int32_t total_count; int32_t delta_count; @@ -94,11 +94,11 @@ int inc_encoder_read_reset_ceti_revs(inc_encoder_t *dev, int32_t *pulse_counter) irq_restore(irq_state); /* The 4X mode counts all falling and rising edges */ - *pulse_counter = (int32_t) total_count / 4; + *rev_counter = (int32_t) total_count / 4; - *pulse_counter *= UNIT_SCALE * GEAR_RED_RATIO_SCALE; - *pulse_counter /= CONFIG_INC_ENCODER_PPR; - *pulse_counter /= CONFIG_INC_ENCODER_GEAR_RED_RATIO; + *rev_counter *= UNIT_SCALE * GEAR_RED_RATIO_SCALE; + *rev_counter /= CONFIG_INC_ENCODER_PPR; + *rev_counter /= CONFIG_INC_ENCODER_GEAR_RED_RATIO; return 0; } diff --git a/drivers/inc_encoder/inc_encoder_saul.c b/drivers/inc_encoder/inc_encoder_saul.c index b956c18ac964..f54bfbc880c4 100644 --- a/drivers/inc_encoder/inc_encoder_saul.c +++ b/drivers/inc_encoder/inc_encoder_saul.c @@ -35,17 +35,17 @@ static int read_rpm(const void *dev, phydat_t *res) return 1; } -static int read_reset_pulse_counter(const void *dev, phydat_t *res) +static int read_reset_rev_counter(const void *dev, phydat_t *res) { inc_encoder_t *d = (inc_encoder_t *)dev; - int32_t counter; - if (inc_encoder_read_reset_ceti_revs(d, &counter)) { + int32_t rev_counter; + if (inc_encoder_read_reset_milli_revs(d, &rev_counter)) { /* Read failure */ return -ECANCELED; } - res->val[0] = (int16_t) counter; + res->val[0] = (int16_t) rev_counter; res->unit = UNIT_CTS; - res->scale = -2; + res->scale = -3; /* millirevolutions */ return 1; } @@ -55,8 +55,8 @@ const saul_driver_t inc_encoder_rpm_saul_driver = { .type = SAUL_SENSE_SPEED, }; -const saul_driver_t inc_encoder_pulse_count_saul_driver = { - .read = read_reset_pulse_counter, +const saul_driver_t inc_encoder_rev_count_saul_driver = { + .read = read_reset_rev_counter, .write = saul_write_notsup, .type = SAUL_SENSE_COUNT, }; diff --git a/drivers/inc_encoder/include/inc_encoder_constants.h b/drivers/inc_encoder/include/inc_encoder_constants.h index 907ba347719d..328a8e4396de 100644 --- a/drivers/inc_encoder/include/inc_encoder_constants.h +++ b/drivers/inc_encoder/include/inc_encoder_constants.h @@ -25,9 +25,9 @@ extern "C" { #define GEAR_RED_RATIO_SCALE 10 /** - * @brief Scaling factor to convert revolutions per minute to hundredths of revolutions per minute. + * @brief Scaling factor to convert revolutions per minute to millirevolutions per minute. */ -#define UNIT_SCALE 100 +#define UNIT_SCALE 1000 #ifdef __cplusplus } diff --git a/drivers/inc_encoder/include/inc_encoder_params.h b/drivers/inc_encoder/include/inc_encoder_params.h index c750c5b0f9b8..641cd9b657b3 100644 --- a/drivers/inc_encoder/include/inc_encoder_params.h +++ b/drivers/inc_encoder/include/inc_encoder_params.h @@ -85,11 +85,10 @@ extern "C" { .direction = INC_ENCODER_DIRECTION } # endif #elif IS_USED(MODULE_INC_ENCODER_HARDWARE) + # ifndef INC_ENCODER_PARAMS # define INC_ENCODER_PARAMS { .qdec_dev = INC_ENCODER_QDEC_DEV } # endif -#else -# define INC_ENCODER_PARAMS {} #endif /**@}*/ diff --git a/drivers/inc_encoder/backends/software/inc_encoder_software.c b/drivers/inc_encoder/software.c similarity index 93% rename from drivers/inc_encoder/backends/software/inc_encoder_software.c rename to drivers/inc_encoder/software.c index ed0b9683f107..53bcdd3a627d 100644 --- a/drivers/inc_encoder/backends/software/inc_encoder_software.c +++ b/drivers/inc_encoder/software.c @@ -86,16 +86,16 @@ int inc_encoder_read_rpm(inc_encoder_t *dev, int32_t *rpm) return 0; } -int inc_encoder_read_reset_ceti_revs(inc_encoder_t *dev, int32_t *pulse_counter) +int inc_encoder_read_reset_milli_revs(inc_encoder_t *dev, int32_t *rev_counter) { int irq_state = irq_disable(); - *pulse_counter = dev->pulse_counter; + *rev_counter = dev->pulse_counter; dev->pulse_counter = 0; irq_restore(irq_state); - *pulse_counter *= UNIT_SCALE * GEAR_RED_RATIO_SCALE; - *pulse_counter /= CONFIG_INC_ENCODER_PPR; - *pulse_counter /= CONFIG_INC_ENCODER_GEAR_RED_RATIO; + *rev_counter *= UNIT_SCALE * GEAR_RED_RATIO_SCALE; + *rev_counter /= CONFIG_INC_ENCODER_PPR; + *rev_counter /= CONFIG_INC_ENCODER_GEAR_RED_RATIO; return 0; } diff --git a/drivers/include/inc_encoder.h b/drivers/include/inc_encoder.h index 99252f260858..49e3b40f6596 100644 --- a/drivers/include/inc_encoder.h +++ b/drivers/include/inc_encoder.h @@ -30,7 +30,8 @@ * the detected rotation direction will be reversed. * * The driver provides functions to read the current RPM - * and the total number of revolutions (in hundredths) since the last measurement. + * and the total number of revolutions since the last measurement. The number of revolutions + * is provided in millirevolutions (thousandths of a full revolution) to allow for higher precision. * * Configuration options are available via Kconfig to specify * the number of pulses per rotation, the maximum RPM, and the gear reduction ratio (in tenths). @@ -68,7 +69,7 @@ * This driver implements the SAUL sensor interface. * It provides two SAUL devices: * - One for reading the current RPM - * - One for reading the total number of revolutions in hundredths since the last read. + * - One for reading the total number of revolutions since the last read. * * @note After 327 revolutions without reading and resetting * the revolution counter, the `phydat` value will overflow. @@ -150,15 +151,16 @@ int inc_encoder_init(inc_encoder_t *dev, const inc_encoder_params_t *params); int inc_encoder_read_rpm(inc_encoder_t *dev, int32_t *rpm); /** - * @brief Read and reset number of revolutions since the last readout in hundredths. + * @brief Read and reset number of revolutions since the last readout in + * thousands of a full revolution. * * @param[in] dev Device descriptor of incremental rotary encoder - * @param[out] pulse_counter Number of revolutions since the last read in hundredths. + * @param[out] rev_counter Number of millirevolutions since the last read. * Negative revolutions signal counter clock wise rotations. * * @return 0 on success */ -int inc_encoder_read_reset_ceti_revs(inc_encoder_t *dev, int32_t *pulse_counter); +int inc_encoder_read_reset_milli_revs(inc_encoder_t *dev, int32_t *rev_counter); #ifdef __cplusplus } diff --git a/drivers/include/saul.h b/drivers/include/saul.h index 2cc578652488..cc8f71508470 100644 --- a/drivers/include/saul.h +++ b/drivers/include/saul.h @@ -140,7 +140,7 @@ enum { SAUL_SENSE_ID_PH, /**< sensor: pH */ SAUL_SENSE_ID_POWER, /**< sensor: power */ SAUL_SENSE_ID_SIZE, /**< sensor: size */ - SAUL_SENSE_ID_SPEED, /**< sensor: speed */ + SAUL_SENSE_ID_SPEED, /**< sensor: speed */ SAUL_SENSE_NUMOF /**< Number of actuators supported */ /* Extend this list as needed, but keep SAUL_SENSE_ID_ANY the first and * SAUL_SENSE_NUMOF the last entry @@ -225,7 +225,7 @@ enum { /** sensor: size */ SAUL_SENSE_SIZE = SAUL_CAT_SENSE | SAUL_SENSE_ID_SIZE, /** sensor: speed */ - SAUL_SENSE_SPEED = SAUL_CAT_SENSE | SAUL_SENSE_ID_SPEED, + SAUL_SENSE_SPEED = SAUL_CAT_SENSE | SAUL_SENSE_ID_SPEED, /** any device - wildcard */ SAUL_CLASS_ANY = 0xff /* extend this list as needed... */ diff --git a/drivers/saul/init_devs/auto_init_inc_encoder.c b/drivers/saul/init_devs/auto_init_inc_encoder.c index 1b5695aa879a..c88341ea62fd 100644 --- a/drivers/saul/init_devs/auto_init_inc_encoder.c +++ b/drivers/saul/init_devs/auto_init_inc_encoder.c @@ -35,7 +35,7 @@ static inc_encoder_t inc_encoder_devs[INC_ENCODER_NUM]; static saul_reg_t saul_entries[INC_ENCODER_NUM * 2]; extern saul_driver_t inc_encoder_rpm_saul_driver; -extern saul_driver_t inc_encoder_pulse_count_saul_driver; +extern saul_driver_t inc_encoder_rev_count_saul_driver; void auto_init_inc_encoder(void) { @@ -56,7 +56,7 @@ void auto_init_inc_encoder(void) /* Pulse Count */ saul_entries[(i * 2) + 1].dev = &(inc_encoder_devs[i]); saul_entries[(i * 2) + 1].name = inc_encoder_saul_info[i][1].name; - saul_entries[(i * 2) + 1].driver = &inc_encoder_pulse_count_saul_driver; + saul_entries[(i * 2) + 1].driver = &inc_encoder_rev_count_saul_driver; saul_reg_add(&(saul_entries[(i * 2)])); saul_reg_add(&(saul_entries[(i * 2) + 1])); diff --git a/tests/drivers/inc_encoder/main.c b/tests/drivers/inc_encoder/main.c index 1392aaaf58ee..abf4c6e0bb86 100644 --- a/tests/drivers/inc_encoder/main.c +++ b/tests/drivers/inc_encoder/main.c @@ -38,13 +38,13 @@ int main(void) puts("Printing sensor state every second."); while (1) { int32_t rpm; - int32_t pulses; + int32_t revs; if (inc_encoder_read_rpm(&dev, &rpm) - || inc_encoder_read_reset_ceti_revs(&dev, &pulses)) { + || inc_encoder_read_reset_milli_revs(&dev, &revs)) { puts("[Failed]"); return 1; } - printf("SENSOR DATA:\n\tRPM : %ld\n\tCREVS: %ld\n", (long) rpm, (long) pulses); + printf("SENSOR DATA:\n\tRPM : %ld\n\tMREVS: %ld\n", (long) rpm, (long) revs); ztimer_sleep(ZTIMER_SEC, 1); } }