diff --git a/src/hal/gpio.h b/src/hal/gpio.h index 624f7e4ecc..642c7d9711 100644 --- a/src/hal/gpio.h +++ b/src/hal/gpio.h @@ -94,4 +94,77 @@ hal_gpio_pin_t hal_gpio_parse_pin(const char *s); */ hal_gpio_pull_t hal_gpio_parse_pull(const char *pull_str); +/* + * Hardware GPIO Pulse Counter API + * + * Some platforms support hardware-based pulse counting on GPIO pins, + * allowing accurate counting without CPU intervention. + */ + +#define HAL_GPIO_COUNTER_INVALID -1 + +typedef int8_t hal_gpio_counter_t; + +typedef enum { + HAL_GPIO_COUNTER_RISING = 0, + HAL_GPIO_COUNTER_FALLING = 1, +} hal_gpio_counter_edge_t; + +/** + * Initialize a hardware pulse counter on a GPIO pin + * @param gpio_pin GPIO pin to count pulses on + * @param edge Edge polarity to trigger counting (rising or falling) + * @param pull Pull resistor configuration for the pin + * @return Counter handle (>=0) on success, HAL_GPIO_COUNTER_INVALID on failure + */ +hal_gpio_counter_t hal_gpio_counter_init(hal_gpio_pin_t gpio_pin, + hal_gpio_counter_edge_t edge, + hal_gpio_pull_t pull); + +/** + * Deinitialize a hardware pulse counter and free resources + * @param counter Counter handle from hal_gpio_counter_init + */ +void hal_gpio_counter_deinit(hal_gpio_counter_t counter); + +/** + * Read current pulse count from hardware counter + * @param counter Counter handle from hal_gpio_counter_init + * @return Current pulse count + */ +uint32_t hal_gpio_counter_read(hal_gpio_counter_t counter); + +/** + * Reset hardware counter to zero + * @param counter Counter handle from hal_gpio_counter_init + */ +void hal_gpio_counter_reset(hal_gpio_counter_t counter); + +/** + * Start an already allocated hardware counter + * @param counter Counter handle from hal_gpio_counter_init + */ +void hal_gpio_counter_start(hal_gpio_counter_t counter); + +/** + * Stop hardware counter but leaves the resource allocated + * @param counter Counter handle from hal_gpio_counter_init + */ +void hal_gpio_counter_stop(hal_gpio_counter_t counter); + +/** + * Read current pulse count and reset the counter to zero + * Note: Counter is stopped during read/reset, pulses during this brief + * window may be lost. Counter is always restarted after this call. + * @param counter Counter handle from hal_gpio_counter_init + * @return Current pulse count before reset + */ +static inline uint32_t hal_gpio_counter_read_and_reset(hal_gpio_counter_t counter) { + hal_gpio_counter_stop(counter); + uint32_t count = hal_gpio_counter_read(counter); + hal_gpio_counter_reset(counter); + hal_gpio_counter_start(counter); + return count; +} + #endif diff --git a/src/silabs/hal/gpio_counter.c b/src/silabs/hal/gpio_counter.c new file mode 100644 index 0000000000..3dcd582546 --- /dev/null +++ b/src/silabs/hal/gpio_counter.c @@ -0,0 +1,35 @@ +#include +#include +#include + +#include "hal/gpio.h" +#include + + +hal_gpio_counter_t hal_gpio_counter_init(hal_gpio_pin_t gpio_pin, + hal_gpio_counter_edge_t edge, + hal_gpio_pull_t pull) { + // No-op for now + + return HAL_GPIO_COUNTER_INVALID; +} + +void hal_gpio_counter_deinit(hal_gpio_counter_t counter) { + // No-op for now +} + +uint32_t hal_gpio_counter_read(hal_gpio_counter_t counter) { + return 0; +} + +void hal_gpio_counter_reset(hal_gpio_counter_t counter) { + // No-op for now +} + +void hal_gpio_counter_start(hal_gpio_counter_t counter) { + // No-op for now +} + +void hal_gpio_counter_stop(hal_gpio_counter_t counter) { + // No-op for now +} diff --git a/src/silabs/slcp_files/zigbee.slcp b/src/silabs/slcp_files/zigbee.slcp index a8c8c985b5..ca1803d9e5 100644 --- a/src/silabs/slcp_files/zigbee.slcp +++ b/src/silabs/slcp_files/zigbee.slcp @@ -10,6 +10,7 @@ source: - {path: ../../silabs/main.c} - {path: ../../silabs/hal/zigbee.c} - {path: ../../silabs/hal/gpio.c} +- {path: ../../silabs/hal/gpio_counter.c} - {path: ../../silabs/hal/nvm.c} - {path: ../../silabs/hal/tasks.c} - {path: ../../silabs/hal/timer.c} diff --git a/src/silabs/slcp_files/zigbee_end_device.slcp b/src/silabs/slcp_files/zigbee_end_device.slcp index d677951ae0..66f7d93284 100644 --- a/src/silabs/slcp_files/zigbee_end_device.slcp +++ b/src/silabs/slcp_files/zigbee_end_device.slcp @@ -10,6 +10,7 @@ source: - {path: ../../silabs/main.c} - {path: ../../silabs/hal/zigbee.c} - {path: ../../silabs/hal/gpio.c} +- {path: ../../silabs/hal/gpio_counter.c} - {path: ../../silabs/hal/nvm.c} - {path: ../../silabs/hal/tasks.c} - {path: ../../silabs/hal/timer.c} diff --git a/src/stub/commands.c b/src/stub/commands.c index 030360eac8..67a4693898 100644 --- a/src/stub/commands.c +++ b/src/stub/commands.c @@ -333,6 +333,31 @@ static int cmd_step_time(int argc, char **argv) { return 0; } +static int cmd_set_counter(int argc, char **argv) { + if (argc != 3) { + fprintf(stderr, "Usage: set_counter \n"); + io_res_err("usage"); + return -1; + } + char *e = NULL; + long pin = strtol(argv[1], &e, 10); + if (*argv[1] == '\0' || *e) { + fprintf(stderr, "Bad pin\n"); + io_res_err("bad_pin=%s", argv[1]); + return -1; + } + long value = strtol(argv[2], &e, 10); + if (*argv[2] == '\0' || *e || value < 0) { + fprintf(stderr, "Bad counter value\n"); + io_res_err("bad_value=%s", argv[2]); + return -1; + } + stub_set_pulse_counter((int)pin, (uint32_t)value); + printf("Set pulse counter on pin %ld to %ld\n", pin, value); + io_res_ok("pin=%ld value=%ld", pin, value); + return 0; +} + /* Command table */ static const SimpleReplCommand kCmds[] = { { "machine", cmd_machine }, @@ -348,6 +373,7 @@ static const SimpleReplCommand kCmds[] = { { "zcl_cmd", cmd_zcl_cmd }, { "freeze_time", cmd_freeze_time }, { "step_time", cmd_step_time }, + { "set_counter", cmd_set_counter }, { "q", cmd_quit }, { "quit", cmd_quit }, }; diff --git a/src/stub/hal/gpio.c b/src/stub/hal/gpio.c index c975551186..23e8fbb26b 100644 --- a/src/stub/hal/gpio.c +++ b/src/stub/hal/gpio.c @@ -178,3 +178,83 @@ void ensure_valid_output_pin(hal_gpio_pin_t gpio_pin) { exit(1); } } + +typedef struct { + uint8_t initialized; + uint32_t value; + hal_gpio_pin_t gpio_pin; + hal_gpio_counter_t *handle; +} stub_gpio_counter_t; + +static stub_gpio_counter_t gpio_counter[2]; + + +hal_gpio_counter_t hal_gpio_counter_init(hal_gpio_pin_t gpio_pin, + hal_gpio_counter_edge_t edge, + hal_gpio_pull_t pull) { + + for (int i = 0; i < 2; i++) { + if (!gpio_counter[i].initialized) { + gpio_counter[i].initialized = 1; + gpio_counter[i].value = 0; + gpio_counter[i].gpio_pin = gpio_pin; + gpio_counter[i].handle = (hal_gpio_counter_t *)(uintptr_t)i; + io_log("GPIO", "Initialized GPIO counter %d on pin %d", i, gpio_pin); + return (hal_gpio_counter_t)(uintptr_t)i; + } + } + + return HAL_GPIO_COUNTER_INVALID; +} + +uint32_t hal_gpio_counter_read(hal_gpio_counter_t counter) { + if (counter < 0 || counter >= 2 || !gpio_counter[counter].initialized) { + io_log("GPIO", "Error: Invalid GPIO counter %d read", counter); + return 0; + } + + io_log("GPIO", "Read GPIO counter %d = %u", counter, + gpio_counter[counter].value); + + for (int i = 0; i < 2; i++) { + if (gpio_counter[i].initialized && gpio_counter[i].handle == counter) { + return gpio_counter[i].value; + } + } + + return 0; +} + +void hal_gpio_counter_reset(hal_gpio_counter_t counter) { + if (!counter) { + return; + } + + for (int i = 0; i < 2; i++) { + if (gpio_counter[i].initialized && gpio_counter[i].handle == counter) { + gpio_counter[i].value = 0; + break; + } + } +} + +void hal_gpio_counter_stop(hal_gpio_counter_t counter) { + // No-op in stub +} + +void hal_gpio_counter_start(hal_gpio_counter_t counter) { + // No-op in stub +} + +void stub_set_pulse_counter(hal_gpio_pin_t gpio_pin, uint32_t value) { + io_log("GPIO", "Stub: set pulse counter on pin %d to %u", gpio_pin, value); + + for (int i = 0; i < 2; i++) { + if (gpio_counter[i].initialized && gpio_counter[i].gpio_pin == gpio_pin) { + gpio_counter[i].value = value; + return; + } + } + + io_log("GPIO", "Stub: no pulse counter found on pin %d to set value", gpio_pin); +} \ No newline at end of file diff --git a/src/stub/hal/stub.h b/src/stub/hal/stub.h index 3a2e35497d..dd781ef7ca 100644 --- a/src/stub/hal/stub.h +++ b/src/stub/hal/stub.h @@ -10,6 +10,9 @@ void stub_gpio_enable_debug(int enable); void stub_gpio_simulate_input(hal_gpio_pin_t gpio_pin, uint8_t value); uint8_t stub_gpio_get_output(hal_gpio_pin_t gpio_pin); +// GPIO counter stub functions +void stub_set_pulse_counter(hal_gpio_pin_t gpio_pin, uint32_t value); + // Tasks stub functions void stub_tasks_poll(void); diff --git a/src/stub/stub_app.c b/src/stub/stub_app.c index 164b802da8..4b83a6c504 100644 --- a/src/stub/stub_app.c +++ b/src/stub/stub_app.c @@ -101,6 +101,7 @@ void stub_app_print_help(void) { "bytes)"); puts(" freeze_time <0|1> - Freeze/unfreeze time"); puts(" step_time - Advance time by ms"); + puts(" set_counter - Set pulse counter value"); puts(" q, quit - Exit"); } diff --git a/src/telink/Makefile b/src/telink/Makefile index be1088869b..8e92dd9e82 100644 --- a/src/telink/Makefile +++ b/src/telink/Makefile @@ -129,6 +129,7 @@ TELINK_SOURCES := \ hal/tasks.c \ hal/gpio.c \ hal/gpio_interrupts.c \ + hal/gpio_counter.c \ hal/nvm.c \ hal/zigbee.c \ hal/zigbee_network.c \ diff --git a/src/telink/hal/gpio.c b/src/telink/hal/gpio.c index 0afcff4c66..4d9ef33140 100644 --- a/src/telink/hal/gpio.c +++ b/src/telink/hal/gpio.c @@ -10,7 +10,7 @@ // hal_gpio_pin_t directly stores GPIO_PinTypeDef values // Convert HAL pull type to Telink pull type -static GPIO_PullTypeDef hal_to_telink_pull(hal_gpio_pull_t pull) { +GPIO_PullTypeDef hal_to_telink_pull(hal_gpio_pull_t pull) { switch (pull) { case HAL_GPIO_PULL_NONE: return PM_PIN_UP_DOWN_FLOAT; diff --git a/src/telink/hal/gpio_counter.c b/src/telink/hal/gpio_counter.c new file mode 100644 index 0000000000..de39941585 --- /dev/null +++ b/src/telink/hal/gpio_counter.c @@ -0,0 +1,172 @@ +#pragma pack(push, 1) +#include "tl_common.h" +#pragma pack(pop) + +#include "hal/gpio.h" +#include + +extern GPIO_PullTypeDef hal_to_telink_pull(hal_gpio_pull_t pull); + +// Maximum number of hardware counters available (TIMER0 and TIMER1) +#define MAX_GPIO_COUNTERS 2 + +typedef struct { + hal_gpio_pin_t gpio_pin; + uint8_t timer_idx; + uint8_t in_use; +} gpio_counter_state_t; + +static gpio_counter_state_t counter_state[MAX_GPIO_COUNTERS]; +static uint8_t counters_initialized = 0; + +static void init_counter_state(void) { + if (!counters_initialized) { + for (int i = 0; i < MAX_GPIO_COUNTERS; i++) { + counter_state[i].gpio_pin = HAL_INVALID_PIN; + counter_state[i].timer_idx = i; // TIMER0=0, TIMER1=1 + counter_state[i].in_use = 0; + } + counters_initialized = 1; + } +} + +hal_gpio_counter_t hal_gpio_counter_init(hal_gpio_pin_t gpio_pin, + hal_gpio_counter_edge_t edge, + hal_gpio_pull_t pull) { + init_counter_state(); + + // Find a free counter slot + gpio_counter_state_t *slot = NULL; + hal_gpio_counter_t handle = HAL_GPIO_COUNTER_INVALID; + + for (int i = 0; i < MAX_GPIO_COUNTERS; i++) { + if (!counter_state[i].in_use) { + slot = &counter_state[i]; + handle = i; + break; + } + } + + if (slot == NULL) { + return HAL_GPIO_COUNTER_INVALID; + } + + // Map HAL edge to Telink polarity + GPIO_PolTypeDef pol = (edge == HAL_GPIO_COUNTER_RISING) ? POL_RISING : POL_FALLING; + GPIO_PinTypeDef telink_pin = (GPIO_PinTypeDef)gpio_pin; + + // Configure GPIO for timer trigger mode + timer_gpio_init(slot->timer_idx, telink_pin, pol); + + // Enable GPIO interrupt for the selected timer + if (slot->timer_idx == 0) { + drv_gpio_irq_risc0_en(telink_pin); + } else if (slot->timer_idx == 1) { + drv_gpio_irq_risc1_en(telink_pin); + } + + // Set pull resistor + gpio_setup_up_down_resistor(telink_pin, hal_to_telink_pull(pull)); + + // Set timer to GPIO trigger (counter) mode + timer_set_mode(slot->timer_idx, TIMER_MODE_GPIO_TRIGGER); + + // Reset counter to 0 + timer_set_init_tick(slot->timer_idx, 0); + + // Set max count value + timer_set_cap_tick(slot->timer_idx, 0xFFFFFFFF); + + // Enable timer interrupt (needed for counter operation) + timer_irq_enable(slot->timer_idx); + + // Start the timer/counter + timer_start(slot->timer_idx); + + // Mark slot as in use + slot->gpio_pin = gpio_pin; + slot->in_use = 1; + + return handle; +} + +void hal_gpio_counter_deinit(hal_gpio_counter_t counter) { + if (counter < 0 || counter >= MAX_GPIO_COUNTERS) { + return; + } + + gpio_counter_state_t *slot = &counter_state[counter]; + if (!slot->in_use) { + return; + } + + // Stop the timer/counter + hal_gpio_counter_stop(counter); + + // Reset the counter + hal_gpio_counter_reset(counter); + + // Disable GPIO interrupt for the selected timer + GPIO_PinTypeDef telink_pin = (GPIO_PinTypeDef)slot->gpio_pin; + if (slot->timer_idx == 0) { + drv_gpio_irq_risc0_dis(telink_pin); + } else if (slot->timer_idx == 1) { + drv_gpio_irq_risc1_dis(telink_pin); + } + + // Reset slot state + slot->gpio_pin = HAL_INVALID_PIN; + slot->in_use = 0; +} + +uint32_t hal_gpio_counter_read(hal_gpio_counter_t counter) { + if (counter < 0 || counter >= MAX_GPIO_COUNTERS) { + return 0; + } + + gpio_counter_state_t *slot = &counter_state[counter]; + if (!slot->in_use) { + return 0; + } + + return reg_tmr_tick(slot->timer_idx); +} + +void hal_gpio_counter_reset(hal_gpio_counter_t counter) { + if (counter < 0 || counter >= MAX_GPIO_COUNTERS) { + return; + } + + gpio_counter_state_t *slot = &counter_state[counter]; + if (!slot->in_use) { + return; + } + + timer_set_init_tick(slot->timer_idx, 0); +} + +void hal_gpio_counter_start(hal_gpio_counter_t counter) { + if (counter < 0 || counter >= MAX_GPIO_COUNTERS) { + return; + } + + gpio_counter_state_t *slot = &counter_state[counter]; + if (!slot->in_use) { + return; + } + + timer_start(slot->timer_idx); +} + +void hal_gpio_counter_stop(hal_gpio_counter_t counter) { + if (counter < 0 || counter >= MAX_GPIO_COUNTERS) { + return; + } + + gpio_counter_state_t *slot = &counter_state[counter]; + if (!slot->in_use) { + return; + } + + timer_stop(slot->timer_idx); +} diff --git a/tests/conftest.py b/tests/conftest.py index 48762b92ea..697ac03394 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -214,6 +214,10 @@ def step_time(self, ms: int) -> None: res = self.p.exec(f"step_time {ms}") assert res.ok, f"Step time failed: {res.payload}" + def set_pulse_counter(self, pin: str, value: int) -> None: + res = self.p.exec(f"set_counter {self._parse_pin(pin)} {value}") + assert res.ok, f"Set pulse counter failed: {res.payload}" + def _evt_parser(self, evt: Event) -> None: if evt.kind == "gpio": pin = int(evt.payload.get("pin", "-1"))