Stephen Smith's Blog

Musings on Machine Learning…

Introducing the Seeed Studio Wio RP2040

leave a comment »

Introduction

When the Raspberry Pi Foundation designed their new microcontroller, the Raspberry Pi Pico, the heart of the board is the RP2040 System on a Chip (SoC). This chip contains dual ARM Cortex-M0+ CPU cores, 296kb RAM, a number of coprocessors and a number of specialty I/O processors. Raspberry made the decision to sell this chip separately and it has since appeared on the boards from various other hardware vendors. Previously, we discussed the Adafruit Feather RP2040 and in this article is about Seeed Studio’s Wio RP2040 which is another of these boards. The Raspberry Pi Pico contains support for several wired communications protocols, but no support for wireless communications, or ethernet, making it difficult to connect directly to the Internet. Seeed Studio paired the RP2040 chip with a ESP based wireless chip, adding both WiFi and Bluetooth in a board smaller than the Pico.

Module vs Development Board

There are two versions of of the Wio RP2040 board:

  • The Wio RP2040 Module containing the RP2040
  • The Wio RP2040 Mini Dev Board which adds a bootsel button, led, usb connector, and the pins to add it to a breadboard.

If you are experimenting, I would recommend getting the development board. If you get the CPU module then at a minimum you need to solder the wires for a USB wire to the board so you can connect it to a host computer to download programs from.

I received the module version, so I learned a bit about the four wires contained in a USB cable. Fortunately, there is a standard for the wire colors, so figuring out the wiring was easy. Soldering the USB cable to the module was fiddly but doable. You also need to solder two wires for the bootsel button, you can either connect these to a button, or just touch them together to activate bootsel. If you want to debug with gdb, then you also need to connect three wires to the two SWD pins and a ground pin, to allow gdb to control the board.

Below is my module with the USB cable soldered in and two leads for the bootsel button. I should add three more wires for debugging, but this isn’t necessary if you are only using MicroPython.

Software Development

The core of this board is the RP2040 processor, so you can develop with this board using Raspberry’s RP2040 SDK. You can also use any of the environments that support the Raspberry Pi Pico like MicroPython or the Arduino system. The only restriction is that WiFi support is only officially supported with MicroPython which we talk about next.

Developing with WiFi

As of this writing, using the WiFI/Bluetooth functionality is only supported from MicroPython. Seeed supplies a custom version of MicroPython which has this module compiled in. This version leverages the work in MicroPython done for the Raspberry Pi Pico and as a consequence works with the Thonny Python IDE. This is an interesting contrast with Seeed’s Wio Terminal which doesn’t have IDE support and you need to rely on REPL for development.

The problem with this approach is that I couldn’t find the source code for this build of MicroPython, which means if I wanted to add more libraries, I don’t have a way to do this, including my own custom C and Assembly Language code. Again, contrast this to the Wio Terminal, which includes all the source code as well as a build system for adding modules and custom code. Hopefully, the source code for this MicroPython build makes it onto Seeed’s Github repository in the near future.

There is speculation on the forums that the WiFi is an ESP8266 board connected via the SPI interface and then controlled using AT commands. These are basically an extension of the old Hayes modem command set, extended to a more modern world. It wouldn’t take much documentation on Seeed’s part to provide some details, such as the SPI port used and SPI configuration parameters. With this detail it would be easy to add WiFi and/or Bluetooth support to programs written in the standard RP2040 SDK. Or better still contribute their WiFi support to the Raspberry Pi Pico Extras GitHub repository.

Summary

The Seeed Wio RP2040 is a compact module to build your projects around. The big current limitation is the lack of software support for the ESP radio module; hopefully, this will be rectified in the near future. Seeed designed this module to be included into custom PCB boards such as their dev board and offer a service to manufacture these. If this is your first Wio RP2040, then you should get the mini dev board as this is far easier to connect up and get working, then use the smaller module in your final project.

Written by smist08

September 12, 2021 at 9:32 am

ArduPy on the Wio Terminal

with one comment

Introduction

Last week, we introduced Seeed Studio’s Wio Terminal and wrote a small program in C using the Arduino IDE. This week, we’ll replicate the same program in Python using ArduPy, Seeed’s version of MicroPython for the Wio Terminal and Seeeduino XIAO. In this article we’ll look at what’s involved in working with ArduPy along with some of its features and limitations.

The Koch Snowflake in ArduPy on the Wio Terminal

What is ArduPy?

ArduPy is an open source adaptation of MicroPython that uses the Arduino API to talk to the underlying device. The goal is to make adding MicroPython support easier for hardware manufacturers, so that if you develop Arduino support for your device, then you get MicroPython support for free. There are a large number of great microcontrollers out there and for board and system manufacturers, and the most time consuming part of getting a new board to market is developing the SDK. Further, great boards run the risk of failing in the market if the software support isn’t there. Similarly, a programmer buying a new board, really likes to be able to use familiar software and not to have to learn a whole new SDK and development system from scratch.

When you develop an Arduino program, you include the libraries used and the program then runs on the device. In the case of ArduPy, the Python runtime is the running Arduino program, but what libraries does it contain? Seeed developed a utility, aip, to rebuild the ArduPy runtime and include additional libraries. This lets you save memory by not including a bunch of libraries you aren’t using, but still have the ability to find and include the libraries you need.

The downside of ArduPy is that currently there isn’t Python IDE integration. As a partial workaround there is REPL support (Read Evaluate Print Loop), which lets you see the output of print statements and execute statements in isolation.

You need to flash the ArduPy runtime to the device, after that the device will boot with a shared drive that you can save a file boot.py that is run everytime the device boots, or main.py which is run every time it is saved.

Our Koch Snowflake Program Again

We described the Koch Snowflake last time and implemented it in C. The following is a Python program, where I took the C program and edited the syntax into shape for Python. I left in a print statement so we can see the output in REPL. The screenshot above shows the program running.

from machine import LCD
import math

lcd = LCD()                            # Initialize LCD and turn the backlight
lcd.fillScreen(lcd.color.BLACK)        # Fill the LCD screen with color black
lcd.setTextSize(2)                     # Setting font size to 2
lcd.setTextColor(lcd.color.GREEN)      # Setting test color to Green

turtleX = 0.0
turtleY = 0.0
turtleHeading = 0.0

DEG2RAD = 0.0174532925

level = 3
size = 200
turtleX = 320/8
turtleY = 240/4

def KochSnowflakeSide(level, size):
  print(“KochSnowFlakeSide ” + str(level) + ” ” + str(size))
  if level == 0:
      forward( size )
  else:
      KochSnowflakeSide( level-1, size / 3 )
      turn( 60 )
      KochSnowflakeSide( level-1, size / 3)
      turn( -120 )
      KochSnowflakeSide( level-1, size / 3)
      turn(60)
      KochSnowflakeSide( level-1, size / 3)

def forward(amount):
  global turtleX, turtleY

  newX = turtleX + math.cos(turtleHeading * DEG2RAD) * amount
  newY = turtleY + math.sin(turtleHeading * DEG2RAD) * amount
  lcd.drawLine(int(turtleX), int(turtleY), int(newX), int(newY), lcd.color.WHITE)
  turtleX = newX
  turtleY = newY

def turn(degrees):
  global turtleHeading
  turtleHeading += degrees

turn( 60 )
KochSnowflakeSide( level , size)
turn( -120 )
KochSnowflakeSide( level, size)
turn( -120 )
KochSnowflakeSide( level, size)
turn( 180 )

