Thursday, June 23, 2022

Otter Mods "GateKeeper"--Trigger to Gate Euro Module

Hello again. I am still working away on a RP2040 toolchain--stuck on getting I2C to work--so I've turned the post over this time to Elton Glover from Otter Mods--they make really cool analog synth modules, and Elton was nice enough to share one of his designs--"GateKeeper" with me and you, faithful AudioDiWhy readers. 

Take it away Elton!


The Timer

The core of this module uses a 555 timer to generate a gate of a desired length. If you are not familiar with this versatile chip, there are many tutorials available online. Here is a link to a tutorial and a calculator to help determine component values:

555 Calculator

555 Tutorial


The 555 timer is set up in monostable mode, often referred to as “one-shot” meaning it will not self-oscillate. With the values I chose, the adjustable output gate can range anywhere from ~0.02 seconds up to around 5 seconds. Some of the resistor and capacitor values (specifically RV2 and C3) can be adjusted for longer or shorter duration, but I felt these gave me a good usable range for the typical patches I’m making.

This version of the module build uses through-hole components for easier assembly, but I have also made a double gatekeeper version using SMD components. You can tune the build to your own taste.

Functional Walkthrough

The clock input (J1) can accept nearly any type of signal pulse, which is then fed through the filter made up of C1-R6 and passed through D1 to produce a spike. This is buffered by U1A and the output turns on Q1, which is set up as an inverter.

This inverted pulse is fed into the Trigger (pin 2) of our 555 (U2). This initiates the charging cycle of C3. The combination of R5 with the adjustable value of RV1 sets how long this charge time takes. Once the charge level of C3 reaches the ⅔ supply threshold, the Discharge (pin 7) is opened and drains C3 until another Trigger event occurs. Concurrently, the Output (Q in this schematic, pin 3) turns on during the charge time of C3. The output is buffered by U1B and is the end result used to output the desired Gate signal. R1 and D2 provide an indicator LED to signify the gate output length. R2 is used as a simple means of circuit protection.

PCB Layout

Here is how I chose to layout the board. I am using the 3.5mm jacks as the mount for the faceplate, and thus wanted to space them out evenly across the board to give the module the most support. 


This module is 3HP wide, and the standard 3U height for the Eurorack format:

You can get your own Gatekeeper PCB and its front panel on PCBWay's project page, here.


COOL!  Thanks again to Elton Glover for letting me post this extremely useful module. Also for PCBWAY for fabbing the PCBS (there are two--main board and PCB used for the front panel).  You can get gerbers for both here.  

If you have questions or need more information, please contact Elton via his website--here.

I'll be back soon with more Euro modules, and maybe someday I'll have an RP2040 MCU working well enough on the bench to do useful things. Stay tuned!

Sunday, June 12, 2022

RP2040--SPI Library for Embedded C

This time I continue working with Raspberry Pi Foundation's RP2040 MCU for use in future in audio projects.  

Over the last few mornings I created a embedded C library for using SPI peripherals for this MCU.  

Breadboard time....

I couldn't locate many videos about programming RP2040 SPI using C; one I found covered both MicroPython and C. It's from Shawn Hymel--this guy has good stuff--video here. Also, the YouTube channel "Life with David" has some good RP2040 content (such as an interesting arbitrary waveform generator video how to--here) but so far not a lot about SPI; David concentrates on the RP2040's PIO features, primarily.

Using those videos, as well as the (surprisingly abstract) examples found here, and the SDK SPI documentation from Raspberry Pi Foundation, I ported/rewrote the Atmel 328 SPI library I create a few years ago (here) to code for the RP2040.  If you are new to SPI and embedded C, you should read up on SPI first--good article from the Sparkfun folks: here

After a few mornings, I got it to work.  

Proof of concept--1K triangle wave created with the library discussed in this post and an MCP4921 SPI DAC. Code is here.

