Friday, May 13, 2022

RP2040/Raspberry Pi Pico Toolchain--Ubuntu, CMAKE, and the Usual Boring Blink Test

 Hello again. I am fully recovered from Covid. It's time to keep marching forward.  

After finishing several projects based on Atmel's 328P MCU (a 328P based complex LFO is here, for instance), it was time to move to a new MCU family--something faster and more powerful. 

Contenders for this next evolutionary step in my AudioDiWHY journey were ST Systems' STM32 MCU's and development  boards--there are a lot of them--and Espressif's ESP32. 


Instead I chose Raspberry Pi's RP2040, the processor found in their PICO development board--mostly because the RP2040 was in stock pretty much everywhere I looked and is extremely affordable.

                      

I chose RP2040 because it's the underdog?
                                    

TOOLCHAIN TIME

Here's how I got an RP2040 C/C++ toolchain up and running on Ubuntu 20.04 Linux.

I targeted the Raspberry Pi Pico development board.  It's inexpensive and well documented, and a toolchain that works with the Pico should work with whatever other RP2040-based PCB's I use for future projects. 

For this toolchain I needed software and hardware to write and debug RP2040 firmware (I code in C, mostly), compile the code, and upload the firmware onto the RP2040.  

I recently set up an Ubuntu 20.04 virtual machine running on VMWARE Workstation Pro 16 on a Windows 10 NUC computer (post about my bench setup is here).  I decided to go with this Ubuntu Linux VM for developing on the Pico--I don't use Linux as much at my day job as I'd like, so I'll use it here. 

There are many videos and posts about setting up a Linux RP2040 toolchain. The Shawn Hymel YouTube series here proved useful, as were the posts here and here; and the official PDF here. Also, The PDF for the RP2040 SDK is here.  

My Ubuntu build was "desktop"--that was my starting point....

First I sudo apt-install'd the software I thought I needed:

(from my home directory….)

mkdir pico

cd pico

sudo apt install git  #installs git

git clone -b master https://github.com/raspberrypi/pico-sdk.git

cd pico-sdk

git submodule update --init

sudo apt upgrade

sudo apt install gcc-arm-none-eabi

sudo apt install libstdc++-arm-none-eabi-newlib

sudo apt install build-essential     

sudo apt  install cmake  # version 3.18.4-2ubuntu1 

Can we use RPi's WGET?

To install a RP2040 toolchain on a Raspberry Pi, the RP Foundation provides a wget statement to build the entire toolchain with minimal end user interaction. It's here--go to page 4. 

We are not using a Raspberry Pi, rather Ubuntu, which is very close, so this "gee whiz it's easy!" single script approach might have worked. Or would it?

A common Linux trick is to remove the wget from the command, copy the rest of it into your browser, and see what happens.  

Here it the resulting URL:

https://raw.githubusercontent.com/raspberrypi/pico-setup/master/pico_setup.sh

Following this URL I saw a shell script....fascinating....and begs the question: who is Liam? regardless, I chased down how the script worked and decided it would be safer to build the Ubuntu part of the toolchain "by hand".  

Back to it: To get the system to locate the SDK files for RP2040, I needed to add this via the command line to the Ubuntu PC:

echo 'export PICO_SDK_PATH=$HOME/pico/pico-sdk' | sudo tee -a /etc/profile.d/pico-sdk.sh

To make that command take effect, I rebooted my Linux virtual machine, but I could have run this additional command:

source /etc/profile

With all this in place, I could compile the example sketches found in ~/pico/examples, build from the command line, drag the UF2 file to the PICO, and--we have BLINK! 

We are headed in the right direction. 

OK--next question: what IDE to use? 

I could write the C code using something like VIM (or Pico--different PICO....) but what fun would that be? 

I chose Visual Studio Code, the free (!!) multiplatform, multilanguage IDE from my bestest, smartest, most capitalistic buddies at Microsoft....a kinder gentler Microsoft, the one that no longer misses its meds--is Microsoft finally turning swords into plowshares? 

Nope.

Here are the commands I used install and configure VSCODE, I got these from this webpage.

sudo apt install code #install vscode for Ubuntu

code –install-extension marus25.cortex-debug

code –install-extension ms-vscode.cmake-tools

code –install-extension ms-vscode.cpptools

