Just published Chapter 1 of my Raspberry Pi Pico Ada tutorial series: “Blinking LEDs – Getting Started with Ada on the Pico”

Starts with Jeremy Grosser’s classic blink, moves on to explicit pin control, renames for clean external LED code, and ends with a look at (the still-experimental) Ravenscar tasking on RP2040.

All in pure Ada, with GNATdoc links and my Makefile improvements along the way.

Feedback very welcome – happy blinking! :rocket:

10 Likes

Thanks very much for this work and for sharing this. Not sure if it’s just me but I’m getting a 404 when trying to access this page. :thinking:

1 Like

Yesterday the link still worked for me. But looking at the domain you can find all articles on the bottom of the page.

If I am not mistaken this is what you are looking for.

1 Like

Nice one thanks, that works! (am accessing it through a mobile browser so, as usual, things get a bit more squashed.. :melting_face:)

1 Like

Ups. I renamed the page to be more in line with the original C-Tutorial. It’s now:

and:

1 Like

Thank you for your work!

This should be a great series! I’m following with interest. I’m looking forward to getting to “Chapter 30. WiFi Working Modes” so we can try out my WiFi driver together.

I’d like to share my experience with the RP Pico. I’m not much of a soldering enthusiast, so I didn’t solder in the reset button or even the SWD connector. Instead, I run a debugger via a USB cable using the pico-debug firmware and a second CPU to debug the code on the first CPU. Jeremy mentions this method on his website (See “Flashing the Pico“). Here’s how it works:

  1. Download pico-debug-gimmecache.uf2 from GitHub Release and drop it on the RP Pico. It will stay there until the power is off. (Not sure about reset, because I don’t have the reset button :grin:)
  2. Then you can use OpenOCD from GNAT Studio, another IDE or the command line:

openocd -f board/pico-debug.cfg program main.bin verify reset exit

(File pico-debug.cfg is located OpenOCD, no full path required here). For GNAT Studio add this to the project file

package Ide is
   for Program_Host use “localhost:4242”;
   for Communication_Protocol use “remote”;
   for Connection_Tool use “openocd”;
   for Connection_Config_File use “board/pico-debug.cfg”;
end Ide;

Maybe you could write about this on your blog? The simpler the first steps in launching RP Pico, the more people will be able to follow them, I hope.

4 Likes

Any advice on modifying for raspberry pico 2 w which uses RP2350? I may have a go at modifying the Jeremy’s code to suit RP2350.

I kind of got it working. See Side Quest: Raspberry Pi Pico 2 W. But it’s all very experimental. I also prepared a GNATdoc reference for the Pico 2 development. It’s quite helpful get an overview on what is already there.

1 Like

Wonderful news! I’ll most likely abandon my own effort and try to build on yours.

Do note that the main work is done Jeremy not me. I just write at tutorial and a few tools along the way. I recently run into trouble with PWM on the Pico 2.

I reported talked about it on the Telegram channel. Do don’t abandon your own effort to early.

Martin

My development on the RP2350 has halted because I keep running into issues with the A2 revision errata and have been unable to get my hands on the newer A4 revision chips from a US distributor.

The remaining work on the 3.x branch at commit a87af34666f7189c7ed48736850fe4a21c64800a is daunting:

RP.GPIO

The implementations of Set/Clear/OE need to be rewritten to use the GPIOC coprocessor for single-cycle inlining where possible.

Unit test coverage is at 83%. The DORMANT_WAKE states are going to be tricky to test well.

All of the HAL.GPIO procedures need to be verified to have the same levels and equal or better timing compared to 2.x with an oscilloscope.

RP.Clock

Unit test coverage is at 72%. GPOUT and frequency counter failure are not tested.

Breaking up the Clock_Id type resulted in separate Enable_ADC and Enable_PERI procedures. While the common case is that you’ll enable these at startup and never touch them again, they should probably have corresponding Disable_ procedures. Maybe it should be type Gated_Clock_Id (ADC, PERI); with Enable and Disable procedures instead- this would mean one less backwards incompatible change from 2.x. I haven’t really decided how I want to approach this.

Both the RP2040 and RP2350 implementations need to be tested on hardware with GPOUT to verify PLL configuration is actually generating the correct frequencies.

RP.DMA

Coverage is at 72%, none of the IRQ procedures are tested. I want to reorganize the _IRQ functions into a RP.DMA.Interrupt child package, similar to RP.GPIO.Interrupt.

I’ve not done any functional testing beyond the unit tests on this module. There are significant changes to the DMA register layout on RP2350, so this should be tested carefully, especially the PIO2 and HSTX DMA_Request_Triggers that didn’t exist in RP2040.

RP.ADC