There are differences aplenty between Atmel 328 and RP2040 hardware and firmware. For example, the RP2040 supports 16 bit SPI communication; as far as I know, the Atmel 328 natively only supports 8 bits. The RP2040 has 2 SPI peripherals on-chip, the 328P has one. 

Pulseview is open source logic analysis software; I used it with an inexpensive Saleae clone to see what was happening with SPI at a low level. A logic analyzer is essential for debugging libraries like this one.

One of the main differences I found is that the RP2040 requires buffers for the data being sent to, and read from, a SPI peripheral, coded into the library. This was easily accommodated; I created a suitably  sized variable or array, then, when calling a SPI write method from the SDK, I used the address of the buffer to pass the data to the peripheral:

void  SPI_TransferTx16_SingleCS(spi_inst_t *spi, const uint16_t data) // cs low, 2 bytes, cs high)


  gpio_put (cs_pin,0);   

          spi_write16_blocking(spi,&data, 1);  

  gpio_put (cs_pin,1);


The 1 above in a uint that indicates how many words occur between the CS down and CS up; so it was  easy to create another function where one can argue in as many bytes as needed:

void SPI_TransferTx8_variable_num_words(spi_inst_t *spi, const uint8_t a[0], uint8_t numwords) 

// 8bit write, cs down; variable # of bytes before CSup 


    gpio_put (cs_pin,0);   

  spi_write_blocking(spi,a, numwords);

          gpio_put (cs_pin,1);


I already had a single method MCP4921.c file I've used for the Atmel 328 and embedded C (here), but since the RP2040 can accommodate 16 bit words for SPI, I could simplify the code for the MCP4921 DAC 12 bit writes to just three lines:

void write4921(uint16_t data4921)


     // code assumes LDAC (4921 pin 5) tied to ground

     data4921 &= ~(1 << 15); // must always be zero 

     // buffered ref in, gain = ref; no shutdown

     data4921 |= (1 << 14) | (1 << 13) | (1 << 12) ;  



To test things and troubleshoot, I used PulseView, an open source logic analyzer. (For me information about how I use that on my bench, go about halfway down the post here). 

Using that tool I could quickly see if a given library call worked or not:

I tested sending hex FF, F0, and 03 (so 24 bits) between a single CS down and up.  Yes, it worked. However, SPI tests don't reveal useful information without an appropriate peripheral connected to the MCU. So, I tested this write method using an AD5761, which requires 24 bit words for each SPI transmission.

I posted the initial build of the RP2040 SPI library on github, here. I didn't have any suitable SPI chips for reads/MISO, so, as of the writing of this post, I am guessing at those functions. I have ordered a few MCP3001/3002 IC's (SPI ADC). I will test with that in a week and update the post and/or the library once I am sure those functions are debugged.  UPDATE: I got the MCP3201's and yep, the functions for MISO/ADC read worked first time.  Here's the wiring I used for the ADC chip; I added MCP3201.c and .h files to the repository. 

One more tidbit: VSCODE, the IDE I chose for this RP2040 toolchain, supports a Microsoft technology called Intellisense

Among other things, Intellisense allows the user to hover over a method and see details about its use. 

OK how did I add Intellsense detail to the 2040 SPI C library?  It's easy: in my declarations (specifically: SPI_rp2040.h) I commented immediately above where I defined the function. But I added no comments on the same line as the declaration: 

These comments appeared via Intellisense when I hovered over a function in Visual Studio Code:


Next, onward to an ADC C library for RP2040.  The RP2040 has 3 12 bit ADC's, a built in temperature sensors, and some interesting features like ADC round robin.  Update: done, here. 

In general, in the coming weeks and months, I will probably port more Atmel 328 libraries I have already written to this new MCU, and try to improve and optimize the code in the process.  Geek out, baby!

Sunday, May 29, 2022

RPPICO/RP2040 Toolchain Part II: Debugging RP2040 projects using a "PicoProbe" and Ubuntu 20.04

This time I continue to set up a toolchain for the RP2040 for Ubuntu Linux 20.04 running as a virtual machine.  To follow along, you'll first need to set up the basics--or at least skim the last post--here.

