Skip to content

Commit 7d5e342

Browse files
committed
stmhal: Allow sending CAN messages with timeout=0.
Thanks to Henrik Sölver for this patch.
1 parent 4c45921 commit 7d5e342

File tree

4 files changed

+151
-18
lines changed

4 files changed

+151
-18
lines changed

docs/library/pyb.CAN.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,14 +126,20 @@ Methods
126126

127127
Return value: buffer of data bytes.
128128

129-
.. method:: can.send(send, addr, \*, timeout=5000)
129+
.. method:: can.send(send, addr, \*, timeout=0)
130130

131131
Send a message on the bus:
132132

133133
- ``send`` is the data to send (an integer to send, or a buffer object).
134134
- ``addr`` is the address to send to
135135
- ``timeout`` is the timeout in milliseconds to wait for the send.
136136

137+
If timeout is 0 the message is placed in a buffer in one of three hardware
138+
buffers and the method returns immediately. If all three buffers are in use
139+
an exception is thrown. If timeout is not 0, the method waits until the
140+
message is transmitted. If the message can't be transmitted within the
141+
specified time an exception is thrown.
142+
137143
Return value: ``None``.
138144

139145
.. method:: can.rxcallback(fifo, fun)

stmhal/can.c

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,96 @@ STATIC void can_clearfilter(uint32_t f) {
170170
HAL_CAN_ConfigFilter(NULL, &filter);
171171
}
172172

