13template <CAN_HandleTypeDef *Handle>
class BxCan {
16 stm32cubemx_helper::set_context<Handle, BxCan>(
this);
17 HAL_CAN_RegisterCallback(
18 Handle, HAL_CAN_RX_FIFO0_MSG_PENDING_CB_ID,
19 [](CAN_HandleTypeDef *hcan) {
20 CAN_RxHeaderTypeDef rx_header;
23 auto bxcan = stm32cubemx_helper::get_context<Handle, BxCan>();
25 while (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header,
26 msg.
data.data()) == HAL_OK) {
27 if (rx_header.FilterMatchIndex >= BxCan::FILTER_BANK_SIZE) {
30 auto rx_callback = bxcan->rx_callbacks_[rx_header.FilterMatchIndex];
32 update_rx_message(msg, rx_header);
35 bxcan->rx_callback_contexts_[rx_header.FilterMatchIndex]);
42 HAL_CAN_UnRegisterCallback(Handle, HAL_CAN_RX_FIFO0_MSG_PENDING_CB_ID);
43 stm32cubemx_helper::set_context<Handle, BxCan>(
nullptr);
47 if (HAL_CAN_ActivateNotification(Handle, CAN_IT_RX_FIFO0_MSG_PENDING) !=
51 return HAL_CAN_Start(Handle) == HAL_OK;
55 if (HAL_CAN_Stop(Handle) != HAL_OK) {
58 return HAL_CAN_DeactivateNotification(
59 Handle, CAN_IT_RX_FIFO0_MSG_PENDING) == HAL_OK;
63 CAN_TxHeaderTypeDef tx_header = create_tx_header(msg);
66 while (HAL_CAN_AddTxMessage(Handle, &tx_header, msg.
data.data(),
67 &tx_mailbox) != HAL_OK) {
68 if (timeout_helper.is_timeout()) {
80 auto filter_index = find_rx_filter_index(filter);
84 if (!enable_rx_filter(filter, *filter_index)) {
87 rx_callbacks_[*filter_index] = callback;
88 rx_callback_contexts_[*filter_index] = context;
93 if (!disable_rx_filter(filter_index)) {
96 rx_callbacks_[filter_index] =
nullptr;
101 static constexpr uint32_t FILTER_BANK_SIZE = 14;
103 std::array<void (*)(
const CanMessage &msg,
void *context), FILTER_BANK_SIZE>
105 std::array<void *, FILTER_BANK_SIZE> rx_callback_contexts_{};
110 std::optional<size_t> find_rx_filter_index(
const CanFilter &) {
111 auto it = std::find(rx_callbacks_.begin(), rx_callbacks_.end(),
nullptr);
112 if (it == rx_callbacks_.end()) {
115 return std::distance(rx_callbacks_.begin(), it);
118 static inline bool enable_rx_filter(
const CanFilter &filter,
119 uint32_t filter_index) {
120 CAN_FilterTypeDef filter_config{};
122 filter_config.FilterIdHigh = filter.id >> 13;
123 filter_config.FilterIdLow = ((filter.id << 3) & 0xFFFF) | 0x4;
124 filter_config.FilterMaskIdHigh = filter.mask >> 13;
125 filter_config.FilterMaskIdLow = ((filter.mask << 3) & 0xFFFF) | 0x4;
127 filter_config.FilterIdHigh = filter.id << 5;
128 filter_config.FilterIdLow = 0x0;
129 filter_config.FilterMaskIdHigh = filter.mask << 5;
130 filter_config.FilterMaskIdLow = 0x0;
132 filter_config.FilterFIFOAssignment = CAN_FILTER_FIFO0;
133 filter_config.FilterBank = filter_index;
135 if (Handle->Instance == CAN2) {
136 filter_config.FilterBank += FILTER_BANK_SIZE;
139 filter_config.FilterMode = CAN_FILTERMODE_IDMASK;
140 filter_config.FilterScale = CAN_FILTERSCALE_32BIT;
141 filter_config.FilterActivation = ENABLE;
142 filter_config.SlaveStartFilterBank = FILTER_BANK_SIZE;
144 return HAL_CAN_ConfigFilter(Handle, &filter_config) == HAL_OK;
147 static inline bool disable_rx_filter(
size_t filter_index) {
148 CAN_FilterTypeDef filter_config{};
149 filter_config.FilterBank = filter_index;
151 if (Handle->Instance == CAN2) {
152 filter_config.FilterBank += FILTER_BANK_SIZE;
155 filter_config.FilterActivation = DISABLE;
157 return HAL_CAN_ConfigFilter(Handle, &filter_config) == HAL_OK;
160 static inline CAN_TxHeaderTypeDef create_tx_header(
const CanMessage &msg) {
161 CAN_TxHeaderTypeDef tx_header{};
163 tx_header.ExtId = msg.id;
164 tx_header.IDE = CAN_ID_EXT;
166 tx_header.StdId = msg.id;
167 tx_header.IDE = CAN_ID_STD;
169 tx_header.RTR = CAN_RTR_DATA;
170 tx_header.DLC = msg.dlc;
171 tx_header.TransmitGlobalTime = DISABLE;
175 static inline void update_rx_message(CanMessage &msg,
176 const CAN_RxHeaderTypeDef &rx_header) {
177 switch (rx_header.IDE) {
179 msg.id = rx_header.StdId;
183 msg.id = rx_header.ExtId;
187 msg.dlc = rx_header.DLC;