element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Raspberry Pi
  • Products
  • More
Raspberry Pi
Raspberry Pi Forum Help. Very confused about RP2040 PIO.
  • Blog
  • Forum
  • Documents
  • Quiz
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Raspberry Pi to participate - click to join for free!
Featured Articles
Announcing Pi
Technical Specifications
Raspberry Pi FAQs
Win a Pi
Raspberry Pi Wishlist
Actions
  • Share
  • More
  • Cancel
Forum Thread Details
  • State Not Answered
  • Replies 24 replies
  • Subscribers 661 subscribers
  • Views 12247 views
  • Users 0 members are here
  • help
  • raspberry_pi
  • rp2040
  • PIO
Related

Help. Very confused about RP2040 PIO.

uncountable
uncountable over 3 years ago

Hi All,

I have a very simple protocol I am trying to implement in the RP2040 PIO module. But I can't make heads or tails of the documentation from the datasheet. There is also not a single helpful example anywhere that I can find. If there is an expert on this board who could give me a couple hints of how I can do the following, I would be very appreciative.

I have a very simple protocol. I have a 6 bit bus. I need to run that bus at half the system clock (66.5 MHz). I have another pin that is the clock, which needs to toggle at system speed. (133 MHz) So an incredibly straightforward and simple, 6 bit, parallel bus streaming continuously at 66.5 MHz.  Now, I have a big block of RAM with data that I need to send out over this bus.  All I am trying to do is understand how to get these FIFO's and shift registers to do what they need to do. Unfortunately, every PIO example seems to assume that your data width is a divisor of 32.  But I don't have that.  Consider, during the first 5 clocks, I will clock out 30 bits. Now I have 2 bits left in my shift register. 

I need to understand how I can transfer, say 16 bits, from the FIFO into my shift register, so that it refills to 18 bits. I can't simply throw away those extra bits I haven't used yet. There is not a single example that I can find in any of the documents that even discusses this issue. I thought maybe I could use 3 state machines each doing 2 bits, but that doesn't work either because I can't get 3 state machines to pull from the same shift register. Maybe there is some complicated way I can tie 3 scripts together at the boundary and keep them perfectly synchronized, but then I need to be very careful to make sure DMA loads the right 32 bit word into the right FIFO at the right time, especially at startup.

What looked like a very simple and elegant solution to my problem now appears to be completely impossible due to the severe limitations of the PIO. Am I missing something about how this works? I am really surprised that the documentation around this peripheral is so dreadful, because the rest of the datasheet and SDK is actually quite good.

I would love to hear any advice or possible solutions from somebody with experience in this area. I can't change the data ordering in RAM. I can barely keep the pipeline full as it is using both cores at 90%. I really hate to abandon the part, because at the price point nothing else can touch it.

Thanks in advance.

  • Sign in to reply
  • Cancel
Parents
  • phoenixcomm
    0 phoenixcomm over 3 years ago

    I caught a webinar on youtube, I don't care how many cores the thing has even the manual does not talk about timing! My best advice its a piece of JUNK.

    why in the world if you have a 8 bit data set do you need to shift 32 time??? duh, very poorly designed! to me it sounded like a Cisco seminar Buy this, and here are the options..with out telling what each option does LOL

    • Cancel
    • Vote Up -1 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • BigG
    0 BigG over 3 years ago in reply to phoenixcomm

    If you read the SDK documentation you would know that it does not need to shift 32 at a time.

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • phoenixcomm
    0 phoenixcomm over 3 years ago in reply to BigG

    I never got to the SDK, Still no timing info!

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • Jan Cumps
    0 Jan Cumps over 3 years ago in reply to phoenixcomm

    Maybe this is worth a collaborative post (document?). Simplify the blog chain to the root question: how can we shift output port bits on a RP2040?
    Then show code, collaborate, refine.
    We'd only need the board, 8 resistors and 8 LEDs, and our analytic mindset.

    • Cancel
    • Vote Up +3 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
Reply
  • Jan Cumps
    0 Jan Cumps over 3 years ago in reply to phoenixcomm

    Maybe this is worth a collaborative post (document?). Simplify the blog chain to the root question: how can we shift output port bits on a RP2040?
    Then show code, collaborate, refine.
    We'd only need the board, 8 resistors and 8 LEDs, and our analytic mindset.

    • Cancel
    • Vote Up +3 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
