Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
25 changes: 25 additions & 0 deletions drivers/hall_effect/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 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 (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"
default 13
help
Number of high flanks per revolution.

endmenu # Hall Effect Driver
1 change: 1 addition & 0 deletions drivers/hall_effect/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include $(RIOTMAKE)/driver_with_saul.mk
3 changes: 3 additions & 0 deletions drivers/hall_effect/Makefile.dep
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FEATURES_REQUIRED += periph_gpio
FEATURES_REQUIRED += periph_gpio_irq
USEMODULE += ztimer_usec
2 changes: 2 additions & 0 deletions drivers/hall_effect/Makefile.include
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
USEMODULE_INCLUDES_hall_effect := $(LAST_MAKEFILEDIR)/include
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_hall_effect)
153 changes: 153 additions & 0 deletions drivers/hall_effect/hall_effect.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* 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 <[email protected]>
*
* @}
*/

#include "hall_effect.h"
#include "hall_effect_params.h"

#include <errno.h>
#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.
*/
#define GEAR_RED_RATIO_SCALE 10

/* 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);

/* 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->ccw = 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 ccw;
if (!_read_delta_t_direction(dev, &delta_t, &ccw)) {
*rpm = 0;
return 0;
}

/* 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 revolution.
*/
*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;
}
return 0;
}

int hall_effect_read_reset_ceti_revs(hall_effect_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;
return 0;
}

/* 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);

/* 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->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)
{
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 *ccw)
{
int irq_state = irq_disable();
/* 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);
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;
}
*ccw = dev->ccw;

irq_restore(irq_state);
return true;
}
60 changes: 60 additions & 0 deletions drivers/hall_effect/hall_effect_saul.c
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
*
* @}
*/

#include <string.h>
#include <stdio.h>

#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_ceti_revs(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,
};
95 changes: 95 additions & 0 deletions drivers/hall_effect/include/hall_effect_params.h
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
*/

#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

/** @} */
Loading
Loading