From fd02fb65ae3c769f77fcb080f80fdceb23aae7ef Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Sat, 19 Jul 2025 14:59:37 +0300 Subject: [PATCH 01/18] Issue #15: Add buzzer driver implementation --- KPI_Rover/Buzzer/driver.c | 91 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 KPI_Rover/Buzzer/driver.c diff --git a/KPI_Rover/Buzzer/driver.c b/KPI_Rover/Buzzer/driver.c new file mode 100644 index 0000000..3bdb93e --- /dev/null +++ b/KPI_Rover/Buzzer/driver.c @@ -0,0 +1,91 @@ +#include "stm32f4xx_hal.h" + +#include "FreeRTOS.h" +#include "task.h" + +#define BUZZER_PIN_ERROR 0x1U +#define BUZZER_NOT_INITIALIZED_ERROR 0x2U +#define BUZZER_HAL_ERROR 0x3U +#define BUZZER_VALUE_ERROR 0x4U +#define BUZZER_ZERO_ONTIME_ERROR 0x5U +#define BUZZER_ZERO_OFFTIME_ERROR 0x6U + +#define CHECK_ERROR(condition, err_value) do { \ + if (condition) { \ + errors = (err_value); \ + goto fail; \ + } \ + } while (0) + +static GPIO_TypeDef *GPIO_buzzer_port; +static uint16_t GPIO_buzzer_pin; +static int buzzer_initialized; + +unsigned int Buzzer_ConfigurePort(const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin) +{ + unsigned int errors = 0; + + CHECK_ERROR(gpio_pin > 15, BUZZER_PIN_ERROR); + + GPIO_buzzer_port = (GPIO_TypeDef *) gpio_port; + GPIO_buzzer_pin = (uint16_t) gpio_pin; + + buzzer_initialized = 1; + + return errors; + +fail: + buzzer_initialized = 0; + return errors; +} + +unsigned int Buzzer_Enable(void) +{ + unsigned int errors = 0; + + CHECK_ERROR(!buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); + + HAL_GPIO_WritePin(GPIO_buzzer_port, GPIO_buzzer_pin, GPIO_PIN_SET); + +fail: + return errors; +} + +unsigned int Buzzer_Disable(void) +{ + unsigned int errors = 0; + + CHECK_ERROR(!buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); + + HAL_GPIO_WritePin(GPIO_buzzer_port, GPIO_buzzer_pin, GPIO_PIN_RESET); + +fail: + return errors; +} + +unsigned int Buzzer_Pulse(const unsigned int on_time_ms, const unsigned int period_time_ms, const unsigned int total_active_time_ms) +{ + unsigned int errors = 0; + + CHECK_ERROR(!buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); + + const TickType_t on_time_ticks = pdMS_TO_TICKS(on_time_ms); + CHECK_ERROR(!on_time_ticks, BUZZER_ZERO_ONTIME_ERROR); + + const TickType_t off_time_ticks = pdMS_TO_TICKS(period_time_ms) - on_time_ticks; + CHECK_ERROR(!off_time_ticks, BUZZER_ZERO_OFFTIME_ERROR); + + const unsigned int total_beep_amount = total_active_time_ms / period_time_ms; + + for (unsigned int i = 0; i < total_beep_amount; i++) + { + Buzzer_Enable(); + vTaskDelay(on_time_ticks); + Buzzer_Disable(); + vTaskDelay(off_time_ticks); + } + +fail: + Buzzer_Disable(); + return errors; +} From 91b6900a9dfa31a1c83cdee5b421039cb155c422 Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Sat, 26 Jul 2025 16:55:40 +0300 Subject: [PATCH 02/18] Issue #15: Extract definitions from driver.c into their separate file driver.h --- KPI_Rover/Buzzer/driver.c | 7 +------ KPI_Rover/Buzzer/driver.h | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 KPI_Rover/Buzzer/driver.h diff --git a/KPI_Rover/Buzzer/driver.c b/KPI_Rover/Buzzer/driver.c index 3bdb93e..4abb232 100644 --- a/KPI_Rover/Buzzer/driver.c +++ b/KPI_Rover/Buzzer/driver.c @@ -3,12 +3,7 @@ #include "FreeRTOS.h" #include "task.h" -#define BUZZER_PIN_ERROR 0x1U -#define BUZZER_NOT_INITIALIZED_ERROR 0x2U -#define BUZZER_HAL_ERROR 0x3U -#define BUZZER_VALUE_ERROR 0x4U -#define BUZZER_ZERO_ONTIME_ERROR 0x5U -#define BUZZER_ZERO_OFFTIME_ERROR 0x6U +#include "driver.h" #define CHECK_ERROR(condition, err_value) do { \ if (condition) { \ diff --git a/KPI_Rover/Buzzer/driver.h b/KPI_Rover/Buzzer/driver.h new file mode 100644 index 0000000..c4a49e0 --- /dev/null +++ b/KPI_Rover/Buzzer/driver.h @@ -0,0 +1,18 @@ +#ifndef __BUZZER_DRIVER +#define __BUZZER_DRIVER + +#include "stm32f4xx_hal.h" + +#define BUZZER_PIN_ERROR 0x1U +#define BUZZER_NOT_INITIALIZED_ERROR 0x2U +#define BUZZER_HAL_ERROR 0x3U +#define BUZZER_VALUE_ERROR 0x4U +#define BUZZER_ZERO_ONTIME_ERROR 0x5U +#define BUZZER_ZERO_OFFTIME_ERROR 0x6U + +unsigned int Buzzer_ConfigurePort(const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin); +unsigned int Buzzer_Enable(void); +unsigned int Buzzer_Disable(void); +unsigned int Buzzer_Pulse(const unsigned int on_time_ms, const unsigned int period_time_ms, const unsigned int total_active_time_ms); + +#endif /* __BUZZER_DRIVER */ From ee933aa4a8533456bf54d8f89a7ae8924ea28a00 Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Fri, 1 Aug 2025 14:14:07 +0300 Subject: [PATCH 03/18] Issue #15: Add buzzer manager task --- KPI_Rover/Buzzer/manager.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 KPI_Rover/Buzzer/manager.c diff --git a/KPI_Rover/Buzzer/manager.c b/KPI_Rover/Buzzer/manager.c new file mode 100644 index 0000000..d26fb04 --- /dev/null +++ b/KPI_Rover/Buzzer/manager.c @@ -0,0 +1,27 @@ +#include "FreeRTOS.h" +#include "task.h" + +#include "ul_ulog.h" + +#include "driver.h" + +void buzzer_manager_task(void *d) +{ + if (Buzzer_ConfigurePort(GPIOD, GPIO_PIN_15)) + { + ULOG_ERROR("Failed to configure buzzer driver port"); + vTaskDelete(NULL); + } + + for ( ; ; ) + { + Buzzer_Enable(); + vTaskDelay(pdMS_TO_TICKS(3000)); + Buzzer_Disable(); + vTaskDelay(pdMS_TO_TICKS(3000)); + Buzzer_Pulse(500, 1000, 5000); + Buzzer_Pulse(200, 1000, 5000); + Buzzer_Pulse(100, 300, 5000); + Buzzer_Pulse(250, 300, 5000); + } +} From f23af02fd8586978a496dae4ab3912c69034ca9b Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Fri, 1 Aug 2025 20:02:14 +0300 Subject: [PATCH 04/18] Issue #15: fix configuration sanity check --- KPI_Rover/Buzzer/driver.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/KPI_Rover/Buzzer/driver.c b/KPI_Rover/Buzzer/driver.c index 4abb232..12678cc 100644 --- a/KPI_Rover/Buzzer/driver.c +++ b/KPI_Rover/Buzzer/driver.c @@ -16,11 +16,27 @@ static GPIO_TypeDef *GPIO_buzzer_port; static uint16_t GPIO_buzzer_pin; static int buzzer_initialized; +static unsigned int count_bits_16bit(uint16_t value) +{ + unsigned int mask = 0x8000, + result = 0; + + while (mask) + { + if (value & mask) + result++; + + mask >>= 1; + } + + return result; +} + unsigned int Buzzer_ConfigurePort(const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin) { unsigned int errors = 0; - CHECK_ERROR(gpio_pin > 15, BUZZER_PIN_ERROR); + CHECK_ERROR(count_bits_16bit(gpio_pin) != 1, BUZZER_PIN_ERROR); GPIO_buzzer_port = (GPIO_TypeDef *) gpio_port; GPIO_buzzer_pin = (uint16_t) gpio_pin; From 31408ae500b9745165aaf208e720e654a09ca6dc Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Fri, 1 Aug 2025 20:22:48 +0300 Subject: [PATCH 05/18] Issue #15: improve total timing precision of pulse function --- KPI_Rover/Buzzer/driver.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/KPI_Rover/Buzzer/driver.c b/KPI_Rover/Buzzer/driver.c index 12678cc..c576be4 100644 --- a/KPI_Rover/Buzzer/driver.c +++ b/KPI_Rover/Buzzer/driver.c @@ -86,6 +86,8 @@ unsigned int Buzzer_Pulse(const unsigned int on_time_ms, const unsigned int peri const TickType_t off_time_ticks = pdMS_TO_TICKS(period_time_ms) - on_time_ticks; CHECK_ERROR(!off_time_ticks, BUZZER_ZERO_OFFTIME_ERROR); + const TickType_t last_on_time_ticks = pdMS_TO_TICKS(total_active_time_ms % period_time_ms); + const unsigned int total_beep_amount = total_active_time_ms / period_time_ms; for (unsigned int i = 0; i < total_beep_amount; i++) @@ -96,6 +98,13 @@ unsigned int Buzzer_Pulse(const unsigned int on_time_ms, const unsigned int peri vTaskDelay(off_time_ticks); } + if (last_on_time_ticks) + { + Buzzer_Enable(); + vTaskDelay(last_on_time_ticks); + Buzzer_Disable(); + } + fail: Buzzer_Disable(); return errors; From 5e86956de395a8806677387dc3d68ecb8633270c Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Fri, 1 Aug 2025 20:28:48 +0300 Subject: [PATCH 06/18] Issue #15: add new task for buzzer manager --- Core/Src/main.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Core/Src/main.c b/Core/Src/main.c index d62d1fe..07dd08f 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -77,7 +77,7 @@ static void MX_TIM3_Init(void); void StartDefaultTask(void *argument); /* USER CODE BEGIN PFP */ - +void buzzer_manager_task(void *); /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ @@ -158,6 +158,13 @@ int main(void) ul_ulog_init(); + static const osThreadAttr_t buzzer_task_attrs = { + .name = "buzzer", + .stack_size = 16 * 4, + .priority = (osPriority_t) osPriorityHigh, + }; + (void) osThreadNew(buzzer_manager_task, NULL, &buzzer_task_attrs); + /* USER CODE END RTOS_THREADS */ From 26aa038f381ac5ab14dffe34ea717f690bb98540 Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Sat, 16 Aug 2025 21:26:16 +0300 Subject: [PATCH 07/18] Issue #15: [fix] make pulse function non-blocking and abortable --- KPI_Rover/Buzzer/driver.c | 135 ++++++++++++++++++++++++++++++++----- KPI_Rover/Buzzer/driver.h | 2 + KPI_Rover/Buzzer/manager.c | 4 ++ 3 files changed, 123 insertions(+), 18 deletions(-) diff --git a/KPI_Rover/Buzzer/driver.c b/KPI_Rover/Buzzer/driver.c index c576be4..0eb2330 100644 --- a/KPI_Rover/Buzzer/driver.c +++ b/KPI_Rover/Buzzer/driver.c @@ -2,9 +2,12 @@ #include "FreeRTOS.h" #include "task.h" +#include "timers.h" #include "driver.h" +#define BUZZER_BLOCKING_TIME_LIMIT 1000 + #define CHECK_ERROR(condition, err_value) do { \ if (condition) { \ errors = (err_value); \ @@ -12,10 +15,29 @@ } \ } while (0) +enum BuzzerState { + BUZZER_ON, + BUZZER_OFF, + BUZZER_NOT_ACTIVE, + IMPOSSIBLE_STATE +}; + +struct BuzzerStateMemory { + TickType_t on_duration; + TickType_t off_duration; + TickType_t total_active_time; + TickType_t active_time_limit; + enum BuzzerState current_state; +}; + static GPIO_TypeDef *GPIO_buzzer_port; static uint16_t GPIO_buzzer_pin; static int buzzer_initialized; +static TimerHandle_t timer_handle; +static StaticTimer_t timer; +static struct BuzzerStateMemory bsm; + static unsigned int count_bits_16bit(uint16_t value) { unsigned int mask = 0x8000, @@ -32,8 +54,18 @@ static unsigned int count_bits_16bit(uint16_t value) return result; } +static void Buzzer_ResetTimer(void) +{ + if (timer_handle == NULL) + return; + + (void) xTimerStop(timer_handle, BUZZER_BLOCKING_TIME_LIMIT); +} + unsigned int Buzzer_ConfigurePort(const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin) { + Buzzer_ResetTimer(); + unsigned int errors = 0; CHECK_ERROR(count_bits_16bit(gpio_pin) != 1, BUZZER_PIN_ERROR); @@ -50,7 +82,7 @@ unsigned int Buzzer_ConfigurePort(const GPIO_TypeDef * const gpio_port, const ui return errors; } -unsigned int Buzzer_Enable(void) +static unsigned int Buzzer_SetON(void) { unsigned int errors = 0; @@ -62,7 +94,7 @@ unsigned int Buzzer_Enable(void) return errors; } -unsigned int Buzzer_Disable(void) +static unsigned int Buzzer_SetOFF(void) { unsigned int errors = 0; @@ -74,8 +106,67 @@ unsigned int Buzzer_Disable(void) return errors; } +unsigned int Buzzer_Enable(void) +{ + Buzzer_ResetTimer(); + return Buzzer_SetON(); +} + +unsigned int Buzzer_Disable(void) +{ + Buzzer_ResetTimer(); + return Buzzer_SetOFF(); +} + +static void Buzzer_TimerCallback(TimerHandle_t t) +{ + TickType_t new_period; + + switch (bsm.current_state) { + case BUZZER_ON: + Buzzer_SetOFF(); // disable first, determine action course next + bsm.current_state = BUZZER_OFF; + bsm.total_active_time += bsm.on_duration; + + new_period = bsm.total_active_time + bsm.off_duration <= bsm.active_time_limit + ? bsm.off_duration + : bsm.active_time_limit - bsm.total_active_time; + + if (!new_period) + break; + + (void) xTimerChangePeriod(timer_handle, new_period, 0); + (void) xTimerStart(timer_handle, 0); + break; + + case BUZZER_OFF: + bsm.current_state = BUZZER_ON; + bsm.total_active_time += bsm.off_duration; + + new_period = bsm.total_active_time + bsm.on_duration <= bsm.active_time_limit + ? bsm.on_duration + : bsm.active_time_limit - bsm.total_active_time; + + if (!new_period) + break; + + (void) xTimerChangePeriod(timer_handle, new_period, 0); + (void) xTimerStart(timer_handle, 0); + Buzzer_SetON(); + break; + + case BUZZER_NOT_ACTIVE: + case IMPOSSIBLE_STATE: + default: + // why are we here then? + break; + } +} + unsigned int Buzzer_Pulse(const unsigned int on_time_ms, const unsigned int period_time_ms, const unsigned int total_active_time_ms) { + Buzzer_ResetTimer(); + unsigned int errors = 0; CHECK_ERROR(!buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); @@ -86,26 +177,34 @@ unsigned int Buzzer_Pulse(const unsigned int on_time_ms, const unsigned int peri const TickType_t off_time_ticks = pdMS_TO_TICKS(period_time_ms) - on_time_ticks; CHECK_ERROR(!off_time_ticks, BUZZER_ZERO_OFFTIME_ERROR); - const TickType_t last_on_time_ticks = pdMS_TO_TICKS(total_active_time_ms % period_time_ms); + const TickType_t active_time_limit = pdMS_TO_TICKS(total_active_time_ms); + CHECK_ERROR(!active_time_limit, BUZZER_ZERO_ACTIVE_TIME_ERROR); + + if (timer_handle == NULL) { + timer_handle = xTimerCreateStatic( + "", + on_time_ticks, + pdFALSE, + 0, + Buzzer_TimerCallback, + &timer); + } else { + CHECK_ERROR(xTimerChangePeriod(timer_handle, on_time_ticks, BUZZER_BLOCKING_TIME_LIMIT) == pdFAIL, BUZZER_TIMER_BUSY); + } - const unsigned int total_beep_amount = total_active_time_ms / period_time_ms; + CHECK_ERROR(xTimerStart(timer_handle, BUZZER_BLOCKING_TIME_LIMIT) == pdFAIL, BUZZER_TIMER_BUSY); + Buzzer_SetON(); - for (unsigned int i = 0; i < total_beep_amount; i++) - { - Buzzer_Enable(); - vTaskDelay(on_time_ticks); - Buzzer_Disable(); - vTaskDelay(off_time_ticks); - } + bsm.on_duration = on_time_ticks; + bsm.off_duration = off_time_ticks; + bsm.total_active_time = 0; + bsm.active_time_limit = active_time_limit; + bsm.current_state = BUZZER_ON; - if (last_on_time_ticks) - { - Buzzer_Enable(); - vTaskDelay(last_on_time_ticks); - Buzzer_Disable(); - } + return 0; fail: - Buzzer_Disable(); + Buzzer_ResetTimer(); + Buzzer_SetOFF(); return errors; } diff --git a/KPI_Rover/Buzzer/driver.h b/KPI_Rover/Buzzer/driver.h index c4a49e0..4b0d15d 100644 --- a/KPI_Rover/Buzzer/driver.h +++ b/KPI_Rover/Buzzer/driver.h @@ -9,6 +9,8 @@ #define BUZZER_VALUE_ERROR 0x4U #define BUZZER_ZERO_ONTIME_ERROR 0x5U #define BUZZER_ZERO_OFFTIME_ERROR 0x6U +#define BUZZER_ZERO_ACTIVE_TIME_ERROR 0x7U +#define BUZZER_TIMER_BUSY 0x8U unsigned int Buzzer_ConfigurePort(const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin); unsigned int Buzzer_Enable(void); diff --git a/KPI_Rover/Buzzer/manager.c b/KPI_Rover/Buzzer/manager.c index d26fb04..077b9da 100644 --- a/KPI_Rover/Buzzer/manager.c +++ b/KPI_Rover/Buzzer/manager.c @@ -20,8 +20,12 @@ void buzzer_manager_task(void *d) Buzzer_Disable(); vTaskDelay(pdMS_TO_TICKS(3000)); Buzzer_Pulse(500, 1000, 5000); + vTaskDelay(pdMS_TO_TICKS(5000)); Buzzer_Pulse(200, 1000, 5000); + vTaskDelay(pdMS_TO_TICKS(5000)); Buzzer_Pulse(100, 300, 5000); + vTaskDelay(pdMS_TO_TICKS(5000)); Buzzer_Pulse(250, 300, 5000); + vTaskDelay(pdMS_TO_TICKS(1000)); // intentionally shorter delay; next command must abort this one } } From ee5f7aabb08268521473c0316a68d0245d90df00 Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Sat, 16 Aug 2025 21:27:35 +0300 Subject: [PATCH 08/18] Issue #15: simplify count_bits_16bit function --- KPI_Rover/Buzzer/driver.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/KPI_Rover/Buzzer/driver.c b/KPI_Rover/Buzzer/driver.c index 0eb2330..545d921 100644 --- a/KPI_Rover/Buzzer/driver.c +++ b/KPI_Rover/Buzzer/driver.c @@ -40,17 +40,12 @@ static struct BuzzerStateMemory bsm; static unsigned int count_bits_16bit(uint16_t value) { - unsigned int mask = 0x8000, - result = 0; + unsigned int result = 0; - while (mask) - { - if (value & mask) + for ( ; value; value >>= 1) + if (value & 1) result++; - mask >>= 1; - } - return result; } From 133cf061dc47c53555e0af20f4e0688dc4763c7d Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Sun, 14 Sep 2025 21:06:17 +0300 Subject: [PATCH 09/18] Issue #15: Extract memory from driver to manager --- KPI_Rover/Buzzer/driver.c | 165 +++++++++++++------------------------ KPI_Rover/Buzzer/driver.h | 31 ++++++- KPI_Rover/Buzzer/manager.c | 49 +++++++++-- 3 files changed, 125 insertions(+), 120 deletions(-) diff --git a/KPI_Rover/Buzzer/driver.c b/KPI_Rover/Buzzer/driver.c index 545d921..d89fef2 100644 --- a/KPI_Rover/Buzzer/driver.c +++ b/KPI_Rover/Buzzer/driver.c @@ -1,8 +1,6 @@ -#include "stm32f4xx_hal.h" +#include -#include "FreeRTOS.h" -#include "task.h" -#include "timers.h" +#include "stm32f4xx_hal.h" #include "driver.h" @@ -15,29 +13,6 @@ } \ } while (0) -enum BuzzerState { - BUZZER_ON, - BUZZER_OFF, - BUZZER_NOT_ACTIVE, - IMPOSSIBLE_STATE -}; - -struct BuzzerStateMemory { - TickType_t on_duration; - TickType_t off_duration; - TickType_t total_active_time; - TickType_t active_time_limit; - enum BuzzerState current_state; -}; - -static GPIO_TypeDef *GPIO_buzzer_port; -static uint16_t GPIO_buzzer_pin; -static int buzzer_initialized; - -static TimerHandle_t timer_handle; -static StaticTimer_t timer; -static struct BuzzerStateMemory bsm; - static unsigned int count_bits_16bit(uint16_t value) { unsigned int result = 0; @@ -49,157 +24,129 @@ static unsigned int count_bits_16bit(uint16_t value) return result; } -static void Buzzer_ResetTimer(void) -{ - if (timer_handle == NULL) - return; - - (void) xTimerStop(timer_handle, BUZZER_BLOCKING_TIME_LIMIT); -} - -unsigned int Buzzer_ConfigurePort(const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin) +unsigned int Buzzer_ConfigurePort(struct BuzzerObject * const self, const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin) { - Buzzer_ResetTimer(); + self->bsm.current_state = BUZZER_NOT_TIMED; unsigned int errors = 0; CHECK_ERROR(count_bits_16bit(gpio_pin) != 1, BUZZER_PIN_ERROR); - GPIO_buzzer_port = (GPIO_TypeDef *) gpio_port; - GPIO_buzzer_pin = (uint16_t) gpio_pin; + self->GPIO_buzzer_port = (GPIO_TypeDef *) gpio_port; + self->GPIO_buzzer_pin = (uint16_t) gpio_pin; - buzzer_initialized = 1; + self->buzzer_initialized = 1; return errors; fail: - buzzer_initialized = 0; + self->buzzer_initialized = 0; return errors; } -static unsigned int Buzzer_SetON(void) +static unsigned int Buzzer_SetON(struct BuzzerObject * const self) { unsigned int errors = 0; - CHECK_ERROR(!buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); + CHECK_ERROR(!self->buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); - HAL_GPIO_WritePin(GPIO_buzzer_port, GPIO_buzzer_pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(self->GPIO_buzzer_port, self->GPIO_buzzer_pin, GPIO_PIN_SET); fail: return errors; } -static unsigned int Buzzer_SetOFF(void) +static unsigned int Buzzer_SetOFF(struct BuzzerObject * const self) { unsigned int errors = 0; - CHECK_ERROR(!buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); + CHECK_ERROR(!self->buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); - HAL_GPIO_WritePin(GPIO_buzzer_port, GPIO_buzzer_pin, GPIO_PIN_RESET); + HAL_GPIO_WritePin(self->GPIO_buzzer_port, self->GPIO_buzzer_pin, GPIO_PIN_RESET); fail: return errors; } -unsigned int Buzzer_Enable(void) +unsigned int Buzzer_Enable(struct BuzzerObject * const self) { - Buzzer_ResetTimer(); - return Buzzer_SetON(); + self->bsm.current_state = BUZZER_NOT_TIMED; + return Buzzer_SetON(self); } -unsigned int Buzzer_Disable(void) +unsigned int Buzzer_Disable(struct BuzzerObject * const self) { - Buzzer_ResetTimer(); - return Buzzer_SetOFF(); + self->bsm.current_state = BUZZER_NOT_TIMED; + return Buzzer_SetOFF(self); } -static void Buzzer_TimerCallback(TimerHandle_t t) +void Buzzer_TimerTask(struct BuzzerObject * const self) { - TickType_t new_period; + if (self->bsm.current_state == BUZZER_NOT_TIMED) + return; - switch (bsm.current_state) { - case BUZZER_ON: - Buzzer_SetOFF(); // disable first, determine action course next - bsm.current_state = BUZZER_OFF; - bsm.total_active_time += bsm.on_duration; + self->bsm.total_active_time_left--; + self->bsm.current_state_valid_for--; - new_period = bsm.total_active_time + bsm.off_duration <= bsm.active_time_limit - ? bsm.off_duration - : bsm.active_time_limit - bsm.total_active_time; + if (!self->bsm.total_active_time_left) { + Buzzer_SetOFF(self); + self->bsm.current_state = BUZZER_NOT_TIMED; + return; + } - if (!new_period) - break; + if (self->bsm.current_state_valid_for) + return; - (void) xTimerChangePeriod(timer_handle, new_period, 0); - (void) xTimerStart(timer_handle, 0); + switch (self->bsm.current_state) { + case BUZZER_ON: + Buzzer_SetOFF(self); // disable first, determine action course next + self->bsm.current_state = BUZZER_OFF; + self->bsm.current_state_valid_for = self->bsm.off_duration; break; case BUZZER_OFF: - bsm.current_state = BUZZER_ON; - bsm.total_active_time += bsm.off_duration; - - new_period = bsm.total_active_time + bsm.on_duration <= bsm.active_time_limit - ? bsm.on_duration - : bsm.active_time_limit - bsm.total_active_time; - - if (!new_period) - break; - - (void) xTimerChangePeriod(timer_handle, new_period, 0); - (void) xTimerStart(timer_handle, 0); - Buzzer_SetON(); + self->bsm.current_state = BUZZER_ON; + self->bsm.current_state_valid_for = self->bsm.on_duration; + Buzzer_SetON(self); break; - case BUZZER_NOT_ACTIVE: + case BUZZER_NOT_TIMED: case IMPOSSIBLE_STATE: default: - // why are we here then? + // how did we get here? break; } } -unsigned int Buzzer_Pulse(const unsigned int on_time_ms, const unsigned int period_time_ms, const unsigned int total_active_time_ms) +unsigned int Buzzer_Pulse(struct BuzzerObject * const self, const unsigned int on_time_ms, const unsigned int period_time_ms, const unsigned int total_active_time_ms) { - Buzzer_ResetTimer(); + self->bsm.current_state = BUZZER_NOT_TIMED; unsigned int errors = 0; - CHECK_ERROR(!buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); + CHECK_ERROR(!self->buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); - const TickType_t on_time_ticks = pdMS_TO_TICKS(on_time_ms); + const int on_time_ticks = on_time_ms / 10; CHECK_ERROR(!on_time_ticks, BUZZER_ZERO_ONTIME_ERROR); - const TickType_t off_time_ticks = pdMS_TO_TICKS(period_time_ms) - on_time_ticks; + const int off_time_ticks = (period_time_ms / 10) - on_time_ticks; CHECK_ERROR(!off_time_ticks, BUZZER_ZERO_OFFTIME_ERROR); - const TickType_t active_time_limit = pdMS_TO_TICKS(total_active_time_ms); + const int active_time_limit = total_active_time_ms / 10; CHECK_ERROR(!active_time_limit, BUZZER_ZERO_ACTIVE_TIME_ERROR); - if (timer_handle == NULL) { - timer_handle = xTimerCreateStatic( - "", - on_time_ticks, - pdFALSE, - 0, - Buzzer_TimerCallback, - &timer); - } else { - CHECK_ERROR(xTimerChangePeriod(timer_handle, on_time_ticks, BUZZER_BLOCKING_TIME_LIMIT) == pdFAIL, BUZZER_TIMER_BUSY); - } - - CHECK_ERROR(xTimerStart(timer_handle, BUZZER_BLOCKING_TIME_LIMIT) == pdFAIL, BUZZER_TIMER_BUSY); - Buzzer_SetON(); + Buzzer_SetON(self); - bsm.on_duration = on_time_ticks; - bsm.off_duration = off_time_ticks; - bsm.total_active_time = 0; - bsm.active_time_limit = active_time_limit; - bsm.current_state = BUZZER_ON; + self->bsm.on_duration = on_time_ticks; + self->bsm.off_duration = off_time_ticks; + self->bsm.total_active_time_left = active_time_limit; + self->bsm.current_state_valid_for = on_time_ticks; + self->bsm.current_state = BUZZER_ON; return 0; fail: - Buzzer_ResetTimer(); - Buzzer_SetOFF(); + self->bsm.current_state = BUZZER_NOT_TIMED; + Buzzer_SetOFF(self); return errors; } diff --git a/KPI_Rover/Buzzer/driver.h b/KPI_Rover/Buzzer/driver.h index 4b0d15d..e4b9e73 100644 --- a/KPI_Rover/Buzzer/driver.h +++ b/KPI_Rover/Buzzer/driver.h @@ -12,9 +12,32 @@ #define BUZZER_ZERO_ACTIVE_TIME_ERROR 0x7U #define BUZZER_TIMER_BUSY 0x8U -unsigned int Buzzer_ConfigurePort(const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin); -unsigned int Buzzer_Enable(void); -unsigned int Buzzer_Disable(void); -unsigned int Buzzer_Pulse(const unsigned int on_time_ms, const unsigned int period_time_ms, const unsigned int total_active_time_ms); +enum BuzzerState { + BUZZER_NOT_TIMED, + BUZZER_ON, + BUZZER_OFF, + IMPOSSIBLE_STATE +}; + +struct BuzzerStateMemory { + int on_duration; + int off_duration; + int total_active_time_left; + int current_state_valid_for; + enum BuzzerState current_state; +}; + +struct BuzzerObject { + GPIO_TypeDef *GPIO_buzzer_port; + uint16_t GPIO_buzzer_pin; + int buzzer_initialized; + struct BuzzerStateMemory bsm; +}; + +unsigned int Buzzer_ConfigurePort(struct BuzzerObject * const self, const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin); +unsigned int Buzzer_Enable(struct BuzzerObject * const self); +unsigned int Buzzer_Disable(struct BuzzerObject * const self); +unsigned int Buzzer_Pulse(struct BuzzerObject * const self, const unsigned int on_time_ms, const unsigned int period_time_ms, const unsigned int total_active_time_ms); +void Buzzer_TimerTask(struct BuzzerObject * const self); #endif /* __BUZZER_DRIVER */ diff --git a/KPI_Rover/Buzzer/manager.c b/KPI_Rover/Buzzer/manager.c index 077b9da..3b8c035 100644 --- a/KPI_Rover/Buzzer/manager.c +++ b/KPI_Rover/Buzzer/manager.c @@ -1,31 +1,66 @@ #include "FreeRTOS.h" #include "task.h" +#include "cmsis_os2.h" + #include "ul_ulog.h" #include "driver.h" +#define LEN(x) ( sizeof(x) / sizeof(x[0]) ) + +static osTimerId_t timer_handle; +static StaticTimer_t timer; +static struct BuzzerObject bo[1]; + +static void buzzer_timer_callback(void *d) +{ + for (uint32_t i = 0; i < LEN(bo); i++) + Buzzer_TimerTask(&(bo[i])); +} + void buzzer_manager_task(void *d) { - if (Buzzer_ConfigurePort(GPIOD, GPIO_PIN_15)) + if (Buzzer_ConfigurePort(&(bo[0]), GPIOD, GPIO_PIN_15)) { ULOG_ERROR("Failed to configure buzzer driver port"); vTaskDelete(NULL); } + { + osTimerAttr_t timer_attrs = { + .name = NULL, + .attr_bits = 0, + .cb_mem = &timer, + .cb_size = sizeof(timer) + }; + + timer_handle = osTimerNew(buzzer_timer_callback, osTimerPeriodic, (void *) 0, &timer_attrs); + + if (timer_handle != (&timer)) { + ULOG_ERROR("Failed to create a timer"); + vTaskDelete(NULL); + } + + if (osTimerStart(timer_handle, 10) != osOK) { + ULOG_ERROR("Failed to start a timer"); + vTaskDelete(NULL); + } + } + for ( ; ; ) { - Buzzer_Enable(); + Buzzer_Enable(&(bo[0])); vTaskDelay(pdMS_TO_TICKS(3000)); - Buzzer_Disable(); + Buzzer_Disable(&(bo[0])); vTaskDelay(pdMS_TO_TICKS(3000)); - Buzzer_Pulse(500, 1000, 5000); + Buzzer_Pulse(&(bo[0]), 500, 1000, 5000); vTaskDelay(pdMS_TO_TICKS(5000)); - Buzzer_Pulse(200, 1000, 5000); + Buzzer_Pulse(&(bo[0]), 200, 1000, 5000); vTaskDelay(pdMS_TO_TICKS(5000)); - Buzzer_Pulse(100, 300, 5000); + Buzzer_Pulse(&(bo[0]), 100, 300, 5000); vTaskDelay(pdMS_TO_TICKS(5000)); - Buzzer_Pulse(250, 300, 5000); + Buzzer_Pulse(&(bo[0]), 250, 300, 5000); vTaskDelay(pdMS_TO_TICKS(1000)); // intentionally shorter delay; next command must abort this one } } From 244f5c6601210b444a40af683c2effa5c28279b4 Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Sun, 14 Sep 2025 21:27:05 +0300 Subject: [PATCH 10/18] Issue #15: switch to CMSISv2 functions in manager to improve OS independance --- KPI_Rover/Buzzer/manager.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/KPI_Rover/Buzzer/manager.c b/KPI_Rover/Buzzer/manager.c index 3b8c035..c36a715 100644 --- a/KPI_Rover/Buzzer/manager.c +++ b/KPI_Rover/Buzzer/manager.c @@ -1,5 +1,5 @@ #include "FreeRTOS.h" -#include "task.h" +#include "timers.h" #include "cmsis_os2.h" @@ -24,7 +24,7 @@ void buzzer_manager_task(void *d) if (Buzzer_ConfigurePort(&(bo[0]), GPIOD, GPIO_PIN_15)) { ULOG_ERROR("Failed to configure buzzer driver port"); - vTaskDelete(NULL); + osThreadExit(); } { @@ -39,28 +39,28 @@ void buzzer_manager_task(void *d) if (timer_handle != (&timer)) { ULOG_ERROR("Failed to create a timer"); - vTaskDelete(NULL); + osThreadExit(); } if (osTimerStart(timer_handle, 10) != osOK) { ULOG_ERROR("Failed to start a timer"); - vTaskDelete(NULL); + osThreadExit(); } } for ( ; ; ) { Buzzer_Enable(&(bo[0])); - vTaskDelay(pdMS_TO_TICKS(3000)); + osDelay(3000); Buzzer_Disable(&(bo[0])); - vTaskDelay(pdMS_TO_TICKS(3000)); + osDelay(3000); Buzzer_Pulse(&(bo[0]), 500, 1000, 5000); - vTaskDelay(pdMS_TO_TICKS(5000)); + osDelay(5000); Buzzer_Pulse(&(bo[0]), 200, 1000, 5000); - vTaskDelay(pdMS_TO_TICKS(5000)); + osDelay(5000); Buzzer_Pulse(&(bo[0]), 100, 300, 5000); - vTaskDelay(pdMS_TO_TICKS(5000)); + osDelay(5000); Buzzer_Pulse(&(bo[0]), 250, 300, 5000); - vTaskDelay(pdMS_TO_TICKS(1000)); // intentionally shorter delay; next command must abort this one + osDelay(1000); // intentionally shorter delay; any next command must abort any previous one } } From d84815584a7a5cde3d2a57fbdcef72fe0c0b0870 Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Sun, 14 Sep 2025 21:42:07 +0300 Subject: [PATCH 11/18] Issue #15: Change all types to platform-independant alternatives --- KPI_Rover/Buzzer/driver.c | 30 +++++++++++++++--------------- KPI_Rover/Buzzer/driver.h | 18 +++++++++--------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/KPI_Rover/Buzzer/driver.c b/KPI_Rover/Buzzer/driver.c index d89fef2..7e81176 100644 --- a/KPI_Rover/Buzzer/driver.c +++ b/KPI_Rover/Buzzer/driver.c @@ -13,9 +13,9 @@ } \ } while (0) -static unsigned int count_bits_16bit(uint16_t value) +static uint8_t count_bits_16bit(uint16_t value) { - unsigned int result = 0; + uint8_t result = 0; for ( ; value; value >>= 1) if (value & 1) @@ -24,11 +24,11 @@ static unsigned int count_bits_16bit(uint16_t value) return result; } -unsigned int Buzzer_ConfigurePort(struct BuzzerObject * const self, const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin) +uint32_t Buzzer_ConfigurePort(struct BuzzerObject * const self, const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin) { self->bsm.current_state = BUZZER_NOT_TIMED; - unsigned int errors = 0; + uint32_t errors = 0; CHECK_ERROR(count_bits_16bit(gpio_pin) != 1, BUZZER_PIN_ERROR); @@ -44,9 +44,9 @@ unsigned int Buzzer_ConfigurePort(struct BuzzerObject * const self, const GPIO_T return errors; } -static unsigned int Buzzer_SetON(struct BuzzerObject * const self) +static uint32_t Buzzer_SetON(struct BuzzerObject * const self) { - unsigned int errors = 0; + uint32_t errors = 0; CHECK_ERROR(!self->buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); @@ -56,9 +56,9 @@ static unsigned int Buzzer_SetON(struct BuzzerObject * const self) return errors; } -static unsigned int Buzzer_SetOFF(struct BuzzerObject * const self) +static uint32_t Buzzer_SetOFF(struct BuzzerObject * const self) { - unsigned int errors = 0; + uint32_t errors = 0; CHECK_ERROR(!self->buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); @@ -68,13 +68,13 @@ static unsigned int Buzzer_SetOFF(struct BuzzerObject * const self) return errors; } -unsigned int Buzzer_Enable(struct BuzzerObject * const self) +uint32_t Buzzer_Enable(struct BuzzerObject * const self) { self->bsm.current_state = BUZZER_NOT_TIMED; return Buzzer_SetON(self); } -unsigned int Buzzer_Disable(struct BuzzerObject * const self) +uint32_t Buzzer_Disable(struct BuzzerObject * const self) { self->bsm.current_state = BUZZER_NOT_TIMED; return Buzzer_SetOFF(self); @@ -118,21 +118,21 @@ void Buzzer_TimerTask(struct BuzzerObject * const self) } } -unsigned int Buzzer_Pulse(struct BuzzerObject * const self, const unsigned int on_time_ms, const unsigned int period_time_ms, const unsigned int total_active_time_ms) +uint32_t Buzzer_Pulse(struct BuzzerObject * const self, const uint32_t on_time_ms, const uint32_t period_time_ms, const uint32_t total_active_time_ms) { self->bsm.current_state = BUZZER_NOT_TIMED; - unsigned int errors = 0; + uint32_t errors = 0; CHECK_ERROR(!self->buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); - const int on_time_ticks = on_time_ms / 10; + const uint32_t on_time_ticks = on_time_ms / 10; CHECK_ERROR(!on_time_ticks, BUZZER_ZERO_ONTIME_ERROR); - const int off_time_ticks = (period_time_ms / 10) - on_time_ticks; + const uint32_t off_time_ticks = (period_time_ms / 10) - on_time_ticks; CHECK_ERROR(!off_time_ticks, BUZZER_ZERO_OFFTIME_ERROR); - const int active_time_limit = total_active_time_ms / 10; + const uint32_t active_time_limit = total_active_time_ms / 10; CHECK_ERROR(!active_time_limit, BUZZER_ZERO_ACTIVE_TIME_ERROR); Buzzer_SetON(self); diff --git a/KPI_Rover/Buzzer/driver.h b/KPI_Rover/Buzzer/driver.h index e4b9e73..73325ab 100644 --- a/KPI_Rover/Buzzer/driver.h +++ b/KPI_Rover/Buzzer/driver.h @@ -20,24 +20,24 @@ enum BuzzerState { }; struct BuzzerStateMemory { - int on_duration; - int off_duration; - int total_active_time_left; - int current_state_valid_for; + uint32_t on_duration; + uint32_t off_duration; + uint32_t total_active_time_left; + uint32_t current_state_valid_for; enum BuzzerState current_state; }; struct BuzzerObject { GPIO_TypeDef *GPIO_buzzer_port; uint16_t GPIO_buzzer_pin; - int buzzer_initialized; + int8_t buzzer_initialized; struct BuzzerStateMemory bsm; }; -unsigned int Buzzer_ConfigurePort(struct BuzzerObject * const self, const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin); -unsigned int Buzzer_Enable(struct BuzzerObject * const self); -unsigned int Buzzer_Disable(struct BuzzerObject * const self); -unsigned int Buzzer_Pulse(struct BuzzerObject * const self, const unsigned int on_time_ms, const unsigned int period_time_ms, const unsigned int total_active_time_ms); +uint32_t Buzzer_ConfigurePort(struct BuzzerObject * const self, const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin); +uint32_t Buzzer_Enable(struct BuzzerObject * const self); +uint32_t Buzzer_Disable(struct BuzzerObject * const self); +uint32_t Buzzer_Pulse(struct BuzzerObject * const self, const uint32_t on_time_ms, const uint32_t period_time_ms, const uint32_t total_active_time_ms); void Buzzer_TimerTask(struct BuzzerObject * const self); #endif /* __BUZZER_DRIVER */ From 508440304773ed9a3586ae65ab0ad5d7ff046388 Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Sun, 28 Sep 2025 13:07:52 +0300 Subject: [PATCH 12/18] Issue #15: increase maximum stack size to prevent accidental task crashes --- Core/Src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/Src/main.c b/Core/Src/main.c index 07dd08f..5b46c23 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -160,7 +160,7 @@ int main(void) static const osThreadAttr_t buzzer_task_attrs = { .name = "buzzer", - .stack_size = 16 * 4, + .stack_size = 64 * 4, .priority = (osPriority_t) osPriorityHigh, }; (void) osThreadNew(buzzer_manager_task, NULL, &buzzer_task_attrs); From 981b69f557b325d27e46bf41ccc376b76bab17a3 Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Sun, 28 Sep 2025 18:19:45 +0300 Subject: [PATCH 13/18] Issue #15: refactor code into a state machine --- KPI_Rover/Buzzer/driver.c | 262 ++++++++++++++++++++++++++++--------- KPI_Rover/Buzzer/driver.h | 71 ++++++++-- KPI_Rover/Buzzer/generic.c | 14 ++ KPI_Rover/Buzzer/generic.h | 9 ++ 4 files changed, 277 insertions(+), 79 deletions(-) create mode 100644 KPI_Rover/Buzzer/generic.c create mode 100644 KPI_Rover/Buzzer/generic.h diff --git a/KPI_Rover/Buzzer/driver.c b/KPI_Rover/Buzzer/driver.c index 7e81176..71a5be5 100644 --- a/KPI_Rover/Buzzer/driver.c +++ b/KPI_Rover/Buzzer/driver.c @@ -2,6 +2,7 @@ #include "stm32f4xx_hal.h" +#include "generic.h" #include "driver.h" #define BUZZER_BLOCKING_TIME_LIMIT 1000 @@ -13,140 +14,271 @@ } \ } while (0) + static uint8_t count_bits_16bit(uint16_t value) { uint8_t result = 0; for ( ; value; value >>= 1) - if (value & 1) - result++; + result += value & 1; return result; } -uint32_t Buzzer_ConfigurePort(struct BuzzerObject * const self, const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin) +static uint32_t Buzzer_SetON(struct BuzzerObject * const self) { - self->bsm.current_state = BUZZER_NOT_TIMED; - uint32_t errors = 0; - CHECK_ERROR(count_bits_16bit(gpio_pin) != 1, BUZZER_PIN_ERROR); - - self->GPIO_buzzer_port = (GPIO_TypeDef *) gpio_port; - self->GPIO_buzzer_pin = (uint16_t) gpio_pin; + CHECK_ERROR(!self->c.initialized, BUZZER_NOT_INITIALIZED_ERROR); - self->buzzer_initialized = 1; - - return errors; + HAL_GPIO_WritePin(self->c.GPIO_port, self->c.GPIO_pin, GPIO_PIN_SET); fail: - self->buzzer_initialized = 0; return errors; } -static uint32_t Buzzer_SetON(struct BuzzerObject * const self) +static uint32_t Buzzer_SetOFF(struct BuzzerObject * const self) { uint32_t errors = 0; - CHECK_ERROR(!self->buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); + CHECK_ERROR(!self->c.initialized, BUZZER_NOT_INITIALIZED_ERROR); - HAL_GPIO_WritePin(self->GPIO_buzzer_port, self->GPIO_buzzer_pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(self->c.GPIO_port, self->c.GPIO_pin, GPIO_PIN_RESET); fail: return errors; } -static uint32_t Buzzer_SetOFF(struct BuzzerObject * const self) + +static void Buzzer_OFF_Enter(struct BuzzerObject * const self, const struct BuzzerEvent * const ev) { - uint32_t errors = 0; + Buzzer_SetOFF(self); +} - CHECK_ERROR(!self->buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); +static void Buzzer_OFF_Handle(struct BuzzerObject * const self) +{ - HAL_GPIO_WritePin(self->GPIO_buzzer_port, self->GPIO_buzzer_pin, GPIO_PIN_RESET); +} -fail: - return errors; +static void Buzzer_OFF_Leave(struct BuzzerObject * const self) +{ + Buzzer_SetOFF(self); } -uint32_t Buzzer_Enable(struct BuzzerObject * const self) + +static void Buzzer_ON_Enter(struct BuzzerObject * const self, const struct BuzzerEvent * const ev) { - self->bsm.current_state = BUZZER_NOT_TIMED; - return Buzzer_SetON(self); + Buzzer_SetON(self); } -uint32_t Buzzer_Disable(struct BuzzerObject * const self) +static void Buzzer_ON_Handle(struct BuzzerObject * const self) { - self->bsm.current_state = BUZZER_NOT_TIMED; - return Buzzer_SetOFF(self); + } -void Buzzer_TimerTask(struct BuzzerObject * const self) +static void Buzzer_ON_Leave(struct BuzzerObject * const self) { - if (self->bsm.current_state == BUZZER_NOT_TIMED) - return; + Buzzer_SetON(self); +} - self->bsm.total_active_time_left--; - self->bsm.current_state_valid_for--; - if (!self->bsm.total_active_time_left) { +static void Buzzer_PULSE_Enter(struct BuzzerObject * const self, const struct BuzzerEvent * const ev) +{ + self->ps.config.pulse_on_for = ev->pulse_on_for; + self->ps.config.pulse_off_for = ev->pulse_off_for; + self->ps.config.pulse_total_for = ev->pulse_total_for; + + self->ps.current_state = BUZZER_TO_ON; +} + +static void Buzzer_PULSE_Handle(struct BuzzerObject * const self) +{ + uint32_t tss; + + switch (self->ps.current_state) { + case BUZZER_TO_ON: + Buzzer_SetON(self); + self->ps.current_state_since = HAL_GetTick(); + + tss = ticks_elapsed_since(self->s.current_state_since); + + if ((tss + self->ps.config.pulse_on_for) >= self->ps.config.pulse_total_for) { + self->ps.current_state = BUZZER_FINAL_DELAY; + self->ps.state_lifetime_override = self->ps.config.pulse_total_for - tss; + } else { + self->ps.current_state = BUZZER_DELAY_BEFORE_OFF; + } + + break; + case BUZZER_DELAY_BEFORE_OFF: + if (ticks_elapsed_since(self->ps.current_state_since) >= self->ps.config.pulse_on_for) + self->ps.current_state = BUZZER_TO_OFF; + + break; + case BUZZER_TO_OFF: Buzzer_SetOFF(self); - self->bsm.current_state = BUZZER_NOT_TIMED; - return; + self->ps.current_state_since = HAL_GetTick(); + + tss = ticks_elapsed_since(self->s.current_state_since); + + if ((tss + self->ps.config.pulse_off_for) >= self->ps.config.pulse_total_for) { + self->ps.current_state = BUZZER_FINAL_DELAY; + self->ps.state_lifetime_override = self->ps.config.pulse_total_for - tss; + } else { + self->ps.current_state = BUZZER_DELAY_BEFORE_ON; + } + + break; + case BUZZER_DELAY_BEFORE_ON: + if (ticks_elapsed_since(self->ps.current_state_since) >= self->ps.config.pulse_off_for) + self->ps.current_state = BUZZER_TO_ON; + + break; + case BUZZER_FINAL_DELAY: + if (ticks_elapsed_since(self->ps.current_state_since) < self->ps.state_lifetime_override) + break; + + self->e.ev = TO_OFF; + break; } +} + +static void Buzzer_PULSE_Leave(struct BuzzerObject * const self) +{ + Buzzer_SetOFF(self); +} - if (self->bsm.current_state_valid_for) - return; - switch (self->bsm.current_state) { +static void Buzzer_Reset(struct BuzzerObject * const self) +{ + switch (self->s.current_state) { + case BUZZER_OFF: + Buzzer_OFF_Leave(self); + break; case BUZZER_ON: - Buzzer_SetOFF(self); // disable first, determine action course next - self->bsm.current_state = BUZZER_OFF; - self->bsm.current_state_valid_for = self->bsm.off_duration; + Buzzer_ON_Leave(self); break; + case BUZZER_PULSE: + Buzzer_PULSE_Leave(self); + break; + case IMPOSSIBLE_STATE: + default: + break; + } + + self->s.current_state = BUZZER_OFF; +} + +static void Buzzer_ProcessEvent(struct BuzzerObject * const self) +{ + if (self->e.ev == NO_EVENT || self->e.ev == IMPOSSIBLE_EVENT) + return; + switch (self->s.current_state) { + case BUZZER_ON: + Buzzer_ON_Leave(self); + break; case BUZZER_OFF: - self->bsm.current_state = BUZZER_ON; - self->bsm.current_state_valid_for = self->bsm.on_duration; - Buzzer_SetON(self); + Buzzer_OFF_Leave(self); + break; + case BUZZER_PULSE: + Buzzer_PULSE_Leave(self); + break; + default: break; + } - case BUZZER_NOT_TIMED: - case IMPOSSIBLE_STATE: + switch (self->e.ev) { + case TO_ON: + self->s.current_state = BUZZER_ON; + Buzzer_ON_Enter(self, &(self->e)); + break; + case TO_OFF: + self->s.current_state = BUZZER_OFF; + Buzzer_OFF_Enter(self, &(self->e)); + break; + case TO_PULSE: + self->s.current_state = BUZZER_PULSE; + Buzzer_PULSE_Enter(self, &(self->e)); + break; default: - // how did we get here? + self->s.current_state = IMPOSSIBLE_STATE; break; } + + self->s.current_state_since = HAL_GetTick(); + self->e.ev = NO_EVENT; } -uint32_t Buzzer_Pulse(struct BuzzerObject * const self, const uint32_t on_time_ms, const uint32_t period_time_ms, const uint32_t total_active_time_ms) + +uint32_t Buzzer_ConfigurePort(struct BuzzerObject * const self, const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin) { - self->bsm.current_state = BUZZER_NOT_TIMED; + Buzzer_Reset(self); uint32_t errors = 0; - CHECK_ERROR(!self->buzzer_initialized, BUZZER_NOT_INITIALIZED_ERROR); + CHECK_ERROR(count_bits_16bit(gpio_pin) != 1, BUZZER_PIN_ERROR); - const uint32_t on_time_ticks = on_time_ms / 10; - CHECK_ERROR(!on_time_ticks, BUZZER_ZERO_ONTIME_ERROR); + self->c.GPIO_port = (GPIO_TypeDef *) gpio_port; + self->c.GPIO_pin = (uint16_t) gpio_pin; - const uint32_t off_time_ticks = (period_time_ms / 10) - on_time_ticks; - CHECK_ERROR(!off_time_ticks, BUZZER_ZERO_OFFTIME_ERROR); + self->c.initialized = 1; - const uint32_t active_time_limit = total_active_time_ms / 10; - CHECK_ERROR(!active_time_limit, BUZZER_ZERO_ACTIVE_TIME_ERROR); + return errors; - Buzzer_SetON(self); +fail: + self->c.initialized = 0; + return errors; +} - self->bsm.on_duration = on_time_ticks; - self->bsm.off_duration = off_time_ticks; - self->bsm.total_active_time_left = active_time_limit; - self->bsm.current_state_valid_for = on_time_ticks; - self->bsm.current_state = BUZZER_ON; +uint32_t Buzzer_Enable(struct BuzzerObject * const self) +{ + self->e.ev = TO_ON; + return 0; +} + +uint32_t Buzzer_Disable(struct BuzzerObject * const self) +{ + self->e.ev = TO_OFF; return 0; +} + +uint32_t Buzzer_Pulse(struct BuzzerObject * const self, const uint32_t on_time_ms, const uint32_t period_time_ms, const uint32_t total_active_time_ms) +{ + uint32_t errors = 0; + + const uint32_t off_time_ms = period_time_ms - on_time_ms; + + CHECK_ERROR(!on_time_ms, BUZZER_ZERO_ONTIME_ERROR); + CHECK_ERROR(!off_time_ms, BUZZER_ZERO_OFFTIME_ERROR); + CHECK_ERROR(!total_active_time_ms, BUZZER_ZERO_ACTIVE_TIME_ERROR); + + self->e.pulse_on_for = on_time_ms; + self->e.pulse_off_for = off_time_ms; + self->e.pulse_total_for = total_active_time_ms; + + self->e.ev = TO_PULSE; fail: - self->bsm.current_state = BUZZER_NOT_TIMED; - Buzzer_SetOFF(self); return errors; } + +void Buzzer_TimerTask(struct BuzzerObject * const self) +{ + (void) Buzzer_ProcessEvent(self); + + switch (self->s.current_state) { + case BUZZER_OFF: + Buzzer_OFF_Handle(self); + break; + case BUZZER_ON: + Buzzer_ON_Handle(self); + break; + case BUZZER_PULSE: + Buzzer_PULSE_Handle(self); + break; + default: + break; + } +} diff --git a/KPI_Rover/Buzzer/driver.h b/KPI_Rover/Buzzer/driver.h index 73325ab..6e8a7a3 100644 --- a/KPI_Rover/Buzzer/driver.h +++ b/KPI_Rover/Buzzer/driver.h @@ -12,26 +12,69 @@ #define BUZZER_ZERO_ACTIVE_TIME_ERROR 0x7U #define BUZZER_TIMER_BUSY 0x8U -enum BuzzerState { - BUZZER_NOT_TIMED, - BUZZER_ON, + +enum BuzzerEventType { + NO_EVENT, + IMPOSSIBLE_EVENT, + TO_ON, + TO_OFF, + TO_PULSE +}; + +struct BuzzerEvent { + enum BuzzerEventType ev; + uint32_t pulse_on_for; + uint32_t pulse_off_for; + uint32_t pulse_total_for; +}; + + +enum BuzzerGlobalStateType { + IMPOSSIBLE_STATE, BUZZER_OFF, - IMPOSSIBLE_STATE + BUZZER_ON, + BUZZER_PULSE +}; + +struct BuzzerGlobalState { + enum BuzzerGlobalStateType current_state; + uint32_t current_state_since; }; -struct BuzzerStateMemory { - uint32_t on_duration; - uint32_t off_duration; - uint32_t total_active_time_left; - uint32_t current_state_valid_for; - enum BuzzerState current_state; + +enum BuzzerPulseSubStateType { + BUZZER_TO_ON, + BUZZER_DELAY_BEFORE_OFF, + BUZZER_TO_OFF, + BUZZER_DELAY_BEFORE_ON, + BUZZER_FINAL_DELAY +}; + +struct BuzzerPulseConfig { + uint32_t pulse_on_for; + uint32_t pulse_off_for; + uint32_t pulse_total_for; +}; + +struct BuzzerPulseSubState { + enum BuzzerPulseSubStateType current_state; + struct BuzzerPulseConfig config; + uint32_t current_state_since; + uint32_t state_lifetime_override; +}; + + +struct BuzzerConfig { + GPIO_TypeDef *GPIO_port; + uint16_t GPIO_pin; + int8_t initialized; }; struct BuzzerObject { - GPIO_TypeDef *GPIO_buzzer_port; - uint16_t GPIO_buzzer_pin; - int8_t buzzer_initialized; - struct BuzzerStateMemory bsm; + struct BuzzerConfig c; + struct BuzzerGlobalState s; + struct BuzzerPulseSubState ps; + struct BuzzerEvent e; }; uint32_t Buzzer_ConfigurePort(struct BuzzerObject * const self, const GPIO_TypeDef * const gpio_port, const uint16_t gpio_pin); diff --git a/KPI_Rover/Buzzer/generic.c b/KPI_Rover/Buzzer/generic.c new file mode 100644 index 0000000..156d1e4 --- /dev/null +++ b/KPI_Rover/Buzzer/generic.c @@ -0,0 +1,14 @@ +#include "stm32f4xx_hal.h" + +#include "generic.h" + +uint32_t ticks_elapsed(const uint32_t old_ticks, const uint32_t new_ticks) +{ + return (uint32_t) (((uint32_t) new_ticks) - ((uint32_t) old_ticks)); +} + +uint32_t ticks_elapsed_since(const uint32_t old_ticks) +{ + int32_t current_ticks = HAL_GetTick(); + return (uint32_t) (((uint32_t) current_ticks) - ((uint32_t) old_ticks)); +} diff --git a/KPI_Rover/Buzzer/generic.h b/KPI_Rover/Buzzer/generic.h new file mode 100644 index 0000000..060a3f7 --- /dev/null +++ b/KPI_Rover/Buzzer/generic.h @@ -0,0 +1,9 @@ +#ifndef __GENERIC_H +#define __GENERIC_H + + +uint32_t ticks_elapsed(const uint32_t old_ticks, const uint32_t new_ticks); +uint32_t ticks_elapsed_since(const uint32_t old_ticks); + + +#endif /* __GENERIC_H */ From eebc972c033dbec00b1cc31549eb0df68238cca7 Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Fri, 22 Aug 2025 14:19:00 +0300 Subject: [PATCH 14/18] Issue #32: Add mock timers.c implementation --- KPI_Rover/Buzzer/mock-suite/inc/FreeRTOS.h | 18 +++ KPI_Rover/Buzzer/mock-suite/inc/fail.h | 55 ++++++++ KPI_Rover/Buzzer/mock-suite/inc/timers.h | 45 +++++++ KPI_Rover/Buzzer/mock-suite/src/timers.c | 148 +++++++++++++++++++++ 4 files changed, 266 insertions(+) create mode 100644 KPI_Rover/Buzzer/mock-suite/inc/FreeRTOS.h create mode 100644 KPI_Rover/Buzzer/mock-suite/inc/fail.h create mode 100644 KPI_Rover/Buzzer/mock-suite/inc/timers.h create mode 100644 KPI_Rover/Buzzer/mock-suite/src/timers.c diff --git a/KPI_Rover/Buzzer/mock-suite/inc/FreeRTOS.h b/KPI_Rover/Buzzer/mock-suite/inc/FreeRTOS.h new file mode 100644 index 0000000..eb8eede --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/inc/FreeRTOS.h @@ -0,0 +1,18 @@ +#ifndef __FREERTOS_H +#define __FREERTOS_H + + +#include + +typedef uint32_t TickType_t; + +#define pdFALSE 0 +#define pdTRUE 1 + +#define pdFAIL (pdFALSE) +#define pdPASS (pdTRUE) + +#define pdMS_TO_TICKS(ms) (TickType_t) ms + + +#endif /* __FREERTOS_H */ diff --git a/KPI_Rover/Buzzer/mock-suite/inc/fail.h b/KPI_Rover/Buzzer/mock-suite/inc/fail.h new file mode 100644 index 0000000..327c932 --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/inc/fail.h @@ -0,0 +1,55 @@ +#ifndef __FAIL_H +#define __FAIL_H + + +#define FAIL_ON(expression, return_on_fail, prefix, counter) \ + do { \ + if (!(expression)) { \ + printf("[%s %s:%d] %s: generic check \"%s\" fails\n", \ + (prefix), __FILE__, __LINE__, __func__, #expression); \ + (counter)++; \ + return (return_on_fail); \ + } \ + } while (0) + +#define FAIL_ON_EQ(exp1, exp2, return_on_fail, prefix, counter) \ + do { \ + if ((exp1) == (exp2)) { \ + printf("[%s %s:%d] %s: check %s (0x%08x) != %s (0x%08x) fails\n", \ + (prefix), __FILE__, __LINE__, __func__, #exp1, (exp1), #exp2, (exp2)); \ + (counter)++; \ + return (return_on_fail); \ + } \ + } while (0) + +#define FAIL_ON_LE(exp1, exp2, return_on_fail, prefix, counter) \ + do { \ + if ((exp1) <= (exp2)) { \ + printf("[%s %s:%d] %s: check %s (0x%08x) > %s (0x%08x) fails\n", \ + (prefix), __FILE__, __LINE__, __func__, #exp1, (exp1), #exp2, (exp2)); \ + (counter)++; \ + return (return_on_fail); \ + } \ + } while (0) + +#define WARN_ON_EQ(exp1, exp2, prefix, counter) \ + do { \ + if ((exp1) == (exp2)) { \ + printf("[%s %s:%d] %s: check %s (0x%08x) != %s (0x%08x) fails, continuing\n", \ + (prefix), __FILE__, __LINE__, __func__, #exp1, (exp1), #exp2, (exp2)); \ + (counter)++; \ + } \ + } while (0) + +#define REPORT_COUNTER(counter) \ + do { \ + issues += (counter); \ + if ((counter) == 1) \ + printf("[ISSUE] %s has been triggered %d time\n", \ + #counter, (counter)); \ + else if ((counter) >= 1) \ + printf("[ISSUE] %s has been triggered %d times\n", #counter, (counter)); \ + } while (0) + + +#endif /* __FAIL_H */ diff --git a/KPI_Rover/Buzzer/mock-suite/inc/timers.h b/KPI_Rover/Buzzer/mock-suite/inc/timers.h new file mode 100644 index 0000000..2ad992a --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/inc/timers.h @@ -0,0 +1,45 @@ +#include "FreeRTOS.h" + +typedef uint32_t UBaseType_t; +typedef int32_t BaseType_t; + +typedef struct StaticTimer_t * TimerHandle_t; + +typedef void (*TimerCallbackFunction_t)(TimerHandle_t xTimer); + +struct StaticTimer_t { + uint8_t status; + TickType_t timer_ticks_left; + + UBaseType_t uxAutoReload; + TickType_t xTimerPeriodInTicks; + TimerCallbackFunction_t pxCallbackFunction; +}; + +typedef struct StaticTimer_t StaticTimer_t; + + +TimerHandle_t xTimerCreateStatic( + const char * const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + StaticTimer_t *pxTimerBuffer +); + +BaseType_t xTimerStop( + TimerHandle_t xTimer, + TickType_t xTicksToWait +); + +BaseType_t xTimerChangePeriod( + TimerHandle_t xTimer, + TickType_t xNewPeriod, + TickType_t xTicksToWait +); + +BaseType_t xTimerStart( + TimerHandle_t xTimer, + TickType_t xTicksToWait +); diff --git a/KPI_Rover/Buzzer/mock-suite/src/timers.c b/KPI_Rover/Buzzer/mock-suite/src/timers.c new file mode 100644 index 0000000..03dcf49 --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/src/timers.c @@ -0,0 +1,148 @@ +#include "stdio.h" + +#include "timers.h" + +#include "fail.h" + +#define TIMER_CREATED ( (uint8_t) 0x1 ) +#define TIMER_ACTIVE ( (uint8_t) 0x2 ) + +/* Settings */ +#define TIMER_QUEUE_SIZE 16 + + +static uint32_t null_timer_ptr_count = 0; +static uint32_t static_timer_recreation_count = 0; +static uint32_t non_positive_timer_period_count = 0; +static uint32_t null_callback_function_specified_count = 0; +static uint32_t stopped_non_active_timer_count = 0; +static uint32_t operated_on_active_timer_count = 0; +static uint32_t timer_queue_overflow_count = 0; + +static TimerHandle_t timer_queue[TIMER_QUEUE_SIZE]; + +static int register_timer(TimerHandle_t t) +{ + TimerHandle_t *vacant_timer = NULL; + + for (int i = 0; i < TIMER_QUEUE_SIZE; i++) { + if (timer_queue[i] == t) + return 0; + + if (timer_queue[i]) + continue; + + if (!vacant_timer) + vacant_timer = &(timer_queue[i]); + } + + if (vacant_timer) { + *vacant_timer = t; + return 0; + } else { + return -1; + } +} + +static int unregister_timer(TimerHandle_t t) +{ + for (int i = 0; i < TIMER_QUEUE_SIZE; i++) { + if (timer_queue[i] != t) + continue; + + timer_queue[i] = NULL; + return 0; + } + + return -1; +} + +uint32_t timers_check_health(void) +{ + uint32_t issues = 0; + + REPORT_COUNTER(null_timer_ptr_count); + REPORT_COUNTER(static_timer_recreation_count); + REPORT_COUNTER(non_positive_timer_period_count); + REPORT_COUNTER(null_callback_function_specified_count); + REPORT_COUNTER(stopped_non_active_timer_count); + REPORT_COUNTER(operated_on_active_timer_count); + REPORT_COUNTER(timer_queue_overflow_count); + + if (static_timer_recreation_count) { + printf("[ERROR] Static timer recreated %d time(s)\n", static_timer_recreation_count); + issues += static_timer_recreation_count; + } + + if (non_positive_timer_period_count) { + printf("[ERROR] Non-positive timer period given %d time(s)\n", non_positive_timer_period_count); + issues += non_positive_timer_period_count; + } + + return issues; +} + +TimerHandle_t xTimerCreateStatic( + const char * const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + StaticTimer_t *pxTimerBuffer) +{ + FAIL_ON_EQ(pxTimerBuffer, NULL, NULL, "WARN", null_timer_ptr_count); + + FAIL_ON_EQ(pxTimerBuffer->status & TIMER_CREATED, TIMER_CREATED, pxTimerBuffer, "CRIT", static_timer_recreation_count); + pxTimerBuffer->status = TIMER_CREATED; + + FAIL_ON_LE(xTimerPeriodInTicks, 0, NULL, "CRIT", non_positive_timer_period_count); + pxTimerBuffer->xTimerPeriodInTicks = xTimerPeriodInTicks; + + pxTimerBuffer->uxAutoReload = uxAutoReload; + + FAIL_ON_EQ(pxCallbackFunction, NULL, NULL, "WARN", null_callback_function_specified_count); + pxTimerBuffer->pxCallbackFunction = pxCallbackFunction; + + return pxTimerBuffer; +} + +BaseType_t xTimerStop( + TimerHandle_t xTimer, + TickType_t xTicksToWait) +{ + FAIL_ON_EQ(xTimer, NULL, pdFAIL, "WARN", null_timer_ptr_count); + FAIL_ON_EQ(xTimer->status & TIMER_ACTIVE, 0, pdPASS, "NOTE", stopped_non_active_timer_count); + xTimer->status &= ~TIMER_ACTIVE; + + (void) unregister_timer(xTimer); + + return pdPASS; +} + +BaseType_t xTimerChangePeriod( + TimerHandle_t xTimer, + TickType_t xNewPeriod, + TickType_t xTicksToWait) +{ + FAIL_ON_EQ(xTimer, NULL, pdFAIL, "WARN", null_timer_ptr_count); + WARN_ON_EQ(xTimer->status & TIMER_ACTIVE, TIMER_ACTIVE, "WARN", operated_on_active_timer_count); + + FAIL_ON_LE(xNewPeriod, 0, pdFAIL, "ERROR", non_positive_timer_period_count); + + xTimer->xTimerPeriodInTicks = xNewPeriod; + + return pdPASS; +} + +BaseType_t xTimerStart( + TimerHandle_t xTimer, + TickType_t xTicksToWait) +{ + FAIL_ON_EQ(xTimer, NULL, pdFAIL, "WARN", null_timer_ptr_count); + xTimer->status |= TIMER_ACTIVE; + xTimer->timer_ticks_left = xTimer->xTimerPeriodInTicks; + + FAIL_ON_EQ(register_timer(xTimer), -1, pdFAIL, "WARN", timer_queue_overflow_count); + + return pdPASS; +} From 205fddc43d6a3cbd307bb149296410df81626787 Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Fri, 22 Aug 2025 20:53:41 +0300 Subject: [PATCH 15/18] Issue #32: Add basic working unit-tests --- KPI_Rover/Buzzer/mock-suite/CMakeLists.txt | 32 +++++++++++++ KPI_Rover/Buzzer/mock-suite/inc/FreeRTOS.h | 2 +- KPI_Rover/Buzzer/mock-suite/inc/fail.h | 6 +-- KPI_Rover/Buzzer/mock-suite/inc/main.h | 9 ++++ KPI_Rover/Buzzer/mock-suite/inc/scheduler.h | 9 ++++ .../Buzzer/mock-suite/inc/stm32f4xx_hal.h | 9 ++++ .../mock-suite/inc/stm32f4xx_hal_gpio.h | 41 ++++++++++++++++ .../mock-suite/inc/stm32f4xx_hal_gpio_ex.h | 10 ++++ KPI_Rover/Buzzer/mock-suite/inc/test.h | 18 +++++++ KPI_Rover/Buzzer/mock-suite/src/scheduler.c | 17 +++++++ .../mock-suite/src/stm32f4xx_hal_gpio.c | 27 +++++++++++ KPI_Rover/Buzzer/mock-suite/src/timers.c | 21 ++++++++ .../Buzzer/mock-suite/test/buzzer-driver.c | 48 +++++++++++++++++++ 13 files changed, 245 insertions(+), 4 deletions(-) create mode 100644 KPI_Rover/Buzzer/mock-suite/CMakeLists.txt create mode 100644 KPI_Rover/Buzzer/mock-suite/inc/main.h create mode 100644 KPI_Rover/Buzzer/mock-suite/inc/scheduler.h create mode 100644 KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal.h create mode 100644 KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_gpio.h create mode 100644 KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_gpio_ex.h create mode 100644 KPI_Rover/Buzzer/mock-suite/inc/test.h create mode 100644 KPI_Rover/Buzzer/mock-suite/src/scheduler.c create mode 100644 KPI_Rover/Buzzer/mock-suite/src/stm32f4xx_hal_gpio.c create mode 100644 KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.c diff --git a/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt b/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt new file mode 100644 index 0000000..560f275 --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt @@ -0,0 +1,32 @@ +set(CFLAGS -O2 -Wall -Wpedantic) + +cmake_minimum_required(VERSION 3.25.1) + +project(buzzer_testing C) + +add_library(scheduler STATIC src/scheduler.c) +add_library(timers STATIC src/timers.c) +add_library(stm32f4xx_hal_gpio STATIC src/stm32f4xx_hal_gpio.c) +add_library(driver STATIC ../driver.c) + +add_executable(buzzer-driver test/buzzer-driver.c) + +target_include_directories(scheduler PRIVATE inc) +target_include_directories(timers PRIVATE inc) +target_include_directories(stm32f4xx_hal_gpio PRIVATE inc) +target_include_directories(driver PRIVATE .. inc) +target_include_directories(buzzer-driver PRIVATE .. inc) + +target_link_libraries(scheduler timers) +target_link_libraries(driver timers stm32f4xx_hal_gpio) +target_link_libraries(buzzer-driver scheduler timers stm32f4xx_hal_gpio driver) + +target_compile_options(scheduler PRIVATE ${CFLAGS}) +target_compile_options(timers PRIVATE ${CFLAGS}) +target_compile_options(stm32f4xx_hal_gpio PRIVATE ${CFLAGS}) +target_compile_options(driver PRIVATE ${CFLAGS}) +target_compile_options(buzzer-driver PRIVATE ${CFLAGS}) + +# include actual driver dir to compile + +# then try to compile whole project diff --git a/KPI_Rover/Buzzer/mock-suite/inc/FreeRTOS.h b/KPI_Rover/Buzzer/mock-suite/inc/FreeRTOS.h index eb8eede..b115d94 100644 --- a/KPI_Rover/Buzzer/mock-suite/inc/FreeRTOS.h +++ b/KPI_Rover/Buzzer/mock-suite/inc/FreeRTOS.h @@ -4,7 +4,7 @@ #include -typedef uint32_t TickType_t; +typedef int32_t TickType_t; #define pdFALSE 0 #define pdTRUE 1 diff --git a/KPI_Rover/Buzzer/mock-suite/inc/fail.h b/KPI_Rover/Buzzer/mock-suite/inc/fail.h index 327c932..d521ad5 100644 --- a/KPI_Rover/Buzzer/mock-suite/inc/fail.h +++ b/KPI_Rover/Buzzer/mock-suite/inc/fail.h @@ -16,7 +16,7 @@ do { \ if ((exp1) == (exp2)) { \ printf("[%s %s:%d] %s: check %s (0x%08x) != %s (0x%08x) fails\n", \ - (prefix), __FILE__, __LINE__, __func__, #exp1, (exp1), #exp2, (exp2)); \ + (prefix), __FILE__, __LINE__, __func__, #exp1, (unsigned int) (exp1), #exp2, (unsigned int) (exp2)); \ (counter)++; \ return (return_on_fail); \ } \ @@ -26,7 +26,7 @@ do { \ if ((exp1) <= (exp2)) { \ printf("[%s %s:%d] %s: check %s (0x%08x) > %s (0x%08x) fails\n", \ - (prefix), __FILE__, __LINE__, __func__, #exp1, (exp1), #exp2, (exp2)); \ + (prefix), __FILE__, __LINE__, __func__, #exp1, (unsigned int) (exp1), #exp2, (unsigned int) (exp2)); \ (counter)++; \ return (return_on_fail); \ } \ @@ -36,7 +36,7 @@ do { \ if ((exp1) == (exp2)) { \ printf("[%s %s:%d] %s: check %s (0x%08x) != %s (0x%08x) fails, continuing\n", \ - (prefix), __FILE__, __LINE__, __func__, #exp1, (exp1), #exp2, (exp2)); \ + (prefix), __FILE__, __LINE__, __func__, #exp1, (unsigned int) (exp1), #exp2, (unsigned int) (exp2)); \ (counter)++; \ } \ } while (0) diff --git a/KPI_Rover/Buzzer/mock-suite/inc/main.h b/KPI_Rover/Buzzer/mock-suite/inc/main.h new file mode 100644 index 0000000..94c9416 --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/inc/main.h @@ -0,0 +1,9 @@ +#ifndef __MAIN_H +#define __MAIN_H + + +#include "stm32f4xx_hal_gpio.h" +#include "stm32f4xx_hal_gpio_ex.h" + + +#endif /* __MAIN_H */ diff --git a/KPI_Rover/Buzzer/mock-suite/inc/scheduler.h b/KPI_Rover/Buzzer/mock-suite/inc/scheduler.h new file mode 100644 index 0000000..71ac41b --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/inc/scheduler.h @@ -0,0 +1,9 @@ +#ifndef __SCHEDULER_H +#define __SCHEDULER_H + +#include "stdint.h" + +void scheduler_run_for(uint32_t ticks); + + +#endif /* __SCHEDULER_H */ diff --git a/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal.h b/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal.h new file mode 100644 index 0000000..d7eff43 --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal.h @@ -0,0 +1,9 @@ +#ifndef __STM32F4XX_HAL_H +#define __STM32F4XX_HAL_H + + +#include "stm32f4xx_hal_gpio.h" +#include "stm32f4xx_hal_gpio_ex.h" + + +#endif /* __STM32F4XX_HAL_H */ diff --git a/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_gpio.h b/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_gpio.h new file mode 100644 index 0000000..61b3d10 --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_gpio.h @@ -0,0 +1,41 @@ +#ifndef __STM32F4XX_HAL_GPIO_H +#define __STM32F4XX_HAL_GPIO_H + + +#include + +#define GPIO_PIN_0 ((uint16_t)0x0001) +#define GPIO_PIN_1 ((uint16_t)0x0002) +#define GPIO_PIN_2 ((uint16_t)0x0004) +#define GPIO_PIN_3 ((uint16_t)0x0008) +#define GPIO_PIN_4 ((uint16_t)0x0010) +#define GPIO_PIN_5 ((uint16_t)0x0020) +#define GPIO_PIN_6 ((uint16_t)0x0040) +#define GPIO_PIN_7 ((uint16_t)0x0080) +#define GPIO_PIN_8 ((uint16_t)0x0100) +#define GPIO_PIN_9 ((uint16_t)0x0200) +#define GPIO_PIN_10 ((uint16_t)0x0400) +#define GPIO_PIN_11 ((uint16_t)0x0800) +#define GPIO_PIN_12 ((uint16_t)0x1000) +#define GPIO_PIN_13 ((uint16_t)0x2000) +#define GPIO_PIN_14 ((uint16_t)0x4000) +#define GPIO_PIN_15 ((uint16_t)0x8000) +#define GPIO_PIN_All ((uint16_t)0xFFFF) + +typedef enum { + GPIO_PIN_RESET = 0, + GPIO_PIN_SET +} GPIO_PinState; + +typedef struct { + uint32_t ODR; +} GPIO_TypeDef; + +void HAL_GPIO_WritePin( + GPIO_TypeDef* GPIOx, + uint16_t GPIO_Pin, + GPIO_PinState PinState +); + + +#endif /* __STM32F4XX_HAL_GPIO_H */ diff --git a/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_gpio_ex.h b/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_gpio_ex.h new file mode 100644 index 0000000..d6b9abd --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_gpio_ex.h @@ -0,0 +1,10 @@ +#ifndef __STM32F4XX_HAL_GPIO_EX_H +#define __STM32F4XX_HAL_GPIO_EX_H + + +#include "stm32f4xx_hal_gpio.h" + +extern GPIO_TypeDef *GPIOD; + + +#endif /* __STM32F4XX_HAL_GPIO_EX_H */ diff --git a/KPI_Rover/Buzzer/mock-suite/inc/test.h b/KPI_Rover/Buzzer/mock-suite/inc/test.h new file mode 100644 index 0000000..dd3256b --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/inc/test.h @@ -0,0 +1,18 @@ +#ifndef __TEST_H +#define __TEST_H + + +#include + +#define TEST(test_func) \ + do { \ + int return_code = test_func(); \ + if (return_code != 0) { \ + printf("TEST FAILED: %s, return code %08x\n", #test_func, (unsigned int) return_code); \ + } else { \ + printf("TEST PASSED: %s\n", #test_func); \ + } \ + } while (0) + + +#endif /* __TEST_H */ diff --git a/KPI_Rover/Buzzer/mock-suite/src/scheduler.c b/KPI_Rover/Buzzer/mock-suite/src/scheduler.c new file mode 100644 index 0000000..d9ec0fd --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/src/scheduler.c @@ -0,0 +1,17 @@ +#define SCHEDULER_EMULATE_TIMERS 1 + +#include "scheduler.h" + +#if SCHEDULER_EMULATE_TIMERS == 1 + #include "timers.h" +#endif /* SCHEDULER_EMULATE_TIMERS == 1 */ + +void scheduler_run_for(uint32_t ticks) +{ + for (int i = 0; i < ticks; i++) + { +#if SCHEDULER_EMULATE_TIMERS == 1 + timers_run(); +#endif /* SCHEDULER_EMULATE_TIMERS == 1 */ + } +} diff --git a/KPI_Rover/Buzzer/mock-suite/src/stm32f4xx_hal_gpio.c b/KPI_Rover/Buzzer/mock-suite/src/stm32f4xx_hal_gpio.c new file mode 100644 index 0000000..fcb2e9d --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/src/stm32f4xx_hal_gpio.c @@ -0,0 +1,27 @@ +#include + +#include "stm32f4xx_hal_gpio.h" + +GPIO_TypeDef *GPIOD; + +void gpio_init(void) +{ + GPIOD = malloc(sizeof(GPIO_TypeDef)); + GPIOD->ODR = 0; +} + +uint32_t gpio_check_health(void) +{ + return 0; +} + +void HAL_GPIO_WritePin( + GPIO_TypeDef* GPIOx, + uint16_t GPIO_Pin, + GPIO_PinState PinState) +{ + if (PinState == GPIO_PIN_RESET) + GPIOx->ODR &= ~GPIO_Pin; + else + GPIOx->ODR |= GPIO_Pin; +} diff --git a/KPI_Rover/Buzzer/mock-suite/src/timers.c b/KPI_Rover/Buzzer/mock-suite/src/timers.c index 03dcf49..bccca62 100644 --- a/KPI_Rover/Buzzer/mock-suite/src/timers.c +++ b/KPI_Rover/Buzzer/mock-suite/src/timers.c @@ -7,6 +7,7 @@ #define TIMER_CREATED ( (uint8_t) 0x1 ) #define TIMER_ACTIVE ( (uint8_t) 0x2 ) + /* Settings */ #define TIMER_QUEUE_SIZE 16 @@ -57,6 +58,8 @@ static int unregister_timer(TimerHandle_t t) return -1; } +void timers_init(void) {} + uint32_t timers_check_health(void) { uint32_t issues = 0; @@ -82,6 +85,24 @@ uint32_t timers_check_health(void) return issues; } +void timers_run(void) +{ + TimerHandle_t chosen_timer = NULL; + + for (int i = 0; i < TIMER_QUEUE_SIZE; i++) { + if (timer_queue[i] == NULL) + continue; + + timer_queue[i]->timer_ticks_left--; + + if (timer_queue[i]->timer_ticks_left <= 0 && chosen_timer == NULL) + chosen_timer = timer_queue[i]; + } + + if (chosen_timer) + chosen_timer->pxCallbackFunction(chosen_timer); +} + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, diff --git a/KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.c b/KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.c new file mode 100644 index 0000000..12e0d8a --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.c @@ -0,0 +1,48 @@ +#include + +#include "main.h" +#include "FreeRTOS.h" +#include "timers.h" + +#include "test.h" + +#include "driver.h" + +#define LEN(a) sizeof(a) / sizeof(a[0]) + +int driver_configure_correct(void) +{ + for (int i = 0x8000; i > 0; i >>= 1) + if (Buzzer_ConfigurePort(GPIOD, i)) + return i; + + return 0; +} + +int driver_configure_wrong(void) +{ + uint16_t broken_values[] = { + 0xF000, + 0xFFFF, + 0x0000, + 0xA5A5 + }; + + for (int i = 0; i < LEN(broken_values); i++) + if (!Buzzer_ConfigurePort(GPIOD, broken_values[i])) + return i+1; + + return 0; +} + +int dummy_broken_test(void) +{ + return Buzzer_ConfigurePort(GPIOD, 0xF000); +} + +int main(void) +{ + TEST(driver_configure_correct); + TEST(driver_configure_wrong); + TEST(dummy_broken_test); +} From bacbd0fa402aa9822152f2d0283d75558a34aea1 Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Sat, 23 Aug 2025 15:18:32 +0300 Subject: [PATCH 16/18] Issue #32: Add more unit-tests, improve timer emulation --- KPI_Rover/Buzzer/mock-suite/CMakeLists.txt | 10 +- KPI_Rover/Buzzer/mock-suite/config.h | 13 ++ KPI_Rover/Buzzer/mock-suite/inc/colors.h | 17 ++ .../mock-suite/inc/stm32f4xx_hal_gpio.h | 2 + KPI_Rover/Buzzer/mock-suite/inc/test.h | 20 ++- KPI_Rover/Buzzer/mock-suite/inc/timers.h | 11 ++ .../mock-suite/src/stm32f4xx_hal_gpio.c | 9 +- KPI_Rover/Buzzer/mock-suite/src/timers.c | 47 +++--- .../Buzzer/mock-suite/test/buzzer-driver.c | 152 +++++++++++++++++- 9 files changed, 244 insertions(+), 37 deletions(-) create mode 100644 KPI_Rover/Buzzer/mock-suite/config.h create mode 100644 KPI_Rover/Buzzer/mock-suite/inc/colors.h diff --git a/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt b/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt index 560f275..d725174 100644 --- a/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt +++ b/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt @@ -1,4 +1,4 @@ -set(CFLAGS -O2 -Wall -Wpedantic) +set(CFLAGS -Og -g3 -Wall -Wpedantic -Wno-pointer-to-int-cast) cmake_minimum_required(VERSION 3.25.1) @@ -11,11 +11,11 @@ add_library(driver STATIC ../driver.c) add_executable(buzzer-driver test/buzzer-driver.c) -target_include_directories(scheduler PRIVATE inc) -target_include_directories(timers PRIVATE inc) +target_include_directories(scheduler PRIVATE . inc) +target_include_directories(timers PRIVATE . inc) target_include_directories(stm32f4xx_hal_gpio PRIVATE inc) -target_include_directories(driver PRIVATE .. inc) -target_include_directories(buzzer-driver PRIVATE .. inc) +target_include_directories(driver PRIVATE .. . inc) +target_include_directories(buzzer-driver PRIVATE .. . inc) target_link_libraries(scheduler timers) target_link_libraries(driver timers stm32f4xx_hal_gpio) diff --git a/KPI_Rover/Buzzer/mock-suite/config.h b/KPI_Rover/Buzzer/mock-suite/config.h new file mode 100644 index 0000000..d97d68c --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/config.h @@ -0,0 +1,13 @@ +#ifndef __CONFIG_H +#define __CONFIG_H + + +/* --- Color settings --- */ +#define COLOR_ENABLE 1 + + +/* --- Timer settings --- */ +#define TIMER_QUEUE_SIZE 16 + + +#endif /* __CONFIG_H */ diff --git a/KPI_Rover/Buzzer/mock-suite/inc/colors.h b/KPI_Rover/Buzzer/mock-suite/inc/colors.h new file mode 100644 index 0000000..62efa50 --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/inc/colors.h @@ -0,0 +1,17 @@ +#ifndef __COLORS_H +#define __COLORS_H + +#include "config.h" + +#if COLOR_ENABLE == 1 + #define COLOR_RESET "\x1b[0m" + #define COLOR_RED "\x1b[0;31m" + #define COLOR_GREEN "\x1b[0;32m" +#else + #define COLOR_RESET "" + #define COLOR_RED "" + #define COLOR_GREEN "" +#endif /* COLOR_ENABLE == 1 */ + + +#endif /* __COLORS_H */ diff --git a/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_gpio.h b/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_gpio.h index 61b3d10..547b287 100644 --- a/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_gpio.h +++ b/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_gpio.h @@ -31,6 +31,8 @@ typedef struct { uint32_t ODR; } GPIO_TypeDef; +void gpio_init(void); + void HAL_GPIO_WritePin( GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, diff --git a/KPI_Rover/Buzzer/mock-suite/inc/test.h b/KPI_Rover/Buzzer/mock-suite/inc/test.h index dd3256b..196e8df 100644 --- a/KPI_Rover/Buzzer/mock-suite/inc/test.h +++ b/KPI_Rover/Buzzer/mock-suite/inc/test.h @@ -4,14 +4,30 @@ #include +#include "colors.h" + +#define INIT(...) int failed_tests = 0, passed_tests = 0; + #define TEST(test_func) \ do { \ int return_code = test_func(); \ if (return_code != 0) { \ - printf("TEST FAILED: %s, return code %08x\n", #test_func, (unsigned int) return_code); \ + printf(COLOR_RED "TEST FAILED" COLOR_RESET ": %s, return code %08x\n", #test_func, (unsigned int) return_code); \ + failed_tests++; \ + } else { \ + printf(COLOR_GREEN "TEST PASSED" COLOR_RESET ": %s\n", #test_func); \ + passed_tests++; \ + } \ + } while (0) + +#define SUMMARY(...) \ + do { \ + if (failed_tests) { \ + printf("--- " COLOR_GREEN "%d" COLOR_RESET " passed / " COLOR_RED "%d" COLOR_RESET " failed ---\n", passed_tests, failed_tests); \ } else { \ - printf("TEST PASSED: %s\n", #test_func); \ + printf("--- " COLOR_GREEN "%d" COLOR_RESET " passed ---\n", passed_tests); \ } \ + return !!failed_tests; \ } while (0) diff --git a/KPI_Rover/Buzzer/mock-suite/inc/timers.h b/KPI_Rover/Buzzer/mock-suite/inc/timers.h index 2ad992a..bbb18e2 100644 --- a/KPI_Rover/Buzzer/mock-suite/inc/timers.h +++ b/KPI_Rover/Buzzer/mock-suite/inc/timers.h @@ -1,5 +1,11 @@ #include "FreeRTOS.h" +#include "config.h" + +#define TIMER_CREATED ( (uint8_t) 0x1 ) +#define TIMER_ACTIVE ( (uint8_t) 0x2 ) + + typedef uint32_t UBaseType_t; typedef int32_t BaseType_t; @@ -19,6 +25,11 @@ struct StaticTimer_t { typedef struct StaticTimer_t StaticTimer_t; +void timers_init(void); +void timers_run(void); +uint32_t timers_check_health(void); +TimerHandle_t *get_timer_queue(void); + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, diff --git a/KPI_Rover/Buzzer/mock-suite/src/stm32f4xx_hal_gpio.c b/KPI_Rover/Buzzer/mock-suite/src/stm32f4xx_hal_gpio.c index fcb2e9d..4bd51ee 100644 --- a/KPI_Rover/Buzzer/mock-suite/src/stm32f4xx_hal_gpio.c +++ b/KPI_Rover/Buzzer/mock-suite/src/stm32f4xx_hal_gpio.c @@ -1,13 +1,16 @@ -#include +#include #include "stm32f4xx_hal_gpio.h" +static GPIO_TypeDef GPIOs[11]; // GPIOA - GPIOK GPIO_TypeDef *GPIOD; void gpio_init(void) { - GPIOD = malloc(sizeof(GPIO_TypeDef)); - GPIOD->ODR = 0; + // Notice: GPIO A & B registers have specific non-zero values at startup + memset(GPIOs, 0, sizeof(GPIOs)); + + GPIOD = &(GPIOs[3]); } uint32_t gpio_check_health(void) diff --git a/KPI_Rover/Buzzer/mock-suite/src/timers.c b/KPI_Rover/Buzzer/mock-suite/src/timers.c index bccca62..12722a8 100644 --- a/KPI_Rover/Buzzer/mock-suite/src/timers.c +++ b/KPI_Rover/Buzzer/mock-suite/src/timers.c @@ -1,22 +1,15 @@ -#include "stdio.h" +#include +#include #include "timers.h" #include "fail.h" -#define TIMER_CREATED ( (uint8_t) 0x1 ) -#define TIMER_ACTIVE ( (uint8_t) 0x2 ) - - -/* Settings */ -#define TIMER_QUEUE_SIZE 16 - static uint32_t null_timer_ptr_count = 0; static uint32_t static_timer_recreation_count = 0; static uint32_t non_positive_timer_period_count = 0; static uint32_t null_callback_function_specified_count = 0; -static uint32_t stopped_non_active_timer_count = 0; static uint32_t operated_on_active_timer_count = 0; static uint32_t timer_queue_overflow_count = 0; @@ -52,13 +45,16 @@ static int unregister_timer(TimerHandle_t t) continue; timer_queue[i] = NULL; - return 0; + return i; } return -1; } -void timers_init(void) {} +void timers_init(void) +{ + memset(timer_queue, 0, sizeof(timer_queue)); +} uint32_t timers_check_health(void) { @@ -68,20 +64,9 @@ uint32_t timers_check_health(void) REPORT_COUNTER(static_timer_recreation_count); REPORT_COUNTER(non_positive_timer_period_count); REPORT_COUNTER(null_callback_function_specified_count); - REPORT_COUNTER(stopped_non_active_timer_count); REPORT_COUNTER(operated_on_active_timer_count); REPORT_COUNTER(timer_queue_overflow_count); - if (static_timer_recreation_count) { - printf("[ERROR] Static timer recreated %d time(s)\n", static_timer_recreation_count); - issues += static_timer_recreation_count; - } - - if (non_positive_timer_period_count) { - printf("[ERROR] Non-positive timer period given %d time(s)\n", non_positive_timer_period_count); - issues += non_positive_timer_period_count; - } - return issues; } @@ -93,16 +78,29 @@ void timers_run(void) if (timer_queue[i] == NULL) continue; - timer_queue[i]->timer_ticks_left--; + if (timer_queue[i]->status & TIMER_ACTIVE) + timer_queue[i]->timer_ticks_left--; + + if (timer_queue[i]->timer_ticks_left > 0) + continue; + + timer_queue[i]->status &= ~TIMER_ACTIVE; - if (timer_queue[i]->timer_ticks_left <= 0 && chosen_timer == NULL) + if (chosen_timer == NULL) { chosen_timer = timer_queue[i]; + timer_queue[i] = NULL; + } } if (chosen_timer) chosen_timer->pxCallbackFunction(chosen_timer); } +TimerHandle_t *get_timer_queue(void) +{ + return timer_queue; +} + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, @@ -132,7 +130,6 @@ BaseType_t xTimerStop( TickType_t xTicksToWait) { FAIL_ON_EQ(xTimer, NULL, pdFAIL, "WARN", null_timer_ptr_count); - FAIL_ON_EQ(xTimer->status & TIMER_ACTIVE, 0, pdPASS, "NOTE", stopped_non_active_timer_count); xTimer->status &= ~TIMER_ACTIVE; (void) unregister_timer(xTimer); diff --git a/KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.c b/KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.c index 12e0d8a..bbdfeaf 100644 --- a/KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.c +++ b/KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.c @@ -4,6 +4,7 @@ #include "FreeRTOS.h" #include "timers.h" +#include "scheduler.h" #include "test.h" #include "driver.h" @@ -25,16 +26,149 @@ int driver_configure_wrong(void) 0xF000, 0xFFFF, 0x0000, - 0xA5A5 + 0xA5A5, + 0xDEAD, + 0xBEEF }; for (int i = 0; i < LEN(broken_values); i++) - if (!Buzzer_ConfigurePort(GPIOD, broken_values[i])) + if (BUZZER_PIN_ERROR != Buzzer_ConfigurePort(GPIOD, broken_values[i])) return i+1; return 0; } +int driver_enable_correct(void) +{ + gpio_init(); + + if (Buzzer_ConfigurePort(GPIOD, 0x8000)) + return 1; + + if (Buzzer_Enable()) + return 2; + + if (!(GPIOD->ODR & 0x8000)) + return 3; + + return 0; +} + +int driver_enable_wrong(void) +{ + gpio_init(); + + if (BUZZER_PIN_ERROR != Buzzer_ConfigurePort(GPIOD, 0xF000)) + return 1; + + if (BUZZER_NOT_INITIALIZED_ERROR != Buzzer_Enable()) + return 2; + + if (GPIOD->ODR & 0xF000) + return 3; + + return 0; +} + +int driver_disable_correct(void) +{ + gpio_init(); + GPIOD->ODR = 0x1; + + if (Buzzer_ConfigurePort(GPIOD, 0x0001)) + return 1; + + if (Buzzer_Disable()) + return 2; + + if (GPIOD->ODR & 0x0001) + return 3; + + return 0; +} + +int driver_disable_wrong(void) +{ + gpio_init(); + GPIOD->ODR = 0x1; + + if (BUZZER_PIN_ERROR != Buzzer_ConfigurePort(GPIOD, 0x0101)) + return 1; + + if (BUZZER_NOT_INITIALIZED_ERROR != Buzzer_Disable()) + return 2; + + if (!(GPIOD->ODR & 0x0001)) + return 3; + + return 0; +} + +int driver_pulse_correct(void) +{ + gpio_init(); + timers_init(); + + TimerHandle_t *timer_queue = get_timer_queue(); + + if (Buzzer_ConfigurePort(GPIOD, 0x0001)) + return 1; + + if (Buzzer_Pulse(200, 500, 2000)) + return 2; + + if (!(GPIOD->ODR & 0x0001)) + return 3; + + scheduler_run_for(200); + + if (GPIOD->ODR & 0x0001) + return 4; + + scheduler_run_for(300); + + if (!(GPIOD->ODR & 0x0001)) + return 5; + + scheduler_run_for(200); + + if (GPIOD->ODR & 0x0001) + return 6; + + scheduler_run_for(1300); + + for (int i = 0; i < TIMER_QUEUE_SIZE; i++) + if (timer_queue[i] != NULL && (timer_queue[i]->status | TIMER_ACTIVE)) + return 7; + + return 0; +} + +int driver_preemptive_pulse_stop(void) +{ + gpio_init(); + timers_init(); + + TimerHandle_t *timer_queue = get_timer_queue(); + + if (Buzzer_ConfigurePort(GPIOD, 0x0001)) + return 1; + + if (Buzzer_Pulse(100, 200, 1000)) + return 2; + + scheduler_run_for(530); + + if (Buzzer_Disable()) + return 3; + + for (int i = 0; i < TIMER_QUEUE_SIZE; i++) + if (timer_queue[i]) + return 4; + + return 0; +} + int dummy_broken_test(void) { return Buzzer_ConfigurePort(GPIOD, 0xF000); @@ -42,7 +176,21 @@ int dummy_broken_test(void) int main(void) { + INIT(); + TEST(driver_configure_correct); TEST(driver_configure_wrong); + + TEST(driver_enable_correct); + TEST(driver_enable_wrong); + + TEST(driver_disable_correct); + TEST(driver_disable_wrong); + + TEST(driver_pulse_correct); + TEST(driver_preemptive_pulse_stop); + TEST(dummy_broken_test); + + SUMMARY(); } From 2461120cfeafb32bfd39a2ba8885c199239d2801 Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Mon, 29 Sep 2025 17:44:01 +0300 Subject: [PATCH 17/18] Issue #32: Add support for CMSISv2 timer calls, adjust unit-tests to updated driver --- KPI_Rover/Buzzer/mock-suite/CMakeLists.txt | 12 +- KPI_Rover/Buzzer/mock-suite/inc/cmsis_os2.h | 31 +++++ KPI_Rover/Buzzer/mock-suite/inc/main.h | 1 + .../Buzzer/mock-suite/inc/stm32f4xx_hal.h | 1 + .../mock-suite/inc/stm32f4xx_hal_systick.h | 11 ++ KPI_Rover/Buzzer/mock-suite/inc/timers.h | 1 + KPI_Rover/Buzzer/mock-suite/src/cmsis_os2.c | 12 ++ KPI_Rover/Buzzer/mock-suite/src/scheduler.c | 7 + .../mock-suite/src/stm32f4xx_hal_systick.c | 8 ++ KPI_Rover/Buzzer/mock-suite/src/timers.c | 6 +- .../Buzzer/mock-suite/test/buzzer-driver.c | 120 +++++++++++------- 11 files changed, 162 insertions(+), 48 deletions(-) create mode 100644 KPI_Rover/Buzzer/mock-suite/inc/cmsis_os2.h create mode 100644 KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_systick.h create mode 100644 KPI_Rover/Buzzer/mock-suite/src/cmsis_os2.c create mode 100644 KPI_Rover/Buzzer/mock-suite/src/stm32f4xx_hal_systick.c diff --git a/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt b/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt index d725174..f43cecf 100644 --- a/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt +++ b/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt @@ -7,24 +7,32 @@ project(buzzer_testing C) add_library(scheduler STATIC src/scheduler.c) add_library(timers STATIC src/timers.c) add_library(stm32f4xx_hal_gpio STATIC src/stm32f4xx_hal_gpio.c) +add_library(stm32f4xx_hal_systick STATIC src/stm32f4xx_hal_systick.c) +add_library(cmsis_os2 STATIC src/cmsis_os2.c) add_library(driver STATIC ../driver.c) +add_library(generic STATIC ../generic.c) add_executable(buzzer-driver test/buzzer-driver.c) target_include_directories(scheduler PRIVATE . inc) target_include_directories(timers PRIVATE . inc) target_include_directories(stm32f4xx_hal_gpio PRIVATE inc) +target_include_directories(stm32f4xx_hal_systick PRIVATE inc) target_include_directories(driver PRIVATE .. . inc) +target_include_directories(cmsis_os2 PRIVATE . inc) +target_include_directories(generic PRIVATE . inc) target_include_directories(buzzer-driver PRIVATE .. . inc) target_link_libraries(scheduler timers) -target_link_libraries(driver timers stm32f4xx_hal_gpio) -target_link_libraries(buzzer-driver scheduler timers stm32f4xx_hal_gpio driver) +target_link_libraries(driver timers stm32f4xx_hal_gpio stm32f4xx_hal_systick) +target_link_libraries(cmsis_os2 timers) +target_link_libraries(buzzer-driver scheduler timers stm32f4xx_hal_gpio driver cmsis_os2 generic) target_compile_options(scheduler PRIVATE ${CFLAGS}) target_compile_options(timers PRIVATE ${CFLAGS}) target_compile_options(stm32f4xx_hal_gpio PRIVATE ${CFLAGS}) target_compile_options(driver PRIVATE ${CFLAGS}) +target_compile_options(cmsis_os2 PRIVATE ${CFLAGS}) target_compile_options(buzzer-driver PRIVATE ${CFLAGS}) # include actual driver dir to compile diff --git a/KPI_Rover/Buzzer/mock-suite/inc/cmsis_os2.h b/KPI_Rover/Buzzer/mock-suite/inc/cmsis_os2.h new file mode 100644 index 0000000..377cb6f --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/inc/cmsis_os2.h @@ -0,0 +1,31 @@ +#ifndef __CMSIS_OS2_H +#define __CMSIS_OS2_H + +#include +#include "timers.h" + +typedef StaticTimer_t * osTimerId_t; + +typedef enum osStatus_t { + osOK, + osError, + osErrorTimeout, + osErrorResource, + osErrorParameter, + osErrorNoMemory, + osErrorISR, + osStatusReserved = 0x7FFFFFFF +} osStatus_t; + +typedef struct { + char *name; + uint32_t attr_bits; + TimerHandle_t cb_mem; + size_t cb_size; +} osTimerAttr_t; + +osTimerId_t osTimerNew(TimerCallbackFunction_t cb, uint32_t repeat, void * arg, osTimerAttr_t * attrs); +osStatus_t osTimerStart(osTimerId_t id, uint32_t interval); + + +#endif /* __CMSIS_OS2_H */ diff --git a/KPI_Rover/Buzzer/mock-suite/inc/main.h b/KPI_Rover/Buzzer/mock-suite/inc/main.h index 94c9416..be59c11 100644 --- a/KPI_Rover/Buzzer/mock-suite/inc/main.h +++ b/KPI_Rover/Buzzer/mock-suite/inc/main.h @@ -4,6 +4,7 @@ #include "stm32f4xx_hal_gpio.h" #include "stm32f4xx_hal_gpio_ex.h" +#include "stm32f4xx_hal_systick.h" #endif /* __MAIN_H */ diff --git a/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal.h b/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal.h index d7eff43..31705a3 100644 --- a/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal.h +++ b/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal.h @@ -4,6 +4,7 @@ #include "stm32f4xx_hal_gpio.h" #include "stm32f4xx_hal_gpio_ex.h" +#include "stm32f4xx_hal_systick.h" #endif /* __STM32F4XX_HAL_H */ diff --git a/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_systick.h b/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_systick.h new file mode 100644 index 0000000..b50b812 --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/inc/stm32f4xx_hal_systick.h @@ -0,0 +1,11 @@ +#ifndef __STM32F4XX_HAL_SYSTICK_H +#define __STM32F4XX_HAL_SYSTICK_H + +#include + +extern uint32_t system_ticks; + +uint32_t HAL_GetTick(void); + + +#endif /* __STM32F4XX_HAL_SYSTICK_H */ diff --git a/KPI_Rover/Buzzer/mock-suite/inc/timers.h b/KPI_Rover/Buzzer/mock-suite/inc/timers.h index bbb18e2..fc0f2ac 100644 --- a/KPI_Rover/Buzzer/mock-suite/inc/timers.h +++ b/KPI_Rover/Buzzer/mock-suite/inc/timers.h @@ -17,6 +17,7 @@ struct StaticTimer_t { uint8_t status; TickType_t timer_ticks_left; + void * pvTimerID; UBaseType_t uxAutoReload; TickType_t xTimerPeriodInTicks; TimerCallbackFunction_t pxCallbackFunction; diff --git a/KPI_Rover/Buzzer/mock-suite/src/cmsis_os2.c b/KPI_Rover/Buzzer/mock-suite/src/cmsis_os2.c new file mode 100644 index 0000000..e87c626 --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/src/cmsis_os2.c @@ -0,0 +1,12 @@ +#include "cmsis_os2.h" + +osTimerId_t osTimerNew(TimerCallbackFunction_t cb, uint32_t repeat, void * arg, osTimerAttr_t * attrs) +{ + return xTimerCreateStatic(NULL, 0, repeat, arg, cb, attrs->cb_mem); +} + +osStatus_t osTimerStart(osTimerId_t id, uint32_t interval) +{ + id->xTimerPeriodInTicks = interval; + return xTimerStart(id, 0); +} diff --git a/KPI_Rover/Buzzer/mock-suite/src/scheduler.c b/KPI_Rover/Buzzer/mock-suite/src/scheduler.c index d9ec0fd..f0f7a58 100644 --- a/KPI_Rover/Buzzer/mock-suite/src/scheduler.c +++ b/KPI_Rover/Buzzer/mock-suite/src/scheduler.c @@ -1,5 +1,10 @@ #define SCHEDULER_EMULATE_TIMERS 1 +#include +#include + +uint32_t system_ticks = 0; + #include "scheduler.h" #if SCHEDULER_EMULATE_TIMERS == 1 @@ -10,6 +15,8 @@ void scheduler_run_for(uint32_t ticks) { for (int i = 0; i < ticks; i++) { + system_ticks++; + #if SCHEDULER_EMULATE_TIMERS == 1 timers_run(); #endif /* SCHEDULER_EMULATE_TIMERS == 1 */ diff --git a/KPI_Rover/Buzzer/mock-suite/src/stm32f4xx_hal_systick.c b/KPI_Rover/Buzzer/mock-suite/src/stm32f4xx_hal_systick.c new file mode 100644 index 0000000..eaadfa0 --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/src/stm32f4xx_hal_systick.c @@ -0,0 +1,8 @@ +#include "stm32f4xx_hal_systick.h" + +extern uint32_t system_ticks; + +uint32_t HAL_GetTick(void) +{ + return system_ticks; +} diff --git a/KPI_Rover/Buzzer/mock-suite/src/timers.c b/KPI_Rover/Buzzer/mock-suite/src/timers.c index 12722a8..3c887be 100644 --- a/KPI_Rover/Buzzer/mock-suite/src/timers.c +++ b/KPI_Rover/Buzzer/mock-suite/src/timers.c @@ -89,6 +89,9 @@ void timers_run(void) if (chosen_timer == NULL) { chosen_timer = timer_queue[i]; timer_queue[i] = NULL; + + if (chosen_timer->uxAutoReload) + xTimerStart(chosen_timer, chosen_timer->xTimerPeriodInTicks); } } @@ -114,7 +117,6 @@ TimerHandle_t xTimerCreateStatic( FAIL_ON_EQ(pxTimerBuffer->status & TIMER_CREATED, TIMER_CREATED, pxTimerBuffer, "CRIT", static_timer_recreation_count); pxTimerBuffer->status = TIMER_CREATED; - FAIL_ON_LE(xTimerPeriodInTicks, 0, NULL, "CRIT", non_positive_timer_period_count); pxTimerBuffer->xTimerPeriodInTicks = xTimerPeriodInTicks; pxTimerBuffer->uxAutoReload = uxAutoReload; @@ -122,6 +124,8 @@ TimerHandle_t xTimerCreateStatic( FAIL_ON_EQ(pxCallbackFunction, NULL, NULL, "WARN", null_callback_function_specified_count); pxTimerBuffer->pxCallbackFunction = pxCallbackFunction; + pxTimerBuffer->pvTimerID = pvTimerID; + return pxTimerBuffer; } diff --git a/KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.c b/KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.c index bbdfeaf..c92ad41 100644 --- a/KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.c +++ b/KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.c @@ -1,20 +1,43 @@ #include +#include #include "main.h" -#include "FreeRTOS.h" -#include "timers.h" - #include "scheduler.h" +#include "cmsis_os2.h" #include "test.h" #include "driver.h" #define LEN(a) sizeof(a) / sizeof(a[0]) +#define PREP(...) \ + struct BuzzerObject bo; \ + struct StaticTimer_t tim; \ + memset(&bo, 0, sizeof(bo)); \ + memset(&tim, 0, sizeof(tim)); \ + system_ticks = 0; \ + gpio_init(); \ + timers_init(); \ + osTimerAttr_t attr = { \ + .name = NULL, \ + .attr_bits = 0, \ + .cb_mem = &tim, \ + .cb_size = sizeof(tim) \ + }; \ + osTimerNew(timer_cb, 1, &bo, &attr); \ + osTimerStart(&tim, 10) + +void timer_cb(TimerHandle_t const tim_ptr) +{ + Buzzer_TimerTask(tim_ptr->pvTimerID); +} + int driver_configure_correct(void) { + PREP(); + for (int i = 0x8000; i > 0; i >>= 1) - if (Buzzer_ConfigurePort(GPIOD, i)) + if (Buzzer_ConfigurePort(&bo, GPIOD, i)) return i; return 0; @@ -22,6 +45,8 @@ int driver_configure_correct(void) int driver_configure_wrong(void) { + PREP(); + uint16_t broken_values[] = { 0xF000, 0xFFFF, @@ -32,7 +57,7 @@ int driver_configure_wrong(void) }; for (int i = 0; i < LEN(broken_values); i++) - if (BUZZER_PIN_ERROR != Buzzer_ConfigurePort(GPIOD, broken_values[i])) + if (BUZZER_PIN_ERROR != Buzzer_ConfigurePort(&bo, GPIOD, broken_values[i])) return i+1; return 0; @@ -40,14 +65,16 @@ int driver_configure_wrong(void) int driver_enable_correct(void) { - gpio_init(); + PREP(); - if (Buzzer_ConfigurePort(GPIOD, 0x8000)) + if (Buzzer_ConfigurePort(&bo, GPIOD, 0x8000)) return 1; - if (Buzzer_Enable()) + if (Buzzer_Enable(&bo)) return 2; + scheduler_run_for(15); + if (!(GPIOD->ODR & 0x8000)) return 3; @@ -56,31 +83,34 @@ int driver_enable_correct(void) int driver_enable_wrong(void) { - gpio_init(); + PREP(); - if (BUZZER_PIN_ERROR != Buzzer_ConfigurePort(GPIOD, 0xF000)) + if (BUZZER_PIN_ERROR != Buzzer_ConfigurePort(&bo, GPIOD, 0xF000)) return 1; - if (BUZZER_NOT_INITIALIZED_ERROR != Buzzer_Enable()) - return 2; + Buzzer_Enable(&bo); + + scheduler_run_for(15); if (GPIOD->ODR & 0xF000) - return 3; + return 2; return 0; } int driver_disable_correct(void) { - gpio_init(); + PREP(); GPIOD->ODR = 0x1; - if (Buzzer_ConfigurePort(GPIOD, 0x0001)) + if (Buzzer_ConfigurePort(&bo, GPIOD, 0x0001)) return 1; - if (Buzzer_Disable()) + if (Buzzer_Disable(&bo)) return 2; + scheduler_run_for(15); + if (GPIOD->ODR & 0x0001) return 3; @@ -89,89 +119,89 @@ int driver_disable_correct(void) int driver_disable_wrong(void) { - gpio_init(); + PREP(); GPIOD->ODR = 0x1; - if (BUZZER_PIN_ERROR != Buzzer_ConfigurePort(GPIOD, 0x0101)) + if (BUZZER_PIN_ERROR != Buzzer_ConfigurePort(&bo, GPIOD, 0x0101)) return 1; - if (BUZZER_NOT_INITIALIZED_ERROR != Buzzer_Disable()) - return 2; + Buzzer_Disable(&bo); + + scheduler_run_for(15); if (!(GPIOD->ODR & 0x0001)) - return 3; + return 2; return 0; } + int driver_pulse_correct(void) { - gpio_init(); - timers_init(); - - TimerHandle_t *timer_queue = get_timer_queue(); + PREP(); - if (Buzzer_ConfigurePort(GPIOD, 0x0001)) + if (Buzzer_ConfigurePort(&bo, GPIOD, 0x0001)) return 1; - if (Buzzer_Pulse(200, 500, 2000)) + if (Buzzer_Pulse(&bo, 200, 500, 2000)) return 2; + scheduler_run_for(15); + if (!(GPIOD->ODR & 0x0001)) return 3; - scheduler_run_for(200); + scheduler_run_for(210); if (GPIOD->ODR & 0x0001) return 4; - scheduler_run_for(300); + scheduler_run_for(310); if (!(GPIOD->ODR & 0x0001)) return 5; - scheduler_run_for(200); + scheduler_run_for(210); if (GPIOD->ODR & 0x0001) return 6; - scheduler_run_for(1300); + scheduler_run_for(1310); - for (int i = 0; i < TIMER_QUEUE_SIZE; i++) - if (timer_queue[i] != NULL && (timer_queue[i]->status | TIMER_ACTIVE)) - return 7; + if (GPIOD->ODR & 0x0001) + return 7; return 0; } int driver_preemptive_pulse_stop(void) { - gpio_init(); - timers_init(); - - TimerHandle_t *timer_queue = get_timer_queue(); + PREP(); - if (Buzzer_ConfigurePort(GPIOD, 0x0001)) + if (Buzzer_ConfigurePort(&bo, GPIOD, 0x0001)) return 1; - if (Buzzer_Pulse(100, 200, 1000)) + if (Buzzer_Pulse(&bo, 100, 200, 1000)) return 2; scheduler_run_for(530); - if (Buzzer_Disable()) + if (Buzzer_Disable(&bo)) return 3; - for (int i = 0; i < TIMER_QUEUE_SIZE; i++) - if (timer_queue[i]) - return 4; + scheduler_run_for(15); + + if (GPIOD->ODR & 1) + return 4; return 0; } int dummy_broken_test(void) { - return Buzzer_ConfigurePort(GPIOD, 0xF000); + PREP(); + + return Buzzer_ConfigurePort(&bo, GPIOD, 0xF000); } int main(void) From 93143cc49f484c7913096ad6c563e5f4b880b5e9 Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Wed, 8 Oct 2025 12:40:53 +0300 Subject: [PATCH 18/18] Issue #32: Migrate to using gTest testing framework --- KPI_Rover/Buzzer/mock-suite/CMakeLists.txt | 23 ++- .../Buzzer/mock-suite/test/buzzer-driver.cpp | 165 ++++++++++++++++++ 2 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.cpp diff --git a/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt b/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt index f43cecf..e3cd2ca 100644 --- a/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt +++ b/KPI_Rover/Buzzer/mock-suite/CMakeLists.txt @@ -1,8 +1,17 @@ set(CFLAGS -Og -g3 -Wall -Wpedantic -Wno-pointer-to-int-cast) +set(CPPFLAGS -std=c++20 -Og -g3 -Wall -Wpedantic) cmake_minimum_required(VERSION 3.25.1) -project(buzzer_testing C) +project(buzzer_testing) + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) + +FetchContent_MakeAvailable(googletest) add_library(scheduler STATIC src/scheduler.c) add_library(timers STATIC src/timers.c) @@ -12,7 +21,7 @@ add_library(cmsis_os2 STATIC src/cmsis_os2.c) add_library(driver STATIC ../driver.c) add_library(generic STATIC ../generic.c) -add_executable(buzzer-driver test/buzzer-driver.c) +add_executable(buzzer-driver test/buzzer-driver.cpp) target_include_directories(scheduler PRIVATE . inc) target_include_directories(timers PRIVATE . inc) @@ -21,20 +30,16 @@ target_include_directories(stm32f4xx_hal_systick PRIVATE inc) target_include_directories(driver PRIVATE .. . inc) target_include_directories(cmsis_os2 PRIVATE . inc) target_include_directories(generic PRIVATE . inc) -target_include_directories(buzzer-driver PRIVATE .. . inc) +target_include_directories(buzzer-driver PRIVATE .. . inc ${CMAKE_BINARY_DIR}/googletest) target_link_libraries(scheduler timers) target_link_libraries(driver timers stm32f4xx_hal_gpio stm32f4xx_hal_systick) target_link_libraries(cmsis_os2 timers) -target_link_libraries(buzzer-driver scheduler timers stm32f4xx_hal_gpio driver cmsis_os2 generic) +target_link_libraries(buzzer-driver gtest gtest_main scheduler timers stm32f4xx_hal_gpio driver cmsis_os2 generic) target_compile_options(scheduler PRIVATE ${CFLAGS}) target_compile_options(timers PRIVATE ${CFLAGS}) target_compile_options(stm32f4xx_hal_gpio PRIVATE ${CFLAGS}) target_compile_options(driver PRIVATE ${CFLAGS}) target_compile_options(cmsis_os2 PRIVATE ${CFLAGS}) -target_compile_options(buzzer-driver PRIVATE ${CFLAGS}) - -# include actual driver dir to compile - -# then try to compile whole project +target_compile_options(buzzer-driver PRIVATE ${CPPFLAGS}) diff --git a/KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.cpp b/KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.cpp new file mode 100644 index 0000000..a896722 --- /dev/null +++ b/KPI_Rover/Buzzer/mock-suite/test/buzzer-driver.cpp @@ -0,0 +1,165 @@ +#include + +#include +#include + +#include + +extern "C" { +#include "main.h" +#include "scheduler.h" +#include "cmsis_os2.h" +//#include "test.h" + +#include "driver.h" +} + +#define LEN(a) sizeof(a) / sizeof(a[0]) + +#define PREP(...) \ + struct BuzzerObject bo; \ + struct StaticTimer_t tim; \ + memset(&bo, 0, sizeof(bo)); \ + memset(&tim, 0, sizeof(tim)); \ + system_ticks = 0; \ + gpio_init(); \ + timers_init(); \ + osTimerAttr_t attr = { \ + .name = NULL, \ + .attr_bits = 0, \ + .cb_mem = &tim, \ + .cb_size = sizeof(tim) \ + }; \ + osTimerNew(timer_cb, 1, &bo, &attr); \ + osTimerStart(&tim, 10) + +void timer_cb(TimerHandle_t const tim_ptr) +{ + Buzzer_TimerTask((struct BuzzerObject *) tim_ptr->pvTimerID); +} + +TEST(buzzer_driver, configure_correct) +{ + PREP(); + + for (int i = 0x8000; i > 0; i >>= 1) + EXPECT_EQ(Buzzer_ConfigurePort(&bo, GPIOD, i), 0) << i << " fails the configuration" << ::std::endl; +} + +TEST(buzzer_driver, configure_wrong) +{ + PREP(); + + uint16_t broken_values[] = { + 0xF000, + 0xFFFF, + 0x0000, + 0xA5A5, + 0xDEAD, + 0xBEEF + }; + + for (unsigned int i = 0; i < LEN(broken_values); i++) + EXPECT_EQ(BUZZER_PIN_ERROR, Buzzer_ConfigurePort(&bo, GPIOD, broken_values[i])) << i << " does not cause pin error" << ::std::endl; +} + +TEST(buzzer_driver, enable_correct) +{ + PREP(); + + EXPECT_EQ(0, Buzzer_ConfigurePort(&bo, GPIOD, 0x8000)); + + Buzzer_Enable(&bo); + + scheduler_run_for(15); + + EXPECT_EQ(0x8000, GPIOD->ODR); +} + +TEST(buzzer_driver, enable_wrong) +{ + PREP(); + + EXPECT_EQ(BUZZER_PIN_ERROR, Buzzer_ConfigurePort(&bo, GPIOD, 0xF000)); + + Buzzer_Enable(&bo); + + scheduler_run_for(15); + + EXPECT_EQ(0x0000, GPIOD->ODR); +} + +TEST(buzzer_driver, disable_correct) +{ + PREP(); + GPIOD->ODR = 0x0001; + + EXPECT_EQ(0, Buzzer_ConfigurePort(&bo, GPIOD, 0x0001)); + + Buzzer_Disable(&bo); + + scheduler_run_for(15); + + EXPECT_EQ(0x0000, GPIOD->ODR); +} + +TEST(buzzer_driver, disable_wrong) +{ + PREP(); + GPIOD->ODR = 0x0001; + + EXPECT_EQ(BUZZER_PIN_ERROR, Buzzer_ConfigurePort(&bo, GPIOD, 0x0101)); + + Buzzer_Disable(&bo); + + scheduler_run_for(15); + + EXPECT_EQ(0x0001, GPIOD->ODR); +} + + +TEST(buzzer_driver, pulse_correct) +{ + PREP(); + + EXPECT_EQ(0, Buzzer_ConfigurePort(&bo, GPIOD, 0x0001)); + + EXPECT_EQ(0, Buzzer_Pulse(&bo, 200, 500, 2000)); + + scheduler_run_for(15); + + EXPECT_EQ(0x0001, GPIOD->ODR); + + scheduler_run_for(210); + + EXPECT_EQ(0x0000, GPIOD->ODR); + + scheduler_run_for(310); + + EXPECT_EQ(0x0001, GPIOD->ODR); + + scheduler_run_for(210); + + EXPECT_EQ(0x0000, GPIOD->ODR); + + scheduler_run_for(1310); + + EXPECT_EQ(0x0000, GPIOD->ODR); +} + +TEST(buzzer_driver, preemptive_pulse_stop) +{ + PREP(); + + EXPECT_EQ(0, Buzzer_ConfigurePort(&bo, GPIOD, 0x0001)); + + Buzzer_Pulse(&bo, 100, 200, 1000); + + scheduler_run_for(530); + + Buzzer_Disable(&bo); + + scheduler_run_for(15); + + EXPECT_EQ(0x0000, GPIOD->ODR); +}