Merge pull request 'libs-rework' (#6) from libs-rework into main

Reviewed-on: https://git.intern.spaceteamaachen.de/ALPAKA/rtos2-utils/pulls/6
Reviewed-by: dario <dario@noreply.git.intern.spaceteamaachen.de>
This commit is contained in:
carlwachter 2023-09-14 20:52:35 +00:00
commit c5f2d8e533
30 changed files with 1167 additions and 753 deletions

4
.gitignore vendored
View File

@ -6,8 +6,8 @@
*.launch
# Build artifacts
Debug/
Release/
/Debug/
/Release/
# Doxygen
docs/html

View File

@ -1,74 +1,42 @@
# STA RTOS Utilities
![pre-release v0.1.0](https://img.shields.io/static/v1?label=pre-release&message=v0.1.0&color=orange&logo=git)
![pre-release v1.0.0](https://img.shields.io/static/v1?label=pre-release&message=v1.0.0&color=orange&logo=git)
![Standard: C++11](https://img.shields.io/static/v1?label=standard&message=C%2B%2B11&color=blue&logo=cplusplus)
Library using CMSIS-RTOS2 functionality for RTOS projects.
Library providing the software stack for use with the ALPAKA hardware design for RTOS projects.
Modules are enabled via defines set in `<sta/config.hpp>` header which must be provided by the application.
Modules can be configured via defines set in `<sta/config.hpp>` header file which must be provided by the application.
# Interface implementations
# ALPAKA Modules
The library provides implementations for the following interfaces using CMSIS-RTOS2 functionality:
* `Mutex`
* `Signal`
All enabled modules are initialized by calling the `startALPAKA` function from the default task.
The function `startupExtras` is called before any module initialization and can be used by the application
to execute additional initialization steps before any task waiting for the startup system event will run.
The function prototype can be found in the `<sta/rtos/startup.hpp>` header and can optionally be implemented
anywhere in the application code.
# Modules
## Can Bus
## System Event
Provides an interface for common system events.
TODO Add description
Configuration:
* `#define STA_RTOS_SYSTEM_EVENT_ENABLE`: Enable module
* `#define STA_RTOS_SYSTEM_EVENT_HANDLE <var_name>`: Override variable name of flag handle (default: systemEventHandle)
Requirements:
* RTOS: event flag
## Watchdog
The watchdog task waits for signals sent either from the heartbeat timer or manually via `sta::notifyWatchdog`
and forwards the event flags to the `sta::watchdogEventHandler` function implemented by the application.
The watchdog task waits for signals sent either from its heartbeat timer or manually via `sta::notifyWatchdog`
and passes the event flags to the `sta::watchdogEventHandler` function. This function must be implemented by the application.
Configuration:
* `#define STA_RTOS_WATCHDOG_ENABLE`: Enable module
* `#define STA_RTOS_WATCHDOG_TIMER_PERIOD <period_ticks>`: Set period in ticks of heartbeat timer (default: 1000)
* `#define STA_RTOS_WATCHDOG_TIMER_HANDLE <var_name>`: Override variable name of heartbeat timer handle (default: heartbeatHandle)
* `#define STA_RTOS_WATCHDOG_TIMER_CALLBACK <func_name>`: Override name of heartbeat timer callback function (default: heartbeatCallback)
* `#define STA_RTOS_WATCHDOG_HANDLE <var_name>`: Override variable name of watchdog task handle (default: watchdogHandle)
* `#define STA_RTOS_WATCHDOG_ENTRY_FUNCTION <func_name>`: Override name of watchdog task entry function (default: watchdogTask)
Requirements:
* Uses the `System Event` module
* RTOS: timer + task
## Startup
# STA-Core Interfaces
The entry function for the startup task must be called manually from the default task.
It provides all setup required by the enabled system tasks. If additional initialization is required by the
application the function `void sta::startupExtras(void *)` declared in `<sta/rtos2/startup.hpp>` can be implemented.
Configuration:
* `#define STA_RTOS_STARTUP_ENABLE`: Enable module
* `#define STA_RTOS_STARTUP_ENTRY_FUNCTION <func_name>`: Override name of startup task entry function (default: startupTask)
Requirements:
* Uses the `System Event` module
* RTOS: task
## Easy Config
Simplify configuration of modules. Intended for use in `<sta/config.hpp>`.
Configuration:
* `#define STA_RTOS_SYSTEM_TASKS_ENABLE`: Enable all modules required for system tasks
* `#define STA_RTOS_WATCHDOG_TIMER_NAME <name>`: Override handle and callback name for watchdog timer
* `#define STA_RTOS_WATCHDOG_NAME <name>`: Override handle and entry function name for watchdog task
The library provides implementations for the following interfaces using CMSIS-RTOS2 functionality:
* `Mutex`
* `Signal`

View File

@ -2,8 +2,8 @@
* @file
* @brief CAN driver message request types for use in C code.
*/
#ifndef STA_RTOS_SYSTEM_CAN_MSG_H
#define STA_RTOS_SYSTEM_CAN_MSG_H
#ifndef STA_RTOS_C_API_CAN_MSG_H
#define STA_RTOS_C_API_CAN_MSG_H
#include <stdint.h>
@ -46,4 +46,4 @@ struct CanSysMsg
};
#endif // STA_RTOS_SYSTEM_CAN_MSG_H
#endif // STA_RTOS_C_API_CAN_MSG_H

View File

@ -0,0 +1,22 @@
#ifndef STA_RTOS_C_API_STARTUP_H
#define STA_RTOS_C_API_STARTUP_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief
*
* @param arg Default task argument
*/
void startALPAKA(void * arg);
#ifdef __cplusplus
}
#endif
#endif // STA_RTOS_C_API_STARTUP_H

View File

@ -1,82 +0,0 @@
/**
* @file
* @brief Helper for easy system task setup in `<sta/config.hpp>`.
*/
#ifndef STA_RTOS_EASY_CONFIG_HPP
#define STA_RTOS_EASY_CONFIG_HPP
#include <sta/rtos/system/names.hpp>
/**
* @defgroup STA_RTOS_EasyConfig Easy Config
* @ingroup STA_RTOS_BuildConfig
* @brief Helpers for easy RTOS module setup.
*
* Use this header only inside the <sta/config.hpp> header of your application.
*/
#ifdef DOXYGEN
/**
* @brief Don't warn about use of <rtos2/easy_config.hpp> outside of <sta/config.hpp>.
*
* @ingroup STA_RTOS_EasyConfig
*/
# define STA_RTOS_EASY_CONFIG_NO_WARNING
#endif // DOXYGEN
#if !defined(STA_CONFIG_HPP) && !defined(STA_RTOS_EASY_CONFIG_NO_WARNING)
#warning "Intended for use in <sta/config.hpp>"
#endif // !STA_CONFIG_HPP && !STA_RTOS_EASY_CONFIG_NO_WARNING
#ifdef DOXYGEN
/**
* @brief Enable all system tasks and required features.
*
* @ingroup STA_RTOS_EasyConfig
*/
# define STA_RTOS_EASY_CONFIG_SYSTEM_TASKS_ENABLE
#endif // DOXYGEN
#ifdef STA_RTOS_EASY_CONFIG_SYSTEM_TASKS_ENABLE
// Enable system events used by system tasks
# define STA_RTOS_SYSTEM_EVENT_ENABLE
// Enable system tasks
# define STA_RTOS_CAN_BUS_ENABLE
# define STA_RTOS_STARTUP_ENABLE
# define STA_RTOS_WATCHDOG_ENABLE
#endif // STA_RTOS_EASY_CONFIG_SYSTEM_TASKS_ENABLE
#ifdef DOXYGEN
/**
* @brief Common base name used for watchdog timer handle and callback names.
*
* @ingroup STA_RTOS_EasyConfig
*/
# define STA_RTOS_EASY_CONFIG_WATCHDOG_TIMER_NAME
#endif // DOXYGEN
#ifdef STA_RTOS_EASY_CONFIG_WATCHDOG_TIMER_NAME
# define STA_RTOS_WATCHDOG_TIMER_HANDLE STA_RTOS_MAKE_HANDLE_NAME(STA_RTOS_EASY_CONFIG_WATCHDOG_TIMER_NAME)
# define STA_RTOS_WATCHDOG_TIMER_CALLBACK STA_RTOS_MAKE_CALLBACK_NAME(STA_RTOS_EASY_CONFIG_WATCHDOG_TIMER_NAME)
#endif // STA_RTOS_EASY_CONFIG_WATCHDOG_TIMER_NAME
#ifdef DOXYGEN
/**
* @brief Common base name used for watchdog task handle and entry function names.
*
* @ingroup STA_RTOS_EasyConfig
*/
# define STA_RTOS_EASY_CONFIG_WATCHDOG_NAME
#endif // DOXYGEN
#ifdef STA_RTOS_EASY_CONFIG_WATCHDOG_NAME
# define STA_RTOS_WATCHDOG_HANDLE STA_RTOS_MAKE_HANDLE_NAME(STA_RTOS_EASY_CONFIG_WATCHDOG_NAME)
# define STA_RTOS_WATCHDOG_ENTRY_FUNCTION STA_RTOS_MAKE_TASK_NAME(STA_RTOS_EASY_CONFIG_WATCHDOG_NAME)
#endif // STA_RTOS_EASY_CONFIG_WATCHDOG_NAME
#endif // STA_RTOS_EASY_CONFIG_HPP

View File

@ -0,0 +1,35 @@
/**
* @file
* @brief RTOS event implementation.
*/
#ifndef STA_RTOS_EVENT_HPP
#define STA_RTOS_EVENT_HPP
#include <cmsis_os2.h>
#include <sta/event.hpp>
namespace sta
{
/**
* @brief Implementation of Event using CMSIS RTOS2.
*
* @ingroup STA_RTOS_API
*/
class RtosEvent : public Event
{
public:
RtosEvent();
~RtosEvent();
void set(uint32_t flags) override;
void clear(uint32_t flags) override;
uint32_t get() override;
uint32_t wait(uint32_t flags, uint32_t timeout = osWaitForever) override;
private:
osEventFlagsId_t event_id; /**< CMSIS RTOS2 Event Flag */
};
} // namespace sta
#endif // STA_RTOS_EVENT_HPP

View File

@ -5,7 +5,7 @@
# error "Internal header. Use <sta/rtos/handle.hpp> instead."
#endif // !STA_RTOS_HANDLE_HPP
#include <sta/assert.hpp>
#include <sta/debug/assert.hpp>
namespace sta

View File

@ -5,7 +5,7 @@
#ifndef STA_RTOS_MUTEX_HPP
#define STA_RTOS_MUTEX_HPP
#include <sta/intf/mutex.hpp>
#include <sta/mutex.hpp>
#include <cmsis_os2.h>

View File

@ -5,7 +5,7 @@
#ifndef STA_RTOS_SIGNAL_HPP
#define STA_RTOS_SIGNAL_HPP
#include <sta/intf/signal.hpp>
#include <sta/signal.hpp>
#include <cmsis_os2.h>

View File

@ -5,8 +5,6 @@
#ifndef STA_RTOS_SYSTEM_CAN_BUS_HPP
#define STA_RTOS_SYSTEM_CAN_BUS_HPP
#include <sta/rtos/system/names.hpp>
/**
* @defgroup STA_RTOS_CanBus CAN driver
@ -25,31 +23,13 @@
# define STA_RTOS_CAN_BUS_ENABLE
#endif // DOXYGEN
/**
* @def STA_RTOS_CAN_BUS_TASK_NAME
* @brief Set name of CAN driver task.
*
* @ingroup STA_RTOS_BuildConfig
*/
#ifndef STA_RTOS_CAN_BUS_TASK_NAME
# define STA_RTOS_CAN_BUS_TASK_NAME canBus
#endif // !STA_RTOS_CAN_BUS_TASK_NAME
/**
* @def STA_RTOS_CAN_BUS_ENTRY_FUNCTION
* @brief Set name of CAN driver task entry function.
*
* @ingroup STA_RTOS_BuildConfig
*/
#ifndef STA_RTOS_CAN_BUS_ENTRY_FUNCTION
# define STA_RTOS_CAN_BUS_ENTRY_FUNCTION STA_RTOS_MAKE_ENTRY_NAME(STA_RTOS_CAN_BUS_TASK_NAME)
#endif // !STA_RTOS_CAN_BUS_ENTRY_FUNCTION
#include <sta/config.hpp>
#ifdef STA_RTOS_CAN_BUS_ENABLE
#include <sta/rtos/system/can_msg.h>
#include <sta/can/controller.hpp>
#include <sta/rtos/c_api/can_msg.h>
#include <cstdint>
@ -121,13 +101,18 @@ namespace sta
* @{
*/
/**
* @brief Initialize CAN bus.
*/
void initCanBus();
/**
* @brief Extra initialization run at start of CAN bus task.
* @brief Return CanController for use in CAN system task.
*
* May be overridden by application if required.
* Implementation must be provided by application.
*/
void setupCanBus();
extern CanController * getCanController();

View File

@ -2,10 +2,8 @@
* @file
* @brief Implementation of system events.
*/
#ifndef STA_RTOS_SYSTEM_SYSTEM_EVENT_HPP
#define STA_RTOS_SYSTEM_SYSTEM_EVENT_HPP
#include <sta/rtos/system/names.hpp>
#ifndef STA_RTOS_SYSTEM_EVENTS_HPP
#define STA_RTOS_SYSTEM_EVENTS_HPP
/**
@ -16,29 +14,6 @@
* Check @ref STA_RTOS_BuildConfig for configuration options.
*/
#ifdef DOXYGEN
/**
* @brief Enable module.
*
* @ingroup STA_RTOS_BuildConfig
*/
# define STA_RTOS2_SYSTEM_EVENT_ENABLE
#endif // DOXYGEN
/**
* @def STA_RTOS_SYSTEM_EVENT_NAME
* @brief Set name of system event flags.
*
* @ingroup STA_RTOS_BuildConfig
*/
#ifndef STA_RTOS_SYSTEM_EVENT_NAME
# define STA_RTOS_SYSTEM_EVENT_NAME systemEvent
#endif // !STA_RTOS_SYSTEM_EVENT_NAME
#include <sta/config.hpp>
#ifdef STA_RTOS_SYSTEM_EVENT_ENABLE
#include <cstdint>
@ -51,13 +26,19 @@
*
* @ingroup STA_RTOS_SysEvent
*/
#define STA_SYSTEM_EVENT_STARTUP 0x100000U
#define STA_RTOS_SYSTEM_EVENTS_STARTUP 0x100000U
namespace sta
{
namespace rtos
{
/**
* @brief Initialize system events.
*/
void initSystemEvents();
/**
* @brief Signal system events.
*
@ -98,6 +79,4 @@ namespace sta
} // namespace sta
#endif // STA_RTOS_SYSTEM_EVENT_ENABLE
#endif // STA_RTOS_SYSTEM_SYSTEM_EVENT_HPP
#endif // STA_RTOS_SYSTEM_EVENTS_HPP

View File

@ -1,12 +0,0 @@
#ifndef STA_RTOS_SYSTEM_NAMES_HPP
#define STA_RTOS_SYSTEM_NAMES_HPP
#define _STA_RTOS_CONCAT(a, b) a ## b
#define STA_RTOS_MAKE_HANDLE_NAME(name) _STA_RTOS_CONCAT(name, Handle)
#define STA_RTOS_MAKE_CALLBACK_NAME(name) _STA_RTOS_CONCAT(name, Callback)
#define STA_RTOS_MAKE_ENTRY_NAME(name) _STA_RTOS_CONCAT(name, Task)
#endif // STA_RTOS_SYSTEM_NAMES_HPP

View File

@ -5,8 +5,6 @@
#ifndef STA_RTOS_SYSTEM_STARTUP_HPP
#define STA_RTOS_SYSTEM_STARTUP_HPP
#include <sta/rtos/system/names.hpp>
/**
* @defgroup STA_RTOS_Startup Startup task
@ -17,41 +15,6 @@
*/
#ifdef DOXYGEN
/**
* @brief Enable module.
*
* @ingroup STA_RTOS_BuildConfig
*/
# define STA_RTOS_STARTUP_ENABLE
#endif // DOXYGEN
/**
* @def STA_RTOS_STARTUP_TASK_NAME
* @brief Set name of startup task.
*
* @ingroup STA_RTOS_BuildConfig
*/
#ifndef STA_RTOS_STARTUP_TASK_NAME
# define STA_RTOS_STARTUP_TASK_NAME startup
#endif // !STA_RTOS_STARTUP_TASK_NAME
/**
* @def STA_RTOS_STARTUP_ENTRY_FUNCTION
* @brief Set name of startup task entry function.
*
* @ingroup STA_RTOS_BuildConfig
*/
#ifndef STA_RTOS_STARTUP_ENTRY_FUNCTION
# define STA_RTOS_STARTUP_ENTRY_FUNCTION STA_RTOS_MAKE_ENTRY_NAME(STA_RTOS_STARTUP_TASK_NAME)
#endif // !STA_RTOS_STARTUP_ENTRY_FUNCTION
#include <sta/config.hpp>
#ifdef STA_RTOS_STARTUP_ENABLE
namespace sta
{
namespace rtos
@ -68,6 +31,4 @@ namespace sta
} // namespace sta
#endif // STA_RTOS_STARTUP_ENABLE
#endif // STA_RTOS_SYSTEM_STARTUP_HPP

View File

@ -5,8 +5,6 @@
#ifndef STA_RTOS_SYSTEM_WATCHDOG_HPP
#define STA_RTOS_SYSTEM_WATCHDOG_HPP
#include <sta/rtos/system/names.hpp>
/**
* @defgroup STA_RTOS_Watchdog Watchdog task
@ -27,71 +25,20 @@
/**
* @def STA_RTOS_WATCHDOG_TIMER_PERIOD
* @def STA_RTOS_SYSTEM_WATCHDOG_TIMER_PERIOD
* @brief Set period in ticks of heartbeat timer.
*
* @ingroup STA_RTOS_BuildConfig
*/
#ifndef STA_RTOS_WATCHDOG_TIMER_PERIOD
# define STA_RTOS_WATCHDOG_TIMER_PERIOD 1000
#endif // !STA_RTOS_WATCHDOG_TIMER_PERIOD
/**
* @def STA_RTOS_WATCHDOG_TIMER_NAME
* @brief Set name of watchdog timer.
*
* @ingroup STA_RTOS_BuildConfig
*/
#ifndef STA_RTOS_WATCHDOG_TIMER_NAME
# define STA_RTOS_WATCHDOG_TIMER_NAME heartbeat
#endif // !STA_RTOS_WATCHDOG_TIMER_NAME
/**
* @def STA_RTOS_WATCHDOG_TIMER_HANDLE
* @brief Set variable name of heartbeat timer handle.
*
* @ingroup STA_RTOS_BuildConfig
*/
#ifndef STA_RTOS_WATCHDOG_TIMER_HANDLE
# define STA_RTOS_WATCHDOG_TIMER_HANDLE STA_RTOS_MAKE_HANDLE_NAME(STA_RTOS_WATCHDOG_TIMER_NAME)
#endif // !STA_RTOS_WATCHDOG_TIMER_HANDLE
/**
* @def STA_RTOS_WATCHDOG_TIMER_CALLBACK
* @brief Set name of heartbeat timer callback function.
*
* @ingroup STA_RTOS_BuildConfig
*/
#ifndef STA_RTOS_WATCHDOG_TIMER_CALLBACK
# define STA_RTOS_WATCHDOG_TIMER_CALLBACK STA_RTOS_MAKE_CALLBACK_NAME(STA_RTOS_WATCHDOG_TIMER_NAME)
#endif // !STA_RTOS_WATCHDOG_TIMER_CALLBACK
/**
* @def STA_RTOS_WATCHDOG_TASK_NAME
* @brief Set name of watchdog task.
*
* @ingroup STA_RTOS_BuildConfig
*/
#ifndef STA_RTOS_WATCHDOG_TASK_NAME
# define STA_RTOS_WATCHDOG_TASK_NAME watchdog
#endif // !STA_RTOS_WATCHDOG_TASK_NAME
/**
* @def STA_RTOS_WATCHDOG_ENTRY_FUNCTION
* @brief Set name of watchdog task entry function.
*
* @ingroup STA_RTOS_BuildConfig
*/
#ifndef STA_RTOS_WATCHDOG_ENTRY_FUNCTION
# define STA_RTOS_WATCHDOG_ENTRY_FUNCTION STA_RTOS_MAKE_ENTRY_NAME(STA_RTOS_WATCHDOG_TASK_NAME)
#endif // !STA_RTOS_WATCHDOG_ENTRY_FUNCTION
#ifndef STA_RTOS_SYSTEM_WATCHDOG_TIMER_PERIOD
# define STA_RTOS_SYSTEM_WATCHDOG_TIMER_PERIOD 1000
#endif // !STA_RTOS_SYSTEM_WATCHDOG_TIMER_PERIOD
#include <sta/config.hpp>
#ifdef STA_RTOS_WATCHDOG_ENABLE
#include <cstdint>
@ -111,11 +58,12 @@ namespace sta
namespace rtos
{
/**
* @brief Start heartbeat timer for watchdog.
* @brief Initialize system watchdog.
*
* @ingroup STA_RTOS_Watchdog
*/
void startWatchdogTimer();
void initWatchdog();
/**
* @brief Send notification to watchdog task.
*

View File

@ -0,0 +1,34 @@
/**
* @file
* @brief RTOS timer implementation.
*/
#ifndef STA_RTOS_TIMER_HPP
#define STA_RTOS_TIMER_HPP
#include <cmsis_os2.h>
#include <sta/timer.hpp>
namespace sta
{
/**
* @brief Implementation of Timer using CMSIS RTOS2.
*
* @ingroup STA_RTOS_API
*/
class RtosTimer : public Timer
{
public:
RtosTimer(void (*callback)(void *arg), void *arg);
~RtosTimer();
void start(uint32_t millis) override;
void stop() override;
private:
osTimerId_t timer_id_; /**< CMSIS RTOS2 Timer */
osTimerAttr_t timer_attr_; /**< CMSIS RTOS2 Timer attributes */
};
} // namespace sta
#endif // STA_RTOS_TIMER_HPP

View File

@ -1,7 +1,7 @@
{
"owner" : "sta",
"name": "sta-rtos",
"version": "0.1.0",
"version": "1.0.0",
"dependencies": [
{
"url": "git@gitlab.com:sta-git/alpaka/sta-core.git",

20
src/debug/runtime_stats.c Normal file
View File

@ -0,0 +1,20 @@
#include <sta/config.hpp>
#ifdef STA_RTOS_RUNTIME_STATS_TIM
#include <tim.h>
void configureTimerForRunTimeStats()
{
// Start timer base
HAL_TIM_Base_Start(&STA_RTOS_RUNTIME_STATS_TIM);
// Reset timer
__HAL_TIM_SET_COUNTER(&STA_RTOS_RUNTIME_STATS_TIM, 0);
}
unsigned long getRunTimeCounterValue()
{
return __HAL_TIM_GET_COUNTER(&STA_RTOS_RUNTIME_STATS_TIM);
}
#endif // STA_RTOS_RUNTIME_STATS_TIM

View File

@ -0,0 +1,35 @@
#include <sta/config.hpp>
#ifdef STA_RTOS_STACK_OVERFLOW_HOOK
#include <sta/assert.hpp>
#include <sta/debug_serial.hpp>
#include <FreeRTOS.h>
#include <task.h>
extern "C" void vApplicationStackOverflowHook(xTaskHandle xTask, char * pcTaskName)
{
STA_DEBUG_PRINT("Stack overflow detected in task ");
if (pcTaskName)
{
// Manually calculate string length
// Limited to configMAX_TASK_NAME_LEN to avoid reading
// garbage values in case TCB has been corrupted
size_t len = 0;
while (len < configMAX_TASK_NAME_LEN)
{
if (pcTaskName[len] == '\0')
{
break;
}
++len;
}
STA_DEBUG_PRINTLN(pcTaskName, len);
}
STA_HALT();
}
#endif // STA_RTOS_STACK_OVERFLOW_HOOK

29
src/event.cpp Normal file
View File

@ -0,0 +1,29 @@
#include <sta/rtos/event.hpp>
namespace sta {
RtosEvent::RtosEvent() {
osEventFlagsAttr_t attr = { 0 };
event_id = osEventFlagsNew(&attr);
}
RtosEvent::~RtosEvent() {
osEventFlagsDelete(event_id);
}
void RtosEvent::set(uint32_t flags) {
osEventFlagsSet(event_id, flags);
}
void RtosEvent::clear(uint32_t flags) {
osEventFlagsClear(event_id, flags);
}
uint32_t RtosEvent::get() {
return osEventFlagsGet(event_id);
}
uint32_t RtosEvent::wait(uint32_t flags, uint32_t timeout) {
return osEventFlagsWait(event_id, flags, osFlagsWaitAny, timeout);
}
} // namespace sta

310
src/heap_useNewlib_ST.c Normal file
View File

@ -0,0 +1,310 @@
/**
* \file heap_useNewlib_ST.c
* \brief Wrappers required to use newlib malloc-family within FreeRTOS.
*
* \par Overview
* Route FreeRTOS memory management functions to newlib's malloc family.
* Thus newlib and FreeRTOS share memory-management routines and memory pool,
* and all newlib's internal memory-management requirements are supported.
*
* \author Dave Nadler
* \date 20-August-2019
* \version 27-Jun-2020 Correct "FreeRTOS.h" capitalization, commentary
* \version 24-Jun-2020 commentary only
* \version 11-Sep-2019 malloc accounting, comments, newlib version check
*
* \see http://www.nadler.com/embedded/newlibAndFreeRTOS.html
* \see https://sourceware.org/newlib/libc.html#Reentrancy
* \see https://sourceware.org/newlib/libc.html#malloc
* \see https://sourceware.org/newlib/libc.html#index-_005f_005fenv_005flock
* \see https://sourceware.org/newlib/libc.html#index-_005f_005fmalloc_005flock
* \see https://sourceforge.net/p/freertos/feature-requests/72/
* \see http://www.billgatliff.com/newlib.html
* \see http://wiki.osdev.org/Porting_Newlib
* \see http://www.embecosm.com/appnotes/ean9/ean9-howto-newlib-1.0.html
*
*
* \copyright
* (c) Dave Nadler 2017-2020, All Rights Reserved.
* Web: http://www.nadler.com
* email: drn@nadler.com
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Use or redistributions of source code must retain the above copyright notice,
* this list of conditions, and the following disclaimer.
*
* - Use or redistributions of source code must retain ALL ORIGINAL COMMENTS, AND
* ANY CHANGES MUST BE DOCUMENTED, INCLUDING:
* - Reason for change (purpose)
* - Functional change
* - Date and author contact
*
* - Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \author Henrik Stickann
* \data 20-January-2023
*
* Changes:
* * Add macro to only use implementation when requested by user
*/
#include <sta/config.hpp>
#ifdef STA_RTOS_MALLOC_ENABLE
// Allow setting configISR_STACK_SIZE_WORDS via the STA_RTOS_ISR_STACK_SIZE_WORDS config macro
#ifdef STA_RTOS_ISR_STACK_SIZE_WORDS
# ifdef configISR_STACK_SIZE_WORDS
# warning "STA_RTOS_ISR_STACK_SIZE_WORDS value overriden by configISR_STACK_SIZE_WORDS!"
# else // configISR_STACK_SIZE_WORDS
# define configISR_STACK_SIZE_WORDS STA_RTOS_ISR_STACK_SIZE_WORDS
# endif // configISR_STACK_SIZE_WORDS
#endif // STA_RTOS_ISR_STACK_SIZE_WORDS
// ================================================================================================
// ======================================= Configuration ========================================
// These configuration symbols could be provided by from build...
#define STM_VERSION // Replace sane LD symbols with STM CubeMX's poor standard exported LD symbols
#define ISR_STACK_LENGTH_BYTES (configISR_STACK_SIZE_WORDS*4) // bytes to reserve for ISR (MSP) stack
// ======================================= Configuration ========================================
// ================================================================================================
#include <stdlib.h> // maps to newlib...
#include <malloc.h> // mallinfo...
#include <errno.h> // ENOMEM
#include <stdbool.h>
#include <stddef.h>
#include "newlib.h"
#if ((__NEWLIB__ == 2) && (__NEWLIB_MINOR__ < 5)) ||((__NEWLIB__ == 3) && (__NEWLIB_MINOR__ > 1))
#warning "This wrapper was verified for newlib versions 2.5 - 3.1; please ensure newlib's external requirements for malloc-family are unchanged!"
#endif
#include "FreeRTOS.h" // defines public interface we're implementing here
#if !defined(configUSE_NEWLIB_REENTRANT) || (configUSE_NEWLIB_REENTRANT!=1)
#warning "#define configUSE_NEWLIB_REENTRANT 1 // Required for thread-safety of newlib sprintf, dtoa, strtok, etc..."
// If you're *REALLY* sure you don't need FreeRTOS's newlib reentrancy support, comment out the above warning...
#endif
#include "task.h"
// ================================================================================================
// External routines required by newlib's malloc (sbrk/_sbrk, __malloc_lock/unlock)
// ================================================================================================
// Simplistic sbrk implementations assume stack grows downwards from top of memory,
// and heap grows upwards starting just after BSS.
// FreeRTOS normally allocates task stacks from a pool placed within BSS or DATA.
// Thus within a FreeRTOS task, stack pointer is always below end of BSS.
// When using this module, stacks are allocated from malloc pool, still always prior
// current unused heap area...
// Doesn't work with FreeRTOS: STM CubeMX 2018-2019 Incorrect Implementation
#if 0
caddr_t _sbrk(int incr)
{
extern char end asm("end"); // From linker: lowest unused RAM address, just beyond end of BSS.
static char *heap_end;
char *prev_heap_end;
if (heap_end == 0) heap_end = &end;
prev_heap_end = heap_end;
if (heap_end + incr > stack_ptr) // Fails here: always true for FreeRTOS task stacks
{
errno = ENOMEM; // ...so first call inside a FreeRTOS task lands here
return (caddr_t) -1;
}
heap_end += incr;
return (caddr_t) prev_heap_end;
}
#endif
register char * stack_ptr asm("sp");
#ifdef STM_VERSION // Use STM CubeMX LD symbols for heap+stack area
// To avoid modifying STM LD file (and then having CubeMX trash it), use available STM symbols
// Unfortunately STM does not provide standardized markers for RAM suitable for heap!
// STM CubeMX-generated LD files provide the following symbols:
// end /* aligned first word beyond BSS */
// _estack /* one word beyond end of "RAM" Ram type memory, for STM32F429 0x20030000 */
// Kludge below uses CubeMX-generated symbols instead of sane LD definitions
#define __HeapBase end
#define __HeapLimit _estack // In K64F LD this is already adjusted for ISR stack space...
static int heapBytesRemaining;
// no DRN HEAP_SIZE symbol from LD... // that's (&__HeapLimit)-(&__HeapBase)
uint32_t TotalHeapSize; // publish for diagnostic routines; filled in first _sbrk call.
#else
// Note: DRN's K64F LD provided: __StackTop (byte beyond end of memory), __StackLimit, HEAP_SIZE, STACK_SIZE
// __HeapLimit was already adjusted to be below reserved stack area.
extern char HEAP_SIZE; // make sure to define this symbol in linker LD command file
static int heapBytesRemaining = (int)&HEAP_SIZE; // that's (&__HeapLimit)-(&__HeapBase)
#endif
#ifdef MALLOCS_INSIDE_ISRs // STM code to avoid malloc within ISR (USB CDC stack)
// We can't use vTaskSuspendAll() within an ISR.
// STM's stunningly bad coding malpractice calls malloc within ISRs (for example, on USB connect function USBD_CDC_Init)
// So, we must just suspend/resume interrupts, lengthening max interrupt response time, aarrggg...
#define DRN_ENTER_CRITICAL_SECTION(_usis) { _usis = taskENTER_CRITICAL_FROM_ISR(); } // Disables interrupts (after saving prior state)
#define DRN_EXIT_CRITICAL_SECTION(_usis) { taskEXIT_CRITICAL_FROM_ISR(_usis); } // Re-enables interrupts (unless already disabled prior taskENTER_CRITICAL)
#else
#define DRN_ENTER_CRITICAL_SECTION(_usis) vTaskSuspendAll(); // Note: safe to use before FreeRTOS scheduler started, but not in ISR
#define DRN_EXIT_CRITICAL_SECTION(_usis) xTaskResumeAll(); // Note: safe to use before FreeRTOS scheduler started, but not in ISR
#endif
#ifndef NDEBUG
static int totalBytesProvidedBySBRK = 0;
#endif
extern char __HeapBase, __HeapLimit; // symbols from linker LD command file
// Use of vTaskSuspendAll() in _sbrk_r() is normally redundant, as newlib malloc family routines call
// __malloc_lock before calling _sbrk_r(). Note vTaskSuspendAll/xTaskResumeAll support nesting.
//! _sbrk_r version supporting reentrant newlib (depends upon above symbols defined by linker control file).
void * _sbrk_r(struct _reent *pReent, int incr) {
#ifdef MALLOCS_INSIDE_ISRs // block interrupts during free-storage use
UBaseType_t usis; // saved interrupt status
#endif
static char *currentHeapEnd = &__HeapBase;
#ifdef STM_VERSION // Use STM CubeMX LD symbols for heap
if(TotalHeapSize==0) {
TotalHeapSize = heapBytesRemaining = (int)((&__HeapLimit)-(&__HeapBase))-ISR_STACK_LENGTH_BYTES;
};
#endif
char* limit = (xTaskGetSchedulerState()==taskSCHEDULER_NOT_STARTED) ?
stack_ptr : // Before scheduler is started, limit is stack pointer (risky!)
&__HeapLimit-ISR_STACK_LENGTH_BYTES; // Once running, OK to reuse all remaining RAM except ISR stack (MSP) stack
DRN_ENTER_CRITICAL_SECTION(usis);
if (currentHeapEnd + incr > limit) {
// Ooops, no more memory available...
#if( configUSE_MALLOC_FAILED_HOOK == 1 )
{
extern void vApplicationMallocFailedHook( void );
DRN_EXIT_CRITICAL_SECTION(usis);
vApplicationMallocFailedHook();
}
#elif defined(configHARD_STOP_ON_MALLOC_FAILURE)
// If you want to alert debugger or halt...
// WARNING: brkpt instruction may prevent watchdog operation...
while(1) { __asm("bkpt #0"); }; // Stop in GUI as if at a breakpoint (if debugging, otherwise loop forever)
#else
// Default, if you prefer to believe your application will gracefully trap out-of-memory...
pReent->_errno = ENOMEM; // newlib's thread-specific errno
DRN_EXIT_CRITICAL_SECTION(usis);
#endif
return (char *)-1; // the malloc-family routine that called sbrk will return 0
}
// 'incr' of memory is available: update accounting and return it.
char *previousHeapEnd = currentHeapEnd;
currentHeapEnd += incr;
heapBytesRemaining -= incr;
#ifndef NDEBUG
totalBytesProvidedBySBRK += incr;
#endif
DRN_EXIT_CRITICAL_SECTION(usis);
return (char *) previousHeapEnd;
}
//! non-reentrant sbrk uses is actually reentrant by using current context
// ... because the current _reent structure is pointed to by global _impure_ptr
char * sbrk(int incr) { return _sbrk_r(_impure_ptr, incr); }
//! _sbrk is a synonym for sbrk.
char * _sbrk(int incr) { return sbrk(incr); };
#ifdef MALLOCS_INSIDE_ISRs // block interrupts during free-storage use
static UBaseType_t malLock_uxSavedInterruptStatus;
#endif
void __malloc_lock(struct _reent *r) {
(void)(r);
#if defined(MALLOCS_INSIDE_ISRs)
DRN_ENTER_CRITICAL_SECTION(malLock_uxSavedInterruptStatus);
#else
bool insideAnISR = xPortIsInsideInterrupt();
configASSERT( !insideAnISR ); // Make damn sure no more mallocs inside ISRs!!
vTaskSuspendAll();
#endif
};
void __malloc_unlock(struct _reent *r) {
(void)(r);
#if defined(MALLOCS_INSIDE_ISRs)
DRN_EXIT_CRITICAL_SECTION(malLock_uxSavedInterruptStatus);
#else
(void)xTaskResumeAll();
#endif
};
// newlib also requires implementing locks for the application's environment memory space,
// accessed by newlib's setenv() and getenv() functions.
// As these are trivial functions, momentarily suspend task switching (rather than semaphore).
// Not required (and trimmed by linker) in applications not using environment variables.
// ToDo: Move __env_lock/unlock to a separate newlib helper file.
void __env_lock() { vTaskSuspendAll(); };
void __env_unlock() { (void)xTaskResumeAll(); };
#if 1 // Provide malloc debug and accounting wrappers
/// /brief Wrap malloc/malloc_r to help debug who requests memory and why.
/// To use these, add linker options: -Xlinker --wrap=malloc -Xlinker --wrap=_malloc_r
// Note: These functions are normally unused and stripped by linker.
size_t TotalMallocdBytes;
int MallocCallCnt;
static bool inside_malloc;
void *__wrap_malloc(size_t nbytes) {
extern void * __real_malloc(size_t nbytes);
MallocCallCnt++;
TotalMallocdBytes += nbytes;
inside_malloc = true;
void *p = __real_malloc(nbytes); // will call malloc_r...
inside_malloc = false;
return p;
};
void *__wrap__malloc_r(void *reent, size_t nbytes) {
(void)(reent);
extern void * __real__malloc_r(size_t nbytes);
if(!inside_malloc) {
MallocCallCnt++;
TotalMallocdBytes += nbytes;
};
void *p = __real__malloc_r(nbytes);
return p;
};
#endif
// ================================================================================================
// Implement FreeRTOS's memory API using newlib-provided malloc family.
// ================================================================================================
void *pvPortMalloc( size_t xSize ) PRIVILEGED_FUNCTION {
void *p = malloc(xSize);
return p;
}
void vPortFree( void *pv ) PRIVILEGED_FUNCTION {
free(pv);
};
size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION {
struct mallinfo mi = mallinfo(); // available space now managed by newlib
return mi.fordblks + heapBytesRemaining; // plus space not yet handed to newlib by sbrk
}
// GetMinimumEverFree is not available in newlib's malloc implementation.
// So, no implementation is provided: size_t xPortGetMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION;
//! No implementation needed, but stub provided in case application already calls vPortInitialiseBlocks
void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION {};
#endif // STA_RTOS_MALLOC_ENABLE

View File

@ -1,20 +0,0 @@
#include <sta/config.hpp>
#ifdef STA_RTOS_STM32_RUNTIME_TIM
#include <tim.h>
void configureTimerForRunTimeStats()
{
// Start timer base
HAL_TIM_Base_Start(&STA_RTOS_STM32_RUNTIME_TIM);
// Reset timer
__HAL_TIM_SET_COUNTER(&STA_RTOS_STM32_RUNTIME_TIM, 0);
}
unsigned long getRunTimeCounterValue()
{
return __HAL_TIM_GET_COUNTER(&STA_RTOS_STM32_RUNTIME_TIM);
}
#endif // STA_RTOS_STM32_RUNTIME_TIM

View File

@ -6,61 +6,101 @@
#ifdef STA_RTOS_CAN_BUS_ENABLE
#include <sta/assert.hpp>
#include <sta/debug_serial.hpp>
#include <sta/can/subscribable.hpp>
#include <sta/debug_serial.hpp>
#include <sta/lang.hpp>
#include <sta/MCP2518FD/controller.hpp>
#include <sta/MCP2518FD/stm32/interrupt.hpp>
#include <sta/proto/isotp/transmitter.hpp>
#include <sta/proto/isotp/receiver.hpp>
#include <sta/rtos/defs.hpp>
#include <sta/rtos/system/can_bus.hpp>
#include <sta/rtos/system/system_event.hpp>
#include <sta/rtos/thread.hpp>
#include <sta/stm32/spi.hpp>
#include <spi.h>
#include <sta/rtos/system/events.hpp>
#include <sta/stm32/hal.hpp>
#include <cmsis_os2.h>
#include <FreeRTOS.h>
#include <cstring>
#define STA_RTOS_MAKE_DATA_QUEUE_NAME(name) _STA_RTOS_CONCAT(name, DataQueue)
#define STA_RTOS_MAKE_SYS_QUEUE_NAME(name) _STA_RTOS_CONCAT(name, SysQueue)
namespace
{
StaticTask_t canBusCB;
StaticQueue_t canBusDataQueueCB;
StaticQueue_t canBusSysQueueCB;
osThreadId_t canBusTaskHandle = nullptr;
osMessageQueueId_t canBusDataQueueHandle = nullptr;
osMessageQueueId_t canBusSysQueueHandle = nullptr;
sta::CanController * canBusController = nullptr;
const size_t queueLength = 8;
// Static memory buffers
CanDataMsg canBusDataQueueBuffer[queueLength];
CanSysMsg canBusSysQueueBuffer[queueLength];
uint32_t canBusStack[256];
}
#define STA_RTOS_CAN_BUS_THREAD STA_RTOS_MAKE_HANDLE_NAME(STA_RTOS_CAN_BUS_TASK_NAME)
#define STA_RTOS_CAN_BUS_DATA_QUEUE STA_RTOS_MAKE_HANDLE_NAME(STA_RTOS_MAKE_DATA_QUEUE_NAME(STA_RTOS_CAN_BUS_TASK_NAME))
#define STA_RTOS_CAN_BUS_SYS_QUEUE STA_RTOS_MAKE_HANDLE_NAME(STA_RTOS_MAKE_SYS_QUEUE_NAME(STA_RTOS_CAN_BUS_TASK_NAME))
// Access handles from freertos.c
extern osThreadId_t STA_RTOS_CAN_BUS_THREAD;
extern osMessageQueueId_t STA_RTOS_CAN_BUS_DATA_QUEUE;
extern osMessageQueueId_t STA_RTOS_CAN_BUS_SYS_QUEUE;
extern "C" void canBusTask(void *);
namespace sta
{
namespace rtos
{
extern CanController * CanBusController;
void initCanBus()
{
// Create thread using static allocation
const osThreadAttr_t taskAttributes = {
.name = "sysCanBus",
.cb_mem = &canBusCB,
.cb_size = sizeof(canBusCB),
.stack_mem = &canBusStack[0],
.stack_size = sizeof(canBusStack),
.priority = (osPriority_t) osPriorityLow,
};
canBusTaskHandle = osThreadNew(canBusTask, NULL, &taskAttributes);
STA_ASSERT_MSG(canBusTaskHandle != nullptr, "System CAN task initialization failed");
STA_WEAK
void setupCanBus()
{}
// Create message queues using static allocation
const osMessageQueueAttr_t dataQueueAttributes = {
.name = "sysCanDataOut",
.attr_bits = 0,
.cb_mem = &canBusDataQueueCB,
.cb_size = sizeof(canBusDataQueueCB),
.mq_mem = &canBusDataQueueBuffer,
.mq_size = sizeof(canBusDataQueueBuffer)
};
canBusDataQueueHandle = osMessageQueueNew(queueLength, sizeof(CanDataMsg), &dataQueueAttributes);
STA_ASSERT_MSG(canBusDataQueueHandle != nullptr, "System CAN data message queue initialization failed");
const osMessageQueueAttr_t sysQueueAttributes = {
.name = "sysCanSysOut",
.attr_bits = 0,
.cb_mem = &canBusSysQueueCB,
.cb_size = sizeof(canBusSysQueueCB),
.mq_mem = &canBusSysQueueBuffer,
.mq_size = sizeof(canBusSysQueueBuffer)
};
canBusSysQueueHandle = osMessageQueueNew(queueLength, sizeof(CanSysMsg), &sysQueueAttributes);
STA_ASSERT_MSG(canBusSysQueueHandle != nullptr, "System CAN system message queue initialization failed");
// Get initialized CAN controller from application
canBusController = getCanController();
}
void notifyCanBus(uint32_t flags)
{
// Send flags to thread
osThreadFlagsSet(STA_RTOS_CAN_BUS_THREAD, flags);
osThreadFlagsSet(canBusTaskHandle, flags);
}
@ -69,10 +109,10 @@ namespace sta
STA_ASSERT((msg.header.sid & STA_CAN_SID_SYS_BITS) == 0);
STA_ASSERT(msg.header.payloadLength <= sizeof(msg.payload));
if (osOK == osMessageQueuePut(STA_RTOS_CAN_BUS_DATA_QUEUE, &msg, 0, timeout))
if (osOK == osMessageQueuePut(canBusDataQueueHandle, &msg, 0, timeout))
{
// Signal thread
osThreadFlagsSet(STA_RTOS_CAN_BUS_THREAD, STA_RTOS_CAN_FLAG_DATA_QUEUED);
osThreadFlagsSet(canBusTaskHandle, STA_RTOS_CAN_FLAG_DATA_QUEUED);
return true;
}
else
@ -85,10 +125,10 @@ namespace sta
{
STA_ASSERT((msg.header.sid & ~STA_CAN_SID_SYS_BITS) == 0);
if (osOK == osMessageQueuePut(STA_RTOS_CAN_BUS_SYS_QUEUE, &msg, 0, timeout))
if (osOK == osMessageQueuePut(canBusSysQueueHandle, &msg, 0, timeout))
{
// Signal thread
osThreadFlagsSet(STA_RTOS_CAN_BUS_THREAD, STA_RTOS_CAN_FLAG_SYS_QUEUED);
osThreadFlagsSet(canBusTaskHandle, STA_RTOS_CAN_FLAG_SYS_QUEUED);
return true;
}
else
@ -100,70 +140,19 @@ namespace sta
bool getCanBusMsg(CanDataMsg * msg, uint32_t timeout)
{
return (osOK == osMessageQueueGet(STA_RTOS_CAN_BUS_DATA_QUEUE, msg, 0, timeout));
return (osOK == osMessageQueueGet(canBusDataQueueHandle, msg, 0, timeout));
}
bool getCanBusMsg(CanSysMsg * msg, uint32_t timeout)
{
return (osOK == osMessageQueueGet(STA_RTOS_CAN_BUS_SYS_QUEUE, msg, 0, timeout));
return (osOK == osMessageQueueGet(canBusSysQueueHandle, msg, 0, timeout));
}
} // namespace rtos
} // namespace sta
using namespace sta;
/**< ISOTP CAN transmitter */
IsotpTransmitter gCanTx(rtos::CanBusController, HAL_GetTick);
/**< ISOTP CAN receiver */
IsotpReceiver gCanRx(rtos::CanBusController, HAL_GetTick);
CanRxCallback filterCallbacks[STA_RTOS_CAN_BUS_MAX_FILTER];
namespace debug
{
/**
* @brief Display ISOTP TX/RX statistics.
*/
void showStatistics()
{
STA_DEBUG_PRINTLN();
STA_DEBUG_PRINTLN("# ######################");
STA_DEBUG_PRINTLN("# ## ISOTP statistics ##");
STA_DEBUG_PRINTLN("# ######################");
STA_DEBUG_PRINTLN("#");
STA_DEBUG_PRINTLN("# Transmitter");
STA_DEBUG_PRINT("# messages: ");
STA_DEBUG_PRINTLN(gCanTx.stats().messages);
STA_DEBUG_PRINT("# blocks: ");
STA_DEBUG_PRINTLN(gCanTx.stats().blocks);
STA_DEBUG_PRINT("# frames: ");
STA_DEBUG_PRINTLN(gCanTx.stats().frames);
STA_DEBUG_PRINT("# timeouts: ");
STA_DEBUG_PRINTLN(gCanTx.stats().timeouts);
STA_DEBUG_PRINTLN("#");
STA_DEBUG_PRINTLN("# Receiver");
STA_DEBUG_PRINT("# messages: ");
STA_DEBUG_PRINTLN(gCanRx.stats().messages);
STA_DEBUG_PRINT("# blocks: ");
STA_DEBUG_PRINTLN(gCanRx.stats().blocks);
STA_DEBUG_PRINT("# frames: ");
STA_DEBUG_PRINTLN(gCanRx.stats().frames);
STA_DEBUG_PRINT("# timeouts: ");
STA_DEBUG_PRINTLN(gCanRx.stats().timeouts);
STA_DEBUG_PRINT("# flow control errors: ");
STA_DEBUG_PRINTLN(gCanRx.stats().flowErrors);
STA_DEBUG_PRINT("# overflows: ");
STA_DEBUG_PRINTLN(gCanRx.stats().overflows);
STA_DEBUG_PRINTLN();
}
/**
* @brief Output CAN frame ID to UART.
*
@ -200,213 +189,357 @@ namespace debug
} // namespace debug
namespace demo
namespace dummy
{
extern void handleRxMessage(const uint8_t * buffer, uint16_t size);
} // namespace demo
/**
* @brief Process received ISOTP messages.
*
* @param msg ISOTP message
*/
void handleRxMessage(const sta::IsotpMessage & msg)
{
STA_DEBUG_PRINTLN("[event] RX message");
debug::printFrameID(msg.frameID);
// TODO Forward message to other threads
demo::handleRxMessage(msg.buffer, msg.size);
}
/**
* @brief Handle received data message CAN frames.
*
* @param header CAN frame header
* @param payload Payload buffer
*/
void receiveDataCallback(const sta::CanRxHeader & header, const uint8_t * payload)
{
// Write frame payload to DebugSerial
STA_DEBUG_PRINTLN("[event] RX data frame");
debug::printPayloadHex(payload, header.payloadLength);
// Process RX frame
auto handle = gCanRx.processFrame(header, payload);
if (handle != sta::IsotpReceiver::INVALID_HANDLE)
void handleSysMessage(const sta::CanRxHeader & header, const uint8_t * payload)
{
// Get message if completed
sta::IsotpMessage msg;
if (gCanRx.getMessage(handle, &msg))
{
handleRxMessage(msg);
}
// Write frame payload to DebugSerial
STA_DEBUG_PRINTLN("[event] RX sys frame");
// Handle FC responses
gCanRx.processFC(handle);
debug::printFrameID(header.id);
debug::printPayloadHex(payload, header.payloadLength);
// TODO Forward message to other threads
}
// Process TX frame
gCanTx.processFrame(header, payload);
}
/**
* @brief Handle received system message CAN frames.
*
* @param header CAN frame header
* @param payload Payload buffer
*/
void receiveSysCallback(const sta::CanRxHeader & header, const uint8_t * payload)
{
// Write frame payload to DebugSerial
STA_DEBUG_PRINTLN("[event] RX sys frame");
debug::printFrameID(header.id);
debug::printPayloadHex(payload, header.payloadLength);
// TODO Forward message to other threads
}
/**
* @brief Configure CAN filters.
*/
void setupCanSubscriptions()
{
// Make sure to receive all messages
CanFilter filter;
// Clear previous subscriptions
rtos::CanBusController->clearFilters();
// All bits except [0:1] of the SID must be zero for system messages
filter.obj = {0, 0};
filter.mask = {~STA_CAN_SID_SYS_BITS, 0};
filter.type = sta::CanFilterIdFormat::ANY;
filter.fifo = 0;
filterCallbacks[0] = receiveSysCallback;
rtos::CanBusController->configureFilter(0, filter, true);
// TODO Limit which data messages are received
// Bits [0:1] of the SID must be zero for data messages
filter.obj = {0, 0};
filter.mask = {STA_CAN_SID_SYS_BITS, 0};
filter.type = sta::CanFilterIdFormat::ANY;
filter.fifo = 1;
filterCallbacks[1] = receiveDataCallback;
rtos::CanBusController->configureFilter(1, filter, true);
}
/**
* @brief Process received CAN messages.
*/
void processMessages()
{
for (auto fifo : rtos::CanBusController->getPendingRxFifos())
void handleDataMessage(const sta::IsotpMessage & msg)
{
CanRxHeader header;
uint8_t payload[STA_RTOS_CAN_BUS_MAX_PAYLOAD_SIZE];
STA_ASSERT(msg.buffer != nullptr);
STA_ASSERT(msg.size != 0);
if (rtos::CanBusController->receiveFrame(fifo, &header, payload))
STA_DEBUG_PRINTLN("[event] RX data message");
debug::printFrameID(msg.frameID);
// TODO Forward message to other threads
// if (buffer[0] == DEMO_BMP_PACKET_ID)
// {
// BmpPacket packet;
// if (unpack(buffer + 1, size - 1, &packet))
// {
// STA_DEBUG_PRINTLN();
// STA_DEBUG_PRINTLN("# ############");
// STA_DEBUG_PRINTLN("# ## BMP380 ##");
// STA_DEBUG_PRINTLN("# ############");
// STA_DEBUG_PRINTLN("#");
//
// STA_DEBUG_PRINT("# temperature: ");
// STA_DEBUG_PRINT(packet.temperature);
// STA_DEBUG_PRINTLN(" *C");
// STA_DEBUG_PRINT("# pressure: ");
// STA_DEBUG_PRINT(packet.pressure);
// STA_DEBUG_PRINTLN(" Pa");
// STA_DEBUG_PRINT("# altitude: ");
// STA_DEBUG_PRINT(packet.altitude);
// STA_DEBUG_PRINTLN(" m");
// STA_DEBUG_PRINTLN();
// }
// else
// {
// STA_DEBUG_PRINTLN("[error] BMP unpack failed");
// }
// }
// else
{
// debug::displayFrameUART(frame);
STA_DEBUG_PRINT("ID: ");
STA_DEBUG_PRINTLN(msg.buffer[0], sta::IntegerBase::HEX);
STA_DEBUG_PRINT("size: ");
STA_DEBUG_PRINTLN(msg.size);
}
}
} // namespace dummy
// Forward frame to filter callback
if (fifo <= STA_RTOS_CAN_BUS_MAX_FILTER && filterCallbacks[header.filter])
namespace sta
{
class AlpakaCanBus
{
public:
using SysMsgHandler = void (*)(const CanRxHeader &, const uint8_t *);
using DataMsgHandler = void (*)(const IsotpMessage &);
static const uint8_t FIFO_SYS = 0;
static const uint8_t FIFO_DATA = 1;
public:
AlpakaCanBus(CanController * controller, TimeMsFn timeMs, SysMsgHandler sysMsgHandler, DataMsgHandler dataMsgHandler);
/**
* @brief Send system message.
*
* @param msg Message
*/
void send(const CanSysMsg & msg);
/**
* @brief Send data message.
*
* @param msg Message
*/
void send(const CanDataMsg & msg);
/**
* @brief Process transmissions.
*
* Call regularly to advance transmission.
*/
void processTx();
/**
* @brief Process received CAN messages.
*/
void processRx();
/**
* @brief Display ISOTP TX/RX statistics.
*/
void showStatistics();
private:
/**
* @brief Configure CAN filters.
*/
void setupSubscriptions();
/**
* @brief Handle received data message CAN frames.
*
* @param header CAN frame header
* @param payload Payload buffer
*/
void receiveDataFrame(const CanRxHeader & header, const uint8_t * payload);
private:
CanController * controller_;
IsotpTransmitter tx_;
IsotpReceiver rx_;
SysMsgHandler handleSysMsg_;
DataMsgHandler handleDataMsg_;
};
AlpakaCanBus::AlpakaCanBus(CanController * controller, TimeMsFn timeMs, SysMsgHandler sysMsgHandler, DataMsgHandler dataMsgHandler)
: controller_{controller}, tx_{controller, timeMs}, rx_{controller, timeMs}, handleSysMsg_{sysMsgHandler}, handleDataMsg_{dataMsgHandler}
{
STA_ASSERT(handleSysMsg_ != nullptr);
STA_ASSERT(handleDataMsg_ != nullptr);
setupSubscriptions();
}
void AlpakaCanBus::send(const CanSysMsg & msg)
{
CanTxHeader header;
header.id.format = static_cast<CanIdFormat>(msg.header.format);
header.id.sid = msg.header.sid & STA_CAN_SID_SYS_BITS;
header.id.eid = msg.header.eid;
header.payloadLength = msg.header.payloadLength;
controller_->sendFrame(header, msg.payload);
}
void AlpakaCanBus::send(const CanDataMsg & msg)
{
CanFrameId frameID;
frameID.format = static_cast<CanIdFormat>(msg.header.format);
frameID.sid = msg.header.sid & ~STA_CAN_SID_SYS_BITS;
frameID.eid = msg.header.eid;
// Start transmission via ISO-TP
tx_.send(frameID, msg.payload, msg.header.payloadLength);
}
inline void AlpakaCanBus::processTx()
{
tx_.process();
}
void AlpakaCanBus::processRx()
{
for (auto fifo : controller_->getPendingRxFifos())
{
CanRxHeader header;
uint8_t payload[STA_RTOS_CAN_BUS_MAX_PAYLOAD_SIZE];
if (controller_->receiveFrame(fifo, &header, payload))
{
filterCallbacks[header.filter](header, payload);
// debug::displayFrameUART(frame);
// Forward frame to callback
switch (fifo)
{
case FIFO_SYS:
handleSysMsg_(header, payload);
break;
case FIFO_DATA:
receiveDataFrame(header, payload);
break;
default:
STA_ASSERT(false);
}
}
}
}
}
extern "C"
{
/**
* @brief CAN driver thread entry function.
*/
void canBusTask(void *)
void AlpakaCanBus::showStatistics()
{
using namespace sta;
STA_DEBUG_PRINTLN();
STA_DEBUG_PRINTLN("# ######################");
STA_DEBUG_PRINTLN("# ## ISOTP statistics ##");
STA_DEBUG_PRINTLN("# ######################");
STA_DEBUG_PRINTLN("#");
// Initialize CAN controller
rtos::setupCanBus();
const auto & txStats = tx_.stats();
STA_DEBUG_PRINTLN("# Transmitter");
STA_DEBUG_PRINT("# messages: ");
STA_DEBUG_PRINTLN(txStats.messages);
STA_DEBUG_PRINT("# blocks: ");
STA_DEBUG_PRINTLN(txStats.blocks);
STA_DEBUG_PRINT("# frames: ");
STA_DEBUG_PRINTLN(txStats.frames);
STA_DEBUG_PRINT("# timeouts: ");
STA_DEBUG_PRINTLN(txStats.timeouts);
STA_DEBUG_PRINTLN("#");
// Configure filters for
setupCanSubscriptions();
const auto & rxStats = rx_.stats();
STA_DEBUG_PRINTLN("# Receiver");
STA_DEBUG_PRINT("# messages: ");
STA_DEBUG_PRINTLN(rxStats.messages);
STA_DEBUG_PRINT("# blocks: ");
STA_DEBUG_PRINTLN(rxStats.blocks);
STA_DEBUG_PRINT("# frames: ");
STA_DEBUG_PRINTLN(rxStats.frames);
STA_DEBUG_PRINT("# timeouts: ");
STA_DEBUG_PRINTLN(rxStats.timeouts);
STA_DEBUG_PRINT("# flow control errors: ");
STA_DEBUG_PRINTLN(rxStats.flowErrors);
STA_DEBUG_PRINT("# overflows: ");
STA_DEBUG_PRINTLN(rxStats.overflows);
STA_DEBUG_PRINTLN();
}
rtos::waitForStartupEvent();
void AlpakaCanBus::setupSubscriptions()
{
// Make sure to receive all messages
CanFilter filter;
while (true)
// Clear previous subscriptions
controller_->clearFilters();
// All bits except [0:1] of the SID must be zero for system messages
filter.obj = {0, 0};
filter.mask = {~STA_CAN_SID_SYS_BITS, 0};
filter.type = sta::CanFilterIdFormat::ANY;
filter.fifo = FIFO_SYS;
controller_->configureFilter(FIFO_SYS, filter, true);
// TODO Limit which data messages are received
// Bits [0:1] of the SID must be zero for data messages
filter.obj = {0, 0};
filter.mask = {STA_CAN_SID_SYS_BITS, 0};
filter.type = sta::CanFilterIdFormat::ANY;
filter.fifo = FIFO_DATA;
controller_->configureFilter(FIFO_DATA, filter, true);
}
void AlpakaCanBus::receiveDataFrame(const CanRxHeader & header, const uint8_t * payload)
{
// Write frame payload to DebugSerial
STA_DEBUG_PRINTLN("[event] RX data frame");
debug::printPayloadHex(payload, header.payloadLength);
// Process RX frame
auto handle = rx_.processFrame(header, payload);
if (handle != IsotpReceiver::INVALID_HANDLE)
{
uint32_t flags = osThreadFlagsWait(STA_RTOS_THREAD_FLAGS_VALID_BITS, osFlagsWaitAny, 50);
if (flags != static_cast<uint32_t>(osErrorTimeout))
// Get message if completed
IsotpMessage msg;
if (rx_.getMessage(handle, &msg))
{
STA_ASSERT_MSG((flags & osStatusReserved) == flags, "Unexpected error occurred in wait");
handleDataMsg_(msg);
}
if (flags & STA_RTOS_CAN_FLAG_SYS_QUEUED)
// Handle FC responses
rx_.processFC(handle);
}
// Process TX frame
tx_.processFrame(header, payload);
}
} // namespace sta
/**
* @brief CAN driver thread entry function.
*/
void canBusTask(void *)
{
using namespace sta;
STA_ASSERT_MSG(canBusController != nullptr, "System CAN bus not initialized");
// Setup ISO-TP transceiver
AlpakaCanBus canBus(canBusController, HAL_GetTick, dummy::handleSysMessage, dummy::handleDataMessage);
rtos::waitForStartupEvent();
while (true)
{
uint32_t flags = osThreadFlagsWait(STA_RTOS_THREAD_FLAGS_VALID_BITS, osFlagsWaitAny, 50);
if (flags != static_cast<uint32_t>(osErrorTimeout))
{
STA_ASSERT_MSG((flags & osStatusReserved) == flags, "Unexpected error occurred in wait");
if (flags & STA_RTOS_CAN_FLAG_SYS_QUEUED)
{
// Take messages from queue until empty
CanSysMsg msg;
while (rtos::getCanBusMsg(&msg, 0))
{
CanSysMsg msg;
CanTxHeader header;
// Take messages from queue until empty
while (rtos::getCanBusMsg(&msg, 0))
{
header.id.format = static_cast<CanIdFormat>(msg.header.format);
header.id.sid = msg.header.sid & STA_CAN_SID_SYS_BITS;
header.id.eid = msg.header.eid;
header.payloadLength = msg.header.payloadLength;
debug::printPayloadHex(msg.payload, header.payloadLength);
rtos::CanBusController->sendFrame(header, msg.payload);
}
}
if (flags & STA_RTOS_CAN_FLAG_DATA_QUEUED)
{
CanDataMsg msg;
CanFrameId frameID;
// Take messages from queue until empty
while (rtos::getCanBusMsg(&msg, 0))
{
frameID.format = static_cast<CanIdFormat>(msg.header.format);
frameID.sid = msg.header.sid & ~STA_CAN_SID_SYS_BITS;
frameID.eid = msg.header.eid;
// Transmit via ISO-TP
gCanTx.send(frameID, msg.payload, msg.header.payloadLength);
}
}
if (flags & STA_RTOS_CAN_FLAG_MSG_AVAIL)
{
STA_DEBUG_PRINTLN("[event] CAN INT");
processMessages();
}
if (flags & STA_RTOS_CAN_FLAG_SHOW_STATS)
{
debug::showStatistics();
canBus.send(msg);
}
}
// Process ISOTP transmissions
gCanTx.process();
if (flags & STA_RTOS_CAN_FLAG_DATA_QUEUED)
{
// Take messages from queue until empty
CanDataMsg msg;
while (rtos::getCanBusMsg(&msg, 0))
{
canBus.send(msg);
}
}
if (flags & STA_RTOS_CAN_FLAG_MSG_AVAIL)
{
STA_DEBUG_PRINTLN("[event] CAN INT");
canBus.processRx();
}
if (flags & STA_RTOS_CAN_FLAG_SHOW_STATS)
{
canBus.showStatistics();
}
}
// Process ISOTP transmissions
canBus.processTx();
}
}

61
src/system/events.cpp Normal file
View File

@ -0,0 +1,61 @@
#include <sta/rtos/system/events.hpp>
#include <sta/debug/assert.hpp>
#include <cmsis_os2.h>
#include <FreeRTOS.h>
namespace
{
// Static memory for system events
StaticEventGroup_t systemEventControlBlock;
// Event handle
osEventFlagsId_t systemEventsHandle = nullptr;
}
namespace sta
{
namespace rtos
{
void initSystemEvents()
{
// Create event using static allocation
const osEventFlagsAttr_t attributes = {
.name = "systemEvent",
.cb_mem = &systemEventControlBlock,
.cb_size = sizeof(systemEventControlBlock),
};
if (systemEventsHandle == nullptr)
{
systemEventsHandle = osEventFlagsNew(&attributes);
}
}
void signalSystemEvents(uint32_t flags)
{
STA_ASSERT_MSG(systemEventsHandle != nullptr, "System events not initialized");
osEventFlagsSet(systemEventsHandle, flags);
}
void waitForSystemEvents(uint32_t flags, uint32_t options, uint32_t timeout)
{
STA_ASSERT_MSG(systemEventsHandle != nullptr, "System events not initialized");
osEventFlagsWait(systemEventsHandle, flags, options | osFlagsNoClear, timeout);
}
void signalStartupEvent()
{
signalSystemEvents(STA_RTOS_SYSTEM_EVENTS_STARTUP);
}
void waitForStartupEvent()
{
waitForSystemEvents(STA_RTOS_SYSTEM_EVENTS_STARTUP, osFlagsWaitAll, osWaitForever);
}
} // namespace rtos
} // namespace sta

View File

@ -1,10 +1,13 @@
#include <sta/rtos/system/startup.hpp>
#ifdef STA_RTOS_STARTUP_ENABLE
#include <sta/rtos/c_api/startup.h>
#include <sta/debug/assert.hpp>
#include <sta/lang.hpp>
#include <sta/rtos/system/system_event.hpp>
#include <sta/rtos/system/can_bus.hpp>
#include <sta/rtos/system/events.hpp>
#include <sta/rtos/system/watchdog.hpp>
#include <sta/stm32/init.hpp>
#include <sta/devices/stm32/init.hpp>
#include <cmsis_os2.h>
@ -17,33 +20,46 @@ namespace sta
STA_WEAK
void startupExtras(void *)
{}
void initSystem()
{
#ifdef STA_RTOS_SYSTEM_EVENTS_ENABLE
initSystemEvents();
#endif // STA_RTOS_SYSTEM_EVENTS_ENABLE
#ifdef STA_RTOS_WATCHDOG_ENABLE
initWatchdog();
#endif // STA_RTOS_WATCHDOG_ENABLE
#ifdef STA_RTOS_CAN_BUS_ENABLE
initCanBus();
#endif // STA_RTOS_CAN_BUS_ENABLE
}
} // namespace rtos
} // namespace sta
// Declare with C linkage
extern "C"
void startALPAKA(void * arg)
{
void STA_RTOS_STARTUP_ENTRY_FUNCTION(void * arg)
STA_ASSERT_MSG(osKernelGetState() != osKernelInactive, "Cannot call startALPAKA() before osKernelInitialize()");
// Call further initialization code
sta::rtos::startupExtras(arg);
// Initialize HAL
sta::initHAL();
// Initialize RTOS system resources
sta::rtos::initSystem();
// Wake threads
sta::rtos::signalStartupEvent();
// Check if called from thread
if (osThreadGetId() != nullptr)
{
// Call further initialization code
sta::rtos::startupExtras(arg);
// Initialize HAL
sta::initHAL();
#ifdef STA_RTOS_WATCHDOG_ENABLE
// Start timers
sta::rtos::startWatchdogTimer();
#endif // STA_RTOS_WATCHDOG_ENABLE
// Wake threads
sta::rtos::signalStartupEvent();
// Terminate thread
// Terminate current thread
osThreadExit();
}
}
#endif // STA_RTOS_STARTUP_ENABLE

View File

@ -1,43 +0,0 @@
#include <sta/rtos/system/system_event.hpp>
#ifdef STA_RTOS_SYSTEM_EVENT_ENABLE
#include <cmsis_os2.h>
#define STA_RTOS_SYSTEM_EVENT_HANDLE STA_RTOS_MAKE_HANDLE_NAME(STA_RTOS_SYSTEM_EVENT_NAME)
// Access handle from freertos.c
extern osEventFlagsId_t STA_RTOS_SYSTEM_EVENT_HANDLE;
namespace sta
{
namespace rtos
{
void signalSystemEvents(uint32_t flags)
{
osEventFlagsSet(STA_RTOS_SYSTEM_EVENT_HANDLE, flags);
}
void waitForSystemEvents(uint32_t flags, uint32_t options, uint32_t timeout)
{
osEventFlagsWait(STA_RTOS_SYSTEM_EVENT_HANDLE, flags, options | osFlagsNoClear, timeout);
}
void signalStartupEvent()
{
signalSystemEvents(STA_SYSTEM_EVENT_STARTUP);
}
void waitForStartupEvent()
{
waitForSystemEvents(STA_SYSTEM_EVENT_STARTUP, osFlagsWaitAll, osWaitForever);
}
} // namespace rtos
} // namespace sta
#endif // STA_RTOS_SYSTEM_EVENT_ENABLE

100
src/system/watchdog.cpp Normal file
View File

@ -0,0 +1,100 @@
#include <sta/rtos/system/watchdog.hpp>
#ifdef STA_RTOS_WATCHDOG_ENABLE
#include <sta/debug/assert.hpp>
#include <sta/lang.hpp>
#include <sta/rtos/defs.hpp>
#include <sta/rtos/system/events.hpp>
#include <cmsis_os2.h>
#include <FreeRTOS.h>
namespace
{
StaticTask_t watchdogCB;
StaticTimer_t watchdogTimerCB;
osThreadId_t watchdogTaskHandle = nullptr;
osTimerId_t watchdogTimerHandle = nullptr;
// Static stack memory
uint32_t stackBuffer[256];
}
extern "C"
{
void watchdogTask(void * arg);
void watchdogTimerCallback(void *);
}
namespace sta
{
namespace rtos
{
void initWatchdog()
{
// Create thread using static allocation
const osThreadAttr_t taskAttributes = {
.name = "sysWatchdog",
.cb_mem = &watchdogCB,
.cb_size = sizeof(watchdogCB),
.stack_mem = &stackBuffer[0],
.stack_size = sizeof(stackBuffer),
.priority = (osPriority_t) osPriorityLow,
};
watchdogTaskHandle = osThreadNew(watchdogTask, NULL, &taskAttributes);
STA_ASSERT_MSG(watchdogTaskHandle != nullptr, "System watchdog task initialization failed");
// Create timer using static allocation
const osTimerAttr_t timerAttributes = {
.name = "sysWatchdogTimer",
.attr_bits = 0, // Reserved, must be set to 0
.cb_mem = &watchdogTimerCB,
.cb_size = sizeof(watchdogTimerCB)
};
watchdogTimerHandle = osTimerNew(watchdogTimerCallback, osTimerPeriodic, nullptr, &timerAttributes);
STA_ASSERT_MSG(watchdogTimerHandle != nullptr, "System watchdog timer initialization failed");
osTimerStart(watchdogTimerHandle, STA_RTOS_SYSTEM_WATCHDOG_TIMER_PERIOD);
}
void notifyWatchdog(uint32_t flags)
{
STA_ASSERT_MSG(watchdogTaskHandle != nullptr, "System watchdog not initialized");
osThreadFlagsSet(watchdogTaskHandle, flags);
}
} // namespace rtos
} // namespace sta
void watchdogTask(void * arg)
{
sta::rtos::waitForStartupEvent();
while (true)
{
// Wait for any flag to be set
uint32_t flags = osThreadFlagsWait(STA_RTOS_THREAD_FLAGS_VALID_BITS, osFlagsWaitAny, osWaitForever);
// Call event handler
sta::rtos::watchdogEventHandler(arg, flags);
}
}
void watchdogTimerCallback(void *)
{
// Notify watchdog task to send heartbeat message
// Required because blocking in a timer callback is not allowed
osThreadFlagsSet(watchdogTaskHandle, STA_WATCHDOG_FLAG_HEARTBEAT);
}
#endif // STA_RTOS_WATCHDOG_ENABLE

View File

@ -1,18 +0,0 @@
#ifndef STA_RTOS_SYSTEM_WATCHDOG_HANDLES_HPP
#define STA_RTOS_SYSTEM_WATCHDOG_HANDLES_HPP
#include <sta/rtos/system/watchdog.hpp>
#include <cmsis_os2.h>
#define WATCHDOG_TASK_HANDLE STA_RTOS_MAKE_HANDLE_NAME(STA_RTOS_WATCHDOG_TASK_NAME)
#define WATCHDOG_TIMER_HANDLE STA_RTOS_MAKE_HANDLE_NAME(STA_RTOS_WATCHDOG_TIMER_NAME)
// Access handles from freertos.c
extern osThreadId_t WATCHDOG_TASK_HANDLE;
extern osTimerId_t WATCHDOG_TIMER_HANDLE;
#endif // STA_RTOS_SYSTEM_WATCHDOG_HANDLES_HPP

View File

@ -1,32 +0,0 @@
#include <sta/rtos/system/watchdog.hpp>
#ifdef STA_RTOS_WATCHDOG_ENABLE
#include "handles.hpp"
namespace sta
{
namespace rtos
{
void startWatchdogTimer()
{
osTimerStart(WATCHDOG_TIMER_HANDLE, STA_RTOS_WATCHDOG_TIMER_PERIOD);
}
} // namespace rtos
} // namespace sta
// Declare with C linkage
extern "C"
{
void STA_RTOS_WATCHDOG_TIMER_CALLBACK(void *)
{
// Notify watchdog task to send heartbeat message
// Required because blocking in a timer callback is not allowed
osThreadFlagsSet(WATCHDOG_TASK_HANDLE, STA_WATCHDOG_FLAG_HEARTBEAT);
}
}
#endif // STA_RTOS_WATCHDOG_ENABLE

View File

@ -1,43 +0,0 @@
#include <sta/rtos/system/watchdog.hpp>
#ifdef STA_RTOS_WATCHDOG_ENABLE
#include <sta/rtos/defs.hpp>
#include <sta/rtos/system/system_event.hpp>
#include "handles.hpp"
namespace sta
{
namespace rtos
{
void notifyWatchdog(uint32_t flags)
{
osThreadFlagsSet(WATCHDOG_TASK_HANDLE, flags);
}
} // namespace rtos
} // namespace sta
// Declare with C linkage
extern "C"
{
void STA_RTOS_WATCHDOG_ENTRY_FUNCTION(void * arg)
{
sta::rtos::waitForStartupEvent();
while (true)
{
// Wait for any flag to be set
uint32_t flags = osThreadFlagsWait(STA_RTOS_THREAD_FLAGS_VALID_BITS, osFlagsWaitAny, osWaitForever);
// Call event handler
sta::rtos::watchdogEventHandler(arg, flags);
}
}
}
#endif // STA_RTOS_WATCHDOG_ENABLE

28
src/timer.cpp Normal file
View File

@ -0,0 +1,28 @@
#include <sta/rtos/timer.hpp>
#include <sta/debug/debug.hpp>
namespace sta {
RtosTimer::RtosTimer(void (*callback)(void *arg), void *arg) {
timer_attr_.name = "Timer";
timer_attr_.attr_bits = osTimerOnce;
timer_attr_.cb_size = sizeof(osTimerAttr_t);
timer_id_ = osTimerNew(callback, osTimerOnce, arg, &timer_attr_);
}
RtosTimer::~RtosTimer() {
osTimerDelete(timer_id_);
}
void RtosTimer::start(uint32_t millis) {
osStatus_t status = osTimerStart(timer_id_, millis);
if (status != osOK) STA_DEBUG_PRINTLN("Timer start failed");
}
void RtosTimer::stop() {
osStatus_t status = osTimerStop(timer_id_);
if (status != osOK) STA_DEBUG_PRINTLN("Timer stop failed");
}
} // namespace sta