Skip to content

Commit 7095dc7

Browse files
authored
Merge pull request #14688 from MubeenHCLite/CAN_rxinterrupt_fix
Solution for mutex problem in CAN read ISR
2 parents 3c2c5be + d477efe commit 7095dc7

File tree

12 files changed

+176
-0
lines changed

12 files changed

+176
-0
lines changed

drivers/include/drivers/RawCAN.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright (C) 2021, STMicroelectronics, All Rights Reserved
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
6+
* not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#ifndef RAWCAN_H
19+
#define RAWCAN_H
20+
21+
#include "platform/platform.h"
22+
#include "drivers/CAN.h"
23+
24+
#if DEVICE_CAN || defined(DOXYGEN_ONLY)
25+
26+
#include "interfaces/InterfaceCAN.h"
27+
#include "hal/can_api.h"
28+
#include "platform/Callback.h"
29+
#include "platform/PlatformMutex.h"
30+
31+
namespace mbed {
32+
#ifndef FEATURE_EXPERIMENTAL_API
33+
class RawCAN: public CAN {
34+
public:
35+
/** Creates an unlocked CAN interface connected to specific pins.
36+
*
37+
* @param rd read from transmitter
38+
* @param td transmit to transmitter
39+
*
40+
* Example:
41+
* @code
42+
* #include "mbed.h"
43+
*
44+
*
45+
* Ticker ticker;
46+
* DigitalOut led1(LED1);
47+
* DigitalOut led2(LED2);
48+
* //The constructor takes in RX, and TX pin respectively.
49+
* //These pins, for this example, are defined in mbed_app.json
50+
* RawCAN can1(MBED_CONF_APP_CAN1_RD, MBED_CONF_APP_CAN1_TD);
51+
* RawCAN can2(MBED_CONF_APP_CAN2_RD, MBED_CONF_APP_CAN2_TD);
52+
*
53+
* unsigned char counter = 0;
54+
*
55+
* void send() {
56+
* if(can1.write(CANMessage(1337U, &counter, 1))) {
57+
* printf("Message sent: %d\n", counter);
58+
* counter++;
59+
* }
60+
* led1 = !led1;
61+
* }
62+
*
63+
* int main() {
64+
* ticker.attach(&send, 1);
65+
* CANMessage msg;
66+
* while(1) {
67+
* if(can2.read(msg)) {
68+
* printf("Message received: %d\n\n", msg.data[0]);
69+
* led2 = !led2;
70+
* }
71+
* ThisThread::sleep_for(200);
72+
* }
73+
* }
74+
*
75+
* @endcode
76+
*/
77+
78+
/* Note: The can apis are unlocked hence using this when multiple
79+
* threads are accessing a single instance of CAN will lead to
80+
* race conditions, can be used in single threaded CAN.
81+
*/
82+
using CAN::CAN;
83+
84+
85+
// override lock apis to create unlocked CAN
86+
void lock() override {};
87+
void unlock() override {};
88+
89+
};
90+
#endif //FEATURE_EXPERIMENTAL_API
91+
}
92+
93+
#endif
94+
#endif //RAWCAN_H

mbed.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
#include "drivers/I2C.h"
6969
#include "drivers/I2CSlave.h"
7070
#include "drivers/CAN.h"
71+
#include "drivers/RawCAN.h"
7172
#include "drivers/UnbufferedSerial.h"
7273
#include "drivers/BufferedSerial.h"
7374
#include "drivers/FlashIAP.h"

targets/TARGET_STM/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,63 @@ For application that require optimized maximum performance, the recommendation i
528528
The SPI DMA transfer support shall be implemented on a case-by-case based on below example
529529
https://github.com/ABOSTM/mbed-os/tree/I2C_SPI_DMA_IMPLEMENTATION_FOR_STM32L4
530530

