Add support for multichannel, CAN-FD, and STM32G4#176
Conversation
The USBD_GS_CAN_SendToHost() function is used to send to struct gs_host_frame_object to the host. Until this patch, after the sending process has been started, the outgoing frame object is already added to the list of free objects and the variable USBD_GS_CAN_HandleTypeDef::TxState is used to track if the transfer to the host is in progress. Instead, hold the outgoing object in USBD_GS_CAN_HandleTypeDef::to_host_buf and move it to the free list after the transfer is finished in USBD_GS_CAN_DataIn(). Use this to track if a transfer is ongoing.
…eceive() with IRQs enabled
…te functions No functional change intended.
No functional change.
…N_CfgDesc is copied to RAM
…e feature for CAN-FD frames
...having so many arguments doesn't scale.
Add code to support the M_CAN core found on the newer STM32 devices. Co-developed-by: Ryan Edwards <ryan.edwards@gmail.com> Co-developed-by: Jonas Martin <j.martin@pengutronix.de> Co-developed-by: Venelin Efremov <ghent360@iqury.us> Co-developed-by: Phil Greenland <phil@beamconnectivity.com> Co-developed-by: Marc Kleine-Budde <mkl@pengutronix.de>
|
Hello, I did some simple testing with this firmware, but run into issues. I am using MKS canable 2 Pro. Compiling, flashing, and configuring worked apparently fine. My problem: Receiving frames with candump works, but sending does not: It gets more confusing: I actually got it working if I had another gs_usb compatible device (for example MKS canable 2.0 with the old firmware from canable.io (available only in binary)) connected before and than replace it with CANable2-MKS_fw on it. So, if you have can0 working it will continue to work. But if you update the USB adapter with this firmware, it will stop working after a reboot or usbreset. Now, candump receives frames, cansend will not work anymore and I also see this error in dmesg: I tried linux kernel 5.15 and 6.12. I also tried #define CONFIG_CANFD 0 in config.h -> BOARD_CANable2_MKS, without success. Is there anything I need to do differently in ifupdown? Because this error doesn't show up when another gs_usb device was connected and working before it might have something to do how gs_usb is initialized in this fw? EDIT: I also have tried this branch with MKS canable 1.0 (CANable_MKS_fw) and don't see this issue (usbreset and bring it back works fine) |
|
@TheFrog4u Looking back at my emails I created the CANable2_MKS_fw target based on a board from AliExpress that a reader of my website had requested. At the time I tested it with Linux 6.1 and it was seemingly working fine and performing well. For your particular error, agreed thats very strange, its not something I've seen before. You should probably treat this PR as abandoned, I invested a lot of time in it at the time, testing the various STM families I had to hand, only for it to sit. I believe @marckleinebudde 's has continued on his branch. You should be able to port the CANable2_MKS_fw target across quite easily. He also maintains the gs_can kernel driver, so would be in a better position to advice on how you might debug. I've been using my STM32G0 based dual channel board on and off since creating it without any issues based on this PR, currently using an Ubuntu provided 6.8 kernel. I've had no need to make any further changes to it. Day to day, the CAN FD enabled projects I've been working on for customers are making use of the higher bandwidth of CAN FD. As such the USB full speed 12Mbit link has become a major bottleneck and my adapter doesn't see all that much use anymore. It's been replaced by a couple of Peak System PCAN-USB FDs. If I was to pickup development on this again, I'd be looking to switch to an STM32 family with USB high speed. Along with getting the hardware RX timestamping in, which I saw floating around a while ago. This is something I use frequently with the peak to check periodic transmissions are meeting their expected timings. Sorry I can't be of more assistance. |
|
I'll add on because I haven't participated in a while. I can confirm everything @pgreenland has said about the CANFD + USBFS troubles. We ran into the same issue - not as much with the 12Mbps as much as the 1ms USB SOF rate limit. Not sure why STM32 didn't have the foresight to add internal HS PHY to their CANFD silicon. Assume that CANFD is not one of their main business cases (NXP, Renesas and Infineon are the defacto automotive uC suppliers). Also, most higher level tools use FPGAs to handle CAN. There are no STM32 chips that support CANFD + USBHS internal PHY. The H5/H7 have parts that support external ULPI PHY but almost all of them you lose a CANFD channel when you enable the ULPI pins. The amount of work that would go into implementing HS and CANFD on the STM32 wasn't worth the effort for me. My other plan was to use the RMII Ethernet MAC as my host connection. Issue there is that I'd need to write my own driver. Again, not worth the effort given the other tools on the market that already have drivers. If you really want CANFD + USBHS the best option is an Atmel SAME70 or iMXRT (Teensy). They support CANFD plus an internal HSPHY. Not sure if any of them have GS-USB ports out there. The Atmel HAL is a joke, though, and wouldn't touch that with a 10ft pole. We have one internal tool that uses it and I spent months battling the HAL and the total lack of documentation. I think the zephyr project has some discussions on a gsusb port (zephyrproject-rtos/zephyr#75207). Also, the HW timestamp for the STM32 is laughable. It's 16-bit so I haven't tried to do anything to create a secondary wider timer. Most 3rd party tools have 32 or even 64 bit timestamps. I still use the G0 and G4 for simple tools that don't need to handle high bus loading. Good for diagnostics and busses that are just CAN2.0. Hope these points help. |
|
@ryedwards Super helpful! I hadn't considered an Ethernet connected adapter until now.....if there was some way to integrate it with socketcan, especially on multiple machines at once that would be the dream. I suppose some sort of host application / SLCAN monstrosity might get it off the ground. Unless a tame kernel developer can be sold the dream too. Part of my attraction to the candlelight project was the low cost of entry for the hardware. Paying over £300 in the UK for each FD adapter feels like highway robbery, had it not been for work I wouldn't have touched them. For general CAN 2.0 / hobbyist FD use the project still holds a lot of potential. You've also saved me from the rabbit hole of pin mappings. I've been working on an H7 based project with dual CAN FD and Ethernet at present....I'm amazed I hadn't considered the connection....although had scrolled past the high speed usb section in the manual. An Ethernet connected adapter might be easier to use from Windows too....gs_can and libusb sounded like a less than fun combination. You could fit 23 FD payloads in a UDP packet....ignoring any headers. I think I feel a weekend project coming on......time to crash the Linux kernel, I mean....write a new can driver 😅 |
Going into boot/dfu mode after short power cycle is a hardware issue in canable2. CAN Rx and Boot0 are the same pin: schematic I studied the datasheet and there is a workaround for this issue: You can disable boot function of the PB8-Boot0 pin by setting nBOOT_SEL. When cleared (0), it enables the BOOT0 pin to control the boot mode. When the bit is set (1), it forces the boot mode to be controlled by the BOOT1 option bit and the BOOT0 pin is ignored. Apparently you can do that with STM32Cube or modifying the firmware. (Also Canable 2.5 has this feature implemented with a special protocol). To add this to candlelight_fw for this device is probably only a few lines of code but I actually have no idea how to do it (no experience with stm32, I am here for the CAN ) - maybe someone could help me with a code snippet? Edit: Found an example how to disable BOOT0 pin: See function system_set_option_bytes() here You should still be able to go into bootloader / dfu mode using dfu-util. I think most people don't know this, but if you have gs_usb compatible firmware running on the device you can do: With that "trick" you can go from normal operation to flashing and back without even touching the device. No setting of boot jumper required (does not work for slcan). If the device doesn't come back after flashing it is most probably a firmware bug. Worked fine with me when running cable.io precompiled candlelight_fw and than flashing Canable 2.5 firmware. |
Use single buffer as @pgreenland mentioned in their PR. Link: candle-usb/candleLight_fw#176 (comment)
Use single buffer as @pgreenland mentioned in their PR. Link: candle-usb/candleLight_fw#176 (comment)
Use single buffer as @pgreenland mentioned in their PR. Link: candle-usb/candleLight_fw#176 (comment)
Use single buffer as @pgreenland mentioned in their PR. Link: candle-usb/candleLight_fw#176 (comment)
Use single buffer as @pgreenland mentioned in their PR. Link: candle-usb/candleLight_fw#176 (comment)
I've been using firmware from something that should be pretty similar to this PR's and think I can explain this. It doesn't handle USB resets correctly and will stop processing incoming transfers on the bulk endpoint from the USB host after the 2nd reset. This is called from the main loop and starts new USB transmit/receives whenever the previous one has finished and there's more to send/free buffers available to receive into. Coming up from a power-on reset, On subsequent USB resets, the reset handling from the ISR leaves the endpoint set to NAK all transmissions and In It probably doesn't come up for most people because most adapters are bus powered, so unplugging will do a power-on-reset anyway, so it will work unless the host is specifically resetting and reconfiguring more than once without unplugging |
Use single buffer as @pgreenland mentioned in their PR. Link: candle-usb/candleLight_fw#176 (comment)
|
Jumping in here. I recently found the canable 2.0 dongle and also would love to use this (or actually we're going to design our own similar to what @pgreenland did) because we spend a fortune on the $350 CAN-FD dongles. I just flashed this branch to the G4 and it's working great, we have very low bus load today but my company would love to continue using this solution. Let me know if I can be helpful, otherwise I will be here anxiously awaiting. |
|
A few more details on my setup, we use CAN-FD with nominal rate of 1Mbps and dbitrate of 5Mbps. With this build I can plug into my host and it shows up as a socketcan interface with no issue (so far). |
|
I rebased this branch to current master (https://github.com/DanielT/candleLight_fw/tree/stm32g4_canable_20) |
|
This PR is pretty big, and it's somewhat important to me now. I also found the bug that @chris-dahlberg found and I think can get it into a state where it no longer sends messages; even without a usbreset. |
|
I wanted to use this fork for a custom STM32G0B1 based board I made. However, I cannot get the USB communication to work, at all. I looked into the communcation with Wireshark and I can see that all The output from dmesg: I tried a bit of debugging of the firmware myself and as far as I can tell everything is in order. The only thing that stood out to me was how none of the callbacks passed to the |
|
Hey @spnda, please try my multichannel branch https://github.com/marckleinebudde/candleLight_fw/tree/multichannel, it should work with stm32g0b1, just tested. |
|
@marckleinebudde That branch shows the same behaviour. Could this perhaps be a hardware issue? My USB wiring is... iffy since I did it with copper cables and soldered directly to pins and cut the traces since I fucked up the PCB design. It does negotiate to full-speed USB (in earlier soldering attempts it only negotiated to low-speed USB), which is why I thought I could rule out a hardware problem. I just don't want to re-order a PCB if I cannot be certain that that is the actual issue at hand... I don't want to pollute this thread much more either. |
|
@spnda can you check if you've swapped the USB DP and DM lines? |
|
@marckleinebudde My PCB fuckup is that I swapped DP and DM. It would only enumerate as a low-speed device then. I swapped them with some suspicious soldering and copper wires and now it enumerates as a full-speed device but that's the status quo right now where I can't get it to work. |
|
Ah ok. This still sounds like a HW problem. |
|
@spnda If your hardware config allows it, may be worth trying to enter the integrated bootloader and test USB DFU communications with say ST's STM32CubeProgrammer app. The stuff on this PR was definitely working on the G0 as my custom board makes use of the same part. @marckleinebudde 's PR would be the one to run going forwards though as it has many improvements. |
|
This PR has still some patches for me to pick up. |
I originally flashed the STM through |
Picked up where @marckleinebudde left off.
Resolved the performance issue, which appeared exactly as @fenugrec had described in his comment.
Added support for the STM32G4, specifically the Makerbase MKS Canable2 (which presumably is based on the canable.io Canable 2.0, although I couldn't find a schematic for the canable.io to compare). A bit of scope creep granted, my goals were support for this board and improved performance for my G0 board.
Followed @fenugrec 's tests using:
Along with the following for canfd as canbusload doesn't support fd frames at present:
Have tested with an F0 based board (DSD_TECH_SH_C30A_fw), G0 based board (my own dual channel adapter) and a G4 based board (CANable2_MKS_fw). Couldn't find anything with an F4 on to test with unfortunately. Although there's only a dev board listed, so may not be too much of an issue?
For reference details of my G0 based adapter can be found on my blog, and board files on a separate branch.
Tested TX and RX performance for each unit, with the same PC/USB controller (and port). Communicating with a Peak PCAN-FD USB adapter, using Linux 6.1.0 on Debian.
Performance is as follows (in frames per second):
The poor / variable performance in CAN FD mode appears to be partly down to how the gs_usb driver in the Linux kernel behaves.
The MCU (on Linux) during RX sends an appropriately sized frame (either a classic or FD depending on the payload).
As such CAN2.0 RX performance is maintained regardless of operating mode.
The linux driver, in FD mode, appears to always sends FD sized frames, even for CAN2.0 frames, see: https://elixir.bootlin.com/linux/v6.1/source/drivers/net/can/usb/gs_usb.c#L778
The performance issue was found to be the error frame generation. Although it isn't fully clear how its changed the performance so significantly when compared to the master branch. I stopped short of reading through assembly.
Given the main loop coordinates both USB and CAN, I added a simple loop iteration counter. Measuring the number of iterations of the main loop performed per second. I did this on the F0 which is supported by both branches.
multichannel main loop iterations per second
with error checking 121156
without error checking 229495
master main loop iterations per second
with error checking 137564
without error checking 242311
It appears that retrieving a frame from the free pool, partially preparing before usually discarding an error frame on each iteration of the loop is quite time consuming.
As the error parsing / frame generation code looked fairly tidy and tricky to split, I've opted to only run it when there's a change in the controller error status register, for which it might want to generate an error frame.
For the G0 and G4 families I've also added bus-off recovery. Unlike the F0's bx_can module, the m_can module will not perform automatic bus-off recovery. Requiring software to detect its fallen back into initialisation mode in response to bus-off and request it advance back into normal mode. Passed the highly advanced screwdriver between CAN L/H after that.
Found one slightly issue which I haven't been able to resolve with the G4 yet. It has problems with the double buffered USB endpoint used for CAN transmission. It performs as expected in CAN2.0 mode but confuses the kernel in CAN FD mode, eventually causing the gs_usb driver to freeze and stop transmitting (due to its 10 echo IDs being exhaused). Switching it to single buffered mode solves the issue, without a notable performance decrease. I've left double buffering enabled for the other targets which don't appear to suffer from whatever's happening in the stack/hw.