Developing and Debugging

Seeed’s instructions are good on how to set up a Wio Terminal for ArduPy, but die out a bit on how to actually develop programs for it. Fortunately, they have a good set of video tutorials that are necessary to watch. I didn’t see the tutorials until after I got my program working, and they would have saved me a fair bit of time.

I started by developing my program in a text editor and then saving it as main.py on the Wio. The program did nothing. I copied the program to Thonny, a Python IDE, which reported the most blatant syntax errors that I fixed. I started debugging by outputting strings to the Wio screen, which showed me how far the program ran before hitting an error. Repeating this, I got the program working. Then I found the video tutorials.

The key is to use REPL, which is accessed via a serial port simulated on the USB connection to your host computer. The tutorial recommended using putty, which I did from my Raspberry Pi. With this you can see the output from print statements and you can execute Python commands. Below is a screenshot of running the program with the included print statement.

I tried copy and pasting the entire Python program into putty/REPL, but copy/paste doesn’t work well in putty and it messes up all the indentation, which is crucial for any Python program. When I write my next ArduPy program, I’m going to find a better terminal program than putty, crucially, one where cut/paste works properly.

Using putty/REPL isn’t as good as debugging in a proper Python IDE, but I found I was able to get my work done, and after all we are programming a microcontroller here, not a full featured laptop.

Summary

ArduPy is an interesting take on MicroPython. The library support for the Wio Terminal is good and it does seem to work. Being able to use an IDE would be nice, but you can get by with REPL. Most people find learning Python easier than learning C, and I think this is a good fit for anyone approaching microcontrollers without any prior programming experience.

Written by smist08

September 3, 2021 at 10:45 am

Wio Terminal First Impressions

with one comment

Introduction

I received a Seeed Studio Wio Terminal, an interesting device that contains an Arduino style microcontroller, but packaged with a 320×240 2.4” color screen, buttons, bluetooth, WiFi, microphone, buzzer, microSD card slot, light sensor and infrared emitter. The packaging means you can work with it, without any soldering or breadboarding. It also has two Grove ports and Raspberry Pi compatible 40-pin GPIO pins.

It is built around a ATSAMD51 microcontroller containing a 32-bit ARM Cortex-M4 processor with a Floating Point Unit (FPU), running at 120 MHz. For memory there is 192KB RAM and 4MB flash.

To program the Wio Terminal, the current options are:

  1. Arduino – Use the Arduino IDE and supplied libraries for the integrated devices.
  2. MicroPython – There is a special MicroPython build for device support.
  3. Codecraft’s TinkerGen cloud development which includes TinyML support.

In this article we’ll look at the Arduino support and a simple fractal program to see what is involved. Below is a picture of the Wio Terminal with our fractal Koch snowflake. The screen is better than shown, as there was some interference between my phone’s shutterspeed and the screen refresh rate.

Koch Snowflake on a Wio Terminal

Koch Snowflakes

Koch snowflakes are simple fractals that are a good way to give an idea of how fractals can build complexity out of simplicity. Basically you start with a base shape, in this case a triangle, then you replace each line segment with a new shape, in this case the two lines with a “v” in the middle. Then you do this recursively to get more and more complicated shapes. Below is the progression from level 0, the base shape to level 1, with the base shape lines replaced by the fractal generator and then so on as the level increases.

This is a fractal because in the limit as the level goes to infinity, the shape has a fractal dimension, in that it is somewhere between 1 dimensional and 2 dimensional in a defined mathematical sense.

Turtle Graphics

To me the easiest way to draw fractals is with a turtle graphics library. This is a simple drawing library where you tell a turtle to either turn or move forwards. As he moves he leaves a trail. Hence the base shape for the Koch snowflake is forward 1, turn 60, forward 1, turn -120, forward 1, turn 60 forward 1. This is then really easy to apply recursively to draw fractals.

Programming the Wio Terminal

Drawing Koch snowflakes on the Wio Terminal turned out to be quite easy. The TFT graphics library Seeed Studio provides is quite good and makes programming easy with routines for lines, colors, text, basic shapes, all the way up to higher level objects like histograms. 320×240 may not seem like much resolution by today’s 4k standards, but remember this is about the same as CGA graphics on the original IBM PCs. Besides initialization, the only routine we used is drawLine. The Wio Terminal came with a USB-C to USB cable to connect the device to a regular computer. You program in the Arduino IDE on your PC and download the result to the WIo Terminal to run.

Below is the complete source code to draw the Koch snowflake.

#include”TFT_eSPI.h”

TFT_eSPI tft;
float turtleX;
float turtleY;
float turtleHeading = 0;

void setup() {
  int level = 3;
  int size = 200;
  tft.begin();
  tft.fillScreen(TFT_WHITE);

  turtleX = 320/10;
  turtleY = 240/2;

  turn( 60 );
  KockSnowflakeSide( level , size);
  turn( -120 );
  KockSnowflakeSide( level, size);
  turn( -120 );
  KockSnowflakeSide( level, size);
  turn( 180 );
}

void KockSnowflakeSide(int level, int size)
{
  if (level == 0)
  {
      forward( size );
  }
  else
  {
      KockSnowflakeSide( level – 1, size / 3 );
      turn( 60 );
      KockSnowflakeSide( level-1, size / 3);
      turn( -120 );
      KockSnowflakeSide( level-1, size / 3);
      turn(60);
      KockSnowflakeSide( level-1, size / 3);
  }
}

void forward(float amount) {
  float newX = turtleX + cos(radians(turtleHeading)) * amount;
  float newY = turtleY + sin(radians(turtleHeading)) * amount;

  tft.drawLine(turtleX, turtleY, newX, newY, TFT_BLACK);
  turtleX = newX;
  turtleY = newY;
}

void turn(float degrees) {
  turtleHeading += degrees;
}

void loop() {
}

I spent most of my professional career as a C programmer, and I really like the Arduino environment. I think learning to program in C is a great skill for any programmer or prospective programmer. It is a great starting point to getting into more technical areas like programming microcontrollers using the GNU GCC tools and all the various low level SDKs you are likely to encounter.

Summary

The Wio Terminal is a great way to get into learning about microcontrollers without needing to know soldering or electronics. The Grove system lets you connect all sorts of sensors to the Wio Terminal using standard cables. The screen is good enough to display things like weather station data and the buttons can be used to build simple UIs.

Programming the Wio using the Arduino IDE works well. The provided board and device support are great, making programming the device easy. C programming has its pitfalls and a novice programmer might want to start with MicroPython first, but learning C is worthwhile. The processor is powerful enough for most applications and having an FPU is a nice bonus.

Overall the Wio Terminal is an interesting entry in the microcontroller world, trying to remove the intimidation and difficulty of learning electronics, but still allowing you to learn programming and construct many of the same DIY projects. Seeed Studio’s website contains many tutorials and suggestions for projects. This would be great in a classroom setting, since you don’t need to police youngsters risking burning themselves with improper care while soldering. The price runs at around $37 which makes it accessible to most people and price competitive against Arduino starter kits.

Written by smist08

August 30, 2021 at 11:43 am

I/O Co-processing on the Raspberry Pi Pico

leave a comment »

Introduction