531+
### CAN receive interrupt problem due to mutex and resolution
532+
533+
In bxCAN and earlier versions the receive interrupt flags can be cleared only on performing a read operation in ST MCUs
534+
But can_read() cannot be used in interrupt context as it is gaurded by lock operation and mbed does not allow locks in
535+
interrupt context. Hence the Rx interrupt is disabled for a while and read is deferred to thread context, the interrupt is
536+
enabled on a successful read.
537+
538+
As an other option RawCAN (with unlocked CAN apis) is also available and can be used directly, if only one thread is accessing
539+
the CAN interface.
540+
541+
While using RxInterrupt with the CAN object the receive ISR callback registered should defer read to thread context.
542+
A simple example is as shown below:
543+
544+
#include "mbed.h"
545+
546+
Ticker ticker;
547+
Thread canReadThread;
548+
549+
DigitalOut led1(LED1);
550+
DigitalOut led2(LED2);
551+
DigitalOut led3(LED3);
552+
553+
CAN can1(PD_0 ,PD_1);
554+
555+
EventQueue queue(32 * EVENTS_EVENT_SIZE);
556+
557+
int counter = 0xABCD;
558+
CANMessage msg;
559+
560+
void canRead(){
561+
if(can1.read(msg)) {
562+
if(msg.id==1100)
563+
led2 = !led2;
564+
if(msg.id==1102){
565+
led3 = !led3;
566+
}
567+
}
568+
}
569+
570+
void canISR(){
571+
queue.call(canRead);
572+
led3 = !led3;
573+
}
574+
575+
int main() {
576+
577+
can1.frequency(100000);
578+
can1.mode(CAN::Normal);
579+
580+
can1.attach(canISR, CAN::RxIrq);
581+
582+
canReadThread.start(callback(&queue, &EventQueue::dispatch_forever));
583+
584+
while(1) {
585+
}
586+
}
587+
531588

532589
## Mbed OS Wiki pages
533590

targets/TARGET_STM/TARGET_STM32F0/objects.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ struct can_s {
156156
CAN_HandleTypeDef CanHandle;
157157
int index;
158158
int hz;
159+
int rxIrqEnabled;
159160
};
160161
#endif
161162

targets/TARGET_STM/TARGET_STM32F1/objects.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ struct can_s {
145145
CAN_HandleTypeDef CanHandle;
146146
int index;
147147
int hz;
148+
int rxIrqEnabled;
148149
};
149150
#endif
150151

targets/TARGET_STM/TARGET_STM32F2/objects.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ struct can_s {
154154
CAN_HandleTypeDef CanHandle;
155155
int index;
156156
int hz;
157+
int rxIrqEnabled;
157158
};
158159
#endif
159160

targets/TARGET_STM/TARGET_STM32F3/objects.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ struct can_s {
143143
CAN_HandleTypeDef CanHandle;
144144
int index;
145145
int hz;
146+
int rxIrqEnabled;
146147
};
147148
#endif
148149

targets/TARGET_STM/TARGET_STM32F4/objects.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ struct can_s {
158158
CAN_HandleTypeDef CanHandle;
159159
int index;
160160
int hz;
161+
int rxIrqEnabled;
161162
};
162163
#endif
163164

targets/TARGET_STM/TARGET_STM32F7/objects.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ struct can_s {
167167
CAN_HandleTypeDef CanHandle;
168168
int index;
169169
int hz;
170+
int rxIrqEnabled;
170171
};
171172
#endif
172173

targets/TARGET_STM/TARGET_STM32L4/objects.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ struct can_s {
157157
CAN_HandleTypeDef CanHandle;
158158
int index;
159159
int hz;
160+
int rxIrqEnabled;
160161
};
161162
#endif
162163

targets/TARGET_STM/TARGET_STM32L5/objects.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ struct can_s {
157157
CAN_HandleTypeDef CanHandle;
158158
int index;
159159
int hz;
160+
int rxIrqEnabled;
160161
};
161162
#endif
162163

