The Raspberry Pi company released version 1.5.0 of the Pico SDK a week ago—right after I issued the latest version of my desktop computer-oriented I²C adaptor, which is based on the Pico’s RP2040 chip. If I’d have known about the SDK update, I would have held back—SDK 1.5.0 solves an irritating problem I’d faced with unexplained adaptor firmware hangs.
Among the many improvements made by SDK 1.5.0 are some that centre on its support for stdio operations over UART and USB. My firmware uses the latter to receive control commands and data from a host computer and which it relays to connected I²C devices and GPIO pins.
It all works as expected—with one exception. Running scripts that frequently send commands and data to the adaptor will eventually cause the adaptor firmware to hang. This might happen after five commands or after 50, but it will occur. The board stops working—you can tell because the heartbeat flash of its LED or NeoPixel, depending on the board you’re using—will stop. The code running on the host computer loses contact with the board: it writes a command, but receives no response.
I’ve been struggling with this for some time. Manually sending a command to the board works perfectly, but scripts that send the commands would soon fail. Output from one of the board’s UARTs revealed my code wasn’t at fault, and upping the stack and heap sizes didn’t help. I couldn’t try monitoring the board with SWD because of long-running issues I’ve had getting Picoprobe to work.
So how does the new SDK help? Here’s the key text from the release notes: “pico_stdio allows for outputting from within an IRQ handler that creates the potential for deadlocks (especially with pico_stdio_usb)… The code has been revamped to avoid more deadlock cases, and a new define PICO_STDIO_DEADLOCK_TIMEOUT_MS has been added to catch remaining cases that might be caused by user level locking.”
So the SDK code underlying the adaptor firmware was getting caught in a cascade of interrupt requests making it unable to return to the initially interrupted code, or causing it to crash but not reboot.
SDK 1.5.0’s changes appear to eliminate the problem I was experiencing. As a test, I rebuilt the firmware with the new SDK, flashed it to an Adafruit Trinkey RP2040 with a four-digit, seven-segment LED attached and ran some Python code that displays the CPU utilisation every second. I set it going yesterday at around 3PM. It was still running at 9:30AM today. I have never seen that level of longevity before, and the minimal firmware changes I’ve made mean that it’s all down to the new SDK.
Rather the relying folks re-compiling the firmware with the new SDK, I’ve pushed out a new version of the firmware code with improvements and architectural changes I was going to make anyway, and with an update to the CMake build system configuration files to highlight the need to update to SDK 1.5.0.
The new version is available at this GitHub repo, and you can read more about it here.
Future work will centre on exploring the callback-on-receipt-of-data functionality also added to the new SDK with a view to replacing the current polling mechanism.
Other to-do list items include supporting higher, non-standard UART bit rates on Linux, and trying to understand why at one point macOS release builds were failing when debug builds worked as expected. There’s always something to figure out and fix…