Merge pull request 'Feature: CAN via IRQ' (#28) from can into main

Reviewed-on: https://git.intern.spaceteamaachen.de/ALPAKA/TACOS/pulls/28
Reviewed-by: dario <dario@noreply.git.intern.spaceteamaachen.de>
This commit is contained in:
dario 2024-05-06 12:56:27 +00:00
commit 6933decf26
10 changed files with 426 additions and 11 deletions

3
.gitignore vendored
View File

@ -13,3 +13,6 @@ Release/
docs/html docs/html
docs/latex docs/latex
# Config
include/sta/config.hpp

View File

@ -34,8 +34,10 @@ In order to use TACOS, you need to provide a configuration file in the path `sta
#ifndef INC_STA_CONFIG_HPP_ #ifndef INC_STA_CONFIG_HPP_
#define INC_STA_CONFIG_HPP_ #define INC_STA_CONFIG_HPP_
// Use the STM32F411 microprocessor. // Using a board with an ASEAG module present.
#include <sta/devices/stm32/mcu/STM32F411xE.hpp> #define STA_STM32_ASEAG
// Use the STM32F407 microprocessor.
#include <sta/devices/stm32/mcu/STM32F407xx.hpp>
// Doesn't really do too much right now. Has to be added for successful compilation. // Doesn't really do too much right now. Has to be added for successful compilation.
#define STA_PRINTF_USE_STDLIB #define STA_PRINTF_USE_STDLIB
@ -44,10 +46,14 @@ In order to use TACOS, you need to provide a configuration file in the path `sta
#define STA_ASSERT_FORCE #define STA_ASSERT_FORCE
#define STA_DEBUGGING_ENABLED #define STA_DEBUGGING_ENABLED
// Settings for the rtos-utils // Enable Features
#define STA_RTOS_SYSTEM_EVENTS_ENABLE #define STA_RTOS_SYSTEM_EVENTS_ENABLE
// #define STA_RTOS_SYSTEM_WATCHDOG_ENABLE // #define STA_RTOS_SYSTEM_WATCHDOG_ENABLE
// #define STA_RTOS_WATCHDOG_ENABLE // #define STA_RTOS_WATCHDOG_ENABLE
#define STA_CAN_BUS_ENABLE
// Statemachine settings.
#define STA_TACOS_NUM_STATES 3
// Uses the default configuration for TACOS. // Uses the default configuration for TACOS.
#include<sta/tacos/configs/default.hpp> #include<sta/tacos/configs/default.hpp>
@ -59,4 +65,18 @@ PS: For not officially supported chips use this as the include:
#include <sta/devices/stm32/mcu/common.hpp> #include <sta/devices/stm32/mcu/common.hpp>
#define STA_MCU_LITTLE_ENDIAN #define STA_MCU_LITTLE_ENDIAN
#define STA_PLATFORM_STM32 #define STA_PLATFORM_STM32
``` ```
## Setting up the CAN Bus
To enable the CAN Bus two things need to be done:
1. Enable CAN in the IOC with the RX0 and RX1 Interrupts enabled.
2. Add the following code to the `sta/config.hpp` file:
```
#define STA_CAN_BUS_ENABLE
```
PS: For not officially supported chips add this:
```
#define STA_STM32_CAN_HANDLE {YOUR_HCAN_HANDLE}
```
After this messages will automatically be forwarded to the task with it's ID. To send messages use the interface defined in `tacos.hpp`.

View File

@ -11,6 +11,7 @@
#include <sta/tacos/thread.hpp> #include <sta/tacos/thread.hpp>
#include <sta/tacos/manager.hpp> #include <sta/tacos/manager.hpp>
#include <sta/tacos/statemachine.hpp> #include <sta/tacos/statemachine.hpp>
#include <sta/tacos/can_bus.hpp>
#include <initializer_list> #include <initializer_list>
#include <type_traits> #include <type_traits>
@ -76,6 +77,32 @@ namespace sta
return thread_ptr; return thread_ptr;
} }
/**
* @brief Queue a message to be sent over the CAN bus.
*
* @param msg The message to be sent.
* @param timeout The time to wait for the message to be sent.
*
* @return bool True if the message was sent successfully.
*
* @ingroup tacos_api
*/
bool queueCanBusMsg(CanSysMsg & msg, uint32_t timeout);
/**
* @brief Publish a state transition message to the CAN bus.
*
* @param from The state we want to transition from.
* @param to The state we want to transition to.
* @param lockout An optional timer blocking state transition for a given time.
*
* @return bool True if the message was sent successfully.
*
* @ingroup tacos_api
*/
bool publishState(uint32_t from, uint32_t to, uint32_t lockout = 0);
} // namespace tacos } // namespace tacos
} }

