Skip to main content

Chapter 1: Blinking LEDs – Getting Started with Ada on the Raspberry Pi Pico

Link Purpose
C Tutorial Chapter 1 – LED (Important) Freenove’s official C version – Chapter 1 introduction to LED control
GNATdoc documentation for this chapter Automatically generated HTML documentation for the Ada code in this chapter

Preparation #

Before diving into the Freenove examples, I decided to start with a known-good Ada blink programme written by Jeremy Grosser. It was the perfect way to verify that my toolchain, hardware, and project setup were working correctly without the extra complication of porting someone else’s code at the same time.

The experience taught me quite a bit – you can read more about the hardware side in Hardware Reset Button and about streamlining the build in From Repetitive to Elegant: My Improved Pico Ada Makefile.

Here is Jeremy’s original blink example:

---
---  Copyright 2021 (C) Jeremy Grosser
---
---  SPDX-License-Identifier: BSD-3-Clause
---
with RP.Device;
with RP.Clock;
with RP.GPIO;
with Pico;

---
--  The Hello_Pico sample from the https://pico-doc.synack.me/ to test basic functionality of the Ada and Pico setup.
--
procedure Blink is
begin
   RP.Clock.Initialize (Pico.XOSC_Frequency);
   Pico.LED.Configure (RP.GPIO.Output);
   RP.Device.Timer.Enable;
   loop
      Pico.LED.Toggle;
      RP.Device.Timer.Delay_Milliseconds (100);
   end loop;
end Blink;

If you want to try setting it up yourself, Jeremy’s own instructions are excellent and I won’t try to improve on them.

Link Purpose
Ada on the Raspberry Pi Pico – Blinking an LED Jeremy Grosser’s clear step-by-step guide to the blink example
Jeremy Grosser’s pico_examples – blink folder Original Ada source code for several Pico blink variations

Chapter 1.1 – Slow, Photograph-friendly Blinking #

Sketch_01_1 does essentially the same job as Jeremy’s version, but I switched to explicit .Set and .Clear calls and lengthened the delay to 1 second. That makes the on/off states much easier to capture in photos.

pragma License (Modified_Gpl);
pragma Ada_2022;
pragma Extensions_Allowed (On);

with RP.Device;
with RP.Clock;
with RP.GPIO;
with Pico;

---
--  Make an led blinking.
--
procedure Sketch_01_1_Blink is
begin
   RP.Clock.Initialize (Pico.XOSC_Frequency);
   Pico.LED.Configure (RP.GPIO.Output);
   RP.Device.Timer.Enable;

   loop
      Pico.LED.Set;
      RP.Device.Timer.Delay_Milliseconds (1_000);
      Pico.LED.Clear;
      RP.Device.Timer.Delay_Milliseconds (1_000);
   end loop;
end Sketch_01_1_Blink;

Pi blinking internal LED

Link Purpose
Freenove Tutorial 1.1 – Project: Blink Freenove’s matching C project – basic internal LED blink
Freenove GitHub – Sketch_01.1_Blink Original C source code for Freenove Chapter 1.1

Chapter 1.2 – External LED with Clean Pin Naming #

Freenove’s C code uses a #define to alias the pin – not very elegant. Ada gives us a much nicer, type-safe alternative: renaming.

I renamed Pico.GP15 to simply LED so the rest of the code reads cleanly.

pragma License (Modified_Gpl);
pragma Ada_2022;
pragma Extensions_Allowed (On);

with RP.Device;
with RP.Clock;
with RP.GPIO;
with Pico;

---
--  Make an led blinking.
--
procedure Sketch_01_2_Blink is
   ---
   --  Use GP 15 for the external LED
   --
   LED  : RP.GPIO.GPIO_Point renames Pico.GP15;
begin
   RP.Clock.Initialize (Pico.XOSC_Frequency);
   LED.Configure (RP.GPIO.Output);
   RP.Device.Timer.Enable;

   loop
      LED.Set;
      RP.Device.Timer.Delay_Milliseconds (1_000);
      LED.Clear;
      RP.Device.Timer.Delay_Milliseconds (1_000);
   end loop;
end Sketch_01_2_Blink;

Pi blinking external LED

Link Purpose
Freenove Tutorial 1.2 – Project: External LED Blink Freenove’s C version using an external LED on GP15
Freenove GitHub – Sketch_01.2_Blink Original C source code for Freenove Chapter 1.2

A Quick Look at Ravenscar #

Finally, I wanted to try the Ravenscar profile. Ravenscar is a restricted subset of Ada’s tasking model, designed for high-integrity and hard real-time systems where you need predictable scheduling and analysability without the full (and heavier) tasking runtime.

Trivia: The profile is named after the small Yorkshire coastal village of Ravenscar, where the 8th International Real-Time Ada Workshop (IRTAW-8) took place in 1997. That meeting produced the initial definition of the restricted tasking model we still use today.

Jeremy has a very elegant Ravenscar version of blink that uses the standard delay statement:

--
--  Copyright 2021 (C) Jeremy Grosser
--
--  SPDX-License-Identifier: BSD-3-Clause
--
with RP.GPIO;
with Pico;

procedure Blink is
begin
   Pico.LED.Configure (RP.GPIO.Output);
   loop
      Pico.LED.Toggle;
      delay 0.1;
   end loop;
end Blink;

It really is beautifully concise – arguably cleaner than the equivalent Python code.

However… this only works when you use a Ravenscar-compatible runtime.

The older (but still very usable) approach is Jeremy’s ravenscar_full_rp2040 Alire crate. It provides tasking support and plays reasonably nicely with rp2040_hal drivers – you just need to disable the conflicting startup code in alire.toml with rp2040_hal.Use_Startup = false and remove some linker switches from your project file.

The newer light-tasking-rpi-pico (and -smp) runtimes that are starting to appear directly in the GNAT arm-eabi toolchain look promising – they give you delay and protected objects without external crates. Unfortunately, I quickly discovered you can’t just drop them into a project that uses pico_bsp or the standard rp2040_hal setup; the runtimes supply their own boot code and linker scripts, leading to immediate conflicts (linker errors galore). It’s effectively a dead end unless you’re willing to forgo the BSP and HAL or heavily reconfigure things.

Ravenscar support on the Pico is still very much experimental / in transition. For now, I’m staying with the standard non-tasking runtime so I can focus on the electronics rather than fighting the toolchain. The explicit timer calls are a bit more verbose, but they’re reliable, easy to reason about, and just work with the examples I’m porting.