Last time we looked at how to access the RP2040’s GPIO registers directly from the CPU in Assembly Language. This is a common technique to access and control hardware wired up to a microcontroller’s GPIO pins; however, the RP2040 contains a number of programmable I/O (PIO) coprocessors that can be used to offload this work from the main ARM CPUs. In this article we’ll give a quick overview of the PIO coprocessors and present an example that moves the LED blinking logic from the CPU over to the coprocessors, freeing the CPU to perform other work. There is a PIO blink program in the SDK samples, which blinks three LEDs at different frequencies, we’ll take that program and modify it to blink the LEDs in turn so that it works the same as the examples we’ve been working with.

PIO Overview

There are eight PIO coprocessors divided into two banks for four. Each bank has a single 32 word instruction memory that contains the program(s) that run on the coprocessors. 32 instructions aren’t very many, but you can do quite a bit with these. The SDK contains samples that implement quite a few communication protocols as well as showing how to do video output. 

Each PIO has an input and output FIFO buffer for exchanging data with the main CPUs.

The PIO coprocessors execute their own Assembly Language which the Raspberry folks call a state machine, though they also say they think it is Turing-complete. Below is a diagram showing one of the banks of four. This block is then duplicated twice in the RP2040 package.

Each processor has an X and Y 32-bit general purpose register, input and output shift registers for transferring data to and from the FIFOs, a clock divider register to help control timing, a program counter and then the register to hold the executing instruction as shown in the following diagram.

Each instruction can contain a few bits that specify a delay value, so for many protocols you can control the timing just by adding a timing delay to each instruction. Combine this with the clock divider register to slow down processing and you have a lot of control of timing without using extra instructions.

Sample LED Blinking Program

You write the Assembly Language PIO part of the program into a .pio file which is then compiled by the PIO Assembler into a .h file to include into your program. You can also include C helper functions here and the Pico SDK recommends including an initialization function. The various RP2040 SDK functions to support this are pretty standard and you tend to copy/paste these from the SDK samples.

We are blinking the LEDS using a 200ms delay time which by computer speeds is very slow, but for humans is quite quick. This means we can’t use the clock divider functionality and instruction delays as they don’t go this slow. Instead we have to rely on an old fashioned delay loop. We calculated the delay value in the main function using the frequency of the processor and then doing a loop. We do this delay loop twice because we need to wait for two other LEDs to flash before it’s our turn again. The pull instruction pulls the delay from the read FIFO, then out transfers it to the y register. We move y to x, turn on the pin and then do the delay loop decementing x until its zero. Then we turn the pin off and do the delay loop twice.

.program blink
    pull block
    out y, 32
.wrap_target
    mov x, y
    set pins, 1   ; Turn LED on
lp1:
    jmp x– lp1   ; Delay for (x + 1) cycles, x is a 32 bit number
    mov x, y
    set pins, 0   ; Turn LED off
lp2:
    jmp x– lp2   ; Delay for the same number of cycles again
    mov x, y
lp3:   ; Do it twice since need to wait for 2 other leds to blink
    jmp x– lp3   ; Delay for the same number of cycles again
.wrap             ; Blink forever!

% c-sdk {
// this is a raw helper function for use by the user which sets up the GPIO output, and configures the SM to output on a particular pin

void blink_program_init(PIO pio, uint sm, uint offset, uint pin) {
   pio_gpio_init(pio, pin);
   pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
   pio_sm_config c = blink_program_get_default_config(offset);
   sm_config_set_set_pins(&c, pin, 1);
   pio_sm_init(pio, sm, offset, &c);
}
%}

Now the main C program. In this one we configure the pins to use. Note that we will use a coprocessor for each pin, so three coprocessors but each one executing the same program. We start a pin flashing, sleep 200ms and then start the next  one. This way we achieve the same effect as we did in our previous programs.

After we get the LED flashing running on the coprocessors, we have an infinite loop that just prints a counter out to the serial port. This is to demonstrate that the CPU can go on and do anything it wants and the LEDs will keep flashing independently without any of the CPU’s attention.

#include <stdio.h>

#include “pico/stdlib.h”
#include “hardware/pio.h”
#include “hardware/clocks.h”
#include “blink.pio.h”

const uint LED_PIN1 = 18;
const uint LED_PIN2 = 19;
const uint LED_PIN3 = 20;
#define SLEEP_TIME 200

void blink_pin_forever(PIO pio, uint sm, uint offset, uint pin, uint freq);

int main() {
    int i = 0;

    setup_default_uart();

    PIO pio = pio0;
    uint offset = pio_add_program(pio, &blink_program);
    printf(“Loaded program at %d\n”, offset);
    blink_pin_forever(pio, 0, offset, LED_PIN1, 5);
    sleep_ms(SLEEP_TIME);
    blink_pin_forever(pio, 1, offset, LED_PIN2, 5);
    sleep_ms(SLEEP_TIME);
    blink_pin_forever(pio, 2, offset, LED_PIN3, 5);

    while(1)
    {
        i++;
        printf(“Busy counting away i = %d\n”, i);
    }
}

void blink_pin_forever(PIO pio, uint sm, uint offset, uint pin, uint freq) {
    blink_program_init(pio, sm, offset, pin);
    pio_sm_set_enabled(pio, sm, true);
    printf(“Blinking pin %d at %d Hz\n”, pin, freq);
    pio->txf[sm] = clock_get_hz(clk_sys) / freq;
}

Summary

This was a quick introduction to the RP2040’s PIO coprocessors. The goal of any microcontroller is to control other interfaced hardware, whether measurement sensors or communications devices (like Wifi). The PIO coprocessors give the RP21040 programmer a powerful weapon to develop sophisticated integration projects without requiring a lot of specialized hardware to make things easier. It might be nice to have a larger instruction memory, but then in a $4 USD device, you can’t really complain.

For people playing with the Raspberry Pi Pico or another RP2040 based board, you can program in 32-bit ARM Assembly Language and might want to consider my book “Raspberry Pi Assembly Language Programming”.

Written by smist08

April 30, 2021 at 10:02 am

Bit-Banging the Raspberry Pi Pico’s GPIO Registers

with 2 comments

Introduction

Last week, I introduced my first Assembly Language program for the Raspberry Pi Pico. This was a version of my flashing LED program that I implemented in a number of programming languages for the regular Raspberry Pi. In the original article, I required three routines written in C to make things work. Yesterday, I showed how to remove one of these C routines, namely to have the main routine written in Assembly Language. Today, I’ll show how to remove the two remaining C routines, which were wrappers for two SDK routines which are implemented as inline C functions and as a consequence only usable from C code.

In this article, we’ll look at the structure for the GPIO registers on the RP2040 and how to access these. The procedure we are using is called bit-banging because we are using one of the two M0+ ARM CPU cores to loop banging the bits in the GPIO registers to turn them on and off. This isn’t the recommended way to do this on the RP2040. The RP2040 implements eight programmable I/O (PIO) co-processors that you can program to offload this sort of thing from the CPU. We’ll look at how to do that in a future article, but as a first step we are going to explore bit-banging mostly to understand the RP2040 hardware better.

The RP2040 GPIO Hardware Registers

There are 28 programmable GPIO pins on the Pico. There are 40 pins, but the others are ground, power and a couple of specialized pins (see the diagram below).

This means that we can assign each one to a bit in a 32-bit hardware register which is mapped to 32-bits of memory in the RP2040’s address space. The GPIO functions are controlled by writing a 1 bit to the correct position in the GPIO register. There is one register to turn on a GPIO pin and a different register to turn it off, this means you don’t need to read the register, change one bit and then write it back. It’s quite easy to program these since you just place one in a CPU register, shift it over by the pin number and then write it to the correct memory location. These registers start at memory location 0xd0000000 and are defined in sio.h. Note there are two sio.h files, one in hardware_regs which contains the offsets and is better for Assembly Language usage and then one in hardware_structs which contains a C structure to map over the registers. Following are the GPIO registers, note that there are a few other non-GPIO related registers at this location and a few unused gaps in case you are wondering why the addresses aren’t contiguous.

RegisterAddress
gpio_in0xd0000004
gpio_hi_in0xd0000008
gpio_out0xd0000010
gpio_set0xd0000014
gpio_clr0xd0000018
gpio_togl0xd000001c
gpio_oe0xd0000020
gpio_oe_set0xd0000024
gpio_oe_clr0xd0000028
gpio_togl0xd000002c
gpio_hi_out0xd0000030
gpio_hi_set0xd0000034
gpio_hi_clr0xd0000038
gpio_hi_togl0xd000003c
gpio_hi_oe0xd0000040
gpio_hi_oe_set0xd0000044
gpio_hi_oe_clr0xd0000048
gpio_hi_oe_togl0xd000004c

Notice that there are a number of _hi_ registers, perhaps indicating that Raspberry plans to come out with a future version with more than 32 GPIO pins.

In the SDK and my code below we just write one bit at a time, I don’t know if the RP2040’s circuitry can handle writing more bits at once, for instance can we set all three pins to output in one write instruction? Remember hardware registers tend to have minimal functionality to simplify the electronics circuitry behind them so often you can’t get too complicated in what you expect of them.

Bit-Banging the Registers in Assembly

Below is the new updated program that doesn’t require the C file. In our routines to control the GPIO pins, we pass the pin number as parameter 1, which means it is in R0. We place 1 in R3 and then shift it left by the value in R0 (the pin number). This gives the value we need to write. We then load the address of the register we need, which we specified in the .data section and write the value. Note that we need two LDR instructions, once to load the address of the memory address and then the second to load the actual value.

@
@ Assembler program to flash three LEDs connected to the
@ Raspberry Pi GPIO port using the Pico SDK.
@
@

.EQU LED_PIN1, 18
.EQU LED_PIN2, 19
.EQU LED_PIN3, 20
.EQU sleep_time, 200

.thumb_func
.global main             @ Provide program starting address to linker

.align  4 @ necessary alignment

main:

@ Init each of the three pins and set them to output

MOV R0, #LED_PIN1
BL gpio_init
MOV R0, #LED_PIN1
BL gpiosetout
MOV R0, #LED_PIN2
BL gpio_init
MOV R0, #LED_PIN2
BL gpiosetout
MOV R0, #LED_PIN3
BL gpio_init
MOV R0, #LED_PIN3
BL gpiosetout

loop:

@ Turn each pin on, sleep and then turn the pin off

MOV R0, #LED_PIN1
BL gpio_on
LDR R0, =sleep_time
BL sleep_ms
MOV R0, #LED_PIN1
BL gpio_off
MOV R0, #LED_PIN2
BL gpio_on
LDR R0, =sleep_time
BL sleep_ms
MOV R0, #LED_PIN2
BL gpio_off
MOV R0, #LED_PIN3
BL gpio_on
LDR R0, =sleep_time
BL sleep_ms
MOV R0, #LED_PIN3
BL gpio_off

B       loop @ loop forever

gpiosetout:
@ write a 1 bit to the pin position in the output set register
movs r3, #1
lsl r3, r0 @ shift over to pin position
ldr r2, =gpiosetdiroutreg @ address we want
ldr r2, [r2]
str r3, [r2]
bx lr

gpio_on:
movs r3, #1
lsl r3, r0 @ shift over to pin position
ldr r2, =gpiosetonreg @ address we want
ldr r2, [r2]
str r3, [r2]
bx lr

gpio_off:
movs r3, #1
lsl r3, r0 @ shift over to pin position
ldr r2, =gpiosetoffreg @ address we want
ldr r2, [r2]
str r3, [r2]
bx lr

.data
      .align  4 @ necessary alignment
gpiosetdiroutreg: .word   0xd0000024 @ mem address of gpio registers
gpiosetonreg: .word   0xd0000014 @ mem address of gpio registers
gpiosetoffreg: .word   0xd0000018 @ mem address of gpio registers

Having separate functions for gpio_in and gpio_out simplifies our code since we don’t need any conditional logic to load the correct register address.

We loaded the actual address from a shared location. We could have loaded the base address of 0xd000000 and then stored things via an offset, but I did this to be a little clearer. If you look at the disassembly of the SDK routine, it does something rather clever to get the base address. It does:

movs r2, #208 @ 0xd0
lsl r2, r2, #24 @ becomes 0xd0000000

And then uses something like:

str r3, [r2, #40] @ 0x28

To store the value using an index which is the offset to the correct register. I thought this was rather clever on the C compiler’s part and represents the optimizations that the ARM engineers have been adding to the GCC generation of ARM code. This technique takes the same time to execute, but doesn’t require saving any values in memory, saving a few bytes which may be crucial in a larger program.

Summary

Writing to the hardware registers directly on the Raspberry Pi Pico is a bit simpler than the Broadcom implementation in the full Raspberry Pi. With these routines we wrote our entire program in Assembly Language. There is still C code in the SDK which will be linked into our program and we are still calling both gpio_init and sleep_ms in the SDK. We could look at the source code in the SDK and reimplement these in Assembly Language, but I don’t think there is any need. Between the RP2040 documentation and the SDK’s source code it is possible to figure out a lot about how the Raspberry Pi Pico works.

For people playing with the Raspberry Pi Pico or another RP2040 based board, you can program in 32-bit ARM Assembly Language and might want to consider my book “Raspberry Pi Assembly Language Programming”.

Written by smist08

April 24, 2021 at 11:50 am

Calling Main in Assembly Language on the RP2040

with one comment

Introduction

In last week’s article, I presented my first Assembly Language program on the Raspberry Pi Pico. The program worked, but it included some C code that I wasn’t happy with. In this article, I’ll explain why I needed to have the main entry point in C, what I missed and how to correct this problem.

The entry point is a function main() with no parameters or return code called by the RP2040 initialization code after it initializes the RP2040 hardware. In C this worked no problem, but in Assembly Language it resulted in a hardware fault on executing the first instruction in my main() routine. This was a bit of a head scratcher and it took a couple of days before I realized what the problem was. My first thought was that it was alignment, but no it wasn’t that. Perhaps I needed to duplicate the first few instructions in the Assembly Language generated by the C compiler, but no that still caused a hardware fault. Rather mystifying and annoying.

Use the Source

The program you run on the Pico contains pretty much everything in a single executable, that initializes the CPU, peripheral hardware and then runs in an endless loop forever. There is no operating system, just your program. The Raspberry Pi Pico contains a bit of firmware which is activated when you power on with the bootsel button pressed, this allows the Pico to connect as a shareable flash drive to a USB host, and will allow you to copy files into the writable part of the Pico’s flash memory. After that it reboots to let the program run.

One of the good things about the Pico is that the SDK contains the source code for this whole thing, and when you build your program, it actually compiles all this source code alongside your code (there are no libraries in this environment). This means you can build a debug build where everything is debuggable including both your code and the SDK code. This means you can set a breakpoint before your code and single step through the SDK into your code. You can’t start debugging at the very first instruction, you need to let the first bit of the SDK initialize the processor before starting, but you can set a breakpoint fairly early. I found a good place was the platform_entry routine, which is an Assembly Language function in crt0.S. This is the function that initializes the SDK environment and then calls your main() starting point. The code for this routine is fairly innocuous:

platform_entry: // symbol for stack traces
    // Use 32-bit jumps, in case these symbols are moved out of branch range
    // (e.g. if main is in SRAM and crt0 in flash)
    ldr r1, =runtime_init
    blx r1
    ldr r1, =main
    blx r1
    ldr r1, =exit
    blx r1

Nothing special, it just loads the address of our main routine and calls it. Stepping through the C code, it works, stepping through the Assembly Language code, hardware fault.

At some point I thought to look at the documentation for the BLX instruction, why were they calling this rather than BL? This turned out to be the root of the problem.

On a full ARM A-series CPU, like those in a full Raspberry Pi or in your cell phone, it can execute a rich set of instructions, which are the regular ARM 32-bit instruction set, but on the microcontroller M-series CPU like in the Pico it only executes the so called “thumb” instructions. On the A-series CPU you switch back and forth between regular and thumb modes using the BLX instruction. Thumb instructions are 16-bit in length, regular instructions are 32-bit, both have to be aligned, on even bytes the other on 4-byte boundaries. Both of these are even addresses so the true address of any instruction is even, which means the low order bit isn’t really used (it has to be zero). The BLX instruction uses this low order bit to specify whether to switch to thumb mode or not. If it is one, then thumb mode, if even then regular instruction mode. Let’s look at the disassembly for this routine:

1000021a <platform_entry>:
1000021a: 4919      ldr r1, [pc, #100] ; (10000280 <__get_current_exception+0x1a>)
1000021c: 4788      blx r1
1000021e: 4919      ldr r1, [pc, #100] ; (10000284 <__get_current_exception+0x1e>)
10000220: 4788      blx r1
10000222: 4919      ldr r1, [pc, #100] ; (10000288 <__get_current_exception+0x22>)
10000224: 4788      blx r1

10000280: 100012bd .word 0x100012bd   ; runtime_init
10000284: 10000361 .word 0x10000360   ; main
10000288: 100013a9 .word 0x100013a9   ; exit

Notice the address for my main routine is even whereas the other two routines are odd. If I compile with the C routine then main has an odd address as well. I didn’t think of this because the RP2040’s M-series CPU only executes thumb instructions, so why have any functionality to switch between modes? I don’t know but if you do tell it to switch to regular instructions then you get a hardware fault.

The other question is why the author of crt0.S in the SDK calls routines with BLX rather than BL? Afterall the Pico doesn’t support regular instructions, so you are always in thumb mode. If platform_entry used BL instead, then I wouldn’t have had any problem. I wonder if this indicates they developed the SDK on an A-series CPU, perhaps before they obtained real RP2040’s and this indicates how they did early development on the SDK? Or perhaps there is a way to emulate the RP2040 on a full A-series CPU and this is how the developers at the Raspberry Pi foundation operate.

To correct the problem, we just need to indicate our main() routine is a thumb routine. We do this by placing a .thumb_func directive in front of the .global directive.

.thumb_func
.global main             @ Provide program starting address to linker

.align  4 @ necessary alignment

main:

The key point is that this is in front of the .global, since it is really just the linker that needs to process this to set up the correct address when it links in crt0.

Summary

This eliminates the need for the C main() function we had last week. Next time we’ll eliminate the two other C routines we had and explore how the Raspberry Pi Pico’s GPIO control registers work. As with most problems, working through the solution, teaches us a bit more about how the RP2040 works and reminds us that there are consequences of using a subset of the full ARM instruction set.

For people using this SDK, you can program in 32-bit ARM Assembly Language and might want to consider my book “Raspberry Pi Assembly Language Programming”.

Written by smist08

April 23, 2021 at 9:11 am

Assembly Language on the Raspberry Pi Pico

with 11 comments

Introduction

The Raspberry Pi Pico is the Raspberry Foundation’s first entry into the domain of Arduino style microcontrollers. The board contains Raspberry’s own designed SoC (System on a Chip) containing a dual core ARM Cortex-M0+ CPU along with memory and a collection of I/O circuitry. There are no keyboard, mouse or monitor ports on the board, only a micro-USB to connect to a host computer, a number of GPIO pins and three debug pins. This SoC is called the RP2040 and is licensed to other companies to use in their own boards. Raspberry supports programming this board in either C/C++ or MicroPython. The C/C++ SDK also supports Assembly Language programming to some degree and this article is a look at my first attempt to write an Assembly Language program for this board. I ran into a few problems and still have a few things to figure out and we’ll explain those in the article. We’ll write an Assembly Language version of the program we wrote in C last time to flash three connected LEDs.

ARM Cortex-M0+ Assembly Language

I blogged about 32-bit ARM Assembly Language here, and then presented the flashing LED Assembly Language program for the Raspberry Pi here. Further I wrote a whole book on 32-bit ARM Assembly Language Programming: “Raspberry Pi Assembly Language Programming”. These are all oriented to ARM’s full A-series processors which include floating point units (FPU), vector processors, virtual memory support and much more. The ARM M-series processors are a subset of these, designed to be low cost, use little memory and be very power efficient. The ARM M-series processors only contain what are called the ARM “thumb” instructions. Normally, on an A-series processor, each instruction takes 32-bits, but for some applications this uses too much memory, so ARM came up with “thumb” instructions where if the processor is operating in “thumb” mode then each instruction is only 16-bits in length, thus only using half the memory. The original set of “thumb” instructions was too limited, so ARM added a way to run some 32-bit instructions in with the 16-bit instructions and that makes the modern “thumb” instructions set used by the M-series processors. One consequence of using the “thumb” instructions is that registers R8 to R12 are not accessible and hence not implemented on the chip, thus saving circuitry. The registers you do have are all 32-bit and the Raspberry RP2040 has special multiplication and division circuitry to perform these operations quickly.

Code

This program uses the C/C++ SDK to access the GPIO pins, this means this Assembly Language program is quite similar to last week’s C program. To call a routine in Assembly, you put the first parameter in R0, the second in R1 and then call Branch with Link (BL). BL places the address of the next instruction into the LR register, so the called return returns by branching to the address contained in the LR register. When calling functions there is a convention on who has to save which register on the stack, but we don’t use any register over the function calls, so we don’t need to do this. This program is set up as an infinite loop, since there is nothing for the main routine to return to and if it does return the processor halts.

Assembly Language code:

@
@ Assembler program to flash three LEDs connected to the
@ Raspberry Pi Pico GPIO port using the Pico SDK.
@
@

.EQU LED_PIN1, 18
.EQU LED_PIN2, 19
.EQU LED_PIN3, 20
.EQU GPIO_OUT, 1
.EQU sleep_time, 200

.global main_asm             @ Provide program starting address to linker
main_asm:

MOV R0, #LED_PIN1
BL gpio_init
MOV R0, #LED_PIN1
MOV R1, #GPIO_OUT
BL link_gpio_set_dir
MOV R0, #LED_PIN2
BL gpio_init
MOV R0, #LED_PIN2
MOV R1, #GPIO_OUT
BL link_gpio_set_dir
MOV R0, #LED_PIN3
BL gpio_init
MOV R0, #LED_PIN3
MOV R1, #GPIO_OUT
BL link_gpio_set_dir
loop:   MOV R0, #LED_PIN1
MOV R1, #1
BL link_gpio_put
LDR R0, =sleep_time
BL sleep_ms
MOV R0, #LED_PIN1
MOV R1, #0
BL link_gpio_put
MOV R0, #LED_PIN2
MOV R1, #1
BL link_gpio_put
LDR R0, =sleep_time
BL sleep_ms
MOV R0, #LED_PIN2
MOV R1, #0
BL link_gpio_put
MOV R0, #LED_PIN3
MOV R1, #1
BL link_gpio_put
LDR R0, =sleep_time
BL sleep_ms
MOV R0, #LED_PIN3
MOV R1, #0
BL link_gpio_put
B       loop

.data

      .align  4 @ necessary alignment

I didn’t intend to include any C code, but I ran into a couple of problems that require it. One is that a large number of SDK functions are inline C functions which means they can’t be called from outside of C. In our case two functions gpio_set_dir and gpio_put are inline and required wrapping. The other problem is that if the main program is Assembly Language then the code to initialize the board doesn’t seem to be called. I think this is a matter of setting the correct CMake options, but I haven’t had a chance to figure it out yet. For now we have main in the C code and then call the Assembly Language main routine.

C code:

#include “hardware/gpio.h”

void link_gpio_set_dir(int pin, int dir)
{
gpio_set_dir(pin, dir);
}

void link_gpio_put(int pin, int value)
{
gpio_put(pin, value);
}

void main()
{
main_asm();
}

The Raspberry Pi Pico SDK uses the CMake system to manage builds. The SDK provides a large set of build rules. You run CMake and then it creates a makefile that compiles your program.

CMake file:

cmake_minimum_required(VERSION 3.13)

include(pico_sdk_import.cmake)

project(test_project C CXX ASM)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

pico_sdk_init()

include_directories(${CMAKE_SOURCE_DIR})

add_executable(flashledsasm
  mainmem.S
  sdklink.c
)

pico_enable_stdio_uart(flashledsasm 1)
pico_add_extra_outputs(flashledsasm)
target_link_libraries(flashledsasm pico_stdlib)

Still To-Do

The program works, but there are a few things I’m not happy about. The Raspberry Pi Pico SDK is pretty new, so there aren’t a lot of answers on StackOverflow yet. The good thing is that it is all open source, so it is just a matter of time to figure out the code. Here is what I’ll be working on:

  1. How to have main be in Assembly Language and have the board properly initialized. Match the C startup sequence.
  2. Figure out the details of the GPIO registers and have Assembly Language versions of the inline C code that accesses these. They are similar to those on the full Raspberry Pi, but different.
  3. How to get constants from the C include file, on first try this didn’t work and gave syntax errors, but the SDK says they should be usable from Assembly Language. They might need a couple of fixes.

Summary

I planned to write a 100% Assembly Language program, but didn’t quite make it. At least the program works, showing you can include Assembly Language in your RP2040 projects. The support to build using the GCC macro assembler is all there and besides some interactions with the SDK all seems to work well. Of course the Raspberry Pi Pico SDK is pretty new so there will be a lot of updates and there are still a number of undocumented holes to investigate.

Written by smist08

April 16, 2021 at 9:43 am

Raspberry Pi Pico First Project

with 3 comments

Introduction

Last week, I blogged about Adafruit’s Feather RP2040 microcontroller. I’ve now received a Raspberry Pi Pico which is also based on the ARM RP2040 processor. The Adafruit version is oriented around programming with CircuitPython and then interfacing to the various companion boards in Adafruit’s Feather lineup. The Raspberry Pi Pico is oriented around programming in C/C++ using the Raspberry Pico SDK and then designing interfaces to other devices yourself. Although you can do either with both boards, I’m going by the direction of the two companies tutorials and volume of documentation. The Raspberry Pi Pico also favours doing your development from a full Raspberry Pi, although you can use a Windows, Linux or Mac, it is quite a bit harder to setup serial communications and debugging.

In this article, I’m going to look at doing a small project with the Raspberry Pi Pico using the C SDK. I’ll look at the elements you use to program, debug and deploy code. I’ll use the same flashing LEDs project that I’ve written about previously.

More Soldering

Like the Adafruit board, if you want to use a breadboard to set up your electronics project, you need to solder pins to the Pico to attach it. Having practiced on the Adafruit Feather, this was no problem. In addition if you want to do proper C debugging using gdb then you need to solder three wires to the debug ports on the end of the board. Note you need wires that can connect to the Raspberry Pi 4’s GPIO pins. All the soldering went fine and when I wired everything up I got serial communications going as well as played a bit with debugging using the SDK’s sample blink program. I was lucky that I could find all the wires, connectors and pins required from previous various Arduino and Raspberry starter kits.

Setting up the LEDs

If you want to use print statements while you are debugging, then you need to use the separate serial communications pins rather than a serial communications channel through the USB port. This is because when you stop the processor in the debugger, it stops it dead and disconnects any USB connections.

The picture below shows the setup. The LEDs are connected each to a GPIO port and then go through a resistor to ground. Remember LEDs have low resistance and you don’t want to short out your device. Also remember that LEDs are directional and you want to connect the plus side to the GPIO pin. The plus side is usually indicated by the longer lead wire.

In the picture above you can also see the 3 wires at the end going off to the Raspberry Pi as well as the three wires by the USB port that lead to serial communications GPIO pins on the Pi. The USB connects to the Pi to provide power, as well as is used to load any program into the flash memory.

A single picture can be hard to see, here is a video showing the LEDs flashing as well as panning around a bit to see all the connections from different angles: https://youtu.be/ogbcMp5Asu4

Code

The C/C++ SDK for the RP2040 provides a set of libraries to help with your device programming along with support for using the GCC toolchain. This means you can write code in C/C++ and Assembly Language and then debug using gdb. If you look at the C code, it is similar to the code we wrote for the regular Raspberry Pi here, though we had to write some of these library type routines against the Linux device driver ourselves.

Here is the C code:

#include <stdio.h>
#include “pico/stdlib.h”
#include “hardware/gpio.h”
#include “pico/binary_info.h”

const uint LED_PIN1 = 18;
const uint LED_PIN2 = 19;
const uint LED_PIN3 = 20;

int main()
{

    bi_decl(bi_program_description(“Copyright (c) 2021 Stephen Smith”));

    stdio_init_all();
    gpio_init(LED_PIN1);
    gpio_init(LED_PIN2);
    gpio_init(LED_PIN3);
    gpio_set_dir(LED_PIN1, GPIO_OUT);
    gpio_set_dir(LED_PIN2, GPIO_OUT);
    gpio_set_dir(LED_PIN3, GPIO_OUT);

    while (1)
    {
        puts(“Flash Loop\n”);
        gpio_put(LED_PIN1, 1);
        sleep_ms(200);
        gpio_put(LED_PIN1, 0);
        sleep_ms(200);
        gpio_put(LED_PIN2, 1);
        sleep_ms(200);
        gpio_put(LED_PIN2, 0);
        sleep_ms(200);
        gpio_put(LED_PIN3, 1);
        sleep_ms(200);
        gpio_put(LED_PIN3, 0);
        sleep_ms(200);
    }

The SDK defines projects using CMake. When you run CMake it will generate a makefile that you use to build your executable using make. Here is the CMake file:

cmake_minimum_required(VERSION 3.13)

include(pico_sdk_import.cmake)

project(test_project C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

pico_sdk_init()

add_executable(flashleds
  flashleds.c
)

pico_enable_stdio_uart(flashleds 1)
pico_add_extra_outputs(flashleds)
target_link_libraries(flashleds pico_stdlib)

Print Statements

In the C code there is a puts() function call to send “Flash Loop” to stdout. You can define what this is in the CMake file, in our case it’s set to the serial port (as opposed to the USB port). We can then read this data by monitoring the serial port on the Raspberry Pi 4 that we’ve connected up. One way to display the output is with the minicom program by calling:

minicom -b 115200 -o -D /dev/serial0

Which then displays:

Debugging

The Pico and the C/++ SDK have support for gdb. You run gdb on the Raspberry Pi 4 and then gdb connects remotely through minicom to the Raspberry Pi Pico. The C tutorial has full instructions on how to do this and when running it works quite well, though it takes several long command lines to get debugging going.

Visual Studio Code

The C/C++ SDK has full support for Visual Studio Code. This is the reason Raspberry added Microsoft repositories to the Raspberry Pi OS to seamlessly install and update this. After all the outcry from users, the calls to the Microsoft repositories have been removed and you need to install this by hand, as documented in the C getting started manual. I’m not really keen on Visual Studio Code, but if you like it and want to use it, go for it.

The best thing about Visual Studio Code is that it automates setting up remote debugging, so you can just say debug and Visual Studio Code does all the setup and connecting behind the scenes.

Summary

Both the Raspberry Pi Pico and Adafruit Feather RP2040 are powerful computers and great value as $5 microcontrollers. The programming environments are rich and powerful. You have lots of choices on how to program these with a lot of good supporting libraries and SDKs. The RP2040 exposes all sorts of GPIO ports and interfacing technology for you to make use of in your projects. It will be interesting to see all the RP2040 based DIY projects being showcased.

For people using this SDK, you can program in 32-bit ARM Assembly Language and might want to consider my book “Raspberry Pi Assembly Language Programming”.

Written by smist08

April 8, 2021 at 3:12 pm

Adafruit Feather RP2040 First Impressions

with 5 comments

Introduction

Last week we talked about the Raspberry Pi’s entry into the microcontroller market with their new RP2040 chip. I had ordered a couple of these, but had yet to receive one. Last Wednesday I received my Adafruit Feather RP2040, this is Adafruit’s entry into this market using Raspberry’s new ARM based RP2040 microcontroller. In this article, I’ll give my first impressions of this Adafruit board, with the caveat that I’ve only had it two days now.

The Adafruit board is similar to the Raspberry Pi Pico, but it is in the form factor for Adafruit’s feather line of peripherals. This means you can stack and intermix the peripherals just like with any other feather microcontrollers. This means the pins correspond to the feather specification rather than those on the Raspberry Pi Pico. This is good since it means you have a large selection of peripherals that are easy to use and pretty much snap together. Further Adafruit has done a good job of providing Python drivers for all their devices. Beyond this, the Adafruit board has a connector for a battery and will manage running off the battery, or charging the battery when connected via USB. The Adafruit board also has an RGB LED that programs can use and a reset button in addition to the boot selection button.

Painful Shipping

The best thing about the Adafruit board was that it was the first RP2040 board that I found that I could actually order, no waiting list, no back order. Kudos, it actually shipped later in the day I ordered it on. The bad news was that the only shipping option was DHL at the exorbitant price of $20USD. I think a lot of companies that sell inexpensive microcontroller parts, usually at around $5 USD each, make all their money by overcharging for shipping. DHL used to be good when they had a big contract to do deliveries for Amazon, then they delivered here reliably every day. Since they lost the Amazon contract, they don’t even deliver to our community and as a result it took an extra four days for the package to get to Gibsons from Vancouver, because they reshipped it with a different courier company and were slow about the process. If Adafruit had an option to use the regular post, it would have been cheaper and would have gotten here quicker. They could have still overcharged for it, but perhaps not as much.

Unboxing

I ordered a few extras along with the Adafruit board, namely a FeatherWing 128×32 OLED display and a lithium ion battery and connector cables. This all arrived in a cardboard box full of bubble wrap with the actual parts safely tucked inside.

I saw that a Raspberry Pi Pico connects to your computer via a micro-USB cable, so I had one of these ready. Picking up the Adafruit RP2040 revealed it uses a USB-C cable, so I had to run upstairs and dig one of these out. This highlights that if there are easy choices in the board design, Adafruit chooses the alternative to what Raspberry chose, I guess to differentiate themselves. Anyway, plugging in the Adafruit to my Raspberry Pi worked, namely it displayed in the Raspberry Pi as a removable disk drive, showing a view to the 8MB flash ROM where you copy your programs to run. Yay, it is working. Next I installed CircuitPython and ran a test program that blinks one of the LEDs on the board. I’ll talk more about CircuitPython after a bit of soldering.

Assembly Requires Soldering

The nice thing about the Arduino Uno and regular Raspberry Pi’s is that they are easy to attach to breadboards for prototyping and experimenting with electronics, everything just snaps together, no soldering required. That isn’t true for either the Adafruit Feather nor the Raspberry Pi Pico. If you want to use a breadboard, you need to solder some pins to them. Both Adafruit and Raspberry have excellent tutorials on how to do this. It was a bit intimidating, since I have a cheap $10 soldering iron from Canadian Tire and the board is very small. Another good thing about the Adafruit RP2040 (and the OLED display board) is that they come with the pins you need for this (you need to order these separately for the Raspberry Pi Pico).

Anyway I went ahead to do this and it turned out to be easier than expected. Heat the pin up with the soldering iron first and then the solder easily flows into position. A bit of a relief since Adafruit recommends buying a $200 soldering iron.

Perhaps down the road, either the various board makers or the companies that assemble kits from these will include an option where these are already soldered in place and perhaps integrated with a breadboard, like the official Arduino Uno starter kit. I see other Adafruit feather boards can be ordered with or without the pins installed, so perhaps they’ll have this available soon.

The reason these pins aren’t automatically built in place, is that this leaves makers a lot of options to package their final products into much smaller packages, since you can solder wires and devices directly to these points.

CircuitPython

The Raspberry Pi tutorial and documentation is all oriented around MicroPython and then the Adafruit documentation and tutorials are all oriented around CircuitPython. CircuitPython is based on Micropython, but taken by Adafruit and made a bit easier to use, especially if you use Adafruit devices. These RP2040 boards basically run one program, so to run Python you need to install the Python runtime to be that one program. To do this you download the CircuitPython UF2 file from Adafruit’s website and copy it to the board’s flash ROM. The board then reboots and the removable drive changes to be a folder inside the CircuitPython environment where you copy your Python program to run. Basically if you copy a file called code.py to this folder then it will be run. You can use the bootsel button to boot into the original view, if you want to later replace the Python runtime with something else.

To actually develop, you need a Python IDE running on a proper computer. Raspberry recommends using Thonny and Adafruit recommends using mu-editor. Thonny comes pre-installed on the Raspberry Pi OS, but adding mu-editor was easy, since it is written in Python, you install it with pip3:

pip3 install mu-editor==1.1.0b3

With this I could cut/paste the blink program from the Adafruit tutorial in and save it as code.py to the device and see the LED on the board flashing. The CircuitPython runtime provides a serial connection back to the IDE via the USB port, so if you click the “Serial” button in the IDE you can see the Python console. At this point you can control Python to some degree through the command line as well as see the output from print statements.

Next up, I connected up the OLED display and wanted to get this to try. Below is a picture of the two devices connected together on a breadboard.

I copy/pasted the Python code for this from another Adafruit tutorial, saved it to the board and nothing happened. Looking at the serial port showed I was getting missing library errors. Of course the next step in the tutorial is to download all  the device specific CircuitPython libraries. Adafruit provides a bundle of all the drivers for their devices, so I downloaded this.

First I tried just copying the libraries I thought I needed, but I didn’t have much luck and kept getting errors. Then I just copied all the libraries and then everything worked as shown in the above photo. The whole library takes 1.9Meg of the 8Meg Flash ROM, so I might try deleting some later.

To give a flavour of CircuitPython programming, below is the sample program to display “Hello World!” in a box on the display:

# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT
“””
This test will initialize the display using displayio and draw a solid white
background, a smaller black rectangle, and some white text.
“””

import board
import displayio
import terminalio
from adafruit_display_text import label
import adafruit_displayio_ssd1306

displayio.release_displays()
i2c = board.I2C()
display_bus = displayio.I2CDisplay(i2c, device_address=0x3C)
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=32)

# Make the display context

splash = displayio.Group(max_size=10)
display.show(splash)
color_bitmap = displayio.Bitmap(128, 32, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0xFFFFFF  # White
bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
splash.append(bg_sprite)

# Draw a smaller inner rectangle

inner_bitmap = displayio.Bitmap(118, 24, 1)
inner_palette = displayio.Palette(1)
inner_palette[0] = 0x000000  # Black
inner_sprite = displayio.TileGrid(inner_bitmap, pixel_shader=inner_palette, x=5, y=4)
splash.append(inner_sprite)

# Draw a label

text = “Hello World!”
text_area = label.Label(terminalio.FONT, text=text, color=0xFFFF00, x=28, y=15)
splash.append(text_area)

while True:
    pass

Summary

The Adafruit Feather RP2040 is a good choice for a more powerful ARM based microcontroller. Its advantage over the Raspberry Pi Pico is the feather hardware ecosystem. Many people will also find the battery connector convenient, since many people will want their final build running off a rechargeable battery. Adafruit has done a good job getting all their CircuitPython support going and there is quite a bit of documentation and tutorials on their website.

I haven’t gotten the RP2040 C/C++ SDK going with the Adafruit board yet, but there is support for this board in the SDK, with a header file of all the pin definitions. For people using this SDK, you can program in 32-bit ARM Assembly Language and might want to consider my book “Raspberry Pi Assembly Language Programming”.

Written by smist08

April 2, 2021 at 11:16 am

Introduction to the RP2040

with 7 comments

Introduction

The Raspberry Pi foundation recently started shipping the Raspberry Pi Pico, a flexible $4 USD microcontroller board. This is Raspberry’s first foray into the world of microcontroller boards, typically Raspberry Pi’s are low cost complete Linux computers and the microcontroller world has been typically dominated by the Arduino line of products, which are typically based on low cost 8-bit processors. One notable part of this announcement is that the processor included on the Pico is a dual core ARM processor with the System on a Chip (SoC) being designed by the Raspberry Pi foundation. In the past all Raspberry Pis have been based on Broadcom SoCs.

This new processor is the RP2040. Not only is this processor being used in the Pi Pico, but the Raspberry Pi foundation is selling this chip to other board manufacturers to use in their own product. Already many companies and foundations that develop Arduino boards are starting to ship products based on the RP2040, but designed to fit into their own eco-system whether it is Arduino or one of its competitors. For instance Arduino, Pimoroni, SparkFun, and Adafruit have all announced either shipping or near shipping products. I haven’t been able to order a Raspberry Pi Pico yet, since they instantly sold out, but I have been able to buy a similar Adafruit board.

All these RP2040 based boards are under $10USD and all of them have lots of GPIO ports to integrate them into your hardware projects. In this article we are going to look into some of the details of the RP2040 chip itself.

RP2040 Overview

The heart of the RP2040 are dual core ARM Cortex-M0+ 32-bit CPUs that operate at 133MHz, along with 264Kb of SRAM. This may not seem like much compared to regular Raspberry Pis or other common inexpensive computers, but in the microcontroller world this is really good. Most microcontrollers are based on slower 8-bit microcontrollers with only a few k of memory, for instance the Arduino Uno operates at 16MHz and has 6kb of memory. There are a sprinkling of 32-bit based microcontrollers based on various ARM or RISC-V models, but the RP2040 is the first to take these mainstream. Keep in mind that the original Apple II operated at 1MHz on an 8-bit CPU and only supported up to 48kb of RAM, yet it was fully capable of word processing, spreadsheets and arcade like video games. The original IBM PC only operated at 4.77MHz, had a 16-bit processor and upto 640Kb of RAM. This $4 microcontroller has far more computing power than any of these early PCs, so it will be pretty amazing to see what people produce.

The ARM Cortex-M0+ core is a low energy 32-bit processor. My book “Raspberry Pi Assembly Language Programming” covers 32-bit Assembly Language programming for a full A series ARM CPU. The M series ARM CPUs for microcontrollers contain a subset of the processing units in the full A series CPUs. For instance, there is no floating point unit (FPU) and no NEON vector processing unit. Further, it doesn’t use the full 32-bit sized instructions, instead it only uses the so called “thumb” instructions which are 16 bit in size. This means there are only eight 32-bit registers for integer arithmetic and other processing (there is only room for three bits to specify a register). With these limitations, the transistor count is greatly reduced for the CPU, reducing power usage and reducing cost. Even with these restrictions, the RP2040 is powerful enough to run Tensorflow Lite.

The RP2040 is manufactured by TSMC using an old 40nm process. This greatly reduces the cost of the chip, since there isn’t much competition for this manufacturing technology. Apple’s latest chips use a 5nm manufacturing process and there is a long waiting list of companies waiting for capacity.

Programming

Typically you program the Raspberry Pi Pico in MicroPython or C/C++ without an operating system. You write your program, compile it and download it to the Pico where it runs as a single program with no operating system. The Raspberry Pi foundation offers full support for MicroPython as well as provides a C/C++ SDK that allows you to compile programs in C, C++ or Assembly Language.

If you are familiar with Arduino and how you program these, then this is exactly how you program the Pico. Which means you need another computer to run the IDE (this could be a regular Raspberry Pi). Then the Pico is connected to this computer via USB.

Of course the Linux folks aren’t sitting still, they take the challenge that Linux should run anywhere quite seriously and have already ported Fuzix, a lightweight Linux to the Pico.

This is more likely to be used for embedded type Linux applications, since connecting monitor, keyboard and mouse are a major challenge that the Pico isn’t designed for.

Will Raspberry Produce Other Chips?

It will be interesting to see if the Raspberry Pi Foundation sticks with Broadcom chips for the regular Raspberry Pis. Will the Raspberry Pi 5 be based on a Pi Foundation designed SoC? If it is, would it allow them to use newer CPU cores? Will it mean they can incorporate an ARM Mali GPU rather than the Broadcom GPU they have been using? Will there be other models of embedded chips beyond the RP2040? Evidently the numbers are significant, the 2 is for the number of cores and the 4 indicates the memory size and then the 0’s are for possible future features. We’ll have to watch Raspberry’s product announcements closely over the next year or two.

Summary

The RP2040 is exciting because it could take the microcontroller market from 8-bit to 32-bit, increasing the programming capabilities of all these DIY electronics projects greatly. Not everyone will move to this, since it will also push the price of 8-bit microcontrollers even lower. But perhaps your next microwave or toaster will contain AI software running on an RP2040.

Written by smist08

March 26, 2021 at 10:38 am