View File

@ -0,0 +1,121 @@
#ifndef INCLUDE_TACOS_CAN_BUS_HPP_
#define INCLUDE_TACOS_CAN_BUS_HPP_
#include <sta/config.hpp>
#ifdef STA_CAN_BUS_ENABLE
#include <sta/rtos/queue.hpp>
#include <sta/rtos/system/can_bus.hpp>
#include <sta/debug/debug.hpp>
#include <sta/lang.hpp>
#include <sta/tacos/thread.hpp>
#include <sta/tacos/statemachine.hpp>
/**
* @defgroup tacos_can_bus Can Bus Task
* @ingroup tacos
* @brief Can Bus task for TACOS.
*/
namespace sta
{
namespace tacos
{
/**
* @brief Can Bus implementation for Tacos.
*
* @ingroup tacos_can_bus
*/
class CanBus : public TacosThread
{
public:
CanBus();
/**
* @brief Getter function for the singleton instance.
*/
static CanBus* instance()
{
static CGuard g;
if (!_instance)
{
// Create the can bus singleton instance.
CanBus::_instance = new CanBus();
}
return _instance;
}
/**
* @brief Place system message in CAN driver TX queue.
*
* @param msg Message to transmit
* @param timeout Timeout for placing message (0 = no wait, osWaitForever = blocking)
* @return True if message was queued successfully
*/
bool queueCanBusMsg(const CanSysMsg & msg, uint32_t timeout);
/**
* @brief Retrieve system message from CAN driver TX queue.
*
* @param[out] msg Destination for retrieved message
* @param timeout Timeout for retrieving message (0 = no wait, osWaitForever = blocking)
* @return True if message was retrieved successfully
*/
bool getCanBusMsg(CanSysMsg * msg, uint32_t timeout);
/**
* @brief Buffers incoming message and sets event to notify the system.
*
* @param fifo FIFO number of the received message.
*/
void canCallback(uint32_t fifo);
void init() override;
void func() override;
private:
static CanBus * _instance;
class CGuard
{
public:
~CGuard()
{
if( NULL != CanBus::_instance )
{
delete CanBus::_instance;
CanBus::_instance = NULL;
}
}
};
sta::STM32CanController * canBusController_;
CanSysMsg* canBusSysQueueBuffer_[STA_RTOS_CAN_BUS_QUEUE_LENGTH];
uint8_t bufferIndex;
RtosQueue<CanSysMsg> canBusSysQueue_;
AlpakaCanBus canBus_;
static RtosEvent messageEvent;
};
/**
* @brief Callback function for handling received messages. Intended for state transitions.
*/
STA_WEAK
void handleSysMessage(CanMsgHeader & header, uint8_t * payload);
} /* namespace tacos */
} /* namespace sta */
#endif /* STA_CAN_BUS_ENABLE */
#endif /* INCLUDE_TACOS_CAN_BUS_HPP_ */

View File