Hello again! There are about 8 billion people in the world; am I the only one who wants to set up a debugger for the RP2040 MCU--what the Raspberry Pi Foundation calls "PicoProbe"--centered around an Ubuntu 20.04 LTS virtual machine?

And code in C, not MicroPython?


Picture this: a debugger interface as big as a box of matches. Works from Linux command line as well as VSCODE. We can put the incredible horsepower of GDB--a well known open source C/C++ debugger--to debug an ARM MCU....the RP2040....all for $4USD?....unbelievable!!!! 

If I can get it to work????

WTPP (or) Rasberry Pi in yo Face

PicoProbe uses an intermediate Raspberry Pi PICO dev board to provide SWD debugging to our target RP2040; we also need a computer to run GDB.  

It's not simple.  

I found generic and perhaps untested Linux debugger toolchain installation steps from Raspberry Pi and elsewhere, but quickly discovered they didn't apply and/or were out of date for Ubuntu Linux 20.04.  And, almost every Linux based build I found used Raspberry Pi as the host Debian based PC, such as the webpage here as opposed to an Ubuntu virtual machine.

Sure I could set this up on a Raspberry Pi SBC, but to paraphrase the great John F. Kennedywe do [Embedded C toolchain setups on Ubuntu] not because they are easy, but because they are hard.  

I created a simple PCB to simplify patching between a PicoProbe SWD Debugger (left) and the RP2040 Pico target (right). I'll go over that this PCB in this post.

Let's go!! 

I found two thorough videos for setting up a PicoProbe toolchain on Windows: here and here, and a good document outlining the same idea here

