From 16d85d1a78bbd29d0177c98754b9a8ccb9f10b42 Mon Sep 17 00:00:00 2001 From: Adrian Keet Date: Sat, 6 Feb 2021 15:11:29 -0800 Subject: fan: Add tachometer support This adds new config options for fans: 'tachometer_pin' to specify the GPIO pin, and 'tachometer_ppr' (default 2) to specify the number of signal pulses per revolution. The rpm is also exposed by get_status for command templates and the API server. For fast fans (at least 10000 RPM), the polling interval can be shortened using the 'tachometer_poll_interval' option. There is a new mcu object for a generic edge counter, which repeatedly polls a GPIO pin and periodically reports the count to the host. Signed-off-by: Adrian Keet --- src/Makefile | 2 +- src/pulse_counter.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/pulse_counter.c (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 98c9a1c1..2f2ab521 100644 --- a/src/Makefile +++ b/src/Makefile @@ -7,4 +7,4 @@ src-$(CONFIG_HAVE_GPIO_SPI) += spicmds.c thermocouple.c src-$(CONFIG_HAVE_GPIO_I2C) += i2ccmds.c src-$(CONFIG_HAVE_GPIO_HARD_PWM) += pwmcmds.c src-$(CONFIG_HAVE_GPIO_BITBANGING) += lcd_st7920.c lcd_hd44780.c buttons.c \ - tmcuart.c spi_software.c neopixel.c sensor_adxl345.c + tmcuart.c spi_software.c neopixel.c sensor_adxl345.c pulse_counter.c diff --git a/src/pulse_counter.c b/src/pulse_counter.c new file mode 100644 index 00000000..c2cf4ea3 --- /dev/null +++ b/src/pulse_counter.c @@ -0,0 +1,99 @@ +// Commands for counting edges on GPIO input pins +// +// Copyright (C) 2021 Adrian Keet +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "basecmd.h" // oid_alloc +#include "board/gpio.h" // struct gpio_in +#include "board/irq.h" // irq_disable +#include "board/misc.h" // timer_read_time +#include "command.h" // DECL_COMMAND +#include "sched.h" // DECL_TASK + +struct counter { + struct timer timer; + uint32_t poll_ticks; + uint32_t sample_ticks, next_sample_time; + uint32_t count, last_count_time; + uint8_t flags; + struct gpio_in pin; +}; + +enum { + CF_PENDING = 1, +}; + +static struct task_wake counter_wake; + +static uint_fast8_t +counter_event(struct timer *timer) +{ + struct counter *c = container_of(timer, struct counter, timer); + + uint32_t time = c->timer.waketime; + uint8_t last_value = c->count & 1; + uint8_t value = gpio_in_read(c->pin); + if (last_value != value) { + c->count++; + c->last_count_time = time; + } + // useful invariant: c->count & 1 == value + + if (timer_is_before(c->next_sample_time, time)) { + c->flags |= CF_PENDING; + c->next_sample_time = time + c->sample_ticks; + sched_wake_task(&counter_wake); + } + + c->timer.waketime += c->poll_ticks; + return SF_RESCHEDULE; +} + +void +command_config_counter(uint32_t *args) +{ + struct counter *c = oid_alloc( + args[0], command_config_counter, sizeof(*c)); + c->pin = gpio_in_setup(args[1], args[2]); + c->timer.func = counter_event; +} +DECL_COMMAND(command_config_counter, + "config_counter oid=%c pin=%u pull_up=%c"); + +void +command_query_counter(uint32_t *args) +{ + struct counter *c = oid_lookup(args[0], command_config_counter); + sched_del_timer(&c->timer); + c->timer.waketime = args[1]; + c->poll_ticks = args[2]; + c->sample_ticks = args[3]; + c->next_sample_time = c->timer.waketime; // sample immediately + sched_add_timer(&c->timer); +} +DECL_COMMAND(command_query_counter, + "query_counter oid=%c clock=%u poll_ticks=%u sample_ticks=%u"); + +void +counter_task(void) +{ + if (!sched_check_wake(&counter_wake)) + return; + + uint8_t oid; + struct counter *c; + foreach_oid(oid, c, command_config_counter) { + if (!(c->flags & CF_PENDING)) + continue; + irq_disable(); + uint32_t time = c->timer.waketime - c->poll_ticks; + uint32_t count = c->count; + uint32_t count_time = c->last_count_time; + c->flags &= ~CF_PENDING; + irq_enable(); + sendf("counter_state oid=%c time=%u count=%u count_time=%u", + oid, time, count, count_time); + } +} +DECL_TASK(counter_task); -- cgit v1.2.3-70-g09d2