@ -1,15 +1,21 @@
#ifndef STA_TACOS_CONFIGS_DEFAULT_HPP #ifndef STA_TACOS_CONFIGS_DEFAULT_HPP
#define STA_TACOS_CONFIGS_DEFAULT_HPP #define STA_TACOS_CONFIGS_DEFAULT_HPP
// Enable the watchdog provided by TACOS.
#define STA_TACOS_WATCHDOG_ENABLED
// Generally, we assume the TACOS threads to have the highest priorties. // Generally, we assume the TACOS threads to have the highest priorties.
#define STA_TACOS_MANAGER_PRIORITY osPriorityHigh #define STA_TACOS_MANAGER_PRIORITY osPriorityHigh
#define STA_TACOS_STATEMACHINE_PRIORITY osPriorityHigh #define STA_TACOS_STATEMACHINE_PRIORITY osPriorityHigh
#define STA_TACOS_WATCHDOG_PRIORITY osPriorityHigh #define STA_TACOS_WATCHDOG_PRIORITY osPriorityHigh
#define STA_TACOS_CAN_BUS_PRIORITY osPriorityHigh
// Per default, we assume state 0 to be the initial state. // Per default, we assume state 0 to be the initial state.
#define STA_TACOS_INITIAL_STATE 0 #define STA_TACOS_INITIAL_STATE 0
// Can Bus settings
#define STA_RTOS_CAN_BUS_QUEUE_LENGTH 5
#define STA_RTOS_CAN_BUS_MAX_FILTER 14
#define STA_RTOS_CAN_BUS_MAX_PAYLOAD_SIZE 8
// State transition message define with highest CAN priority
#define STA_TACOS_CAN_BUS_SYS_MSG_ID 0x0
#endif // STA_TACOS_CONFIGS_DEFAULT_HPP #endif // STA_TACOS_CONFIGS_DEFAULT_HPP

View File

@ -12,6 +12,8 @@
#include <sta/config.hpp> #include <sta/config.hpp>
#include <sta/rtos/thread.hpp> #include <sta/rtos/thread.hpp>
#include <sta/rtos/queue.hpp>
#include <sta/rtos/system/can_bus.hpp>
/** /**
* @defgroup tacos_thread TACOS Thread * @defgroup tacos_thread TACOS Thread
@ -126,6 +128,24 @@ namespace sta
*/ */
void sleep(uint32_t ticks); void sleep(uint32_t ticks);
#ifdef STA_CAN_BUS_ENABLE
/**
* @brief Set the ID of the CAN bus this thread is associated with.
*
* @param canID
*/
void setCanID(uint32_t canID);
/**
* @brief Get the ID of the CAN bus this thread is associated with.
*
* @return can ID
*/
uint32_t getCanID();
sta::RtosQueue<CanSysMsg> CAN_queue_;
#endif // STA_CAN_BUS_ENABLE
#ifdef STA_TACOS_WATCHDOG_ENABLED #ifdef STA_TACOS_WATCHDOG_ENABLED
/** /**
* @brief This macro wraps a given statement into waiting() and heartbeat() to make the code more readable. * @brief This macro wraps a given statement into waiting() and heartbeat() to make the code more readable.
@ -191,6 +211,9 @@ namespace sta
bool running_; bool running_;
#ifdef STA_TACOS_WATCHDOG_ENABLED #ifdef STA_TACOS_WATCHDOG_ENABLED
ThreadStatus status_; ThreadStatus status_;
#endif // STA_TACOS_WATCHDOG_ENABLED
#ifdef STA_CAN_BUS_ENABLE
uint32_t canID_;
#endif // STA_TACOS_WATCHDOG_ENABLED #endif // STA_TACOS_WATCHDOG_ENABLED
bool terminate_; bool terminate_;
}; };

164
src/can_bus.cpp Normal file
View File

@ -0,0 +1,164 @@
#include <sta/config.hpp>
#ifdef STA_CAN_BUS_ENABLE
#include <sta/tacos/can_bus.hpp>
#include <sta/debug/assert.hpp>
#include <sta/tacos/manager.hpp>
#include <sta/tacos.hpp>
extern CAN_HandleTypeDef STA_STM32_CAN_HANDLE;
namespace sta
{
namespace tacos
{
CanBus::CanBus()
: TacosThread{"Can Bus", STA_TACOS_CAN_BUS_PRIORITY},
canBusController_(new STM32CanController(&STA_STM32_CAN_HANDLE)),
canBusSysQueue_(STA_RTOS_CAN_BUS_QUEUE_LENGTH),
canBus_{canBusController_, HAL_GetTick}
{
bufferIndex = 0;
for(int i = 0; i < STA_RTOS_CAN_BUS_QUEUE_LENGTH; i++){
canBusSysQueueBuffer_[i] = nullptr;
}
}
void CanBus::init()
{
canBusController_->start();
if (HAL_CAN_ActivateNotification(&STA_STM32_CAN_HANDLE, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK ||
HAL_CAN_ActivateNotification(&STA_STM32_CAN_HANDLE, CAN_IT_RX_FIFO1_MSG_PENDING))
{
Error_Handler();
}
}
void CanBus::func()
{
messageEvent.clear(STA_RTOS_CAN_ANY);
uint32_t flags = messageEvent.wait(STA_RTOS_CAN_ANY, osWaitForever);
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 (CanBus::_instance->getCanBusMsg(&msg, 0))
{
canBus_.send(msg);
}
}
if (flags & STA_RTOS_CAN_FLAG_MSG_AVAIL)
{
CanSysMsg sysMsg;
// Iterate through buffer and set back to nullptr after use
for(int i = 0; i < STA_RTOS_CAN_BUS_QUEUE_LENGTH; i++){
if(canBusSysQueueBuffer_[i] != nullptr){
sysMsg = *canBusSysQueueBuffer_[i];
canBusSysQueueBuffer_[i] = nullptr;
// Append to the correct thread's queue
for (std::shared_ptr<TacosThread> thread : Manager::instance()->getActiveThreads()){
if (thread->getCanID() == sysMsg.header.sid){
thread->CAN_queue_.put(sysMsg);
break;
}
}
if(sysMsg.header.sid == STA_TACOS_CAN_BUS_SYS_MSG_ID){
// Handle system message
handleSysMessage(sysMsg.header, sysMsg.payload);
}
}
}
}
}
}
bool CanBus::queueCanBusMsg(const CanSysMsg& msg, uint32_t timeout)
{
// This technically should check if we are using a system message, but we just pretending that everything is one of those rn
//STA_ASSERT((msg.header.sid & ~STA_CAN_SID_SYS_BITS) == 0);
if (canBusSysQueue_.put(msg, timeout))
{
// Signal task
messageEvent.set(STA_RTOS_CAN_FLAG_SYS_QUEUED);
messageEvent.clear(STA_RTOS_CAN_ANY);
return true;
}
else
{
return false;
}
}
void CanBus::canCallback(uint32_t fifo){
if(messageEvent.get() != STA_RTOS_CAN_FLAG_MSG_AVAIL){
// get here does not work since FreeRTOS is a buggy mess
messageEvent.set(STA_RTOS_CAN_FLAG_MSG_AVAIL);
CanRxHeader rxHeader; //CAN Bus Receive Header
uint8_t canRX[8] = {0,0,0,0,0,0,0,0}; //CAN Bus Receive Buffer
bool received_ = canBusController_->receiveFrame(fifo, &rxHeader, canRX);
if(received_){
CanSysMsg sysMsg;
sysMsg.header.sid = rxHeader.id.sid;
sysMsg.header.payloadLength = rxHeader.payloadLength;
for(int i = 0; i < rxHeader.payloadLength; i++){
sysMsg.payload[i] = canRX[i];
}
canBusSysQueueBuffer_[bufferIndex] = &sysMsg;
bufferIndex++;
if (bufferIndex >= STA_RTOS_CAN_BUS_QUEUE_LENGTH) bufferIndex = 0;
}
}
}
bool CanBus::getCanBusMsg(CanSysMsg * msg, uint32_t timeout)
{
return (canBusSysQueue_.get(msg, timeout));
}
CanBus* CanBus::_instance = nullptr;
RtosEvent CanBus::messageEvent;
} /* namespace tacos */
} /* namespace sta */
namespace sta {
void CanBus_RxPendingCallback(uint32_t fifo){
sta::tacos::CanBus::instance()->canCallback(fifo);
}
namespace tacos
{
void handleSysMessage(CanMsgHeader & header, uint8_t * payload)
{
// This is a weak function that can be overridden by the user,
// if they want to handle system messages in a different way, i.e. ignore them
STA_ASSERT(header.payloadLength == 2);
// First byte of payload is the origin state, second byte is the destination state
tacos::setState(payload[0], payload[1]);
}
} // namespace tacos
} // namespace sta
#endif // STA_CAN_BUS_ENABLE

View File