targets/TARGET_STM/can_api.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,7 @@ void can_irq_init(can_t *obj, can_irq_handler handler, uint32_t id)
778778
{
779779
irq_handler = handler;
780780
can_irq_ids[obj->index] = id;
781+
obj->rxIrqEnabled = false;
781782
}
782783

783784
void can_irq_free(can_t *obj)
@@ -787,6 +788,7 @@ void can_irq_free(can_t *obj)
787788
can->IER &= ~(CAN_IT_FMP0 | CAN_IT_FMP1 | CAN_IT_TME | \
788789
CAN_IT_ERR | CAN_IT_EPV | CAN_IT_BOF);
789790
can_irq_ids[obj->index] = 0;
791+
obj->rxIrqEnabled = false;
790792
}
791793

792794
void can_free(can_t *obj)
@@ -1012,6 +1014,10 @@ int can_read(can_t *obj, CAN_Message *msg, int handle)
10121014
/* Release the FIFO */
10131015
can->RF0R |= CAN_RF0R_RFOM0;
10141016

1017+
if(obj->rxIrqEnabled == true) {
1018+
__HAL_CAN_ENABLE_IT(&obj->CanHandle, CAN_IT_FMP0);
1019+
}
1020+
10151021
return 1;
10161022
}
10171023

@@ -1025,6 +1031,7 @@ void can_reset(can_t *obj)
10251031

10261032
/* restore registers state as saved in obj context */
10271033
can_registers_init(obj);
1034+
obj->rxIrqEnabled = false;
10281035
}
10291036

10301037
unsigned char can_rderror(can_t *obj)
@@ -1177,6 +1184,12 @@ static void can_irq(CANName name, int id)
11771184
tmp1 = __HAL_CAN_MSG_PENDING(&CanHandle, CAN_FIFO0);
11781185
tmp2 = __HAL_CAN_GET_IT_SOURCE(&CanHandle, CAN_IT_FMP0);
11791186

1187+
// In legacy can (bxCAN and earlier), reading is the only way to clear rx interrupt. But can_read has mutex locks
1188+
// since mutexes cannot be used in ISR context, rx interrupt is masked here to temporary disable it
1189+
// rx interrupts will be unamsked in read operation. reads must be deffered to thread context.
1190+
// refer to the CAN receive interrupt problem due to mutex and resolution section of README doc.
1191+
__HAL_CAN_DISABLE_IT(&CanHandle, CAN_IT_FMP0);
1192+
11801193
if ((tmp1 != 0) && tmp2) {
11811194
irq_handler(can_irq_ids[id], IRQ_RX);
11821195
}
@@ -1276,6 +1289,7 @@ void can_irq_set(can_t *obj, CanIrqType type, uint32_t enable)
12761289
ier = CAN_IT_FMP0;
12771290
irq_n = CAN1_IRQ_RX_IRQN;
12781291
vector = (uint32_t)&CAN1_IRQ_RX_VECT;
1292+
obj->rxIrqEnabled = true;
12791293
break;
12801294
case IRQ_TX:
12811295
ier = CAN_IT_TME;
@@ -1308,6 +1322,7 @@ void can_irq_set(can_t *obj, CanIrqType type, uint32_t enable)
13081322
ier = CAN_IT_FMP0;
13091323
irq_n = CAN2_IRQ_RX_IRQN;
13101324
vector = (uint32_t)&CAN2_IRQ_RX_VECT;
1325+
obj->rxIrqEnabled = true;
13111326
break;
13121327
case IRQ_TX:
13131328
ier = CAN_IT_TME;
@@ -1341,6 +1356,7 @@ void can_irq_set(can_t *obj, CanIrqType type, uint32_t enable)
13411356
ier = CAN_IT_FMP0;
13421357
irq_n = CAN3_IRQ_RX_IRQN;
13431358
vector = (uint32_t)&CAN3_IRQ_RX_VECT;
1359+
obj->rxIrqEnabled = true;
13441360
break;
13451361
case IRQ_TX:
13461362
ier = CAN_IT_TME;

0 commit comments

Comments
 (0)