173+
// We have our own version of CAN transmit so we can handle Timeout=0 correctly.
174+
STATIC HAL_StatusTypeDef CAN_Transmit(CAN_HandleTypeDef *hcan, uint32_t Timeout) {
175+
uint32_t transmitmailbox;
176+
uint32_t tickstart;
177+
uint32_t rqcpflag;
178+
uint32_t txokflag;
179+
180+
// Check the parameters
181+
assert_param(IS_CAN_IDTYPE(hcan->pTxMsg->IDE));
182+
assert_param(IS_CAN_RTR(hcan->pTxMsg->RTR));
183+
assert_param(IS_CAN_DLC(hcan->pTxMsg->DLC));
184+
185+
// Select one empty transmit mailbox
186+
if ((hcan->Instance->TSR&CAN_TSR_TME0) == CAN_TSR_TME0) {
187+
transmitmailbox = CAN_TXMAILBOX_0;
188+
rqcpflag = CAN_FLAG_RQCP0;
189+
txokflag = CAN_FLAG_TXOK0;
190+
} else if ((hcan->Instance->TSR&CAN_TSR_TME1) == CAN_TSR_TME1) {
191+
transmitmailbox = CAN_TXMAILBOX_1;
192+
rqcpflag = CAN_FLAG_RQCP1;
193+
txokflag = CAN_FLAG_TXOK1;
194+
} else if ((hcan->Instance->TSR&CAN_TSR_TME2) == CAN_TSR_TME2) {
195+
transmitmailbox = CAN_TXMAILBOX_2;
196+
rqcpflag = CAN_FLAG_RQCP2;
197+
txokflag = CAN_FLAG_TXOK2;
198+
} else {
199+
transmitmailbox = CAN_TXSTATUS_NOMAILBOX;
200+
}
201+
202+
if (transmitmailbox != CAN_TXSTATUS_NOMAILBOX) {
203+
// Set up the Id
204+
hcan->Instance->sTxMailBox[transmitmailbox].TIR &= CAN_TI0R_TXRQ;
205+
if (hcan->pTxMsg->IDE == CAN_ID_STD) {
206+
assert_param(IS_CAN_STDID(hcan->pTxMsg->StdId));
207+
hcan->Instance->sTxMailBox[transmitmailbox].TIR |= ((hcan->pTxMsg->StdId << 21) | \
208+
hcan->pTxMsg->RTR);
209+
} else {
210+
assert_param(IS_CAN_EXTID(hcan->pTxMsg->ExtId));
211+
hcan->Instance->sTxMailBox[transmitmailbox].TIR |= ((hcan->pTxMsg->ExtId << 3) | \
212+
hcan->pTxMsg->IDE | \
213+
hcan->pTxMsg->RTR);
214+
}
215+
216+
// Set up the DLC
217+
hcan->pTxMsg->DLC &= (uint8_t)0x0000000F;
218+
hcan->Instance->sTxMailBox[transmitmailbox].TDTR &= (uint32_t)0xFFFFFFF0;
219+
hcan->Instance->sTxMailBox[transmitmailbox].TDTR |= hcan->pTxMsg->DLC;
220+
221+
// Set up the data field
222+
hcan->Instance->sTxMailBox[transmitmailbox].TDLR = (((uint32_t)hcan->pTxMsg->Data[3] << 24) |
223+
((uint32_t)hcan->pTxMsg->Data[2] << 16) |
224+
((uint32_t)hcan->pTxMsg->Data[1] << 8) |
225+
((uint32_t)hcan->pTxMsg->Data[0]));
226+
hcan->Instance->sTxMailBox[transmitmailbox].TDHR = (((uint32_t)hcan->pTxMsg->Data[7] << 24) |
227+
((uint32_t)hcan->pTxMsg->Data[6] << 16) |
228+
((uint32_t)hcan->pTxMsg->Data[5] << 8) |
229+
((uint32_t)hcan->pTxMsg->Data[4]));
230+
// Request transmission
231+
hcan->Instance->sTxMailBox[transmitmailbox].TIR |= CAN_TI0R_TXRQ;
232+
233+
if (Timeout == 0) {
234+
return HAL_OK;
235+
}
236+
237+
// Get tick
238+
tickstart = HAL_GetTick();
239+
// Check End of transmission flag
240+
while (!(__HAL_CAN_TRANSMIT_STATUS(hcan, transmitmailbox))) {
241+
// Check for the Timeout
242+
if (Timeout != HAL_MAX_DELAY) {
243+
if ((HAL_GetTick() - tickstart) > Timeout) {
244+
// When the timeout expires, we try to abort the transmission of the packet
245+
__HAL_CAN_CANCEL_TRANSMIT(hcan, transmitmailbox);
246+
while (!__HAL_CAN_GET_FLAG(hcan, rqcpflag)) {
247+
}
248+
if (__HAL_CAN_GET_FLAG(hcan, txokflag)) {
249+
// The abort attempt failed and the message was sent properly
250+
return HAL_OK;
251+
} else {
252+
return HAL_TIMEOUT;
253+
}
254+
}
255+
}
256+
}
257+
return HAL_OK;
258+
} else {
259+
return HAL_BUSY;
260+
}
261+
}
262+
173263
/******************************************************************************/
174264
// Micro Python bindings
175265