@ -29,7 +29,7 @@
#include <sta/tacos/manager.hpp> #include <sta/tacos/manager.hpp>
#include <sta/tacos/statemachine.hpp> #include <sta/tacos/statemachine.hpp>
#include <sta/tacos/watchdog.hpp> #include <sta/tacos/watchdog.hpp>
#include <sta/tacos/can_bus.hpp>
// The UART mutex defined in freertos.c // The UART mutex defined in freertos.c
extern osMutexId_t uartMutexHandle; extern osMutexId_t uartMutexHandle;
@ -39,8 +39,6 @@ extern osMutexId_t uartMutexHandle;
* @ingroup tacos * @ingroup tacos
* @brief Functions that are called during startup. * @brief Functions that are called during startup.
*/ */
namespace sta namespace sta
{ {
#ifdef STA_DEBUGGING_ENABLED #ifdef STA_DEBUGGING_ENABLED
@ -125,6 +123,25 @@ namespace sta
Watchdog::instance()->start(); Watchdog::instance()->start();
} }
#endif // STA_TACOS_WATCHDOG_ENABLED #endif // STA_TACOS_WATCHDOG_ENABLED
#ifdef STA_CAN_BUS_ENABLE
/**
* @brief Function that is called before the Can Bus task is started. Override it to adjust
* the Can bus to your specifications.
*
* @ingroup tacos_startup
*/
STA_WEAK
void onCanBusInit()
{}
void initCanBus()
{
onCanBusInit();
CanBus::instance()->start();
}
#endif //STA_CAN_BUS_ENABLE
} // namespace tacos } // namespace tacos
@ -144,6 +161,10 @@ namespace sta
#ifdef STA_TACOS_WATCHDOG_ENABLED #ifdef STA_TACOS_WATCHDOG_ENABLED
tacos::initWatchdog(); tacos::initWatchdog();
#endif // STA_TACOS_WATCHDOG_ENABLED #endif // STA_TACOS_WATCHDOG_ENABLED
#ifdef STA_CAN_BUS_ENABLE
tacos::initCanBus();
#endif // STA_CAN_BUS_ENABLE
} }
} // namespace rtos } // namespace rtos
} // namespace sta } // namespace sta

View File

@ -7,7 +7,6 @@
#include <sta/tacos.hpp> #include <sta/tacos.hpp>
namespace sta namespace sta
{ {
namespace tacos namespace tacos
@ -26,6 +25,23 @@ namespace sta
{ {
Statemachine::instance()->requestTimedStateTransition(from, to, millis, lockout); Statemachine::instance()->requestTimedStateTransition(from, to, millis, lockout);
} }
bool queueCanBusMsg(CanSysMsg & msg, uint32_t timeout){
return CanBus::instance()->queueCanBusMsg(msg, timeout);
}
bool publishState(uint32_t from, uint32_t to, uint32_t lockout /* = 0 */){
CanSysMsg msg;
msg.header.sid = STA_TACOS_CAN_BUS_SYS_MSG_ID;
msg.header.payloadLength = 2;
msg.payload[0] = from;
msg.payload[1] = to;
msg.header.eid = 0;
msg.header.format = 0;
return CanBus::instance()->queueCanBusMsg(msg, lockout);
}
} // namespace tacos } // namespace tacos
} // namespace sta } // namespace sta

View File

@ -27,6 +27,10 @@ namespace sta
#ifdef STA_TACOS_WATCHDOG_ENABLED #ifdef STA_TACOS_WATCHDOG_ENABLED
status_{ThreadStatus::STOPPED}, status_{ThreadStatus::STOPPED},
#endif // STA_TACOS_WATCHDOG_ENABLED #endif // STA_TACOS_WATCHDOG_ENABLED
#ifdef STA_CAN_BUS_ENABLE
CAN_queue_{STA_RTOS_CAN_BUS_QUEUE_LENGTH},
canID_{0},
#endif // STA_CAN_BUS_ENABLE
terminate_{false} terminate_{false}
{ {
STA_ASSERT(stack_size >= 0); STA_ASSERT(stack_size >= 0);
@ -165,6 +169,16 @@ namespace sta
TacosThread::~TacosThread(){} TacosThread::~TacosThread(){}
#ifdef STA_CAN_BUS_ENABLE
void TacosThread::setCanID(uint32_t canID){
canID_ = canID;
}
uint32_t TacosThread::getCanID(){
return canID_;
}
#endif // STA_CAN_BUS_ENABLE
} // namespace tacos } // namespace tacos
} // namespace sta } // namespace sta