Got the start of an #ARGB #WS2812 signal parser working using #RaspberryPi #RP2040 PIO. Going to use this with my #OpenRGB RP2040 ARGB splitter project to send configuration data (LED channel counts) using a standard ARGB controller output. RP2040 PIO is awesome for dealing with high speed serial signals!
New one for me, ordered a bigtreetech skr pico board for an @rpimag project... got a free slightly crushed rubber ducky! Hope this doesnt mean a load of rubber duck debugging! #rp2040
I’m slowly learning embedded programming. I’m currently reading the rp2040 chip’s (the chip in the Raspberry Pi Pico board) datasheet as if it were just a book. It’s really good! Holds up well so far in a linear read. A lot of hard work must have gone into explaining things clearly! #embedded #rp2040
My #RP2040 #OpenRGB Smart ARGB Splitter PCBs came! #OSHPark did a great job on them and they work as expected. Splitting one ARGB/WS2812 signal into up to 8 output channels.

RE: https://oldbytes.space/@thelastpsion/116345290344668595

And the results are in!

  • Pico C
  • Rust
  • Free Pascal
  • Joint with Pico C++, MicroZig and Yarg.
  • So, what am I going to do? Well, I'm leaning towards porting the existing Arduino C code to the Pico C SDK. I'm currently using one class for encapsulation and abstraction, but I could replace that with structs and static functions. I'm not a great C programmer, but I'm pretty comfortable with it, so it makes sense

    However, before I do that, I see a bigger challenge of getting a good setup without using VS Code. I've been using #NeoVim for a while now, and I'd like to get a comfortable setup using that on #Linux.

    So, I'm going to try to build Blinky projects for at least Pico C, Rust and Free Pascal, using Linux and NeoVim. Hopefully this will give me a better feel for how well these languages actually suit me. I've never done any Rust before, either, so that's going to be quite the learning curve!

    If I have time, I'm going to give Yarg a go, too, because I think the premise is really cool. If I'm on a roll, I'll try #MicroZig too.

    And if I really feel like I have the capacity, I'll port the code to one of these other languages.

    I'm acutely aware of all the other projects I've given myself to do, such as the SIBO SDK and other small Psion-related projects, not to mention $dayjob and $reallife. So we'll see how things go!

    #Pascal #FreePascal #ObjectPascal #RustLang #YargLang #RaspberryPiPico #PiPico #PiPico2 #RP2040 #RP2350

    RE: https://oldbytes.space/@thelastpsion/116345290344668595

    Only a couple of hours left for this poll.

    C is clearly out in front for my embedded project, but Rust and Pascal aren't far behind!

    #RaspberryPiPico #PiPico2 #PiPico #RP2040 #RP2350

    Simulating The AVR8 For A Browser-based Arduino Emulator

    It’s always nice to simulate a project before soldering a board together. Tools like QUCS run locally and work quite well for analog circuits, but can fall short with programmable logic. Tool…

    Hackaday

    PIO on the Raspberry Pi Pico – Part 2

    Having got all the theory out of the way in PIO on the Raspberry Pi Pico now is the time to actually start programming. Whilst I have the option of using the C/C++ SDK or one of the Python variants, I’m particularly interested in getting it going from within the Arduino environment, just because that is where I do pretty much all of my other microcontroller messing about.

    I’m not using the official Arduino for Pico support though, I’m using Earl Philhower’s version from here: https://github.com/earlephilhower/arduino-pico

    Pico Arduino Getting Started

    Before getting too far into PIO land, there are a few things to note about using the unofficial Arduino Pico core with the Raspberry Pi Pico.

    On first boot, hold down the BOOT switch and the Pico will be detected as a “UF2 Board”. This will allow the first upload to take place (more here). I’ve selected “Raspberry Pi Pico” or “Raspberry Pi Pico 2” as appropriate for the board.

    Prior to the first download, the configuration should set the Debug Port to Serial. Then once the first sketch is downloaded the board can be redetected via a serial link which will allow both Serial.print() and automatic reset on download of new sketches.

    Aside: there are three serial ports (more here):

    • Serial – the USB serial port – the one used here
    • Serial1 – UART0
    • Serial2 – UART1

    Here is a simple starter program to make sure everything is working:

    void setup() {
    Serial.begin(9600);
    pinMode (LED_BUILTIN, OUTPUT);
    }

    unsigned counter;
    void loop() {
    Serial.println(counter);
    counter++;
    delay(1000);
    digitalWrite (LED_BUILTIN, (counter & 1));
    }

    Assuming everything is working, every second the LED will flash on or off and the counter value will be printed to the serial monitor.

    Some PIO API Principles

    Pin groups are an important idea when dealing with PIO state machines.

    There are four different sets of pins that can be enacted upon via the PIO system: OUT, IN, SET and Sidestep. Each has its own group of API functions. For example, to set the start and number of pins in a specific group there are the following functions (from here):

    pio_sm_set_out_pins (PIO pio, uint sm, uint out_base, uint out_count)
    pio_sm_set_set_pins (PIO pio, uint sm, uint set_base, uint set_count)
    pio_sm_set_in_pins (PIO pio, uint sm, uint in_base)
    pio_sm_set_sideset_pins (PIO pio, uint sm, uint sideset_base)
    pio_sm_set_jmp_pin (PIO pio, uint sm, uint pin)

    Oh, and there is a special “jmp” pin too which is kind of a 5th group all on its own, independent of the other four.

    This pattern is mirrored for other PIO functions too, for example, the sm_config_ range of functions to update a state machine configuration:

    sm_config_set_xxx_pin_base (pio_sm_config *c, uint xxx_base)
    sm_config_set_xxx_pin_count (pio_sm_config *c, uint xxx_count)
    sm_config_set_xxx_pins (pio_sm_config *c, uint xxx_base, uint xxx_count)

    xxx = out, in, set, sidestep

    So each of the pin groups has a set of APIs functions that act directly on the state machine (pio_sm_set_xxx as described first) and a set that acts on a state machine’s configuration (sm_config_set_xxx as described above).

    There is also a third set that encodes actual PIO instructions related to the pin groups, although this isn’t a direct one-for-one with the previous two, as it relates to the actual PIO instruction set itself.

    Some examples:

    pio_encode_in (enum pio_src_dest src, uint count) // in src,cnt
    pio_encode_out (enum pio_src_dest dest, uint count) // out dst,cnt
    pio_encode_set (enum pio_src_dest dest, uint value) // set dst,val
    pio_encode_sideset (uint sideset_bit_count, uint value)
    pio_encode_jmp_pin (uint addr) // jmp pin,adr

    Note it is worth adding at this point that although there are API functions for all of the above, it is also possible to directly set state machine parameters and operational configuration using direct access to state machine registers. For more, see section “11.7 List of Registers” in the RP2350 datasheet.

    Hello PIO

    I’m starting off with a simple pulse on a GPIO pin and will be using the online PIO assembler from https://wokwi.com/tools/pioasm to build it.

    My PIO Source:

    .program pulse

    .wrap_target
    set pins, 1 [3] // 4 cycles
    set pins, 0 [11] // 12 cycles
    .wrap

    % c-sdk {
    static inline void pulse_program_init(PIO pio, uint sm, uint offset, uint pin) {
    pio_sm_config c = pulse_program_get_default_config(offset);

    // set_base=pin, count=1
    sm_config_set_set_pins(&c, pin, 1);
    pio_gpio_init(pio, pin);

    // pins_base=pin, pin_count=1, is_out=true
    pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);

    // 440 Hz pulse over 16 cycles
    float div = (float)clock_get_hz(clk_sys) / (440.0 * 16.0);
    sm_config_set_clkdiv(&c, div);

    pio_sm_init(pio, sm, offset, &c);
    pio_sm_set_enabled(pio, sm, true);
    }
    %}

    The online assembler turns the above into the following, which is pasted into a pulse_pio.h file within an Arduino sketch.

    // -------------------------------------------------- //
    // This file is autogenerated by pioasm; do not edit! //
    // -------------------------------------------------- //

    #pragma once

    #if !PICO_NO_HARDWARE
    #include "hardware/pio.h"
    #endif

    // ----- //
    // pulse //
    // ----- //

    #define pulse_wrap_target 0
    #define pulse_wrap 1

    static const uint16_t pulse_program_instructions[] = {
    // .wrap_target
    0xe301, // 0: set pins, 1 [3]
    0xeb00, // 1: set pins, 0 [11]
    // .wrap
    };

    #if !PICO_NO_HARDWARE
    static const struct pio_program pulse_program = {
    .instructions = pulse_program_instructions,
    .length = 2,
    .origin = -1,
    };

    static inline pio_sm_config pulse_program_get_default_config(uint offset) {
    pio_sm_config c = pio_get_default_sm_config();
    sm_config_set_wrap(&c, offset + pulse_wrap_target, offset + pulse_wrap);
    return c;
    }

    static inline void pulse_program_init(PIO pio, uint sm, uint offset, uint pin) {
    pio_sm_config c = pulse_program_get_default_config(offset);
    // set_base=pin, count=1
    sm_config_set_set_pins(&c, pin, 1);
    pio_gpio_init(pio, pin);
    // pins_base=pin, pin_count=1, is_out=true
    pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
    // 440 Hz pulse over 16 cycles
    float div = (float)clock_get_hz(clk_sys) / (440.0 * 16.0);
    sm_config_set_clkdiv(&c, div);
    pio_sm_init(pio, sm, offset, &c);
    pio_sm_set_enabled(pio, sm, true);
    }

    #endif

    Adding the appropriate additional PIO initialisation code to my previous test sketch now gives me the following complete code:

    #include <PIOProgram.h>
    #include "pulse_pio.h"

    #define PULSE_PIN 2

    void setup() {
    Serial.begin(9600);
    pinMode (LED_BUILTIN, OUTPUT);
    PIO pio;
    uint sm, offset;
    if (!pio_claim_free_sm_and_add_program(&pulse_program, &pio, &sm, &offset)) {
    for (;;) {
    Serial.print("No PIO or SM");
    delay(10000);
    }
    }
    pulse_program_init(pio, sm, offset, PULSE_PIN);
    }

    unsigned counter;
    void loop() {
    Serial.println(counter);
    counter++;
    delay(1000);
    digitalWrite (LED_BUILTIN, (counter & 1));
    }

    Notes:

    • As I’m using “set” in the pio program I need to use the “set” group of pins in the various API calls – hence the use of sm_config_set_set_pins() which configures which pins to use with the set command. In this case, just one pin determined by the “pin” parameter.
    • I’m using the wait [] instructions to put the pulse HIGH for 4 cycles and LOW for 12 cycles, giving 16 cycles in total.
    • The waiting cycles have to account for the single cycle of the actual executed instruction, hence using [3] and [11].
    • When setting the clock divisor, I’m dividing the system frequency by my required frequency * 16 as there are 16 cycles in the complete program.
    • When I had a single cycle HIGH and 3 cycles LOW and then used the value 440.0 * 4.0 I wasn’t getting an accurate frequency (I was getting ~1.9K rather than 440). I’m guessing (I haven’t done the maths) this was overflowing the integer part of the divisor maybe.

    The PIO and state machine used are allocated dynamically by the system using pio_claim_free_sm_and_add_program(). The first version had hard-coded PIO 0, state machine 0:

    PIO pio = pio0;
    int sm = 0;
    uint offset = pio_add_program(pio, &pulse_program);

    The final result can be seen on the oscilloscope trace below.

    Other Interesting PIO API Groups

    The PIO hardware documentation describes two modules for PIO APIs:

    • sm_config – everything related to setting up state machines prior to setting them running. All functions prefaced with sm_config_set_xxxx().
    • pio_instructions – ways to actually run state machine instructions at run time. All functions are prefaced with pio_encode_xxxx().

    But there are also the direct state machine APIs which have the form pio_(sm|get|gpio|other)_. These appear to have several functional groups as follows:

    • PIO control: manage instances of PIOs and state machines within them.
    • Programs and instruction memory: add, remove, write to, determine offsets in, and so on.
    • GPIO pins: configure, attach to state machines, set ranges, etc.
    • State machines: set parameters associated with state machines; configure IRQs; clock divider; control the sm; set details for execution such as pins, wrap, shifts, and so on.
    • Data interface to state machines: push and pull data; access fifos; etc.

    Unfortunately there isn’t a particularly consistent API naming convention that I’ve spotted for the pio_ functions apart from an awful lot of them start pio_sm_. Apart from the state machines functions that don’t…

    Conclusion

    I’ve now been through the theory and a real, albeit simple, application and am feeling like I understand a lot more what is going on now. I still am somewhat bewildered by the huge array of API calls and do feel like they could be grouped together somehow to make them more accessible to people who haven’t swallowed the entire chip datasheet and SDK guidebooks…

    But yes, I’m slowly starting to feel like I’m getting to grips with PIO a bit more now. I want to do something that now grabs some input from the GPIO and sticks it into memory, ideally using the DMA system, so that is probably where I’ll go next.

    Kevin

    #pio #raspberryPiPico #rp2040 #rp2350
    Simulating The AVR8 For A Browser-based Arduino Emulator

    It’s always nice to simulate a project before soldering a board together. Tools like QUCS run locally and work quite well for analog circuits, but can fall short with programmable logic. Tool…

    Hackaday

    It has come to my attention that this exists.

    https://github.com/tabemann/zeptoforth

    I can't update my poll without resetting the results. But, uh, I guess it's an option now?

    #RaspberryPiPico #RP2040 #Forth

    GitHub - tabemann/zeptoforth: A not-so-small Forth for Cortex-M

    A not-so-small Forth for Cortex-M. Contribute to tabemann/zeptoforth development by creating an account on GitHub.

    GitHub