Skip to content

Commit 0abbafd

Browse files
committed
stm32/can: Add "list" param to CAN.recv() to receive data inplace.
This API matches (as close as possible) how other pyb classes allow inplace operations, such as pyb.SPI.recv(buf).
1 parent 5e1279d commit 0abbafd

2 files changed

Lines changed: 66 additions & 17 deletions

File tree

docs/library/pyb.CAN.rst

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,12 @@ Methods
187187

188188
Return ``True`` if any message waiting on the FIFO, else ``False``.
189189

190-
.. method:: CAN.recv(fifo, \*, timeout=5000)
190+
.. method:: CAN.recv(fifo, list=None, \*, timeout=5000)
191191

192192
Receive data on the bus:
193193

194194
- *fifo* is an integer, which is the FIFO to receive on
195+
- *list* is an optional list object to be used as the return value
195196
- *timeout* is the timeout in milliseconds to wait for the receive.
196197

197198
Return value: A tuple containing four values.
@@ -201,6 +202,24 @@ Methods
201202
- The FMI (Filter Match Index) value.
202203
- An array containing the data.
203204

205+
If *list* is ``None`` then a new tuple will be allocated, as well as a new
206+
bytes object to contain the data (as the fourth element in the tuple).
207+
208+
If *list* is not ``None`` then it should be a list object with a least four
209+
elements. The fourth element should be a memoryview object which is created
210+
from either a bytearray or an array of type 'B' or 'b', and this array must
211+
have enough room for at least 8 bytes. The list object will then be
212+
populated with the first three return values above, and the memoryview object
213+
will be resized inplace to the size of the data and filled in with that data.
214+
The same list and memoryview objects can be reused in subsequent calls to
215+
this method, providing a way of receiving data without using the heap.
216+
For example::
217+
218+
buf = bytearray(8)
219+
lst = [0, 0, 0, memoryview(buf)]
220+
# No heap memory is allocated in the following call
221+
can.recv(0, lst)
222+
204223
.. method:: CAN.send(data, id, \*, timeout=0, rtr=False)
205224

206225
Send a message on the bus:

ports/stm32/can.c

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@
2929
#include <stdarg.h>
3030

3131
#include "py/objtuple.h"
32+
#include "py/objarray.h"
3233
#include "py/runtime.h"
3334
#include "py/gc.h"
35+
#include "py/binary.h"
3436
#include "py/stream.h"
3537
#include "py/mperrno.h"
3638
#include "py/mphal.h"
@@ -645,18 +647,20 @@ STATIC mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *
645647
}
646648
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_can_send_obj, 1, pyb_can_send);
647649

648-
/// \method recv(fifo, *, timeout=5000)
650+
/// \method recv(fifo, list=None, *, timeout=5000)
649651
///
650652
/// Receive data on the bus:
651653
///
652654
/// - `fifo` is an integer, which is the FIFO to receive on
655+
/// - `list` if not None is a list with at least 4 elements
653656
/// - `timeout` is the timeout in milliseconds to wait for the receive.
654657
///
655658
/// Return value: buffer of data bytes.
656659
STATIC mp_obj_t pyb_can_recv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
657-
enum { ARG_fifo, ARG_timeout };
660+
enum { ARG_fifo, ARG_list, ARG_timeout };
658661
static const mp_arg_t allowed_args[] = {
659662
{ MP_QSTR_fifo, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
663+
{ MP_QSTR_list, MP_ARG_OBJ, {.u_obj = mp_const_none} },
660664
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} },
661665
};
662666

@@ -700,23 +704,49 @@ STATIC mp_obj_t pyb_can_recv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *
700704
}
701705
}
702706

703-
// return the received data
704-
// TODO use a namedtuple (when namedtuple types can be stored in ROM)
705-
mp_obj_tuple_t *tuple = mp_obj_new_tuple(4, NULL);
706-
if (rx_msg.IDE == CAN_ID_STD) {
707-
tuple->items[0] = MP_OBJ_NEW_SMALL_INT(rx_msg.StdId);
707+
// Create the tuple, or get the list, that will hold the return values
708+
// Also populate the fourth element, either a new bytes or reuse existing memoryview
709+
mp_obj_t ret_obj = args[ARG_list].u_obj;
710+
mp_obj_t *items;
711+
if (ret_obj == mp_const_none) {
712+
ret_obj = mp_obj_new_tuple(4, NULL);
713+
items = ((mp_obj_tuple_t*)MP_OBJ_TO_PTR(ret_obj))->items;
714+
items[3] = mp_obj_new_bytes(&rx_msg.Data[0], rx_msg.DLC);
708715
} else {
709-
tuple->items[0] = MP_OBJ_NEW_SMALL_INT(rx_msg.ExtId);
716+
// User should provide a list of length at least 4 to hold the values
717+
if (!MP_OBJ_IS_TYPE(ret_obj, &mp_type_list)) {
718+
mp_raise_TypeError(NULL);
719+
}
720+
mp_obj_list_t *list = MP_OBJ_TO_PTR(ret_obj);
721+
if (list->len < 4) {
722+
mp_raise_ValueError(NULL);
723+
}
724+
items = list->items;
725+
// Fourth element must be a memoryview which we assume points to a
726+
// byte-like array which is large enough, and then we resize it inplace
727+
if (!MP_OBJ_IS_TYPE(items[3], &mp_type_memoryview)) {
728+
mp_raise_TypeError(NULL);
729+
}
730+
mp_obj_array_t *mv = MP_OBJ_TO_PTR(items[3]);
731+
if (!(mv->typecode == (0x80 | BYTEARRAY_TYPECODE)
732+
|| (mv->typecode | 0x20) == (0x80 | 'b'))) {
733+
mp_raise_ValueError(NULL);
734+
}
735+
mv->len = rx_msg.DLC;
736+
memcpy(mv->items, &rx_msg.Data[0], rx_msg.DLC);
710737
}
711-
tuple->items[1] = rx_msg.RTR == CAN_RTR_REMOTE ? mp_const_true : mp_const_false;
712-
tuple->items[2] = MP_OBJ_NEW_SMALL_INT(rx_msg.FMI);
713-
vstr_t vstr;
714-
vstr_init_len(&vstr, rx_msg.DLC);
715-
for (mp_uint_t i = 0; i < rx_msg.DLC; i++) {
716-
vstr.buf[i] = rx_msg.Data[i]; // Data is uint32_t but holds only 1 byte
738+
739+
// Populate the first 3 values of the tuple/list
740+
if (rx_msg.IDE == CAN_ID_STD) {
741+
items[0] = MP_OBJ_NEW_SMALL_INT(rx_msg.StdId);
742+
} else {
743+
items[0] = MP_OBJ_NEW_SMALL_INT(rx_msg.ExtId);
717744
}
718-
tuple->items[3] = mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
719-
return tuple;
745+
items[1] = rx_msg.RTR == CAN_RTR_REMOTE ? mp_const_true : mp_const_false;
746+
items[2] = MP_OBJ_NEW_SMALL_INT(rx_msg.FMI);
747+
748+
// Return the result
749+
return ret_obj;
720750
}
721751
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_can_recv_obj, 1, pyb_can_recv);
722752

0 commit comments

Comments
 (0)