← index #18922PR #18992
Duplicate · high · value 0.767
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: Fix Classic CAN issue where initialising CAN1 corrupts CAN2.

openby projectgusopened 2026-03-25updated 2026-03-26
port-stm32

Summary

  • Closes #18922 - remaining part of this issue where initialising CAN1 after CAN2 on Classic CAN would clear CAN1's filters.
  • Extends test coverage to this case for both pyb.CAN and machine.CAN
  • Fixed another potential issue where de-initialising CAN2 would reset CAN1 in RCC block (not necessary to fix the test case from #18922, but may have shown up in other sequences.)
  • Extends test coverage to initialising 3x CAN peripherals at one time in any order.

This PR is marked draft until I get some hardware to address the last item.

Testing

  • Ran the updated unit tests on a PYBV11 (Classic CAN) and a STM32G4 (FDCAN). PYBV11 fails without this fix, passes with this fix.
  • Re-run ports/stm32/pyb_can*.py extmod_hardware/machine_can*.py unit tests, multi_extmod/machine_can_*.py multi_pyb_can/*.py multi-tests.

Trade-offs and Alternatives

  • The way pyb.CAN handles the CAN1/CAN2 filter split is a bit awkward, but as we want to focus on machine.CAN it's probably not worth spending more time thinking about it.

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