ARM Embedded Tutorial - Raspberry Pi Pico Blinking the onboard LED

From Lazarus wiki
Revision as of 03:36, 31 January 2021 by MiR (talk | contribs)
Jump to navigationJump to search

Finally we are ready to craft some code. To make things easier I have created a git repository that hosts the examples and extra binaries needed for successfully building the code.

Please close this repository:


Our first version of the blinking LED theme will stay close to the example provided by the Raspberry Foundation in their pico examples repository on github. The code will look pretty much the same as the C-Code, we will also re-use code already done by the Foundation by linking to object files from the SDK.

This technique is helpfull when you want to get working results fast, we can already get something going without the need for us to fully understand the new Microcontroller Architecture.

The main Pascal Code will look like this:

   const
     LED_PIN=25;
     GPIO_OUT=true;
   var
     i : longWord;
   begin
     runtime_init;
     gpio_init(LED_PIN);
     gpio_set_dir(LED_PIN,GPIO_OUT);
     repeat
       gpio_put(LED_PIN,true);
       for i := 0 to 300000 do ;
       gpio_put(LED_PIN,false);
       for i := 0 to 300000 do ;
     until 1=0;
   end.


to make this code work we will need some additional components, first of all we need a working gpio library for the raspberry pi pico.

when you use my repo the file is already checked in, when you want to create your own version your need to follow the chapter 'Chapter 3. Blinking an LED in C' in the getting started pdf and build the C-Version of the blinking LED example.

When done search for a file named 'gpio.c.obj' that is hidden deep down in our build directory. On my mac the file is located here:

   ./CMakeFiles/blink.dir/Users/ring/devel/pico-sdk/src/rp2_common/hardware_gpio/gpio.c.obj

copy this file into the directory where you store code for this blinky example.

Now change the code to include this library and look up the correct definition for the functions we need from the gpio headerfiles:

   program Blinky;
   {$L gpio.c.obj}
   procedure gpio_init(gpio : longWord); external;
   procedure gpio_put(gpio : longWord; value : longBool); external;
   procedure gpio_set_dir(gpio : longWord; &out : longBool); external;
   procedure runtime_init;
   const
     RESETS_SAFE_BITS=     %1111111111100110110111111;
     RESETS_PRECLOCK_BITS= %0001111000100110110111110;
     RESETS_POSTCLOCK_BITS=%1110000111000000000000001;
   begin
     resets.reset_set := RESETS_SAFE_BITS;
     resets.reset_clr := RESETS_PRECLOCK_BITS;
     repeat
     until (resets.reset_done and RESETS_PRECLOCK_BITS) = RESETS_PRECLOCK_BITS;
   end;


and we are ready to compile our code.

As we still have a few steps of work ahead of us my recommendation is that we first use commandline to do the initial compiles as it is a little easier to see what is still necessary to fix.

We will need to use the fpc wrapper of fpcupdeluxe, so make sure you call it with the correct path of your fpcupdeluxe installation:

Windows

todo

MacOSX x86_64

   $HOME/fpcupdeluxe-embedded/fpc/bin/x86_64-darwin/fpc.sh -Tembedded -Parm -Cparmv6m -Wpraspi_pico -O1 -Xu Blinky.lpr

MacOSX aarch64

   $HOME/fpcupdeluxe-embedded/fpc/bin/aarch64-darwin/fpc.sh -Tembedded -Parm -Cparmv6m -Wpraspi_pico -O1 -Xu Blinky.lpr

Our code compiles, but linking fails:

   Free Pascal Compiler version 3.3.1 [2021/01/25] for arm
   Copyright (c) 1993-2020 by Florian Klaempfl and others
   Target OS: Embedded
   Compiling Blinky.lpr
   Assembling blinky
   Linking Blinky
   Blinky.o: In function `main':
   /Users/ring/devel/rppico/Blinky//Blinky.lpr:16: undefined reference to `gpio_set_dir'
   /Users/ring/devel/rppico/Blinky//Blinky.lpr:18: undefined reference to `gpio_put'
   /Users/ring/devel/rppico/Blinky//Blinky.lpr:20: undefined reference to `gpio_put'
   Blinky.lpr(24) Error: Error while linking
   Blinky.lpr(24) Fatal: There were 1 errors compiling module, stopping
   Fatal: Compilation aborted
   Error: /Users/ring/fpcupdeluxe/fpc/bin/aarch64-darwin/ppcarm returned an error exitcode 


Why's that? We saw the definitions in the gpio Headerfiles and we linked to the correct object file....

The reason is that those two functions are defined as inline and for that reason we cannot access them.

So here we need to manually create implementation, the good thing is that inlined functions are usually quite simple and our missing functions are no exception.

After reading through the gpio headerfiles we have implementations of the missing calls in pascal:

   program Blinky;
   {$L gpio.c.obj}
   procedure gpio_init(gpio : longWord); external;
   procedure runtime_init;
   const
     RESETS_SAFE_BITS=     %1111111111100110110111111;
     RESETS_PRECLOCK_BITS= %0001111000100110110111110;
     RESETS_POSTCLOCK_BITS=%1110000111000000000000001;
   begin
     resets.reset_set := RESETS_SAFE_BITS;
     resets.reset_clr := RESETS_PRECLOCK_BITS;
     repeat
     until (resets.reset_done and RESETS_PRECLOCK_BITS) = RESETS_PRECLOCK_BITS;
   end;
   procedure gpio_set_dir(gpio : longWord; &out : longbool);
   var
     mask : longWord;
   begin
     mask := 1 shl  gpio;
     if out = true then
       sio.gpio_oe_set := mask
     else
       sio.gpio_oe_clr := mask;
   end;
   procedure gpio_put(gpio : longWord; value : boolean);
   var
     mask : longWord;
   begin
     mask := 1 shl gpio;
     if value=true then
       sio.gpio_set := mask
     else
       sio.gpio_clr := mask;
   end;
   const
     LED_PIN=25;
     GPIO_OUT=true;
   var
     i : longWord;
   begin
     runtime_init;
     gpio_init(LED_PIN);
     gpio_set_dir(LED_PIN,GPIO_OUT);
     repeat
       gpio_put(LED_PIN,true);
       for i := 0 to 300000 do ;
       gpio_put(LED_PIN,false);
       for i := 0 to 300000 do ;
     until 1=0;
   end.

This approach looks a little overcomplicated at first but the gpio's are always the most simple example, for more complex peripherals like i2c or usb this approach really shines as it saves a lot of time in the end.

We now have a binary, but does it work????


To find out we need to upload the elf file to the board, a task easily done with our Debug Probe and the help of arm-none-eabi-gdb.....

First we have to connect our development board to either our Raspi-based debug probe or to the 2nd pico we converted to a picoprobe. If you have not yet prepared your debug probe read instructions on how to do so in this document:

ARM Embedded Tutorial - Raspberry Pi Pico Setting up for Development

Fire up your openocd instance:

   $HOME/devel/openocd-rpi/src/openocd -f interface/picoprobe.cfg -f target/rp2040.cfg -s /Users/ring/devel/openocd-rpi/tcl
   Open On-Chip Debugger 0.10.0+dev-g14c0d0d33-dirty (2021-01-29-15:13)    
   Licensed under GNU GPL v2
   Info : only one transport option; autoselect 'swd'
   Warn : Transport "swd" was already selected
   adapter speed: 5000 kHz
   Info : Hardware thread awareness created
   Info : Hardware thread awareness created
   Info : RP2040 Flash Bank Command
   Info : Listening on port 6666 for tcl connections
   Info : Listening on port 4444 for telnet connections
   Info : clock speed 5000 kHz
   Info : SWD DPIDR 0x0bc12477
   Info : SWD DLPIDR 0x00000001
   Info : SWD DPIDR 0x0bc12477
   Info : SWD DLPIDR 0x10000001   
   Info : rp2040.core0: hardware has 4 breakpoints, 2 watchpoints
   Info : rp2040.core1: hardware has 4 breakpoints, 2 watchpoints
   Info : starting gdb server for rp2040.core0 on 3333
   Info : Listening on port 3333 for gdb connections


Now we can fire up our gdb and see our program run:

Windows todo

MacOSX x86_64

   $HOME/fpcupdeluxe-embedded/fpc/bin/x86_64-darwin/arm-embedded-gdb Blinky.elf

MacOSX aarch64

   $HOME/fpcupdeluxe-embedded/fpc/bin/aarch64-darwin/arm-embedded-gdb Blinky.elf
   GNU gdb (GNU Tools for STM32 7-2018-q2-update.20190328-1800) 8.1.0.20180315-git
   Copyright (C) 2018 Free Software Foundation, Inc.
   ....
   Reading symbols from Blinky.elf...done.
   (gdb) target remote localhost:3333
   Remote debugging using localhost:3333
   0x00001bd0 in ?? ()
   (gdb) load
   Loading section .text, size 0x748 lma 0x10000000
   Loading section .data, size 0x71 lma 0x10000748
   Start address 0x10000678, load size 1977
   Transfer rate: 1 KB/sec, 988 bytes/write.
   (gdb) cont
   Continuing.

And you should see the LED blink......