Skip to content

Commit a1ff223

Browse files
committed
[stm32] Fix FDCAN message TX handling
1 parent e2ee809 commit a1ff223

File tree

1 file changed

+33
-11
lines changed

1 file changed

+33
-11
lines changed

src/modm/platform/can/stm32-fdcan/can.cpp.in

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ sendMsg(const modm::can::Message& message)
135135
{
136136
using namespace modm::platform;
137137

138-
if (!Fdcan{{ id }}::isReadyToSend()) {
138+
if (isHardwareTxQueueFull()) {
139139
return false;
140140
}
141141

@@ -215,20 +215,25 @@ modm::platform::Fdcan{{ id }}::initializeWithPrescaler(
215215
MODM_ISR({{ reg }}_IT0)
216216
{
217217
%% if options["buffer.tx"] > 0
218-
if (txQueue.isNotEmpty()) {
219-
sendMsg(txQueue.get());
220-
txQueue.pop();
218+
if ({{ reg }}->IR & FDCAN_IR_TC) {
219+
{{ reg }}->IR = FDCAN_IR_TC;
220+
if (txQueue.isNotEmpty()) {
221+
const bool success = sendMsg(txQueue.get());
222+
if (success) {
223+
txQueue.pop();
224+
}
225+
}
221226
}
222227
%% endif
223228

224-
const auto callback = modm::platform::Fdcan{{ id }}::getErrorInterruptCallback();
225-
if (callback) {
226-
const bool hasErrorInterrupt = ({{ reg }}->IR & (FDCAN_IR_BO | FDCAN_IR_EW | FDCAN_IR_EP));
227-
if (hasErrorInterrupt) {
229+
const bool hasErrorInterrupt = ({{ reg }}->IR & (FDCAN_IR_BO | FDCAN_IR_EW | FDCAN_IR_EP));
230+
if (hasErrorInterrupt) {
231+
const auto callback = modm::platform::Fdcan{{ id }}::getErrorInterruptCallback();
232+
if (callback) {
228233
callback();
229234
}
235+
{{ reg }}->IR = FDCAN_IR_BO | FDCAN_IR_EW | FDCAN_IR_EP;
230236
}
231-
{{ reg }}->IR = FDCAN_IR_TC | FDCAN_IR_BO | FDCAN_IR_EW | FDCAN_IR_EP;
232237
}
233238

234239

@@ -258,7 +263,7 @@ MODM_ISR({{ reg }}_IT1)
258263
"CAN receive software buffer full, not reading new message(s)!");
259264
// disable rx ISR until we read data from the rxQueue (IST for tx remains active)
260265
// The interrupt remains unacknowledged, so it fires again after the read.
261-
{{ reg }}->ILE = FDCAN_ILE_EINT0;
266+
{{ reg }}->ILE &= ~FDCAN_ILE_EINT1;
262267
} else {
263268
{{ reg }}->IR = FDCAN_IR_RF0N | FDCAN_IR_RF1N; // acknowledge interrupt flags
264269
}
@@ -402,10 +407,27 @@ modm::platform::Fdcan{{ id }}::isReadyToSend()
402407
%% endif
403408
}
404409

405-
406410
bool
407411
modm::platform::Fdcan{{ id }}::sendMessage(const can::Message& message)
408412
{
413+
%% if options["buffer.tx"] > 0
414+
/* Disable CAN interrupts to prevent race condition:
415+
* sendMsg() could be called concurrently from the TX interrupt and
416+
* simultaneously access the same TX buffer.
417+
* isHardwareTxQueueFull() must be checked while interrupts are off to
418+
* prevent a "time-of-check to time-of-use" bug. */
419+
struct Lock
420+
{
421+
const uint32_t flags;
422+
/* An edge case could occur where the RX interrupt gets disabled
423+
* in the RX handler when the queue is full between reading ILE and
424+
* writing ILE = 0. This will invoke the RX handler once
425+
* after sendMessage() but not cause any further issues. */
426+
Lock() : flags({{ reg }}->ILE) { {{ reg }}->ILE = 0; }
427+
~Lock() { {{ reg }}->ILE = flags; }
428+
} lock;
429+
%% endif
430+
409431
if (isHardwareTxQueueFull()) {
410432
%% if options["buffer.tx"] > 0
411433
if (txQueue.isFull()) {

0 commit comments

Comments
 (0)