82% coverage is pretty good actually. On the higher pin count RP2350 packages the ADC is on pins 40 .. 47, not 26 .. 29. Someone will have to find or make a board with the larger package to test this properly, I believe the Pico 2 uses the lower pin count package.

RP.I2C and RP.I2C_Master

RP.I2C is deleted from the 3.x branch. I don’t want to maintain two separate I2C implementations going forward. This means 3.x has no I2C target/slave functionality right now. It would be nice to introduce a RP.I2C_Target package to fill that gap, though this is more of a wishlist feature than a requirement for a 3.x release.

After much consternation, RP.I2C_Master is in a good place and should function well on both boards… But I can’t test it on the RP2350 A2 revision I have because the I2C pins get latched up by errata E9 in the middle of the unit test. I can even replicate the hardware bug with my Softdev.I2C_Master bit banging implementation, independent of the Synopsys IP block. Without the A4 silicon, it’s impossible to guarantee correct I2C operation.

RP.Interpolator

I haven’t looked at the RP2350 version of it. At a glance, it’s the same, but the register layout and base addresses need to be verified correct. We’ve never had a unit test for this.

RP.PIO

I believe the only change on RP2350 is that there’s one more PIO block, but the registers and base addresses need to be checked against the datasheet. It might be nice to reorganize the IRQ procedures similar to RP.GPIO.Interrupt, but I’m not going to be too picky about this because PIO is already such a pain to work with.

All of the assembled PIO programs need to be re-tested and their example programs updated: Audio_I2S, Touch_Sense, WS2812

There is zero unit test coverage of PIO currently.

bunnie recently blogged about some of his gripes with PIO and I largely agree. Too much out-of-band configuration, nobody gets it right the first time and it’s difficult to debug without a simulator of some sort.

RP.PWM

Coverage at 84%, unit tests pass, but I haven’t done any verification that it does what it should on hardware. Need to hook up an oscilloscope, generate some waveforms, and test the counting/gating functionality between two pins.

RP.Reset

The list of resets has been updated for RP2350, no reason to doubt that it works as advertised, but coverage is at 33% because the tests never encounter RESET_DONE=False after asserting reset. It always happens within a cycle or two. Not a big issue, but I’d be interested in finding a way to test that branch, especially with a timeout.

RP.SPI

I’ve verified this one with real hardware driving a MAX7219 based LED matrix. The generated clock frequency was slightly lower than the Set_Speed call, I suspect an off-by-one in the clock divider calculation or frequency counter output.
Coverage is at 46%.

RP.Timer

Honestly not sure what to do with this. With the runtime supporting Ada.Real_Time, you don’t need RP.Timer for most applications. There’s a second AON timer on RP2350 that could be configured to run at a different frequency, but I’m not really sure why you’d want that. You can get the same functionality out of a PWM slice.

RP.UART

I’ve done no testing on RP2350. I assume it’s the same Synopsys IP block. I’d like to move away from the SVD definitions if possible, but hiding them from the spec is good enough. Coverage is 67%, mostly missing tests for odd error cases. Might need some external fault injection to trigger those.

RP.USB_Device

Haven’t looked at it, but I’ve still got unprocessed trauma from the last time I did. This thing scares me.

RP.Watchdog

Haven’t looked at it, but how complicated could a watchdog possibly be?

RP.Flash

Completely different on RP2350. I haven’t looked at it, but I suspect this will be annoying to test.

New stuff

HSTX, external PSRAM, SHA256, OTP, TRNG, security modes, low power modes, ROM functions, voltage regulators, RISC-V cores. There’s no support for any of this in rp2040_hal right now.

Documentation

Once all of the above is implemented and tested, pico_doc and pico_examples need to be updated with a complete changelog, 2.x to 3.x migration guide, and documentation of new behaviors. I consider this to be a requirement before merging the branch.

Contribution policy

I’m open to contributions that fill some of these gaps but I want to be clear that I will not accept any AI generated code in this repository. I want to keep the copyright status clean and traceable.

Most of these changes require probing real hardware with real electrons, which I’ve yet to see an LLM do. Personally, I’ve been writing drivers by hand, then feed it into Claude Code for review, not implementation. Whether this has saved me any meaningful amount of time or debugging is up for debate.

5 Likes

Enjoy: Fix PWM slice calculation for RP2350 in RP.PWM.To_PWM by krischik · Pull Request #76 · JeremyGrosser/rp2040_hal · GitHub

Your repository your rules — however considering that both VSCode and the JetBrains IDEs now do AI enhanced code completion this might become very tricky in future. If you are very strict about this you and all contributors will eventually need to deactivate code completion.

I’m currently trying out the AI enhanced code completion but I’m not sure it is really helpful. Might just switch it off because it just get’s in the way.

Of course I was able to do the correction without any AI code generation. Purely with “y” “p” “p” and a few cursor movements. :grin:

Martin