summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--docs/library/pyb.CAN.rst72
-rw-r--r--stmhal/can.c177
-rw-r--r--stmhal/can.h4
-rw-r--r--stmhal/main.c7
-rw-r--r--stmhal/mpconfigport.h3
-rw-r--r--stmhal/qstrdefsport.h1
-rw-r--r--stmhal/stm32f4xx_it.c19
-rw-r--r--tests/pyb/can.py72
-rw-r--r--tests/pyb/can.py.exp24
9 files changed, 345 insertions, 34 deletions
diff --git a/docs/library/pyb.CAN.rst b/docs/library/pyb.CAN.rst
index e160f67970..8f417f735b 100644
--- a/docs/library/pyb.CAN.rst
+++ b/docs/library/pyb.CAN.rst
@@ -26,30 +26,30 @@ Constructors
initialised (it has the settings from the last initialisation of
the bus, if any). If extra arguments are given, the bus is initialised.
See ``init`` for parameters of initialisation.
-
+
The physical pins of the CAN busses are:
-
+
- ``CAN(1)`` is on ``YA``: ``(RX, TX) = (Y3, Y4) = (PB8, PB9)``
- ``CAN(2)`` is on ``YB``: ``(RX, TX) = (Y5, Y6) = (PB12, PB13)``
Class Methods
-------------
.. method:: CAN.initfilterbanks(nr)
-
+
Reset and disable all filter banks and assign how many banks should be available for CAN(1).
-
+
STM32F405 has 28 filter banks that are shared between the two available CAN bus controllers.
- This function configures how many filter banks should be assigned to each. ``nr`` is the number of banks
- that will be assigned to CAN(1), the rest of the 28 are assigned to CAN(2).
+ This function configures how many filter banks should be assigned to each. ``nr`` is the number of banks
+ that will be assigned to CAN(1), the rest of the 28 are assigned to CAN(2).
At boot, 14 banks are assigned to each controller.
-
+
Methods
-------
.. method:: can.init(mode, extframe=False, prescaler=100, \*, sjw=1, bs1=6, bs2=8)
Initialise the CAN bus with the given parameters:
-
+
- ``mode`` is one of: NORMAL, LOOPBACK, SILENT, SILENT_LOOPBACK
- if ``extframe`` is True then the bus uses extended identifiers in the frames
(29 bits); otherwise it uses standard 11 bit identifiers
@@ -83,14 +83,14 @@ Methods
Turn off the CAN bus.
.. method:: can.setfilter(bank, mode, fifo, params)
-
+
Configure a filter bank:
-
+
- ``bank`` is the filter bank that is to be configured.
- ``mode`` is the mode the filter should operate in.
- - ``fifo`` is which fifo (0 or 1) a message should be stored in, if it is accepted by this filter.
+ - ``fifo`` is which fifo (0 or 1) a message should be stored in, if it is accepted by this filter.
- ``params`` is an array of values the defines the filter. The contents of the array depends on the ``mode`` argument.
-
+
+-----------+---------------------------------------------------------+
|``mode`` |contents of parameter array |
+===========+=========================================================+
@@ -106,11 +106,11 @@ Methods
+-----------+---------------------------------------------------------+
|CAN.MASK32 |As with CAN.MASK16 but with only one 32 bit id/mask pair.|
+-----------+---------------------------------------------------------+
-
+
.. method:: can.clearfilter(bank)
Clear and disables a filter bank:
-
+
- ``bank`` is the filter bank that is to be cleared.
.. method:: can.any(fifo)
@@ -120,22 +120,56 @@ Methods
.. method:: can.recv(fifo, \*, timeout=5000)
Receive data on the bus:
-
+
- ``fifo`` is an integer, which is the FIFO to receive on
- ``timeout`` is the timeout in milliseconds to wait for the receive.
-
+
Return value: buffer of data bytes.
.. method:: can.send(send, addr, \*, timeout=5000)
Send a message on the bus:
-
+
- ``send`` is the data to send (an integer to send, or a buffer object).
- ``addr`` is the address to send to
- ``timeout`` is the timeout in milliseconds to wait for the send.
-
+
Return value: ``None``.
+.. method:: can.rxcallback(fifo, fun)
+
+ Register a function to be called when a message is accepted into a empty fifo:
+
+ - ``fifo`` is the receiving fifo.
+ - ``fun`` is the function to be called when the fifo becomes non empty.
+
+ The callback function takes two arguments the first is the can object it self the second is
+ a integer that indicates the reason for the callback.
+
+ +--------+------------------------------------------------+
+ | Reason | |
+ +========+================================================+
+ | 0 | A message has been accepted into a empty FIFO. |
+ +--------+------------------------------------------------+
+ | 1 | The FIFO is full |
+ +--------+------------------------------------------------+
+ | 2 | A message has been lost due to a full FIFO |
+ +--------+------------------------------------------------+
+
+ Example use of rxcallback::
+
+ def cb0(bus, reason):
+ print('cb0')
+ if reason == 0:
+ print('pending')
+ if reason == 1:
+ print('full')
+ if reason == 2:
+ print('overflow')
+
+ can = CAN(1, CAN.LOOPBACK)
+ can.rxcallback(0, cb0)
+
Constants
---------
@@ -151,4 +185,4 @@ Constants
.. data:: CAN.LIST32
.. data:: CAN.MASK32
- the operation mode of a filter
+ the operation mode of a filter
diff --git a/stmhal/can.c b/stmhal/can.c
index b10ecd13d9..654daddf6f 100644
--- a/stmhal/can.c
+++ b/stmhal/can.c
@@ -32,6 +32,8 @@
#include "py/nlr.h"
#include "py/objtuple.h"
#include "py/runtime.h"
+#include "py/gc.h"
+#include "py/pfenv.h"
#include "bufhelper.h"
#include "can.h"
#include "pybioctl.h"
@@ -64,14 +66,27 @@
/// can.send('message!', 123) # send message with id 123
/// can.recv(0) # receive message on FIFO 0
+typedef enum _rx_state_t {
+ RX_STATE_FIFO_EMPTY = 0,
+ RX_STATE_MESSAGE_PENDING,
+ RX_STATE_FIFO_FULL,
+ RX_STATE_FIFO_OVERFLOW,
+} rx_state_t;
+
typedef struct _pyb_can_obj_t {
mp_obj_base_t base;
+ mp_obj_t rxcallback0;
+ mp_obj_t rxcallback1;
mp_uint_t can_id : 8;
bool is_enabled : 1;
bool extframe : 1;
+ byte rx_state0;
+ byte rx_state1;
CAN_HandleTypeDef can;
} pyb_can_obj_t;
+STATIC mp_obj_t pyb_can_deinit(mp_obj_t self_in);
+
STATIC uint8_t can2_start_bank = 14;
// assumes Init parameters have been set up correctly
@@ -124,18 +139,18 @@ STATIC bool can_init(pyb_can_obj_t *can_obj) {
return true;
}
-STATIC void can_deinit(pyb_can_obj_t *can_obj) {
- can_obj->is_enabled = false;
- CAN_HandleTypeDef *can = &can_obj->can;
- HAL_CAN_DeInit(can);
- if (can->Instance == CAN1) {
- __CAN1_FORCE_RESET();
- __CAN1_RELEASE_RESET();
- __CAN1_CLK_DISABLE();
- } else if (can->Instance == CAN2) {
- __CAN2_FORCE_RESET();
- __CAN2_RELEASE_RESET();
- __CAN2_CLK_DISABLE();
+void can_init0(void) {
+ for (uint i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_can_obj_all)); i++) {
+ MP_STATE_PORT(pyb_can_obj_all)[i] = NULL;
+ }
+}
+
+void can_deinit(void) {
+ for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_can_obj_all)); i++) {
+ pyb_can_obj_t *can_obj = MP_STATE_PORT(pyb_can_obj_all)[i];
+ if (can_obj != NULL) {
+ pyb_can_deinit(can_obj);
+ }
}
}
@@ -260,6 +275,11 @@ STATIC mp_obj_t pyb_can_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n
} else {
o->can_id = mp_obj_get_int(args[0]);
}
+ o->rxcallback0 = mp_const_none;
+ o->rxcallback1 = mp_const_none;
+ MP_STATE_PORT(pyb_can_obj_all)[o->can_id - 1] = o;
+ o->rx_state0 = RX_STATE_FIFO_EMPTY;
+ o->rx_state1 = RX_STATE_FIFO_EMPTY;
if (n_args > 1 || n_kw > 0) {
// start the peripheral
@@ -280,7 +300,21 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_can_init_obj, 1, pyb_can_init);
/// Turn off the CAN bus.
STATIC mp_obj_t pyb_can_deinit(mp_obj_t self_in) {
pyb_can_obj_t *self = self_in;
- can_deinit(self);
+ self->is_enabled = false;
+ HAL_CAN_DeInit(&self->can);
+ if (self->can.Instance == CAN1) {
+ HAL_NVIC_DisableIRQ(CAN1_RX0_IRQn);
+ HAL_NVIC_DisableIRQ(CAN1_RX1_IRQn);
+ __CAN1_FORCE_RESET();
+ __CAN1_RELEASE_RESET();
+ __CAN1_CLK_DISABLE();
+ } else if (self->can.Instance == CAN2) {
+ HAL_NVIC_DisableIRQ(CAN2_RX0_IRQn);
+ HAL_NVIC_DisableIRQ(CAN2_RX1_IRQn);
+ __CAN2_FORCE_RESET();
+ __CAN2_RELEASE_RESET();
+ __CAN2_CLK_DISABLE();
+ }
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_can_deinit_obj, pyb_can_deinit);
@@ -334,7 +368,7 @@ STATIC mp_obj_t pyb_can_send(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_
// send the data
CanTxMsgTypeDef tx_msg;
- if (self->extframe){
+ if (self->extframe) {
tx_msg.ExtId = args[1].u_int & 0x1FFFFFFF;
tx_msg.IDE = CAN_ID_EXT;
} else {
@@ -385,6 +419,33 @@ STATIC mp_obj_t pyb_can_recv(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_
mp_hal_raise(status);
}
+ // Manage the rx state machine
+ if ((args[0].u_int == CAN_FIFO0 && self->rxcallback0 != mp_const_none) ||
+ (args[0].u_int == CAN_FIFO1 && self->rxcallback1 != mp_const_none)) {
+ byte *state = (args[0].u_int == CAN_FIFO0) ? &self->rx_state0 : &self->rx_state1;
+
+ switch (*state) {
+ case RX_STATE_FIFO_EMPTY:
+ break;
+ case RX_STATE_MESSAGE_PENDING:
+ if (__HAL_CAN_MSG_PENDING(&self->can, args[0].u_int) == 0) {
+ // Fifo is empty
+ __HAL_CAN_ENABLE_IT(&self->can, (args[0].u_int == CAN_FIFO0) ? CAN_IT_FMP0 : CAN_IT_FMP1);
+ *state = RX_STATE_FIFO_EMPTY;
+ }
+ break;
+ case RX_STATE_FIFO_FULL:
+ __HAL_CAN_ENABLE_IT(&self->can, (args[0].u_int == CAN_FIFO0) ? CAN_IT_FF0 : CAN_IT_FF1);
+ *state = RX_STATE_MESSAGE_PENDING;
+ break;
+ case RX_STATE_FIFO_OVERFLOW:
+ __HAL_CAN_ENABLE_IT(&self->can, (args[0].u_int == CAN_FIFO0) ? CAN_IT_FOV0 : CAN_IT_FOV1);
+ __HAL_CAN_ENABLE_IT(&self->can, (args[0].u_int == CAN_FIFO0) ? CAN_IT_FF0 : CAN_IT_FF1);
+ *state = RX_STATE_MESSAGE_PENDING;
+ break;
+ }
+ }
+
// return the received data
// TODO use a namedtuple (when namedtuple types can be stored in ROM)
mp_obj_tuple_t *tuple = mp_obj_new_tuple(4, NULL);
@@ -525,6 +586,39 @@ error:
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_can_setfilter_obj, 1, pyb_can_setfilter);
+STATIC mp_obj_t pyb_can_rxcallback(mp_obj_t self_in, mp_obj_t fifo_in, mp_obj_t callback_in) {
+ pyb_can_obj_t *self = self_in;
+ mp_int_t fifo = mp_obj_get_int(fifo_in);
+ mp_obj_t *callback;
+
+ callback = (fifo == 0) ? &self->rxcallback0 : &self->rxcallback1;
+ if (callback_in == mp_const_none) {
+ __HAL_CAN_DISABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FMP0 : CAN_IT_FMP1);
+ __HAL_CAN_DISABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FF0 : CAN_IT_FF1);
+ __HAL_CAN_DISABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FOV0 : CAN_IT_FOV1);
+ *callback = mp_const_none;
+ } else if (*callback != mp_const_none) {
+ // Rx call backs has already been initialized
+ // only the callback function should be changed
+ *callback = callback_in;
+ } else if (mp_obj_is_callable(callback_in)) {
+ *callback = callback_in;
+ uint32_t irq;
+ if (self->can_id == PYB_CAN_1) {
+ irq = (fifo == 0) ? CAN1_RX0_IRQn : CAN1_RX1_IRQn;
+ } else {
+ irq = (fifo == 0) ? CAN2_RX0_IRQn : CAN2_RX1_IRQn;
+ }
+ HAL_NVIC_SetPriority(irq, 7, 0);
+ HAL_NVIC_EnableIRQ(irq);
+ __HAL_CAN_ENABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FMP0 : CAN_IT_FMP1);
+ __HAL_CAN_ENABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FF0 : CAN_IT_FF1);
+ __HAL_CAN_ENABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FOV0 : CAN_IT_FOV1);
+ }
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(pyb_can_rxcallback_obj, pyb_can_rxcallback);
+
STATIC const mp_map_elem_t pyb_can_locals_dict_table[] = {
// instance methods
{ MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_can_init_obj },
@@ -535,6 +629,7 @@ STATIC const mp_map_elem_t pyb_can_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_initfilterbanks), (mp_obj_t)&pyb_can_initfilterbanks_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_setfilter), (mp_obj_t)&pyb_can_setfilter_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_clearfilter), (mp_obj_t)&pyb_can_clearfilter_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_rxcallback), (mp_obj_t)&pyb_can_rxcallback_obj },
// class constants
// Note: we use the ST constants >> 4 so they fit in a small-int. The
@@ -543,7 +638,6 @@ STATIC const mp_map_elem_t pyb_can_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_LOOPBACK), MP_OBJ_NEW_SMALL_INT(CAN_MODE_LOOPBACK >> 4) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_SILENT), MP_OBJ_NEW_SMALL_INT(CAN_MODE_SILENT >> 4) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_SILENT_LOOPBACK), MP_OBJ_NEW_SMALL_INT(CAN_MODE_SILENT_LOOPBACK >> 4) },
-
{ MP_OBJ_NEW_QSTR(MP_QSTR_MASK16), MP_OBJ_NEW_SMALL_INT(MASK16) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_LIST16), MP_OBJ_NEW_SMALL_INT(LIST16) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_MASK32), MP_OBJ_NEW_SMALL_INT(MASK32) },
@@ -573,6 +667,59 @@ mp_uint_t can_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *err
return ret;
}
+void can_rx_irq_handler(uint can_id, uint fifo_id) {
+ mp_obj_t callback;
+ pyb_can_obj_t *self;
+ mp_obj_t irq_reason = MP_OBJ_NEW_SMALL_INT(0);
+ byte *state;
+
+ self = MP_STATE_PORT(pyb_can_obj_all)[can_id - 1];
+
+ if (fifo_id == CAN_FIFO0) {
+ callback = self->rxcallback0;
+ state = &self->rx_state0;
+ } else {
+ callback = self->rxcallback1;
+ state = &self->rx_state1;
+ }
+
+ switch (*state) {
+ case RX_STATE_FIFO_EMPTY:
+ __HAL_CAN_DISABLE_IT(&self->can, (fifo_id == CAN_FIFO0) ? CAN_IT_FMP0 : CAN_IT_FMP1);
+ irq_reason = MP_OBJ_NEW_SMALL_INT(0);
+ *state = RX_STATE_MESSAGE_PENDING;
+ break;
+ case RX_STATE_MESSAGE_PENDING:
+ __HAL_CAN_DISABLE_IT(&self->can, (fifo_id == CAN_FIFO0) ? CAN_IT_FF0 : CAN_IT_FF1);
+ irq_reason = MP_OBJ_NEW_SMALL_INT(1);
+ *state = RX_STATE_FIFO_FULL;
+ break;
+ case RX_STATE_FIFO_FULL:
+ __HAL_CAN_DISABLE_IT(&self->can, (fifo_id == CAN_FIFO0) ? CAN_IT_FOV0 : CAN_IT_FOV1);
+ irq_reason = MP_OBJ_NEW_SMALL_INT(2);
+ *state = RX_STATE_FIFO_OVERFLOW;
+ break;
+ case RX_STATE_FIFO_OVERFLOW:
+ // This should never happen
+ break;
+ }
+
+ if (callback != mp_const_none) {
+ gc_lock();
+ nlr_buf_t nlr;
+ if (nlr_push(&nlr) == 0) {
+ mp_call_function_2(callback, self, irq_reason);
+ nlr_pop();
+ } else {
+ // Uncaught exception; disable the callback so it doesn't run again.
+ pyb_can_rxcallback(self, MP_OBJ_NEW_SMALL_INT(fifo_id), mp_const_none);
+ printf("uncaught exception in CAN(%u) rx interrupt handler\n", self->can_id);
+ mp_obj_print_exception(printf_wrapper, NULL, (mp_obj_t)nlr.ret_val);
+ }
+ gc_unlock();
+ }
+}
+
STATIC const mp_stream_p_t can_stream_p = {
//.read = can_read, // is read sensible for CAN?
//.write = can_write, // is write sensible for CAN?
diff --git a/stmhal/can.h b/stmhal/can.h
index 07b654d2e8..b9d431e79b 100644
--- a/stmhal/can.h
+++ b/stmhal/can.h
@@ -34,3 +34,7 @@
#endif
extern const mp_obj_type_t pyb_can_type;
+
+void can_init0(void);
+void can_deinit(void);
+void can_rx_irq_handler(uint can_id, uint fifo_id);
diff --git a/stmhal/main.c b/stmhal/main.c
index a53fd05ca2..f91d10d325 100644
--- a/stmhal/main.c
+++ b/stmhal/main.c
@@ -57,6 +57,7 @@
#include "accel.h"
#include "servo.h"
#include "dac.h"
+#include "can.h"
#include "modnetwork.h"
#include MICROPY_HAL_H
@@ -399,6 +400,9 @@ soft_reset:
extint_init0();
timer_init0();
uart_init0();
+#if MICROPY_HW_ENABLE_CAN
+ can_init0();
+#endif
#if MICROPY_HW_ENABLE_RNG
rng_init0();
@@ -540,6 +544,9 @@ soft_reset_exit:
printf("PYB: soft reboot\n");
timer_deinit();
uart_deinit();
+#if MICROPY_HW_ENABLE_CAN
+ can_deinit();
+#endif
first_soft_reset = false;
goto soft_reset;
diff --git a/stmhal/mpconfigport.h b/stmhal/mpconfigport.h
index 1c4008667d..0224d93f41 100644
--- a/stmhal/mpconfigport.h
+++ b/stmhal/mpconfigport.h
@@ -153,6 +153,9 @@ extern const struct _mp_obj_module_t mp_module_network;
/* pointers to all UART objects (if they have been created) */ \
struct _pyb_uart_obj_t *pyb_uart_obj_all[6]; \
\
+ /* pointers to all CAN objects (if they have been created) */ \
+ struct _pyb_can_obj_t *pyb_can_obj_all[2]; \
+ \
/* list of registered NICs */ \
mp_obj_list_t mod_network_nic_list; \
diff --git a/stmhal/qstrdefsport.h b/stmhal/qstrdefsport.h
index 85ab206756..62d9d191e1 100644
--- a/stmhal/qstrdefsport.h
+++ b/stmhal/qstrdefsport.h
@@ -211,6 +211,7 @@ Q(params)
Q(initfilterbanks)
Q(clearfilter)
Q(setfilter)
+Q(rxcallback)
Q(NORMAL)
Q(LOOPBACK)
Q(SILENT)
diff --git a/stmhal/stm32f4xx_it.c b/stmhal/stm32f4xx_it.c
index e54318e70d..f06c4081c3 100644
--- a/stmhal/stm32f4xx_it.c
+++ b/stmhal/stm32f4xx_it.c
@@ -75,6 +75,7 @@
#include "timer.h"
#include "uart.h"
#include "storage.h"
+#include "can.h"
extern void __fatal_error(const char*);
extern PCD_HandleTypeDef pcd_handle;
@@ -414,3 +415,21 @@ void UART4_IRQHandler(void) {
void USART6_IRQHandler(void) {
uart_irq_handler(6);
}
+
+#if MICROPY_HW_ENABLE_CAN
+void CAN1_RX0_IRQHandler(void) {
+ can_rx_irq_handler(PYB_CAN_1, CAN_FIFO0);
+}
+
+void CAN1_RX1_IRQHandler(void) {
+ can_rx_irq_handler(PYB_CAN_1, CAN_FIFO1);
+}
+
+void CAN2_RX0_IRQHandler(void) {
+ can_rx_irq_handler(PYB_CAN_2, CAN_FIFO0);
+}
+
+void CAN2_RX1_IRQHandler(void) {
+ can_rx_irq_handler(PYB_CAN_2, CAN_FIFO1);
+}
+#endif // MICROPY_HW_ENABLE_CAN
diff --git a/tests/pyb/can.py b/tests/pyb/can.py
index f1cad860b9..132da23069 100644
--- a/tests/pyb/can.py
+++ b/tests/pyb/can.py
@@ -48,3 +48,75 @@ else:
print('passed')
else:
print('failed, wrong data received')
+
+del can
+
+# Test RxCallbacks
+can = CAN(1, CAN.LOOPBACK)
+can.setfilter(0, CAN.LIST16, 0, (1, 2, 3, 4))
+can.setfilter(1, CAN.LIST16, 1, (5, 6, 7, 8))
+def cb0(bus, reason):
+ print('cb0')
+ if reason == 0:
+ print('pending')
+ if reason == 1:
+ print('full')
+ if reason == 2:
+ print('overflow')
+
+def cb1(bus, reason):
+ print('cb1')
+ if reason == 0:
+ print('pending')
+ if reason == 1:
+ print('full')
+ if reason == 2:
+ print('overflow')
+
+def cb0a(bus, reason):
+ print('cb0a')
+ if reason == 0:
+ print('pending')
+ if reason == 1:
+ print('full')
+ if reason == 2:
+ print('overflow')
+
+def cb1a(bus, reason):
+ print('cb1a')
+ if reason == 0:
+ print('pending')
+ if reason == 1:
+ print('full')
+ if reason == 2:
+ print('overflow')
+
+
+can.rxcallback(0, cb0)
+can.rxcallback(1, cb1)
+
+can.send('11111111',1)
+can.send('22222222',2)
+can.send('33333333',3)
+can.rxcallback(0, cb0a)
+can.send('44444444',4)
+
+can.send('55555555',5)
+can.send('66666666',6)
+can.send('77777777',7)
+can.rxcallback(1, cb1a)
+can.send('88888888',8)
+
+print(can.recv(0))
+print(can.recv(0))
+print(can.recv(0))
+print(can.recv(1))
+print(can.recv(1))
+print(can.recv(1))
+
+can.send('11111111',1)
+can.send('55555555',5)
+
+print(can.recv(0))
+print(can.recv(1))
+
diff --git a/tests/pyb/can.py.exp b/tests/pyb/can.py.exp
index 4058202046..b0ede7b9f4 100644
--- a/tests/pyb/can.py.exp
+++ b/tests/pyb/can.py.exp
@@ -8,3 +8,27 @@ True
passed
CAN(1, CAN.LOOPBACK, extframe=True)
passed
+cb0
+pending
+cb0
+full
+cb0a
+overflow
+cb1
+pending
+cb1
+full
+cb1a
+overflow
+(1, 0, 0, b'11111111')
+(2, 0, 1, b'22222222')
+(4, 0, 3, b'44444444')
+(5, 0, 0, b'55555555')
+(6, 0, 1, b'66666666')
+(8, 0, 3, b'88888888')
+cb0a
+pending
+cb1a
+pending
+(1, 0, 0, b'11111111')
+(5, 0, 0, b'55555555')