To understand how the toolchain (PC > PICO running a "PicoProbe UF2 > target RP2040) works, please watch the beginning (at least) of the excellent video from Shawn Hymel, here.  I am not going to outline each piece of the computer > Picoprobe > PICO debug toolchain puzzle in this post, because Shawn does an excellent job explaining them in his video. 

Me? I followed the RPI PICO Getting Started Document, page 58, and installed these packages on the Ubuntu 20.04 VM:

#sudo apt install automake autoconf build-essential texinfo libtool libftdi-dev libusb-1.0-0-dev

Next I followed the instructions on page 57 of Getting Started to set up a special pico version of OpenOCD, the service that allows the GDB debugger to talk to the Pico.

#git clone --branch picoprobe --depth=1 --no-single-branch

#cd openocd


#./configure --enable-picoprobe 

#make -j4

Here I found my first serious bump.  OpenOCD wouldn't compile--error 2 during the make command. The error was an indent bug (?) deep in the guts of the downloaded openocd.git code. 

No way I had time to rewrite the customized openocd source code so it'd compile on Ubuntu, that would have taken forever. Crap!  

After digging into the branches of the github repo, I guessed that the git clone command provided by RPi was out of date; I figured the repo changed and the official documentation didn't keep up. 

With fingers crossed I issued this command:

#make clean

then this:

git clone

cd openocd


./configure --enable-picoprobe 

make -j4

sudo make install

Ha! It compiled!  I was right I think; the repo had been updated, but not the documentation. Time wasted: maybe 45 minutes.

Next I created a UF2 file for the Picoprobe.  This was straightforward, I used the toolchain  I set up in last time and ran this:

(cd to your projects folder)

#git clone

#mkdir build

#cd build

#cmake ..


No issues here. 

I uploaded the PicoProbe UF2 file to the Raspberry Pi Pico dev board I was going to use as the "go between". You can see how to do that here

"hold down the BOOTSEL button while plugging the board into USB. The uf2 file below should then be copied to the USB mass storage device that appears."


I could have wired up this second PICO running the PicoProbe UF2 on another breadboard, but instead I got help from my faithful sponsors; I designed a super simple PCB and uploaded it to PCBWAY for quick fabrication. 

Back it came:

Boards arrived super fast from my generous sponsors PCBWAY.  Please help out a blogger (me) who is trying to teach himself electrical engineering in his spare time and check 'em out.

 I put edge connectors into a Pico Dev board and soldered it to the PCB:

bottom view....

Next I wired the new Pico Probe PCB (left) to the blink board from last time using dupont type M/M cables:

(The board on the right is running the Boring Blink Sketch from last time.....)

For wiring I matched the females on the probe board (left) to a Pico dev board (right).  The PCB has the names of the pins to connect to the target--so, wire SWCLK legend on the PicoProbe PCB to the SWCLK Pin on the target breadboard, the GP0 pin on the PicoProbe PCB to GP0 on the target, and so on.  

The LED on the PICO board went solid--and the target BLINKCL PICO still binked--still alive! The PCB worked. I put the PicoProbe PCB on PCBWAY's projects page; if you want to get one, go here.

Next I needed to get OpenOCD configured. In this use case, OpenOCD allows the computer to "talk to" GDB.

But did openOCD start up? Not at first. I started up a new terminal on my Ubuntu system, but the bash  code on page 60 of getting started didn't start openOCD on Ubuntu correctly and kept throwing errors. 


After messing around with different command line variations, I eventually go this to work:

#sudo ./openocd -s ../tcl  -f ../tcl/interface/picoprobe.cfg -f target/rp2040.cfg 

And could see this from the Ubuntu terminal:

Open On-Chip Debugger 0.11.0-g610f137d2 (2022-05-28-16:27)

Licensed under GNU GPL v2

For bug reports, read

Info : only one transport option; autoselect 'swd'

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

So--what was in the Linux docs section from the official documentation didn't work on Ubuntu and had to be slightly modified. What do you want for free?  Time wasted: about 20 minutes.

Next I had to get GDB going in a second terminal. That should have been easy, but there was a (big) hitch--this wasn't Linux GDB I needed; rather, a special version of GDB for ARM processors, and arm gbd does not come with the gcc-arm-none-eabi package for Ubuntu. 

A baby's ARM, holding an apple? 


After doing some research, I found the stack exchange post here, which I used to solve the problem:

Install libcurses:
#sudo apt install libncurses5

Then, from a browser, I went to this URL and downloaded the x86/64 version of the ARM dev tools: 

Next I created a brand new directory for the GDB executable:

#cd /usr
#sudo mkdir gdb  #creating new folder: /usr/gdb

I figured all I needed was GDB, the rest of the arm-none-eabi already worked, and assumed (!!) this newer ARM GDB version could coexist with the arm-none-eabi-gcc compiler I was already using, as long as the compiler was older than GDB.

This was a guess, but I had observed GDB used for other non-embedded projects to be pretty forgiving....

I unpacked all the gcc-arm-none-eabi files into the new directory:

#sudo tar xjf ~/Downloads/gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 -C /usr/gdb

Finally I simlink'd gdb, to fool the RP tools to thinking it was in /usr/bin:

#sudo ln -s /usr/gdb/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gdb /usr/bin/arm-none-eabi-gdb

Did I have all the tools we need?  I ran the next four commands--yes--I didn't record the results, but the tools I needed were now in place.

arm-none-eabi-gcc --version

arm-none-eabi-g++ --version

arm-none-eabi-gdb --version

arm-none-eabi-size --version

Next I ran gdb (finally!) vs. the BLINKCL elf file from last time:

#arm-none-eabi-gdb BLINKCL.elf  

and got this at stdout: 

target remote localhost:3333


OMG!  The beloved (gdb) prompt!  Was this going to work????

(gdb) load   

 rp2040 running BLINKCL stopped blinking

(gdb) monitor reset init

(gdb) continue 

the rp2040 started blinking!!!

and we see the (gdp) prompt again!  

It worked!!!!  I could now use command line gdb to debug, same as if I was debugging C code on Linux!!!


But wait--we're not done! 

Could I get debugging to work without leaving VSCODE? The video here and accompanying webpage here made this look--doable.

I followed this Shawn Hymel webpage and followed the section on installing the Cortex Debug plugin on.  It all seemed to be OK, including coding the settings.json and launch.json scripts.

However, when i tried to use Cortex/VSCODE to debug the blink elf file I got this:

Great, there was some sort of log to review, but where was this gdb-server's output?  

I wasted at least two hours throwing every bit of GDB kung fu I knew at we enable logging?  Does log.txt show up somewhere? 

It should right?  However, there was frightening little information I could find on line. Again, there are 8 billion of us, but I may very well be the only person trying to set up this exact version of the toolchain. Damn.

After many frustrating hours on this, I decided to watch videos about STM32 VSCODE toolchains, since I remembered that some of them used the same VSCODE Cortex Debug plug in. There was zilch about RP2040 and this error, but maybe it occurs for other MCUs?

Bingo! That did the trick, STM32's VSCODE toolchains indeed can throw the same error, and I saw a video that revealed the output for GDB was in VSCODE's terminal--click on the TERMINAL tab then on the right, the gdb-server link--my lord, how did I miss this???

From here--now that I could read the logs--it was obvious that it was a Linux rights issue for the USB port for the PicoProbe. 

I had seen this issue before; to fix it I ran this from the Ubuntu VM:

#sudo lsusb

Which showed me:

Bus 002 Device 005: ID 2e8a:0004 Raspberry Pi Picoprobe

I chmodded that....

sudo chmod 666 /dev/bus/002/005

 After, the VSCODE debugger worked! Time wasted--oh, never mind.

BONG HIT VICTORY--MACH II! it's done!  I can next/step/continue/print etc. with VSCODE. 

That was fun, but man, much harder and more time consuming that I expected. 

But for $4, for all this amazing code and functionality, that developers, RPi, etc. etc., must have put hundreds and thousands of hours into, well, I can't complain, so I won't.....but, this setup kinda sucked.


There are lots of videos about using gdb to debug C. (here for instance--"Low Level Learning" has a lot of great content).  

Lots of posts too; use the usual web searches. 

There are less videos about debugging using Cortex/VSCODE for RP2040, but there are several for using it to debug STM32's (e.g., here).  

And remember: after a debug, you still have to copy your repaired/modified UF2 file to the dev board--the debugger won't do that for you.

As usual: I'm not completely done, I'd still like to get a disassembler working, and I found some promising python code online, but I need to experiment with it further....more one of these days. Until then, don't debug the fumes.

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. 

But instead I chose Raspberry Pi's RP2040, the processor found in their PICO development board


I chose RP2040 because it's the underdog?


This time I'll document how I got an RP2040 C/C++ toolchain up and running on Ubuntu 20.04 Linux.

Initially I didn't consider the RP2040 processor for my next projects due to its footprint--it's a QFM-56 package, and its pins are primarily underneath the chip--not sticking out of its sides--which means: tiny pads requiring very precise soldering. I have found QFN-56 IC's difficult and frustrating to solder, maybe that's just me. I need to practice soldering QFN chips? 

For now I will use a Raspberry Pi Pico development board. It's inexpensive and well documented, and the 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 and 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

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, but may not have.

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:

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/

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? 

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?


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. 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.  Good read, but dry). 