OK, one of the plug-ins needed for VSCode is CMAKE ("cmake-tools").  What is that?

ENTER CMAKE

It's a high level language--yes! an entire scripting language of its own--used to create Makefiles and perform essential software building/compiling automation.  CMAKE's webpage is here. Think autotools on steriods.

But--I got frustrated quickly with CMAKE. 

Most CMAKE videos and webpages were either geared toward total newbies like me and only discussed basics, like creating a Makefile for a single "hello world.c" file, or were extremely, and I mean extremely, complicated.

For software development shops, needing to compile, test and deploy complex software, CMAKE can do everything--however, CMAKE might be overkill for DIY folks trying to blink LEDs?  

In my case CMAKE kept throwing errors. It couldn't find the Pico SDK.  This should be easy!

I wasted over 3 hours trying to get this to work. 

To get to the bottom of this frustrating issue, I ended up buying a CMAKE book on Amazon (the book I got is the one here). 

With that in hand, I figured out the issue (see the "export source" statement above, that alone shoul have worked, but didn't, and the export $PATH statement provided by RP foundation didn't work for me--nor did tweaking path variables in VSCODE).

I am still not sure what caused the issue, or what fixed it, exactly--this should have been a really easy fix; it's just an export statement, but for some reason it wouldn't work for a very long time, then started working for reasons unknown.

Whatever--there is a whole career scripting CMAKE, right?  To learn more, the original CMAKE guy talks about his whole enchilada, here

Monkey CMAKE, Monkey C-DO 

CMAKE requires a single script in the project's root to work--called CMakeLists.txt.  No, you can't rename this file. No, you can't move it to some other folder. 

Here is the CMakeLists.txt I got to work for a blinkcl.c test program, which, well, blinks a damn LED. 

######

cmake_minimum_required(VERSION 3.13)

#include the cmake build functions from pico SDK

include(pico_sdk_import.cmake)

#project name and code here

project(BLINKCL C CXX ASM)

set(CMAKE_C_STANDARD 11)

set(CMAKE_CXX_STANDARD 17)

#bring in this function to add cmake Pico SDK functionality to build

pico_sdk_init()

#list out files used

add_executable(${PROJECT_NAME}

blinkcl.c add.c

)

#create extra uf2 etc files to copy to PICO via USB

pico_add_extra_outputs(${PROJECT_NAME})

#linker needs to use these libraries

target_link_libraries(${PROJECT_NAME} 

    pico_stdlib

)

###################

You may want to change the project name ("BLINKCL") and filenames ("blinkcl.c", "add.c") if you want to use the CMakeLists.txt files below as a basis for your own projects. 

And! One more thing--you don't need to list out .h files in CMakeLists.txt if they are in the same directory as your .c files.  

 I also copied the pico_sdk_import.cmake file into the root directory of my project ("BLINKCL").  This appears to be an include file (not sure that is the right term) that helps CMAKE find the Pico SDK.

For uploading files to the RP2040, I put the pico downstream from a USB hub with a power switch--you copy firmware onto a PICO by copying a UF2 formatted file from Ubuntu to the PICO, but copying these over requires holding down a button on the PICO while plugging in the USB cable.   

The switch on the USB hub makes that process much easier: hold down the white button on the PICO and flip the switch on the hub....



Blink me up, Scotty? 

My goal was to have 2 .c files, one .h file and everything else needed to build a multifile blink project.  

Here's the code:

#################################

#blinkcl.c

#include "pico/stdlib.h"

#include "add.h"

int main() {

 

    uint16_t time;

    time = add2nums(100,100);

    const uint LED_PIN = PICO_DEFAULT_LED_PIN;

    gpio_init(LED_PIN);

    gpio_set_dir(LED_PIN, GPIO_OUT);

    while (true) {

        gpio_put(LED_PIN, 1);

        sleep_ms(time);

        gpio_put(LED_PIN, 0);

        sleep_ms(100);

    }

 

}

#############################

#add.c

#include "add.h"


uint16_t add2nums(uint16_t a, uint16_t b) {

   uint16_t x = (a + b);

   return x;

}

#############################
#add.h

#ifndef ADDCH
#define ADDCH

#include <stdint.h>
uint16_t add2nums(uint16_t a, uint16_t b);

#endif

#############################

I built the firmware from these files without having to leave VSCODE.  

Yes the add.h file, the add2nums function, etc. are not needed, but I wanted to make sure the toolchain could deal with multiple .h and .c files. Yes, it could.

A tip, mentioned everywhere online when CMAKE is being discussed: if I altered my CMakeLists.txt file, I had to delete the entire build directory in my BLINKCL project folder then build the software again. Yes, I saw this, over and over.  

Last thing I did was get USB serial working for simple debugging.  I wanted to know the value of variable x?  

printf("x is: %d",x); 

This is very basic debugging.  

It turned out to be pretty easy.  I had to add this to the bottom of the CMakeLists.txt, rename the project, and rename .c files used:

#control USB output--1 means on.

pico_enable_stdio_usb(${PROJECT_NAME} 1)

pico_enable_stdio_uart(${PROJECT_NAME} 0)

Then I used this bash script from terminal to get minicom going.  

#!/bin/bash 

sudo minicom -b 115200 -o -D /dev/ttyACM0

I then wrote a simple hello.c project to printf "hello" 1000 times. It's very similar to the Pico example for "hello world".  I am omitting the actual code I used for this last test, but if anyone is interested, comment below, I can provide it.

I built it, and copied the UF2 file to the Pico.

The ascii output went out of the PICO, into the VM, and showed up in Minicom on the Ubuntu VM.

Joy!

Serial Ports--RP2 as a removable device on an Ubuntu VM--Why don't they work consistently?

As mentioned, my toolchain uses an Ubuntu virtual machine running on a Windows 10 PC.  The hypervisor for virtualization is VMware Workstation Pro 16.  A feature that caught my eye is what VMWare calls "USB passthrough"; I figured I'd need that since the RP2040 toolchain relies on the ubuntu VM reaching the Dev board via USB.

However, I quickly found that USB passthrough didn't always--pass through.

I had issues after the initial setup--the USB port used by the Pico/RP2040 was occasionally invisible to the Virtual Machine. Less frequently I lost the ability to see the pico dev board as a storage device, so i couldn't drag UF2 files to it.

Best I could tell this was because the PICO device was recognized as a plug and play serial device by Windows 10, which disallowed the device to be visible on the Virtual machine. So--in spite of my attempts to have USB "pass through" the W10 host, it--didn't.

Of course I messed with various settings as per the page here. No help.

To fix this, first I powered down the Ubuntu virtual machine.