@@ -348,7 +438,7 @@ STATIC mp_obj_t pyb_can_send(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_
348438
static const mp_arg_t allowed_args[] = {
349439
{ MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
350440
{ MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
351-
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} },
441+
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
352442
};
353443

354444
// parse args
@@ -380,7 +470,7 @@ STATIC mp_obj_t pyb_can_send(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_
380470
tx_msg.Data[i] = ((byte*)bufinfo.buf)[i]; // Data is uint32_t but holds only 1 byte
381471
}
382472
self->can.pTxMsg = &tx_msg;
383-
HAL_StatusTypeDef status = HAL_CAN_Transmit(&self->can, args[2].u_int);
473+
HAL_StatusTypeDef status = CAN_Transmit(&self->can, args[2].u_int);
384474

385475
if (status != HAL_OK) {
386476
mp_hal_raise(status);

tests/pyb/can.py

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from pyb import CAN
2+
import pyb
23

34
CAN.initfilterbanks(14)
45
can = CAN(1)
@@ -11,19 +12,19 @@
1112
# Catch all filter
1213
can.setfilter(0, CAN.MASK16, 0, (0, 0, 0, 0))
1314

14-
can.send('abcd', 123)
15+
can.send('abcd', 123, timeout=5000)
1516
print(can.any(0))
1617
print(can.recv(0))
1718

18-
can.send('abcd', -1)
19+
can.send('abcd', -1, timeout=5000)
1920
print(can.recv(0))
2021

21-
can.send('abcd', 0x7FF + 1)
22+
can.send('abcd', 0x7FF + 1, timeout=5000)
2223
print(can.recv(0))
2324

2425
# Test too long message
2526
try:
26-
can.send('abcdefghi', 0x7FF)
27+
can.send('abcdefghi', 0x7FF, timeout=5000)
2728
except ValueError:
2829
print('passed')
2930
else:
@@ -39,7 +40,7 @@
3940
print(can)
4041

4142
try:
42-
can.send('abcde', 0x7FF + 1)
43+
can.send('abcde', 0x7FF + 1, timeout=5000)
4344
except ValueError:
4445
print('failed')
4546
else:
@@ -95,17 +96,17 @@ def cb1a(bus, reason):
9596
can.rxcallback(0, cb0)
9697
can.rxcallback(1, cb1)
9798

98-
can.send('11111111',1)
99-
can.send('22222222',2)
100-
can.send('33333333',3)
99+
can.send('11111111',1, timeout=5000)
100+
can.send('22222222',2, timeout=5000)
101+
can.send('33333333',3, timeout=5000)
101102
can.rxcallback(0, cb0a)
102-
can.send('44444444',4)
103+
can.send('44444444',4, timeout=5000)
103104

104-
can.send('55555555',5)
105-
can.send('66666666',6)
106-
can.send('77777777',7)
105+
can.send('55555555',5, timeout=5000)
106+
can.send('66666666',6, timeout=5000)
107+
can.send('77777777',7, timeout=5000)
107108
can.rxcallback(1, cb1a)
108-
can.send('88888888',8)
109+
can.send('88888888',8, timeout=5000)
109110

110111
print(can.recv(0))
111112
print(can.recv(0))
@@ -114,9 +115,39 @@ def cb1a(bus, reason):
114115
print(can.recv(1))
115116
print(can.recv(1))
116117

117-
can.send('11111111',1)
118-
can.send('55555555',5)
118+
can.send('11111111',1, timeout=5000)
119+
can.send('55555555',5, timeout=5000)
119120

120121
print(can.recv(0))
121122
print(can.recv(1))
122123

124+
del can
125+
126+
# Testing asyncronous send
127+
can = CAN(1, CAN.LOOPBACK)
128+
can.setfilter(0, CAN.MASK16, 0, (0, 0, 0, 0))
129+
130+
while can.any(0):
131+
can.recv(0)
132+
133+
can.send('abcde', 1, timeout=0)
134+
print(can.any(0))
135+
while not can.any(0):
136+
pass
137+
138+
print(can.recv(0))
139+
140+
try:
141+
can.send('abcde', 2, timeout=0)
142+
can.send('abcde', 3, timeout=0)
143+
can.send('abcde', 4, timeout=0)
144+
can.send('abcde', 5, timeout=0)
145+
except OSError as e:
146+
if str(e) == '16':
147+
print('passed')
148+
else:
149+
print('failed')
150+
151+
pyb.delay(500)
152+
while can.any(0):
153+
print(can.recv(0))

tests/pyb/can.py.exp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,9 @@ cb1a
3232
pending
3333
(1, 0, 0, b'11111111')
3434
(5, 0, 0, b'55555555')
35+
False
36+
(1, 0, 0, b'abcde')
37+
passed
38+
(2, 0, 0, b'abcde')
39+
(3, 0, 0, b'abcde')
40+
(4, 0, 0, b'abcde')

0 commit comments

Comments
 (0)