Skip to content

Commit

Permalink
Thread Safe CAN for all platforms + STM CAN Interrupt fix (#341)
Browse files Browse the repository at this point in the history
* Add extended frame support to CanMessage

* stm32 can driver, remove mutex on bus for now

* Merge to hpp

* Convert shared and stm mcal CMake libaries to Interface so they link properly

* Move Linux to Interface library, fix headers & CMake bindings

* atomic buffer

* Update Nucleo Docs for Demo/CAN and CANGEn docs

* remove unused imports

* Remove unused imports

* Abstract out AtomicBuffer and use in CAN bus.

* Rename Register function

* Add forware declare comment

* Inclusion guard

* feat: Move `pack` and `unpack`.

Moved out of templates / generated code into `shared/comms/can/msg.hpp`. Prevents redefinition if multiple `_messages.hpp` files exist (eg. Veh and Pt CAN)

* fix: Fix GetTimestamp calculation.

We should not multiply by `HAL_GetTickFreq()`. The `HAL_GetTick()` method "Provides a tick value in millisecond." according to the HAL guide UM 1905. Also, multiplying by freq would give the wrong units since its 1/second
  • Loading branch information
BlakeFreer authored Dec 11, 2024
1 parent cdc1af3 commit c10c596
Show file tree
Hide file tree
Showing 36 changed files with 396 additions and 320 deletions.
9 changes: 4 additions & 5 deletions firmware/mcal/cli/periph/can.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <chrono>
#include <cstring>
#include <iomanip>
#include <format>
#include <iostream>

#include "shared/comms/can/msg.hpp"
Expand All @@ -26,8 +24,9 @@ class CanBase : public shared::periph::CanBase {
std::cout << "can interface: " << iface_ << std::endl;
}

void Send(const shared::can::RawMessage& msg) {
std::cout << std::format("{} {}", iface_, msg) << std::endl;
void Send(const shared::can::RawMessage& msg) override {
std::cout << std::format("CanBase {}: Sending\n| {}", iface_, msg)
<< std::endl;
}

private:
Expand Down
4 changes: 2 additions & 2 deletions firmware/mcal/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
add_library(mcal-linux STATIC)
add_library(mcal-linux INTERFACE)

target_include_directories(mcal-linux PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../..)
target_include_directories(mcal-linux INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/../..)

add_subdirectory(periph)
target_link_libraries(mcal-linux INTERFACE shared)
4 changes: 2 additions & 2 deletions firmware/mcal/linux/periph/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
target_sources(mcal-linux
PRIVATE
INTERFACE
adc.cc
can.cc
digital_input.cc
digital_output.cc
)

add_subdirectory(vcan)
target_link_libraries(mcal-linux PUBLIC vcan pthread)
target_link_libraries(mcal-linux INTERFACE vcan pthread)
2 changes: 1 addition & 1 deletion firmware/mcal/linux/periph/adc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include <format>
#include <iostream>

#include "adc.h"
#include "adc.hpp"

namespace mcal::lnx::periph {

Expand Down
12 changes: 6 additions & 6 deletions firmware/mcal/linux/periph/can.cc
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
#include <linux/can.h>
#include <sys/types.h>

#include <chrono>
#include <cstring>
#include <format>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <thread>

#include "can.h"
#include "can.hpp"
#include "shared/comms/can/msg.hpp"
#include "vcan/vcan.h"
#include "vcan/vcan.hpp"

static can_frame to_can_frame(const shared::can::RawMessage& msg) {
struct can_frame frame{
Expand Down Expand Up @@ -53,8 +50,11 @@ void CanBase::StartReading() {
exit(1);
}

shared::can::RawMessage raw_msg(frame.can_id, frame.can_dlc,
shared::can::RawMessage raw_msg(frame.can_id, true, frame.can_dlc,
frame.data);
std::cout << std::format("CanBase {}: Received\n| {}",
socket_.GetIface(), raw_msg)
<< std::endl;

AddToBus(raw_msg);
}
Expand Down
2 changes: 1 addition & 1 deletion firmware/mcal/linux/periph/can.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#include "mcal/linux/periph/vcan/vcan.hpp"
#include "shared/periph/can.hpp"
#include "vcan/vcan.h"
#include "vcan/vcan.hpp"

namespace mcal::lnx::periph {

Expand Down
2 changes: 1 addition & 1 deletion firmware/mcal/linux/periph/digital_input.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include <iostream>
#include <string>

#include "digital_input.h"
#include "digital_input.hpp"

namespace mcal::lnx::periph {

Expand Down
2 changes: 1 addition & 1 deletion firmware/mcal/linux/periph/digital_output.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include <iostream>
#include <string>

#include "digital_output.h"
#include "digital_output.hpp"

namespace mcal::lnx::periph {

Expand Down
2 changes: 1 addition & 1 deletion firmware/mcal/linux/periph/vcan/vcan.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#include <iostream>
#include <string>

#include "vcan.h"
#include "vcan.hpp"

namespace mcal::lnx::periph::vcan {

Expand Down
2 changes: 1 addition & 1 deletion firmware/mcal/stm32f767/periph/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
# Empty file - implicitly adds this folder as an include directory
target_sources(mcal-stm32f767 INTERFACE can.cc)
141 changes: 141 additions & 0 deletions firmware/mcal/stm32f767/periph/can.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#include "stm32f7xx_hal.h"
#ifdef HAL_CAN_MODULE_ENABLED

#include <cstdint>

#include "can.hpp"

// FIFO0 is the "high priority" queue on stm32f7. We do not use FIFO1
constexpr uint32_t kCanFifo = CAN_RX_FIFO0;
constexpr uint32_t kCanInterrupt = CAN_IT_RX_FIFO0_MSG_PENDING;

namespace { // InterruptHandler is private to this file

/// Prior to this class, we spent several hours debugging an issue where the
/// interrupt was activated but the handler was not being called, causing the
/// program to get repeatedly executed the interupt callback and stall the rest
/// of the program.
/// This class ensures that interrupts activated and handled together.
class InterruptHandler {
using CanBase = mcal::stm32f767::periph::CanBase;

public:
void RegisterCanBase(CAN_HandleTypeDef* hcan, CanBase* can_base) {
int index = HandleToIndex(hcan);
if (index == -1) return;

can_bases[index] = can_base;
HAL_CAN_ActivateNotification(hcan, kCanInterrupt);
}

void Handle(CAN_HandleTypeDef* hcan) {
int index = HandleToIndex(hcan);
if (index == -1) return;

auto can_base = can_bases[index];
if (can_base != nullptr) {
can_bases[index]->Receive();
}
}

private:
CanBase* can_bases[3] = {nullptr, nullptr, nullptr};

int HandleToIndex(CAN_HandleTypeDef* hcan) {
// CANx are not contiguous in memory, so we can't use a simple array
if (hcan->Instance == CAN1) {
return 0;
} else if (hcan->Instance == CAN2) {
return 1;
} else if (hcan->Instance == CAN3) {
return 2;
} else {
return -1;
}
}
};
} // namespace

static InterruptHandler interrupt_handler;

extern "C" {
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef* hcan) {
// Overrides the "weak" callback defined by CubeMX
interrupt_handler.Handle(hcan);
}
}

namespace mcal::stm32f767::periph {

CanBase::CanBase(CAN_HandleTypeDef* hcan) : hcan_{hcan} {}

void CanBase::Setup() {
ConfigFilters();
interrupt_handler.RegisterCanBase(hcan_, this);
HAL_CAN_Start(hcan_);
}

void CanBase::Send(const shared::can::RawMessage& msg) {
uint32_t tx_mailboxes_free_level = HAL_CAN_GetTxMailboxesFreeLevel(hcan_);
if (tx_mailboxes_free_level < 1) {
dropped_tx_frames_ += 1;
return;
}

CAN_TxHeaderTypeDef stm_tx_header;
stm_tx_header.RTR = CAN_RTR_DATA; // we only support data frames currently
stm_tx_header.DLC = msg.data_length;

if (msg.is_extended_frame) {
stm_tx_header.IDE = CAN_ID_EXT;
stm_tx_header.ExtId = msg.id;
} else {
stm_tx_header.IDE = CAN_ID_STD;
stm_tx_header.StdId = msg.id;
}

HAL_CAN_AddTxMessage(hcan_, &stm_tx_header, msg.data, &tx_mailbox_addr_);
}

void CanBase::Receive() {
shared::can::RawMessage rx_msg;

CAN_RxHeaderTypeDef stm_rx_header;
HAL_CAN_GetRxMessage(hcan_, kCanFifo, &stm_rx_header, rx_msg.data);

rx_msg.is_extended_frame = stm_rx_header.IDE == CAN_ID_EXT;
rx_msg.data_length = static_cast<uint8_t>(stm_rx_header.DLC);
if (rx_msg.is_extended_frame) {
rx_msg.id = stm_rx_header.ExtId;
} else {
rx_msg.id = stm_rx_header.StdId;
}

AddToBus(rx_msg);
}

inline uint32_t CanBase::GetTimestamp() const {
return HAL_GetTick();
}

void CanBase::ConfigFilters() {
// Currently we don't support filtering - all messages are accepted
CAN_FilterTypeDef filter_config{
.FilterIdHigh = 0x0000,
.FilterIdLow = 0x0000,
.FilterMaskIdHigh = 0x0000,
.FilterMaskIdLow = 0x0000,
.FilterFIFOAssignment = kCanFifo,
.FilterBank = 0,
.FilterMode = CAN_FILTERMODE_IDMASK,
.FilterScale = CAN_FILTERSCALE_32BIT,
.FilterActivation = CAN_FILTER_ENABLE,
.SlaveStartFilterBank = 14,
};

HAL_CAN_ConfigFilter(hcan_, &filter_config);
}

} // namespace mcal::stm32f767::periph

#endif // HAL_CAN_MODULE_ENABLED
Loading

0 comments on commit c10c596

Please sign in to comment.