home.social

#raspberry-pi-pico — Public Fediverse posts

Live and recent posts from across the Fediverse tagged #raspberry-pi-pico, aggregated by home.social.

fetched live
  1. A big part of the answer seems to be don't use the official docs.

    Thanks to @gadgetoid we have picodocs.pinout.xyz/ which I'd somehow forgotten about until seeing it pop up again 🥳 .

    #RaspberryPi #RaspberryPiPico #PicoSDK

  2. Years later and the Raspberry Pi Pico-SDK documentation website is still as unusable as ever 🤯 .

    Searching for things like "adc_read" I get results in areas other than "Pico C SDK" and none of the so call results even contain "adc_read"!

    The best I've come up with is to search in the PDF or am I missing something?

    #RaspberryPi #RaspberryPiPico #PicoSDK #Documentation

  3. There I was worrying about making a couple of buffers 1KB. It turns out I really didn't need to worry 😆 .

    #RaspberrypiPico #RaspberryPi #Pico

  4. Simplified Pico VGA – Part 2

    Having spent far too long chewing over how the Raspberry Pi Pico Scanvideo library works over in Simplified Pico VGA, this post actually starts to do something with it.

    One constraint I have for myself, is that I want to keep using the Arduino IDE for the Pico, so the first step is to see if I can get the Scanvideo library over to an Arduino sketch. This means grabbing the files from the following locations:

    The pico_util_buffer code is used for buffer management, so that is required too. The last one houses some examples, I’m just using the https://github.com/raspberrypi/pico-playground/tree/master/scanvideo/test_pattern as a basis for my own example.

    As with my other PIO experiments, I’m using the unofficial Arduino RP2040 core.

    Starting the Arduino Sketch

    In order to get things started, I’m just taking all the files from the above projects and dropping them directly into the Arduino Sketch folder, with no further hierarchy. My file list looks as follows:

    25/07/2026  19:26               392 buffer.c
    25/07/2026 19:25 2,265 buffer.h
    25/07/2026 19:20 1,425 composable_scanline.h
    25/07/2026 19:27 1,675 PicoScanVideo.ino
    25/07/2026 19:49 6,293 scanvideo-pio.h
    25/07/2026 19:26 83,925 scanvideo.c
    25/07/2026 19:24 1,949 scanvideo.h
    25/07/2026 19:16 13,613 scanvideo_base.h
    25/07/2026 19:49 2,488 timing-pio.h
    25/07/2026 19:20 17,057 vga_modes.c

    Each one of these files must be edited to change the include paths to drop down to a single directory.

    There are three files here that are not in the original libraries. The .ino I’ll come to shortly, but the two *-pio.h files are the assembled files from the two original .pio files. These are created using https://wokwi.com/tools/pioasm and the output is pasted into these two new h files.

    Initial Test Code

    I’ve created an example based on https://github.com/raspberrypi/pico-playground/tree/master/scanvideo/test_pattern. But I’ve removed the multicore and keyboard input. This will (hopefully) just display the “test card” and nothing else.

    #include "scanvideo.h"
    #include "composable_scanline.h"

    #define vga_mode vga_mode_320x240_60
    static bool invert = false;

    void draw_color_bar(scanvideo_scanline_buffer_t *buffer) {
    // figure out 1/32 of the color value
    uint line_num = scanvideo_scanline_number(buffer->scanline_id);
    uint32_t primary_color = 1u + (line_num * 7 / vga_mode.height);
    uint32_t color_mask = PICO_SCANVIDEO_PIXEL_FROM_RGB5(
    0x1f * (primary_color & 1u),
    0x1f * ((primary_color >> 1u) & 1u),
    0x1f * ((primary_color >> 2u) & 1u));
    uint bar_width = vga_mode.width / 32;

    uint16_t *p = (uint16_t *) buffer->data;

    uint32_t invert_bits = invert ? PICO_SCANVIDEO_PIXEL_FROM_RGB5(0x1f,0x1f,0x1f) : 0;
    for (uint bar = 0; bar < 32; bar++) {
    *p++ = COMPOSABLE_COLOR_RUN;
    uint32_t color = PICO_SCANVIDEO_PIXEL_FROM_RGB5(bar, bar, bar);
    *p++ = (color & color_mask) ^ invert_bits;
    *p++ = bar_width - 3;
    }

    // 32 * 3, so we should be word aligned
    assert(!(3u & (uintptr_t) p));

    // black pixel to end line
    *p++ = COMPOSABLE_RAW_1P;
    *p++ = 0;
    // end of line with alignment padding
    *p++ = COMPOSABLE_EOL_SKIP_ALIGN;
    *p++ = 0;

    buffer->data_used = ((uint32_t *) p) - buffer->data;
    assert(buffer->data_used < buffer->data_max);

    buffer->status = SCANLINE_OK;
    }

    void setup() {
    // initialize video and interrupts on core 1
    scanvideo_setup(&vga_mode);
    scanvideo_timing_enable(true);
    }

    void loop() {
    scanvideo_scanline_buffer_t *scanline_buffer =
    scanvideo_begin_scanline_generation(true);
    draw_color_bar(scanline_buffer);
    scanvideo_end_scanline_generation(scanline_buffer);
    }

    This it the same as the provided example apart from the fact that the core1 function has been split into the initialisation code, placed in setup(), and the infinite loop code, now placed in loop().

    This requires the circuit from the pico_playground here: https://github.com/raspberrypi/pico-playground/tree/master/scanvideo, to provide a 5-bit (RGB555) VGA interface.

    VGA Breakout

    As can be seen in the above photo I’m using a breakout board between the Pico and the VGA display. I designed this to let me experiment with VGA and the Pico.

    Bill of Materials:

    • VGA socket (see photos and PCB for footprint).
    • Pin headers.
    • Range of resistors and diodes depending on the DAC design.

    The photo below shows two configurations. On the left is a RGB555 as detailed in the RP2040 Hardware Design Guide and the pico_playground repository. This uses a 5-resistor DAC comprising: 8K, 4K, 2K, 1K, 500R resistors. Although I only had 4K7 resistors, so as you can see have used two 2K resistors in series. It uses two 47R resistors for HSYNC and VSYNC.

    On the right is the 330R/100R and diode configuration mentioned in Part 1 for a RGBY1111 configuration. This uses two 100R resistors for HSYNC and VSYNC (I don’t know why they are different to the RGB555 case).

    There are suggested resistor values for different options printed on the board, although there is one mistake. The RGBY1111 suggests 300R+100R in the text, whereas 330R+100R against the components. 330R is the recommended value.

    To wire up the board for the default RGB555 configuration with a Pico uses the following resistor to GPIO mapping:

    GP08KRed MSBitGP14KRedGP22KRedGP31KRedGP4500RRed LSBitGP5N/CN/CGP68KGreen MSBitGP74KGreenGP82KGreenGP91KGreenGP10500RGreen LSBitGP118KBlue MSBitGP124KBlueGP132KBlueGP141KBlueGP15500RBlue LSBitGP1647RHSYNCGP1747RVSYNC

    VGA to CGA

    The reason for doing all this is to get to that simpler CGA-like mode that mirrors the ZX Spectrum video capability. To do this I need to get the scanvideo library implementing RGBY1111 as mentioned in Part 1.

    The scanvideo library assumes the use of compiler directives to set the video modes, but these can’t just be set prior to including the appropriate header files in my .ino file as they need to actually be compiled into the library C files too.

    To do this, I created a vgamode.h file which can be included once in scanvideo.h which is included everywhere within the library.

    vgamode.h:

    #define VGA_RGBY111
    #define PICO_SCANVIDEO_COLOR_PIN_COUNT 4u
    #define PICO_SCANVIDEO_DPI_PIXEL_RCOUNT 2u
    #define PICO_SCANVIDEO_DPI_PIXEL_GCOUNT 1u
    #define PICO_SCANVIDEO_DPI_PIXEL_BCOUNT 1u
    #define PICO_SCANVIDEO_DPI_PIXEL_RSHIFT 2u
    #define PICO_SCANVIDEO_DPI_PIXEL_GSHIFT 1u
    #define PICO_SCANVIDEO_DPI_PIXEL_BSHIFT 0u
    #define PICO_SCANVIDEO_COLOR_PIN_BASE 12u

    As described in Part 1 this sets the hardware interface up for 4 GPIO pins for RGB and Y (as a second R) and sets the GPIO base pin to 12. The SYNC pins are automatically set elsewhere to be:

    #define PICO_SCANVIDEO_SYNC_PIN_BASE (PICO_SCANVIDEO_COLOR_PIN_BASE + PICO_SCANVIDEO_COLOR_PIN_COUNT)

    This creates the following GPIO mapping and resistor usage:

    GP12330R+100RBlueGP13330R+100RGreenGP14330R+100RRedGP15Diodes + 330R x 3Second Red (Y)GP16100RHSYNCGP17100RVSYNC

    This also now requires an alternative scanline generator, so I’ve implemented the following:

    void draw_zxcolor_bar(scanvideo_scanline_buffer_t *buffer) {
    uint32_t line_num =
    scanvideo_scanline_number(buffer->scanline_id);
    uint32_t col = (line_num * 16ul) / vga_mode.height;
    uint16_t num_pxls = vga_mode.width;

    uint16_t *p = (uint16_t *) buffer->data;

    // | jmp color_run | color | count-3 |
    *p++ = COMPOSABLE_COLOR_RUN ;
    *p++ = zxd_colour_words[col];
    *p++ = num_pxls - 3;
    *p++ = COMPOSABLE_EOL_ALIGN;

    buffer->data_used = ((uint32_t *) p) - buffer->data;
    assert(buffer->data_used < buffer->data_max);
    buffer->status = SCANLINE_OK;
    }

    This creates a “COLOR_RUN” line of a single colour using the mapping in zxd_colour_words[] which has been taken from the pico_zxspectrum code, but rather than providing 32-bit (duplicated) values (as described last time) I’m just using direct, single, 16-bit values:

    #define VGA_RGBY_1111(r,g,b,y) ((y<<3)|(r<<2)|(g<<1)|b)
    static uint16_t zxd_colour_words[16] = {
    VGA_RGBY_1111(0,0,0,0), // Black
    VGA_RGBY_1111(0,0,1,0), // Blue
    VGA_RGBY_1111(1,0,0,0), // Red
    VGA_RGBY_1111(1,0,1,0), // Magenta
    VGA_RGBY_1111(0,1,0,0), // Green
    VGA_RGBY_1111(0,1,1,0), // Cyan
    VGA_RGBY_1111(1,1,0,0), // Yellow
    VGA_RGBY_1111(1,1,1,0), // White
    VGA_RGBY_1111(0,0,0,0), // Bright Black
    VGA_RGBY_1111(0,0,1,1), // Bright Blue
    VGA_RGBY_1111(1,0,0,1), // Bright Red
    VGA_RGBY_1111(1,0,1,1), // Bright Magenta
    VGA_RGBY_1111(0,1,0,1), // Bright Green
    VGA_RGBY_1111(0,1,1,1), // Bright Cyan
    VGA_RGBY_1111(1,1,0,1), // Bright Yellow
    VGA_RGBY_1111(1,1,1,1) // Bright White
    };

    This seems to work pretty well, but all the colours are a bit too dark. The bottom bar should be “bright white” for example, and it is a pretty dirty grey really.

    I’m also not sure what is going on at the top – it looks like there is a bit of bleed-through of the last colour at the start. I don’t know if this is a sync/coordination issue with scanline number and the processing code or if this is something else.

    Looking more closely at what is produced between the two modes, the only difference I could see was that I was creating a single scanline entry for the whole line of pixels, whereas the testcard pattern was splitting each line up in to 32 “bars”.

    Rewriting the code to do the same, albeit with the same colour in each “bar” gives me:

    void draw_zxcolor_bar(scanvideo_scanline_buffer_t *buffer) {
    uint32_t line_num =
    scanvideo_scanline_number(buffer->scanline_id);
    uint32_t col = (line_num * 8ul) / vga_mode.height;
    uint bar_width = vga_mode.width / 32;

    uint16_t *p = (uint16_t *) buffer->data;

    // | jmp color_run | color | count-3 |
    for (uint bar = 0; bar < 32; bar++) {
    *p++ = COMPOSABLE_COLOR_RUN ;
    if (bar < 16) {
    *p++ = zxd_colour_words[col];
    } else {
    *p++ = zxd_colour_words[col+8];
    }
    *p++ = bar_width - 3;
    }

    // 32 * 3, so we should be word aligned
    assert(!(3u & (uintptr_t) p));

    // black pixel to end line
    *p++ = COMPOSABLE_RAW_1P;
    *p++ = 0;
    // end of line with alignment padding
    *p++ = COMPOSABLE_EOL_SKIP_ALIGN;
    *p++ = 0;

    buffer->data_used = ((uint32_t *) p) - buffer->data;
    assert(buffer->data_used < buffer->data_max);
    buffer->status = SCANLINE_OK;
    }

    This is so much better! I’m getting proper colours now.

    In the first version (as shown above) it isn’t obvious if the brightness is working or not (it is – I used cut-and-paste on the photo to compare the top and the bottom!), so I created a version (given above) to show the bright and non-bright colours side by side. Now we can see the full 15 colours properly.

    I have no idea why this makes a difference, but for some reason it does. My initial working theory is that having too long a COLOR_RUN causes too much time to be taken up in the PIO program, as COLOR_RUN is basically implemented as “set the colour values; then wait around for the number of pixels time before getting the next video instruction”, so a large number of pixels means the PIO is stuck in a loop without consuming data from DMA.

    I don’t know what effect this will have, but with each scan line split into 32 chunks, there is more data to DMA to the PIO, but the PIO also consumes it quicker.

    I don’t know why this would affect the video signal, but maybe it means there are gaps in the GPIO output video signal meaning the voltage gets averaged to a lower value somehow (a bit like how PWM would work)? If that is the case I should be able to see that on a scope…

    In the following traces, yellow is the B GPIO pin and blue is the HSYNC. On the left is the correctly functioning 32-block version and on the right is the “draw a whole line in one go” version.

    Zooming out a little…

    Ok, so now I’ve even less idea what is going on. For the failing “write a whole line in one go” case the B GPIO never seems to let up – it is constantly HIGH. For the working case, it drops back to zero synchronised with each scan line.

    Then I realised what is causing that – there is an additional black pixel added for the working case – the 32 blocks is probably irrelevant! So third try:

    void draw_zxcolor_bar(scanvideo_scanline_buffer_t *buffer) {
    uint32_t line_num = scanvideo_scanline_number(buffer->scanline_id);
    uint32_t col = (line_num * 16ul) / vga_mode.height;
    uint16_t numpxls = vga_mode.width;

    uint16_t *p = (uint16_t *) buffer->data;

    // | jmp color_run | color | count-3 |
    *p++ = COMPOSABLE_COLOR_RUN ;
    *p++ = zxd_colour_words[col];
    *p++ = numpxls - 3;

    *p++ = COMPOSABLE_RAW_1P;
    *p++ = 0;
    *p++ = COMPOSABLE_EOL_ALIGN;

    buffer->data_used = ((uint32_t *) p) - buffer->data;
    assert(buffer->data_used < buffer->data_max);
    buffer->status = SCANLINE_OK;
    }

    And this works!! This gives me the nice full-coloured patterns with brightness.

    A bit of searching turns up this note on the scanvideo readme that I’d missed:

    “Important; You MUST end the scanline with one or more black pixels of your own (otherwise your color will bleed into the blanking!!!).”

    Well that explains the problem, but I still don’t really understand what “bleed into the blanking” means and why that results in a much lower intensity colour on the screen. Searching some more has not enlightened me, so if you know why this is the case, do let me know.

    Update: Someone sent me a link to this, which explains it: https://www.righto.com/2018/04/#fn:blanking

    “When I forgot to blank the pixels outside the valid screen area, the monitor still managed to display an image, but it was very dim because the monitor got confused about what voltage represented “dark”. Just a tip in case you find your display mysteriously darkened.”

    So it is something to do with ensuring the monitor can detect what “off” looks like in the signal, which would explain why everything went screwy when it what it thought was “off” was still set to a voltage.

    Anyway. It all now works. I have a working, ZX Spectrum compatible video mode implemented on a Raspberry Pi Pico using just 6 GPIO in the end (as I still have both SYNCs).

    Kevin

    #cga #pio #raspberryPiPico #scanvideo #vga #zxSpectrum
  5. Nice. My VGA breakout seems to work.

    Better yet, I've managed to build the RPi Pico scanvideo library using the unofficial Arduino IDE (which I find a lot easier to use than the SDK if I'm honest).

    #RaspberryPiPico #VGA #Arduino

  6. Is there some reliable way to reduce noise on a Pi Pico ADC pin? I'm using GPIO pin 28 to measure input voltage, and when literally nothing is connected, I get values ranging from 1000 to 8000.

    I'm using a USB powered hub to supply power, if that makes a difference.

    #raspberryPiPico

  7. Finally finished documenting my Garage Door Roller Shutter controller for #HomeAssistant integration, using #Rust and a #RaspberryPiPico:

    codeberg.org/davidmpye/garage-

  8. 💻🤦‍♂️ Oh, Kevin's back at it again, attempting to marry a Raspberry Pi Pico RP2350 with a Z80, like setting up a blind date between a toaster and a typewriter. 🚀🙄 With all the riveting excitement of watching paint dry, he boldly provides no new insights, just linking to a website and leaving the heavy lifting to someone else. 🤷‍♂️📚
    emalliab.wordpress.com/2026/05 #RaspberryPiPico #Z80Integration #TechFails #BlindDate #Humor #HackerNews #ngated

  9. The #commodore64ultimate #commodore64 #raspberryPiPico #raspberrypi 5 PC is alive!

    I could finally afford the pi5 and various accessories. Lot's of 3d printing to mount everything securely and plug up the unused holes... plus some custom firmware to write so I can get the keyboard wired clean and direct into the pi5s GPIO pins.

    I'll also finish printing keycaps so I can upcycle the capless #vic20 keyboard I also have lying around.

    This is most likely becoming new target spec PC for gamedev!

  10. It's freebie Friday!!

    Create a radar-like parameter display in #MicroPython using either a VL53L0X, VL53L4CD, or SONAR distance sensor!

    I've uploaded the demo program for both BEAPER Pico and BEAPER Nano (BEAPER Nano version here: github.com/mirobotech/BEAPER-N) along with my VL53L4CD ToF driver.

    #STEMeducation #robotics #RaspberryPiPico #ArduinoNanoESP32