← index #18922PR #18946
Duplicate · high · value 0.965
QUERY · ISSUE

STM32 pyb.CAN: Instantiating one CAN instance can corrupts settings of another instance.

openby chrismas9opened 2026-03-13updated 2026-03-20
bugport-stm32

Port, board and/or hardware

STM32 PYBv1.0

MicroPython version

MicroPython v1.28.0-preview.259.g4625f97d09.dirty on 2026-03-13; PYBv1.0 with STM32F405RG

Reproduction

I found a problem with my pyb.CAN test script. Creating an instance of CAN2 corrupts the settings of CAN1, probably because they share a hardware block. I wrote the test way back when I did the F413 port. F413 has three CAN ports. I create and initialise up to three instances in a loop. It works on 1.17, but not on 1.28, possibly since the introduction of FDCAN.

Minimum code demonstrating the problem. This runs on PYBv1.0

from pyb import CAN
can1 = CAN(1, CAN.LOOPBACK)
can1.setfilter(0, CAN.LIST16, 0, (123, 124, 125, 126))  # set a filter to receive messages with id=123, 124, 125 and 126
can2 = CAN(2, CAN.LOOPBACK)
can2.setfilter(0, CAN.LIST16, 0, (123, 124, 125, 126))  # set a filter to receive messages with id=123, 124, 125 and 126

can1.send('message1', 123)   	# send a message with id 123
print(can1.recv(0, timeout=50)) # receive message on FIFO 0
can2.send('message2', 123)   	# send a message with id 123
print(can2.recv(0, timeout=50)) # receive message on FIFO 0

Expected behaviour

Works on 1.17

(123, False, False, 0, b'message1')
(123, False, False, 0, b'message2')

Observed behaviour

Fails on 1.28

Traceback (most recent call last):
  File "<stdin>", line 8, in <module>
OSError: [Errno 110] ETIMEDOUT

Additional Information

Moving can1.setfilter fixes it.

can1 = CAN(1, CAN.LOOPBACK)
can2 = CAN(2, CAN.LOOPBACK)
can1.setfilter(0, CAN.LIST16, 0, (123, 124, 125, 126))  # set a filter to receive messages with id=123, 124, 125 and 126
can2.setfilter(0, CAN.LIST16, 0, (123, 124, 125, 126))  # set a filter to receive messages with id=123, 124, 125 and 126

I don't think I can help debug this, but I am happy to help with testing on different STM32 MCUs.

This is my original loopback test script for F413. It is a cutdown version of an external CAN test that requires CAN tranceivers.

available_CANs = [1, 2, 3] is for F413.
available_CANs = [1, 2] works on PYBv3 1.14, fails on PYBv1.0 1.28
available_CANs = [2,1] passes for CAN2, then fails for CAN1
available_CANs = [1,] passes.

Note also that the receive message position changed in1.19 and I have updated th script to handle that.

# MicroPython CAN loopback test.
# This test works on all boards with one or more CAN port.
# No external hardware is required.
# Set which CAN controllers to test in "available_CANs".
import pyb
from pyb import CAN
import sys
if sys.implementation[1][1] >18:
    msg_idx = 4
else: 
    msg_idx = 3


# -----------------------------
available_CANs = [1, 2, 3]
# -----------------------------

cans = []

for i in range(0, max(available_CANs)):
    cans.append('none')
    if (i+1) in available_CANs:
        cans[i] = CAN(i+1, CAN.LOOPBACK)
        cans[i].setfilter(0, CAN.LIST16, 0, (123, 124, 125, 126))   # set a filter to receive messages with id=123, 124, 125 and 126

# To fix this for 1.27 comment out the line above and enable the next two lines.

#for i in available_CANs:
#    cans[i-1].setfilter(0, CAN.LIST16, 0, (123, 124, 125, 126))   # set a filter to receive messages with id=123, 124, 125 and 126

def CAN_send(s):
    message = 'CAN ' + str(s) + '->'
    cans[s-1].send(message, 123)                            # send a message with id 123
        
def CAN_recv(r):
    res = cans[r-1].recv(0, timeout=50)                     # receive message on FIFO 0
    return res

try:   
    while True:
        for i in available_CANs:
            CAN_send(i)
            pyb.delay(100)
            res = CAN_recv(i)
            print(res[msg_idx].decode("utf-8") + str(i), " passed internal loopback") # change index to 3 for older versions.
        print('----------------------------------')
                
finally:
    print('FAIL, CAN dev', i, '\n')

Code of Conduct

Yes, I agree

CANDIDATE · PULL REQUEST

stm32: Update pyb.CAN loopback unit tests for FDCAN, fix CAN2 filter bug

mergedby projectgusopened 2026-03-18updated 2026-03-18
port-stm32

Summary

  • Updates the loopback unit tests in tests/ports/stm32/can*.py to pass on CANFD hardware, renames them to pyb_can*.py to account for incoming machine.CAN functionality.
  • As part of updating pyb_can2.py to work with FDCAN, was also able to fix #18922 and make that unit test a regression test for it.
  • Cleaned up some parameter names and types for can_clearfilter() as they weren't very clear.
  • Enabled CAN2 peripheral on the NUCLEO_G474RE board, as used for testing.

This work is to support #18572 - now tests pass for pyb.CAN we can verify there's no regression when adding machine.CAN. Have made into a separate PR as the other is already too big!

Testing

  • Ran ports/stm32/pyb_can*.py unit tests on PYBDV11 (Classic CAN), NUCLEO_G474RE and NUCLEO_H723ZG boards (both flavours of FDCAN). All now passing.
  • Connected a CAN transceiver to CAN2 on NUCLEO_G474RE and confirmed the CAN bus also works on the configured pins in non-loopback mode.
  • NUCLEO_H723ZG doesn't have externally usable FDCAN2 pins without changing solder jumper settings (and possibly other things), so I didn't commit the change to this PR as it's only immediately usable for loopback tests. For refernece, the patch for loopback testing is:
--- a/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.h
+++ b/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.h
@@ -97,6 +97,10 @@
 #define MICROPY_HW_CAN1_TX                  (pin_D1)
 #define MICROPY_HW_CAN1_RX                  (pin_D0)
 
+#define MICROPY_HW_CAN2_NAME                "FDCAN2"
+#define MICROPY_HW_CAN2_TX                  (pin_B13) // B13, B6
+#define MICROPY_HW_CAN2_RX                  (pin_B12) // B12, B5
+
 // SD card detect switch
 #define MICROPY_HW_SDCARD_DETECT_PIN        (pin_G2)
 #define MICROPY_HW_SDCARD_DETECT_PULL       (GPIO_PULLUP)

Generative AI

I did not use generative AI tools when creating this PR.

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied