Developing in C on the RP2040: New Project

4 minute read

Where I demonstrate bringing up a new project in C on the PR2040 including embedding project information in the binary.



You have played with the examples the Raspberry Foundation has thoughtfully provided. Now you want to create something on your own! I like the information provided by the Foundation as to how to create a project and how to add easily binary information to the binary code of project. This means you can build a project, write pin information to the binary and then pick up the board weeks(months?) later and determine what is on the board and how to use it.

Starting a New Project (Chapter 8. Creating your own Project)

We will want to start a new project to do this. Remember, learning how to create a new project is our main reason for this entry, Picotool(below) will add interest!

First let’s follow the example exactly out of Chapt 8:

cd pico
mkdir test
cd test
# open main.c in your code editor
# paste the code below (from the Guide) as our starting text
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "pico/binary_info.h"

const uint LED_PIN = 25;

int main() {

   bi_decl(bi_program_description("This is a test binary."));
   bi_decl(bi_1pin_with_name(LED_PIN, "On-board LED"));


   gpio_set_dir(LED_PIN, GPIO_OUT);
   while (1) {
      gpio_put(LED_PIN, 0);
      gpio_put(LED_PIN, 1);
      puts("Hello World\n");

Continue on

When copying from the Guide, be sure not to copy the 1 and 2 that appear in circles in the code. Those are for notes and not code.

# create a CMAKELists.txt file in your code editor
cmake_minimum_required(VERSION 3.13)
project(test_project C CXX ASM)
pico_enable_stdio_usb(test 1)
pico_enable_stdio_uart(test 1)
target_link_libraries(test pico_stdlib)
# and copy the cmake file in the folder
 cp ../pico-sdk/external/pico_sdk_import.cmake .

Everything Worked

Once I fixed my induced bugs in the code, everything else worked for me. My next step is to checkout PicoTool.


Its best to start out with the Picotool as this is the tool used to write and read information on the board. The tool is interesting in two specific ways:

  1. The information is stored in binary, which means it consumes very little room.
  2. It is read (and written, if need be) with the board as a uf2. Which is the simplest method of testing, press BOOTSEL while inserting the USB cable and run Picotool.

Why is this important? It provides an efficient, easy and standardized form of documentation which we can all use. I start with this as it is important to establish strong, professional habits even if we are “only experimenting or hacking.” This allows you or someone you provide a board to, to quickly understand the board requirements.

Building Picotool

Building Picotool on Linux is straightforward. Follow the Guide and you will have a Picotool executable in pico/picotool/build/, however it might not run and it might not be able to access the drive. The solution is this:

# if in folder, pico/picotool/build/
# to access the USB port, use sudo
sudo ./picotool info -a
Program Information
 name:          picoprobe
 binary start:  0x10000000
 binary end:    0x100054a0

Fixed Pin Information
 0:   UART0 TX
 25:  LED

Build Information
 sdk version:       1.2.0
 pico_board:        pico
 boot2_name:        boot2_w25q080
 build date:        Aug 23 2021
 build attributes:  Release

Device Information
 flash size:   2048K
 ROM version:  2

Using Picotool

The best way to start is to understand that there are macros set up to define the information which is written to the file. To use them, you must have at the beginning of your file (along with the other headers).

#include "pico/binary_info.h"

And to include information, here are quite a few macros for which you would add information:

#define bi_binary_end(end)
#define bi_program_name(name)
#define bi_program_description(description)
#define bi_program_version_string(version_string)
#define bi_program_build_date_string(date_string)
#define bi_program_url(url)
#define bi_program_feature(feature)
#define bi_program_build_attribute(attr)
#define bi_1pin_with_func(p0, func)
#define bi_2pins_with_func(p0, p1, func)
#define bi_3pins_with_func(p0, p1, p2, func)
#define bi_4pins_with_func(p0, p1, p2, p3, func)
#define bi_5pins_with_func(p0, p1, p2, p3, p4, func)
#define bi_pin_range_with_func(plo, phi, func)
#define bi_pin_mask_with_name(pmask, label)
#define bi_pin_mask_with_names(pmask, label)
#define bi_1pin_with_name(p0, name)
#define bi_2pins_with_names(p0, name0, p1, name1)
#define bi_3pins_with_names(p0, name0, p1, name1, p2, name2)
#define bi_4pins_with_names(p0, name0, p1, name1, p2, name2, p3, name3)

And they provide this simple program to demonstrate:

 #include <stdio.h>
2 #include "pico/stdlib.h"
3 #include "hardware/gpio.h"
4 #include "pico/binary_info.h"
 6 const uint LED_PIN = 25;
 8 int main() {
10     bi_decl(bi_program_description("This is a test binary."));
11     bi_decl(bi_1pin_with_name(LED_PIN, "On-board LED"));
13     setup_default_uart();
14     gpio_set_function(LED_PIN, GPIO_FUNC_PROC);
15     gpio_set_dir(LED_PIN, GPIO_OUT);
16 while (1) {
22     }
23 }
gpio_put(LED_PIN, 0);
gpio_put(LED_PIN, 1);
puts("Hello World\n");

So…let’s put all of this to test!

Comments powered by Talkyard.