stm32: Add machine.CAN low-level driver#18572
Conversation
1547a8d to
fc11501
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #18572 +/- ##
=======================================
Coverage 98.45% 98.45%
=======================================
Files 175 175
Lines 22635 22646 +11
=======================================
+ Hits 22286 22297 +11
Misses 349 349 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Code size report: |
fc11501 to
3b52285
Compare
|
Thanks for keeping at this! I've been using the previous branch for a while. |
|
Thanks for putting this PR up! I tested with 2x PYBV10 connected through some CAN tranceivers and the new tests added here all pass. Testing the existing I noticed there's not a simple multitest that just sends and recv's a few frames, without using IRQs. I managed to get something simple working with a poll on |
3b52285 to
eb7affc
Compare
|
Still quite a bit to do (next year now), but it's coming along! Also have some tests written for error state transitions and restart, but they need a little bit of tidying up. |
3e498df to
c94131c
Compare
c94131c to
1908837
Compare
1cae2ab to
efee6da
Compare
4732f7e to
86c57a0
Compare
|
I think this is ready for review, I've updated the PR description with the current status. Still plenty of follow-up work (also listed in description). The one thing I'm not 100% happy with is the code size hit of +4400 bytes on PYBV10. If I manually disable pyb.CAN and rebuild I get back 3864 bytes, so the new driver is not a lot bigger than the old driver (and has a more flexible API design) - but it's still a big hit. |
|
After closer inspection, there are a few remaining bug which must be fixed before this PR can be merged. Background: @andrewleech has been working on an LLM review bot which @mattytrentini tested against this PR. See the results here: mattytrentini#35 . I went through the LLM review very carefully and triaged all of its comments (see linked PR). I will repeat/rephrase the important ones here. |
|
|
||
| machine_can_obj_t *self = MP_STATE_PORT(machine_can_objs)[can_idx]; | ||
| if (self == NULL) { | ||
| self = mp_obj_malloc(machine_can_obj_t, &machine_can_type); |
There was a problem hiding this comment.
Not needed now, but I think we need to eventually define an mp_obj_malloc0() and use it in places like this where all fields need to be initialised to zero (in your previous revision of this part of the code it did zero everything out explicitly by assigning to the struct).
|
Just to clarify my review comments above: a lot of the points are minor but I added them because I was anyway doing a full re-review and they caught my attention. The show stoppers for this PR are: null dereference in IRQ handler, messed up mode constants for FDCAN due to Also, a fourth show stopper is that the existing
One final point: the existing |
|
It looks like testing CAN2 properly is going to take a little while, so suggest bumping this PR to 1.29. Sorry about |
|
@projectgus If you are testing CAN2 you may be interested in an issue I just found 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.25, possibly since the introduction of FDCAN. Normally I do hardware abstraction (instantiation) first, then inititialisation, so its not a problem in my applications, just the test code. I don't know if this is considered an issue. Should I raise an issue for pyb.CAN? Minimum code demonstrating the problem. This should run on a pyboard. Result Moving can1.setfilter fixes it. |
|
@chrismas9 thanks for the report. I think we should open a separate issue for that, because it's not related to this PR. Are you able to do that? |
|
I have raised an issue for pyb.CAN. I only raised it here because machine.CAN maybe should be tested for the same issue. #18922 STM32 pyb.CAN: Intantiating second instance corrupts settings of first instance. |
Simplifies the pattern of an optional arg which can be a list of at least a certain length, otherwise one is lazily initialised. Modify pyb.CAN and ESP-NOW APIs to use the helper. Note this changes the return type of pyb.CAN.recv() from tuple to list. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <[email protected]>
This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <[email protected]>
f69842e to
2332712
Compare
API is different to the original machine.CAN proposal, as numerous shortcomings were found during initial implementation. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <[email protected]>
These are oddly missing from the STM32G4 HAL, but the reference manual describes being able to use them and the implementations seem to work as expected. Note that unlike STM32H7 it doesn't seem like we must use this approach, because HAL_FDCAN_AddMessageToTxFifoQ() does seem to not have the issues with priority inversion seen on the H7. However it's simpler to use the same API for both... Signed-off-by: Angus Gratton <[email protected]> Signed-off-by: Angus Gratton <[email protected]>
Implemented according to API docs in a parent comment. Adds new multi_extmod/machine_can_* tests which pass when testing between NUCLEO_G474RE, NUCLEO_H723ZG and PYBDV11. This work was mostly funded through GitHub Sponsors. Signed-off-by: Angus Gratton <[email protected]>
2332712 to
6cac2d2
Compare
|
I think this is ready again. I believe I've addressed everything, plus re-tested - including the pyb.CAN unit tests with the changes from #18946 included, and a newly added unit test for
Have put this in its own PR: #18951 |
dpgeorge
left a comment
There was a problem hiding this comment.
Thanks for updating and addressing all the feedback. Much appreciated.
I tested on:
- PYBV10 standalone (tests bxCAN, CAN1 and CAN2)
- PYBD_SF2 standalone (tests bxCAN, CAN1 only)
- NUCLEO_H723ZG (tests FDCAD, CAN1 only)
- PYBV10 - PYBV10 multi tests
Everything I tested passes.
|
🎉 🚀 🎉 🚀 |
|
Hats off to @projectgus for working on this for so long and seeing it through. Doing the initial docs draft, the common bindings, stm32 refactoring and implementation (for both bxCAN and FDCAN peripherals), extensive tests and testing, and final docs. A lot of work went into this. |
|
Hurrah, thanks @dpgeorge. Let's hope it goes to good use! |
It will. I have an automotive / industrial control project coming up that will put it to good use. |
| #define MICROPY_PY_MACHINE_BITSTREAM (1) | ||
| #endif | ||
| #ifndef MICROPY_PY_MACHINE_CAN | ||
| #ifdef MICROPY_HW_CAN1_TX |
There was a problem hiding this comment.
@projectgus @dpgeorge Is there any reason why this is not enabled for CAN2_TX too? I have boards that use CAN2, and I just added a || defined(CAN2_TX), am I missing something?
There was a problem hiding this comment.
I think originally the thought was that if the board has two CAN peripherals then both CAN1 and CAN2 will be enabled.
But I guess it's possible that only CAN2 is enabled. So changing as you suggest makes sense.
Summary
This work follows on from #13149 and closes #12337. The goal is to have a
machine.CANAPI that can be used on multiple ports, and an implementation that works on stm32 port. The existing pyb.CAN API is being kept alongside this.The
machine.CANAPI has changed substantially from the draft submitted in #13149. See comment with more explanation about what happened.There's also one change in the old
pyb.CANAPI, which is thatrecv()now always returns a list rather than either a list or a tuple depending on how it is called. This allows using a new shared helper function that's added in one of the commits, but this change could also be pulled out at a small code size cost.Status
Documented functionality is implemented, and the supplied new tests all pass.
CAN.recv()error flags and add test coverage.CAN.get_state(),CAN.Stateenum,CAN.IRQ_STATEinterrupt trigger.CAN.get_counters()CAN.get_timings()CAN.reset()andCAN.restart(), and/or simplify the API to remove one of these.CAN.init(mode=x)and add test coverage. (Note: Previously there was going to be aCAN.mode()function to change modes on the fly, but this isn't really supported by the controller so instead have todeinit()and theninit()into a new mode. There is test coverage for doing this.pyb.CANstill works, usingmulti_pyb_cantests.Future PRs
target_wiringin themulti_extmod/machine_can_*tests. Currently these test cases are all hard-wired for CAN instance 1.can&aiocanmodules in micropython-lib. The goal ofmachine.CANis to only implement the bare bones needed to make a higher level "Pythonic" CAN library on top.Testing
Ran
pyb.CANand newmachine.CANtests between pairs ofNUCLEO_H723ZGboard (fdcan peripheral),NUCLEO_G474REboard (simpler fdcan peripheral), and aPYBDV11(bxCAN peripheral).Specific commands:
and for each pairing of the three boards:
Also ran the all the same tests with assertions on for all three boards (setting
DEBUG=1plusCOPT=-Osfor the G4 board). Some timeouts needed tweaking for these tests to pass.In order to run
machine_can2.pyandpyb_can2.pytests on STM32H7, the same patch described in #18946 was applied.Trade-offs and Alternatives
micropython-liblibraries, but it should be easier to implement all of it in Python now the Python-C interface is simpler.