Next, I went to device manager in Windows, checked "showed hidden devices", right clicked on the PICO device under "serial ports", left clicked on "uninstalled device" (I didn't uninstall drivers, just the device)

I then unplugged and plugged in the PICO to Windows 10 USB cable. 

The first time the PICO came back, and was visible again via Device Manager. Again this robbed it from being visible to the VM! But after doing this procedure twice, it appeared to be gone again.

After that, powering up the VM, I could see /dev/ttyACM0, drop UF2 files on the PICO, and so on. I was back in business.

Update: the problem is back. 

I blew away hours and hours today trying to understand how this works, so i could hope to get a final fix, but had little success. After several hours it started working again...not sure why.

What I think:

To make the serial transmissions "work as a USB storage device" on Windows 10, you really can just plug 'em in. I tried it on a W10 laptop--yes, it is plug and play.

However, to make the same features work on an Ubuntu VM, I had to get rid of all traces of "the RP2040 dev board talking to Windows". I used Windows device manager for this. I right click and uninstalled anything that looked Pico-ish: "RP2", "RP2 boot", "Pico", "PicoProbe" and so on.  

Once again this fixed it--for now.

As a baseline, here's a look at Windows 10 device manager when a Pico Dev Board is working as a storage device and "pass through" is working:



And here is it when it's actively sending serial data over USB (so, a correctly crafted printf statement, a correct CMakeLists.txt file; a successfully uploaded UF2 file)--the enabled USB serial device "moves up one":




The correct driver for these "VMware USB Devices"--and the only one that seems to work--is this one. 
 




A reboot of the Windows 10 host was needed when changing drivers....

Because the PICO could mount on the Windows machine or VM, it queried me to ask my choice--on which which device do I want the USB to be visible? I was tempted to say "always mount the Pico on the Ubuntu VM" when asked, but after wasting hours trying to get to the bottom of this issue, I changed my mind. Let it ask me each time.

Have I seen the last of this? I figure not. Until i know more about why this keeps breaking, it will keep breaking. 

Frustrating! There is a real lack of information online about how any of this really works--at a deep level--mostly I can find: forW10 just plug it in--it works; the drivers are included; and for vmware workstation pro--just plug it in--it works--the drivers are included. Uh huh. By magic, right?

Some basic considerations before I shelve this for now:
  • The serial port won't work (and minicom will close immediately) if there is no UF2 file loaded on the dev board.
  • Same if the printf or whatever is generating serial data is incorrectly crafted in code--just because it compiles doesn't mean it will work.
Update 6-22-22.  More digging into this--still having issues, especially with serial data over USB-- being able to see RP2040 USB serial content on the Ubuntu VM is still not working reliably.

 By using various tools perhaps I see what's going on here:
  • I upload a USB serial UF2 on the RP2040 dev board--it has a printf() statement for instance. 
  • this sends "hello world" or whatever out the USB port for a computer to display via a terminal program.
  • The Windows machine sees new USB serial traffic
  • An interrupt is sent to the Windows OS (?? How does this really work? I paid for a support ticket at VMware, to have them explain--but the tech seemed unwilling or unable do that beyond pointing me to the documents I've already linked in this post. If I get time, I may dig into this more and try to reverse engineer vmware "USB passthrough" technology, to better understand it, beyond the dreaded "plug it in and it just works" excuse) 
  • I choose "USB needs to appear on the VM" from the VMware USB passthrough UI.
  • Windows puts a VMware USB driver on the newly found port (?), giving up control of it.
  • ttyASM0 is created on Ubuntu, hopefully.  This is the /dev file created when Ubuntu sees a CDC USB device, which apparently is what the passed through USB is.
  • I load up minicom and can (on a good day) see the serial output via /dev/ttyASM0.
OK, and when it all works it's great, but the whole process, from rebooting the RP2040 dev board to being able to see serial output via Ubuntu minicom can take a lot of time--from a few seconds to over a minute.  

In the meantime, the serial traffic may have already have gone by!

Therefore, I have given up on using USB serial on the Ubuntu VM. 

Instead I wired TX, RX, GND from pins 0, 1 and 2 on the PICO dev board to 14 and 15 along with ground on a headless Raspberry Pi 4 on my bench. I had to change some settings on the Rpi--see the video here and webpage here--to make incoming serial work. Not difficult.

I also found that the serial data can be found on /dev/ttyAMA0 on the Raspberry pi 4 when it's working.

I ran a ground wire between GND pins on both devices. You get scrambled eggs for STDOUT if you don't do this.

In CMakeLists.txt, i selected "Serial not USB" and recompiled:

#control USB output--1 means on.
pico_enable_stdio_usb(${PROJECT_NAME} 0)
pico_enable_stdio_uart(${PROJECT_NAME} 1)

next I ssh'd into the RPi host and ran minicom (actually, I created the bash script below:

#!/bin/bash
# basic config settings to have USB to serial in Ubuntu minicom 
sudo minicom -b 115200 -o -D /dev/ttyAMA0

Then chmodded it:

~chmod 777 ./minicom.bash

Now I can run it:

~./minicom.bash

Works!





No more delays--no more hassles with USB-> serial password not working some of the time.

I can leave the RPi Minicom terminal program up and running all the time and send it data using things like printf() or puts().  

No issues so far.....Whatever comes down the wire is almost instantaneously seen on the RPi4.  Seems perfect!

"Dr! Dr! It hurts when I stand up!"
"Well don't stand up!"

This could be cleaned up....maybe a breakout board for UART is needed, with LEDs for TX and RX activity? I already fabricated a better looking serial cable.  That might be good enough.

 



Outro: RP well and Live 

Next time assuming I can see the damn dev board at all, I will be setting up more complex debugging, then on to some real audio projects.  Update: hardware debugger works. post is here.

The debugger PCB is off to PCBWAY, my faithful sponsor, and they are always good about getting the PCBs back quick. So: more posts soon. See ya next time.



1 comment:

  1. Bravo!! Some things I am going to try myself!

    ReplyDelete

JTAG to SWD Converter

Readers: If you'd like to build the project featured in today's post, please go to PCBWAY's Community pages--gerber file, KiCAD ...