With that in hand, I figured out the issue (see the "export source" statement above, I think that did it--while the export $PATH statement provided by RP foundation didn't--nor did tweaking path variables in VSCODE--but I am still not sure what caused the issue, or what fixed it, exactly).

However, because the RP2040 development is targeted to work across all the major OS's, and MakeFiles can vary greatly, I figure CMAKE was probably a smart move on Raspberry Pi's part, in spite of the added complexity. 

Otherwise, we'd be limited to a subset of the major OS's out there for RP2040 development, the way Atmel Studio is.

And--there is a whole career scripting CMAKE, right? Interesting stuff. 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


#project name and code here




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


#list out files used


blinkcl.c add.c


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


#linker needs to use these libraries





You will need to change the project name ("BLINKCL") and filenames ("blinkcl.c", "add.c") if you want to use the CMakeLists.txt file 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. Good!

 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:



#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_set_dir(LED_PIN, GPIO_OUT);

    while (true) {

        gpio_put(LED_PIN, 1);


        gpio_put(LED_PIN, 0);







#include "add.h"

uint16_t add2nums(uint16_t a, uint16_t b) {

   uint16_t x = (a + b);

   return x;



#ifndef ADDCH
#define ADDCH

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



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.  


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.


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 relied 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 shelf 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.  
  • 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" or at least better understand it, beyond the dreaded "plug it in and it works" cop out. 
  • 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 from pins 1 and 2 on the PICO dev board to 14 and 15 on the headless Raspberry Pi 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 all 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:

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

Then chmodded it:

~chmod 666 ./minicom.bash

Now I can run it:



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!

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. 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.

Saturday, April 30, 2022

RISC-V and Virgule: Amazing Web Based Simulator

 I finally took a long vacation with my psychiatrist girlfriend--Portugal, all the from the US.  A 10 1/2 hour flight.Super Fun! Of course I ignored the warnings like this one. Bad news--when we were leaving, she tested negative for COVID-19, but I didn't, so she left, and I was stuck.  

I self-isolated in a hotel--six days--what to do?  Learn some new programming skills, of course--why not. RISC-V assembly!!

RISC-V based dev board from Sparkfun

I've already written posts about assembly for the 6502 priocessor (here) and ARM (here). With 6502 it was pretty easy to learn the basics; ARM, well, not so much.

Another instruction set architecture ("ISA") is RISC-V; it's open source and designed to help teach newbies like me how processors work. 

I expect it to become increasing popular as chip makers become weary of giving ARM and Intel bags of money, because RISC-V has no license fee.  

So, here I was, stuck in Lisbon, Portugal, alone in a hotel room, with only my phone and an ancient MacBook Pro. How am I going to learn a new ISA without going crazy?  Fortunately I found the most amazing online simulator for learning RISC-V--"Virgule" (simulated hardware) with "emulsiV" (emulated RISC-V software)--find them here.

Documentation for Virgule/emulsiV is a bit sparse, but I could follow them--which means you can too--go here

A good RISC-V/Virgule introduction video I found--short and informative--is here. As the video correctly notes: Someone put a lot of work into this simulator. This must have been a scripting project of passion, and it's the most bodacious online simulator I've ever seen. Viva la France! 

It has an ASCII display, behind glass GPIO, about 2k of simulated RAM and even a memory location where you can load and view bitmap images. 

It gets better:  You can animate what happens inside Virgule as you step through your code, and control the speed of the animation.  So not only can you see if your code works, you can see how it works at a snails pace.. This greatly helped me understand how the various parts of the processor interacted while running my code.

There are different ways to get code into the simulator; but for my Portuguese parole, I hand-coded instructions into the memory column on the left side of the simulator, then stepped through my code to watch what happened. 

If you make a really dumb data entry mistake the simulator tries to correct it and does a remarkably good job. Amazing! As a learning tool, this couldn't be more straightforward.

You can enter assembly code right into memory slots in this sim!


The simulator has 32 general purpose registersThe RISCV specification indicates that RISCV hardware does not have a central accumulator and that register x0 is always 0.  You can’t write anything else to it but this design choice is useful when using immediates (see below).

Programmatically we call these registers x0 x1 etc....this in not hex which is denoted 0x000 in the simulator, rather the registers are called out via decimel with a preceeding "x".  

The documentation talks about RS1, RS2 etc.--these are considered SOURCE registers; also rd1, rd2 etc, these are the DESTINATION registers. Other than x0, any register can be used as a source or destination.  


These are frequently found and used throughout the RISC-V ISA, and don't seem to have some of the odd rules, complexities, and head scratching limitations regarding Immediates that I found the ARM ISA. 

The documentation and simulator call these "IMMs".

 An easy way, I think, to get immediate values into registers is to use addi instruction—"add immediate":

 Addi dest, source, value

Source can be an existing register with a value stored in it….this next instruction puts 32 in x1, and x0 has 0 as its value 

addi x1, x0, 32

However, For some instructions, like lui, Immediate values are entered like this


where  yyyyy is a 32 bit hex value. As far as I can tell, LUI is a good way to put a large number into a register quickly. 

But what about the 3 LSBs, they never get can a value?

Ha!—use addi after a lui—a trick!

addi x1, x0, 32

In this instruction: x1 is destination register;  x0 is source reg and has value of 0, 32 is value to put in there as LSBs



 Virgule emulates the 32 bit flavor of RISC-V. 

You can store bytes (Syntax for SB) half words--16 bits (SH) or words--32 bits (SW) 

The general instruction for storing a byte (bits 0:7) is this:

SB (where to store) (what to store) so---

SB x1, 0{x2)

SB means take 0:7 bits of what to store.  0 is an immediate (IMM) value added to what is in x2.

So--In this case, we are storing the contents of x2, offset by 0, to x1.

And--you can store to memory (as opposed to a register) using a value greater than 0—see the "Extra Ram" section below.


Works the same way as storing but going the other direction. Doh!  THANK YOU, THIS IS EASY.

LD x3, 0(x2). 

Load to x3 the value of x2.  If 0 is 1 or greater, you are loading from a memory cell, 0:7, offset by the value found in register x2.


Not that well documented, but you can store and load values to virtual RAM inside Vigule, not just in the 32 registers provided.

For instance:

SB x5 1(x1) 

stores the 0:7 bits of source register x5 into a virtual a memory slot--in this case 1 added to whatever value is in source register x1; so if x1 contains zero, you are storing a byte into memory location 1.

If x1 contains 3,  SB x5 1(x1) will copy 0:7 of x5 into memory cell 4.  Again--EASY!

As far as how much memory we can use in Virgule: As far as I can tell, you have 1-2047 memory locations to work with, if you go outside that range things may not work the way you want.  The documentation seems to indicate 0-3071,more like 3K than 2K, but I couldn’t make things work with any offset value > 2047.  Maybe I am doing something wrong? But--for what we are doing here, 2047 slots is good enough!

Unfortunately the sim does not have a way to display what is in this memory. Only register values are shown, but not memory. But hey--It works!  


Output from Virgule's virtual ASCII display.  Get the hex code, ready to load into Virgule, here.

Here's one way to do this...

 lui x2, 0xc0000000  ;put c0000000 hex in reg 2

addi x1, x10, 75. ;put 75 in reg 1

sb x1, 0(x2) ;store value at x1 (75 is ascii letter “K”) to the simulated text display

IMPORTANT! always make your code use offset 0 above.....I assumed to make the cursor appear at the next spot to the right, use offset 1, and for the next, use offset 2, etc. but that crashed the simulator.

The simulator will automatically increment the cursor to the next available slot the next time you do an ascii write. Just keep writing to the ascii display over and over--this is not well documented, but you can figure this out from the examples.


GPIO's! Of course, this simulator has them.  Virgule has an impressive array of LEDs, switches, and push buttons. They can be configured by right clicking on one of the GPIO points and choosing what you want (impressive, just by itself). You need to set the direction (read vs. write) using an SB instruction, then use instructions to make things do other things.

I wrote some simple code to light the first row of LEDS:

we put the memory location for our GPIO into reg x1

lui x1, 0xd0000000 ; memory location for virtual GPIO

sb x0, 0(x1) ; this makes the LEDs outputs  

addi x3, x3, 126. ; put 126 into x3 (it can be anything from 1 to 255)

sb x3, 16(x1)

Final instruction writes what is in x3 to x1 offset by 16.....16 is the memory location for VAL, the values you want to write to the GPIO "pin".


These instructions sounded intimidating after watching videos and some online reading, but after experimenting, they are not too difficult:

JAL: jump the program counter to immediate value; then put the count of the next program counter instruction to run, after the jump has occurred, into a register. 

let;s take the instruction

jal x1, +24   

So! If Jal x1, 24 is put into instruction "memory" position 00, the program counter jumps to PC 18 (24/4 is 6, you are jumping the program counter forward by 6 instruction memory “slots”); then, Virgule puts 1c into register X1 since 1c is next PC slot after 18.

JALR; register and offset contain where to jump to.

Jalr: x3, x2, 0

x3 stores the next program counter “slot” that would have occurred if the jump didn’t happen.  X2 is the value of where to jump to, offset by value 0.


If you want to understand RISC-V at its deepest level, check out how assembly is turned into the 32 bit hex values then sent to the processor. 

Understanding encoding would be critical if you are writing a compiler, pulling apart RISC-V hex instructions using C, and so on. Complex and interesting! A detailed video (long, deep, and quite informative) covering RISC-V encoding is here.

THE WHOLE J, S, R, U thing: RISC-V in its 32 bit build expects all its opcodes, functions, values, etc, to fit into 32 bit words. The opcode is always in bits 6:0.  But from there it varies how the opcode, values, register/memory locations etc, are encoded. In the RISC-V spec, there are a few different “formats” used, with letters like J, S, R, U to designate how this it's done; in other words, each letter designates how a given 32 bit word is encoded

Encoding instructions: I assumed for everything RISC-V the opcode contained the instruction itself. "When you assume" right? 

Um, no. In some cases the opcode alone isn’t enough. Instead, an instruction's opcode (bits 0:6) can be a general marching order, while one or two “functions” further define what needs to be done during decode step. They are called “function 3” and “function 7”. You knew that right?

For instance, for ADDI, opcode is OP-IMM. And function is “ADDI”.  

However, for the LUI instruction, the opcode itself tells the decoder everything it needs. You knew this as well?

Encoding Register values: I dug into how a 32 bit RISCV instruction knows what registers we want to use for an instruction. 

Turns out RISC-V, in its 32 bit version, uses 5 bits for register values. Hello?  That should allow for 2 raised to the 5 registers or 32.   

So, how does the system encode the value 0xC000000 to register x30?  and not send it to x7 by mistake? 

Here is how: mask everything else in the 32 bit word, that, by the aforementioned letter designation, does not represent a register, then turn the 5 bits left into a decimal number. For instance you have an RD of 0 0 1 0 1 that means we want to use register x5.


The entire encoding process, turning commands into bytes, opcodes + functions into instructions, numbers into signed or unsigned numbers, etc., is a bit complex, but like all things RISC-V--it's manageable. This is what happens when friendly college professors write an ISA targeted at pimple faced freshmen? But hey, if it works, and it's free, so why not?


I will spare you the puns of RISCing things by traveling abroad...oh too late.

Experimenting with RISC-V using this simulator helped me keep my limited sanity during the days I was locked in a hotel room, waiting for the portuguese government to let me back on the plane. I have to say that for a vacation bummer, this RISC part of the trip was really fun! 

RISC-V is a blast to code as I see it--it's fun, interesting, and recommended!

In the future I will create some RISC-V based projects and get my sponsors PCBWAY (had to get a plug in right?) to help with the fab. Stay tuned.

In the meantime: there are code examples included in Virgule but some of them seemed a bit complex for me, starting as a complete RISC-V newbie; I wrote some of my own code, attempting to make things as simple as possible. You can get my code examples, in hex, ready to load into Virgule, with a brief readme about what does what,.  Go to my github--here.

Not that I wanted to get covid--turns out I was lucky and my symtops were mild. In six days I was back on the plane to the good old US. Now I am happily blogging, and the weather here is fantastic. COVID! It's what you get when you breathe the fumes. Oh well. Keep coding, kids. 

Otter Mods "GateKeeper"--Trigger to Gate Euro Module

Hello again. I am still working away on a  RP2040 toolchain --stuck on getting I2C to work--so I've turned the post over this time to ...