makerbase-mks / CANable-MKS

157 stars 41 forks source link

buggy CAN transceiver init behavior #6

Open SebKuzminsky opened 4 months ago

SebKuzminsky commented 4 months ago

Hi there, I have a MKS-CANable v2.0 PRO which i think has slightly buggy behavior on init/reset. I'm using the candleLight firmware blob from this repo (https://github.com/makerbase-mks/CANable-MKS/tree/main/Firmware/CANable%20V2.0), but i think the problem is caused by a hardware bug that applies to any firmware.

The problem is that if you power cycle the CANable2 too quickly, it boots into firmware bootloader mode instead of into the firmware application. I think this because the STM32G431 uses the same pin, PB8-BOOT0, for both CAN_RX and BOOT0, and the ADM3050 CAN transceiver does not reset correctly during short power cycles.

I have some scope traces that demonstrate the problem. The scope traces show the following channels:

  1. Yellow: ADM3050 Vdd1 (power to the logic side of the isolated CAN transceiver)
  2. Blue: 3.3v rail powering the STM32
  3. Purple: the PB8-BOOT0 pin

Normal power-on - good

first-power-on All three signals start out low (powered down). I connect the USB cable and the 3.3V rail (channel 2, blue) immediately stabilizes, which causes the STM32 to start booting. The power supply to the ADM3050 CAN transceiver has not stabilized yet (channel 1, yellow), and it is not asserting CAN_RX, so the booting STM32 sees logic false on its PB8-BOOT0 pin (channel 3, purple). This causes the STM32 to boot into the firmware application as desired, so this situation is good.

Long power cycle - good

long-power-cycle Here I start with the CANable2 powered up and working, and i do a "long" power cycle, approximately 1,500 ms. At the left we see all three signals up and running normally. Note that the CAN_RX output from the ADM3050 is in the high state, indicating the CAN bus is in the recessive state (idle, or transmitting a logic 0). This is normal. I remove power, and all three signals drop to 0V. After 1,500 ms I reapply power, and the board boots in the same way as the "Normal power-on" above, as desired.

Short power cycle - bad

short-power-cycle This is where things go wrong. Here again I start with the board powered and running normally. Again i remove power, and all three signals go to 0V. But then i reapply power after only ~300 ms. Note that the logic power supply for the ADM 3050 immediately stabilizes, and the CAN transceiver immediately asserts CAN_RX. The booting STM32 sees logic true on its PB8-BOOT0 input pin, and incorrectly boots into the firmware boot loader instead of into the firmware application. At this point the board does not function as a USB to CAN adapter, and is instead sitting in its bootloader waiting to receive a new firmware application binary to write to its flash storage. The only way to restore functionality here is to do a long power cycle, so that the ADM3050 resets correctly and de-asserts CAN_RX, so that the STM32 sees a logic false on its PB8-BOOT0 pin and boots into the firmware application.

SebKuzminsky commented 4 months ago

I've worked around this "quick reboot" problem by setting the STM32G4 Flash Option Bytes to not get boot-time direction from the PB8-BOOT0 pin, and instead get it from a persistent register value stored in flash. This makes the board boot from flash every time, independent of the BOOT jumper, and independent of the CAN transceiver asserting CAN_RX at STM32 boot time.

I did this by connecting to the CANable2's SWD pins and running these commands in openocd:

reset halt

# Read current option bytes from "Flash option register (FLASH_OPTR) 0x20".
mdw 0x40022020
# 0x40022020: ffeffcaa

# read "Flash status register (FLASH_SR) 0x10", make sure bit 16 ("BUSY")
# is cleared.
mdw 0x40022010
#0x40022010: 00000000

# Read "Flash control register FLASH_CR 0x14", see that bit 31 "LOCK"
# and bit 30 "OPT LOCK" are both set.
mdw 0x40022014
# 0x40022014: c0000000

# Write flash unlock keys to the "Flash key register (FLASH_KEYR) 0x08)"
# to unlock "Flash control register (FLASH_CR) 0x14".
mww 0x40022008 0x45670123
mww 0x40022008 0xCDEF89AB

# verify FLASH_CR 0x14 bit 31 is cleared
mdw 0x40022014
# 0x40022014: 40000000

# Write flash option unlock keys to the "Flash option key register
# (FLASH_OPTKEYR) 0x0c" to unlock option bytes.
mww 0x4002200c 0x08192A3B
mww 0x4002200c 0x4C5D6E7F

# verify FLASH_CR 0x14 bit 30 OPT LOCK is cleared
mdw 0x40022014
# 0x40022014: 00000000

# Write the new option bytes to the "Flash option register (FLASH_OPTR)
# 0x20".  This should be the original value (0xffeffcaa), but with
# these changes:
#
# nSWBOOT0 (bit 26):
#     0: FLASH_OPTR.nBoot0 selects boot target
#     1: PB8-BOOT0 selects boot target
#
# nBOOT0 (bit 27), nBOOT1 (bit 23):
#     1, X: flash ("fw app")
#     0, 1: System Memory (ST ROM bootloader)
#     0, 0: SRAM1 (dont use this)

# original default
# PB8-BOOT0 pin selects boot target, flash app or ST ROM bootloader
# nSWBOOT0=1, nBOOT0=1, bBOOT1=1
#mww 0x40022020 0xffeffcaa

# boot to flash
# nSWBOOT0=0, nBOOT0=1, bBOOT1=1
mww 0x40022020 0xfbeffcaa

# boot to System Memory (ST ROM) bootloader
# nSWBOOT0=0, nBOOT0=0, bBOOT1=1
#mww 0x40022020 0xf3effcaa

# Set the Options Start bit OPTSTRT in the Flash control register (FLASH_CR)
mww 0x40022014 0x00020000

# read "Flash status register (FLASH_SR) 0x10", make sure bit 16 ("BUSY")
# is cleared.
mdw 0x40022010
#0x40022010: 00000000

This workaround gets my project moving, but it's unfortunate that it's needed.