Children
  • BigG
    0 BigG over 3 years ago in reply to Jan Cumps
    Jan Cumps said:
    Maybe this is worth a collaborative post (document?).

    We could use Wokwi to simulate, as it provides PIO, Arduino, microPython and C/C++ SDK capability... https://docs.wokwi.com/parts/wokwi-pi-pico

    I've never used before but it seems straight forward enough. Here is the json diagram I just created using 6 LED's...

    {
      "version": 1,
      "author": "BigG",
      "editor": "wokwi",
      "parts": [
        { "type": "wokwi-pi-pico", "id": "pico", "top": 0, "left": 0, "attrs": {} },
        { "type": "wokwi-led", "id": "led1", "top": 8, "left": 282, "attrs": { "color": "red" } },
        { "type": "wokwi-resistor", "id": "r1", "top": 186.33, "left": 197.33, "attrs": {} },
        { "type": "wokwi-led", "id": "led2", "top": 2, "left": 259.67, "attrs": { "color": "red" } },
        { "type": "wokwi-resistor", "id": "r2", "top": 169.67, "left": 184, "attrs": {} },
        { "type": "wokwi-led", "id": "led3", "top": 24, "left": 223.33, "attrs": { "color": "red" } },
        { "type": "wokwi-resistor", "id": "r3", "top": 156.33, "left": 144, "attrs": {} },
        {
          "type": "wokwi-led",
          "id": "led4",
          "top": 13.34,
          "left": 202.33,
          "attrs": { "color": "red" }
        },
        { "type": "wokwi-resistor", "id": "r4", "top": 141.67, "left": 131.33, "attrs": {} },
        {
          "type": "wokwi-led",
          "id": "led5",
          "top": 6.67,
          "left": 180.67,
          "attrs": { "color": "red" }
        },
        { "type": "wokwi-resistor", "id": "r5", "top": 127, "left": 119.33, "attrs": {} },
        { "type": "wokwi-led", "id": "led6", "top": -2, "left": 158.32, "attrs": { "color": "red" } },
        { "type": "wokwi-resistor", "id": "r6", "top": 111, "left": 105, "attrs": {} }
      ],
      "connections": [
        [ "pico:GP0", "$serialMonitor:RX", "", [] ],
        [ "pico:GP1", "$serialMonitor:TX", "", [] ],
        [ "pico:GP21", "r6:1", "gold", [ "h0" ] ],
        [ "r6:2", "led6:A", "gold", [ "v-0.09", "h18.35", "v-85.91" ] ],
        [ "pico:GP20", "r5:1", "gold", [ "h0" ] ],
        [ "led5:A", "r5:2", "gold", [ "v0" ] ],
        [ "pico:GP19", "r4:1", "gold", [ "h0" ] ],
        [ "led4:A", "r4:2", "gold", [ "v0" ] ],
        [ "pico:GP18", "r3:1", "gold", [ "h0" ] ],
        [ "led3:A", "r3:2", "gold", [ "v0" ] ],
        [ "pico:GP17", "r2:1", "green", [ "h0" ] ],
        [ "led2:A", "r2:2", "green", [ "v0" ] ],
        [ "pico:GP16", "r1:1", "green", [ "v0.66", "h92.33" ] ],
        [ "led1:A", "r1:2", "green", [ "v0" ] ],
        [ "led6:C", "pico:GND.7", "black", [ "v0" ] ],
        [ "led5:C", "pico:GND.7", "black", [ "v0" ] ],
        [ "led4:C", "pico:GND.7", "black", [ "v0" ] ],
        [ "led3:C", "pico:GND.7", "black", [ "v0" ] ],
        [ "led2:C", "pico:GND.7", "black", [ "v0" ] ],
        [ "led1:C", "pico:GND.7", "black", [ "v0" ] ]
      ]
    }

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • BigG
    0 BigG over 3 years ago in reply to BigG

    Here's a quick demo simulating PIO with wokwi...

    You don't have permission to edit metadata of this video.
    Edit media
    x
    image
    Upload Preview
    image

    • Cancel
    • Vote Up +3 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • BigG
    0 BigG over 3 years ago in reply to BigG

    Here is a slightly modified version of my Arduino Pico code:

    #include "hello.pio.h"
    #define BasePin 16
    
    PIO pio = pio0;
    uint sm = 0;
    
    void setup() {
     
      uint offset = pio_add_program(pio, &hello_program);
      hello_program_init(pio, sm, offset, BasePin);
      
    }
    
    void loop() {
      for (uint8_t i = 0; i<6; i++) {
        pio_sm_put_blocking(pio, sm, pow(2, i));
        delay(125);
      }
      pio_sm_put_blocking(pio, sm, 0);
      delay(250);
      uint8_t BaseVal = 0;
      for (uint8_t i = 0; i<6; i++) {
        BaseVal += pow(2, i);
        pio_sm_put_blocking(pio, sm, BaseVal);
        delay(125);
      }
      pio_sm_put_blocking(pio, sm, 0);
      delay(250);
      
    }

    The PIO header file uses the hello_pio example (official raspberry pico example found in the pio folder) with a couple of modifications to make it a 6 pin parallel bus.

    // -------------------------------------------------- //
    // This file is autogenerated by pioasm; do not edit! //
    // -------------------------------------------------- //
    
    #pragma once
    
    #if !PICO_NO_HARDWARE
    #include "hardware/pio.h"
    #endif
    
    // ----- //
    // hello //
    // ----- //
    
    #define hello_wrap_target 0
    #define hello_wrap 2
    
    static const uint16_t hello_program_instructions[] = {
                //     .wrap_target
        0x80a0, //  0: pull   block                      
        0x6006, //  1: out    pins, 6                    
        0x0000, //  2: jmp    0                          
                //     .wrap
    };
    
    #if !PICO_NO_HARDWARE
    static const struct pio_program hello_program = {
        .instructions = hello_program_instructions,
        .length = 3,
        .origin = -1,
    };
    
    static inline pio_sm_config hello_program_get_default_config(uint offset) {
        pio_sm_config c = pio_get_default_sm_config();
        sm_config_set_wrap(&c, offset + hello_wrap_target, offset + hello_wrap);
        return c;
    }
    
    static inline void hello_program_init(PIO pio, uint sm, uint offset, uint pin) {
        pio_sm_config c = hello_program_get_default_config(offset);
        // Map the state machine's OUT pin group to one pin, namely the `pin`
        // parameter to this function.
        sm_config_set_out_pins(&c, pin, 6);
        // Set this pin's GPIO function (connect PIO to the pad)
        for (int i = 0; i<6; i++)
            pio_gpio_init(pio, pin+i);
        // Set the pin direction to output at the PIO
        pio_sm_set_consecutive_pindirs(pio, sm, pin, 6, true);
        // Load our configuration, and jump to the start of the program
        pio_sm_init(pio, sm, offset, &c);
        // Set the state machine running
        pio_sm_set_enabled(pio, sm, true);
    }
    
    #endif

    Here's a video demo:

    You don't have permission to edit metadata of this video.
    Edit media
    x
    image
    Upload Preview
    image

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • BigG
    0 BigG over 3 years ago in reply to BigG

    If you want delays to be clock cycle timed rather than set by milli's or microseconds you can add "delays" to the PIO instructions by using the "nop" command.

    Here is another quick demo using the same sketch as above. To make these nop delays visible on video, I also changed the PIO clock frequency by using the sm_config_set_clkdiv command. In the first part of the video demo I first used a 12.5Hz frequency. Here you can see the LED's blinking quickly as they LED's are delayed by the nop cycles - in the PIO code I used a nop delay of 24 cycles. When I increased the frequency to 125,000 Hz the LED's look permanently illuminated, even though the sketch is switching signals using the same code.

    #include "sixpins.pio.h"
    #define BaseOutPin 16
    
    PIO pio = pio0;
    uint smOut = 0;
    
    void setup() {
    
      uint offset = pio_add_program(pio, &hello_program);
      hello_program_init(pio, smOut, offset, BaseOutPin, 12.5);
      
    }
    
    void loop() {
      for (uint8_t i = 0; i<6; i++) {
        pio_sm_put_blocking(pio, smOut, pow(2, i));
      }
      pio_sm_put_blocking(pio, smOut, 0);                               
      uint8_t BaseVal = 0;
      for (uint8_t i = 0; i<6; i++) {
        BaseVal += pow(2, i);
        pio_sm_put_blocking(pio, smOut, BaseVal);
      }
      pio_sm_put_blocking(pio, smOut, 0);
      
      pio_sm_put_blocking(pio, smOut, 0b101010);
      pio_sm_put_blocking(pio, smOut, 0);
      pio_sm_put_blocking(pio, smOut, 0b010101);
      pio_sm_put_blocking(pio, smOut, 0);
      pio_sm_put_blocking(pio, smOut, 0b101101);
      pio_sm_put_blocking(pio, smOut, 0);
    }

    // -------------------------------------------------- //
    // This file is autogenerated by pioasm; do not edit! //
    // -------------------------------------------------- //
    
    #pragma once
    
    #if !PICO_NO_HARDWARE
    #include "hardware/pio.h"
    #define CLKSYS_HZ (125000000u)
    #endif
    
    // ----- //
    // hello //
    // ----- //
    
    #define hello_wrap_target 0
    #define hello_wrap 3
    
    static const uint16_t hello_program_instructions[] = {
                //     .wrap_target
        0x80a0, //  0: pull   block                      
        0x6006, //  1: out    pins, 6                    
        0xb842, //  2: nop                           [24]
        0x0000, //  3: jmp    0                          
                //     .wrap
    };
    
    #if !PICO_NO_HARDWARE
    static const struct pio_program hello_program = {
        .instructions = hello_program_instructions,
        .length = 4,
        .origin = -1,
    };
    
    static inline pio_sm_config hello_program_get_default_config(uint offset) {
        pio_sm_config c = pio_get_default_sm_config();
        sm_config_set_wrap(&c, offset + hello_wrap_target, offset + hello_wrap);
        return c;
    }
    
    static inline void hello_program_init(PIO pio, uint sm, uint offset, uint pin, float freq) {
    
        pio_sm_config c = hello_program_get_default_config(offset);
        // Map the state machine's OUT pin group to one pin, namely the `pin`
        // parameter to this function.
        sm_config_set_out_pins(&c, pin, 6);
        // Set this pin's GPIO function (connect PIO to the pad)
        for (uint i = 0; i < 6; i++) pio_gpio_init(pio, pin+i);
        // Set the pin direction to output at the PIO
        pio_sm_set_consecutive_pindirs(pio, sm, pin, 6, true);
        if (freq>0.0) {
          float div = (float) CLKSYS_HZ / (freq);
          sm_config_set_clkdiv (&c, div);
        }
        // Load our configuration, and jump to the start of the program
        pio_sm_init(pio, sm, offset, &c);
        // Set the state machine running
        pio_sm_set_enabled(pio, sm, true);
    }
    
    #endif
    

    You don't have permission to edit metadata of this video.
    Edit media
    x
    image
    Upload Preview
    image

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • BigG
    0 BigG over 3 years ago in reply to BigG

    Here is yet another Arduino PIO sketch.

    This time I am looking at how to make maximum use out of the 32 bit data parameter used in the pio_sm_put_blocking() function.

    In this example I wanted to send my data to the PIO routine and then within the PIO routine allow it to shift 6 bits to the output pins 5 times with a defined clock delay in between. Thus in total 30 bits will be shifted from the Output Shift Register (OSR) before the PIO routine waits for the next PULL data request.

    The logic flow is as follows:

    Send 32 bit value to PIO -> within PIO shift right 6 bits (these are sent to LED pins) -> short delay -> shift right next 6 bits -> short delay -> repeat until 30bits data used -> pull next 32 bit value -> repeat.

    The challenge I had with this logic flow was how to visualise this process so that I and others could understand what is going on.

    Well in this case I utilised the blocking feature of the pio_sm_put_blocking() function and also made use of the X scratch register within the PIO code to temporarily store my data and then manipulate to show the results SLOWLY.

    The result is as follows:

    You don't have permission to edit metadata of this video.
    Edit media
    x
    image
    Upload Preview
    image

    The Arduino Code is as follows:

    #include "sixpins.pio.h"
    #define BaseOutPin 16
    
    
    PIO pio = pio0;
    uint smOut = 0;
    
    void setup() {
    
      uint offset = pio_add_program(pio, &hello_program);
      hello_program_init(pio, smOut, offset, BaseOutPin);
      
    }
    
    void loop() {  
      pio_sm_put_blocking(pio, smOut, 0b00101001010101110100110001101111);
      delay(1000);
      pio_sm_put_blocking(pio, smOut, 1);       // Send random bit to trigger the next 6-bit within the PIO routine
      delay(500);
      pio_sm_put_blocking(pio, smOut, 1);       // Send random bit to trigger the next 6-bit within the PIO routine
      delay(1000);
      pio_sm_put_blocking(pio, smOut, 1);       // Send random bit to trigger the next 6-bit within the PIO routine
      delay(500);
      pio_sm_put_blocking(pio, smOut, 1);       // Send random bit to trigger the next 6-bit within the PIO routine
      delay(1000);
      pio_sm_put_blocking(pio, smOut, 0);       // Send a 0 value to turn off all LED's to then repeat the 32 bit pattern
      delay(2500);
    
    }

    And here is the PIO routine:

    // -------------------------------------------------- //
    // This file is autogenerated by pioasm; do not edit! //
    // -------------------------------------------------- //
    
    #pragma once
    
    #if !PICO_NO_HARDWARE
    #include "hardware/pio.h"
    #endif
    
    // ----- //
    // hello //
    // ----- //
    
    #define hello_wrap_target 0
    #define hello_wrap 19
    
    static const uint16_t hello_program_instructions[] = {
                //     .wrap_target
        0x80a0, //  0: pull   block                      
        0x6006, //  1: out    pins, 6                    
        0xa027, //  2: mov    x, osr                     
        0x80a0, //  3: pull   block                      
        0xa0e1, //  4: mov    osr, x                     
        0x6006, //  5: out    pins, 6                    
        0x603a, //  6: out    x, 26                      
        0x80a0, //  7: pull   block                      
        0xa0e1, //  8: mov    osr, x                     
        0x6006, //  9: out    pins, 6                    
        0x603a, // 10: out    x, 26                      
        0x80a0, // 11: pull   block                      
        0xa0e1, // 12: mov    osr, x                     
        0x6006, // 13: out    pins, 6                    
        0x80a0, // 14: pull   block                      
        0xa0e1, // 15: mov    osr, x                     
        0x6006, // 16: out    pins, 6                    
        0x80a0, // 17: pull   block                      
        0x6006, // 18: out    pins, 6                    
        0x0000, // 19: jmp    0                          
                //     .wrap
    };
    
    #if !PICO_NO_HARDWARE
    static const struct pio_program hello_program = {
        .instructions = hello_program_instructions,
        .length = 20,
        .origin = -1,
    };
    
    static inline pio_sm_config hello_program_get_default_config(uint offset) {
        pio_sm_config c = pio_get_default_sm_config();
        sm_config_set_wrap(&c, offset + hello_wrap_target, offset + hello_wrap);
        return c;
    }
    
    static inline void hello_program_init(PIO pio, uint sm, uint offset, uint pin) {
        pio_sm_config c = hello_program_get_default_config(offset);
        // Map the state machine's OUT pin group to one pin, namely the `pin`
        // parameter to this function.
        sm_config_set_out_pins(&c, pin, 6);
        sm_config_set_out_shift(&c, true, true, 30);
        // Set this pin's GPIO function (connect PIO to the pad)
        for (uint i = 0; i < 6; i++) pio_gpio_init(pio, pin+i);
        // Set the pin direction to output at the PIO
        pio_sm_set_consecutive_pindirs(pio, sm, pin, 6, true);
        // Load our configuration, and jump to the start of the program
        pio_sm_init(pio, sm, offset, &c);
        // Set the state machine running
        pio_sm_set_enabled(pio, sm, true);
    }
    
    #endif

    If anyone is confused as to what is going on here, or if you spot a mistake, please let me know.

    Note that this code can be greatly simplified if you do not want to visualise and just make use of the "nop" delay function.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube