Skip to content

Latest commit

 

History

History
111 lines (75 loc) · 6.07 KB

README.md

File metadata and controls

111 lines (75 loc) · 6.07 KB

Firmware for the RPi Pico for Oscilloscope Renderer

Downloads

main.elf

Introduction

The Firmware handles data and pushes it to the DACs resulting in a signal for the oscilloscope to be display on.

The RPi Pico is connected via USB to a host machine which provide the image. The Pico can run in two modes: Raw (Signal Data is generated on the host and just passed through) amd Instruction (The Pico recv instructions to generated the signal on the fly and the outputs it)

I'm using an RP2040 on a Raspberry Pico. Code is written in C/C++ with the PicoSDKwith FreeRTOS. One of the rp2040's PIO cores is used in combination with a DMA-Channel to archive higher data rate.

Documentation is provided via doxygen and can be found here

Development Environment.

This Project is developed with VSCode

Setup

The VSCode project comes pre-configured is self contained (FreeRTOS, PicoSDK). But the appropriate tools like gcc, gdb and openocd need to be installed. You could also use docker to build the project

Docker

For this you only need docker to be installed

Building the container

docker build -t rpi_pico_build ./firmware/docker

Running the docs: firmware

docker run -v "./":/data --rm rpi_pico_build "/data/firmware/docker/build_docs.sh"

Running the build: firmware

docker run -v "./":/data --rm rpi_pico_build "/data/firmware/docker/build_firmware.sh"

Installing the Build tools

sudo apt install build-essential cmake gcc-arm-none-eabi gdb-multiarch

(install command for ubuntu or debian based distributions)

(I once had an issue with debug in gdb-multiarch, which seems to gone now. But if you have any problems it helps building gdb-multiarch from sources. I'm using v12.2)

Installing OpenOCD

You also need to install openocd which is done by compiling from sources:

git clone https://github.com/raspberrypi/openocd.git
cd openocd 
./bootstrap 
./configure --enable-picoprobe
make -j4
sudo make install

For Syntax-highlight and debugging you need to load the rpiPico-code-profile.

Debugging and Programming is done via a SWD, I used a Raspberry Pi Debug Probe to do so. But a Pico Probe or any other SWD-Debugger should work to. You just need to edit the firmware/scripts/launch_openocd.sh to fit your debugger.

Usage

Building is done with the CMake: build main VSCode-task

Flashing can be USB: Just press the BOOTSEL Button on the Pico and connect the RPi-Pico via USB. It should now show up as a USB-drive. You can now move the .uf2 file form the firmware/build/bin folder. The Pico should now restart and run the uploaded binary.

OR

Flashing can be one via SWD (recommended). You need to connect your Debugger to debug pins of the Pico (see pinout). Then you need to start openocd via the Pico: launch openocd VSCode-Task. The Pico can now be flashed with the Pico: flash main-Task (This task automatically builds everything before flashing)

Data/Buffer Flow

The Data/Buffer Flow is concert how buffer move with the RP2040, without the need of dynamic allocation. The Flow is based FreeRTOS's Queues.

Diagram of Data Flow

There are two type of buffers:

  • frameBuffer_t: These hold samples that are sent to DACs
  • instructionBuffer_t: These hold instruction that are used to generate a signal

All buffers are preallocated and are only passed around by pointer, because they are to large (around 8kB in case of the frame_buffer) to be copied. frameBuffer_t/instructionBuffer_t struct only holds information of about the size and a pointer to the frame/instruction buffer.

The Systems main buffer-cycles:

1. instructionBuffer_t Cycle (in Blue)

The IO-handler (e.g USB or UART handler) requests a instructionBuffer_t via dac_acquireInstructionBufferPointer() from the g_unusedInstructionBufferQueue and submits the fill instructionBuffer_t via dac_submitInstructions() to the g_instructionBufferQueue.

The gen_processingTaskFunction() takes one instructionBuffer_tr (if available) and generates the output signal with this. The now used instructionBuffer_t is submitted to g_unusedInstructionBufferQueue for the IO-handler to be picket up and filled again

The Structure/Format of a instruction is documented here

2. frameBuffer_t Cycle (in Yellow)

The gen_processingTaskFunction() (aka the Signal Generator) request one unused frameBuffer_t from the g_unusedFrameBufferQueue. The Signal Generator now fills this frameBuffer_t with the signal generated by the instruction it got. This fill buffer is then submitted to the g_frameBufferQueue.

The isrDma() Interrupt services routine is trigger on completion of the transfer of data to the PIO sm. On Trigger, the next_buf is submitted to the DMA, an stored as the current frameBuffer_t. The now old frameBuffer_t is then pushed to the g_unusedFrameBufferQueue, which is then refilled by the gen_processingTaskFunction() again. Then the isrDma() pulls one frameBuffer_t from the g_frameBufferQueue.

This is not done on every cycle but on every FRAME_REPEATnth cycle (can be configured in the dacConfig.h). There for every frameBuffer_t is repeated FRAME_REPEAT-times. This is done, to have a less flickery-image on the output-oscilloscope.

In Case of an empty g_frameBufferQueue the current frame is repeated (after the FRAME_REPEAT) until one new frame becomes available.