Raspberry Pi CM5 IoT In C - Getting Started With PIO
Written by Harry Fairhead   
Wednesday, 04 June 2025
Article Index
Raspberry Pi CM5 IoT In C - Getting Started With PIO
PIO Basic Concepts
The PIO Assembler
Configure the state machine

With the state machine that we are going to use settled, we can now configure it. You could do this directly, but the simplest and best way to do the job is to use the SDK. This allows you to set up a struct with all of the configuration values set and then initialize the state machine in one go. First we need a default configuration and this is another utility function provided by the header file that the assembler creates:

pio_sm_config c = squarewave_program_get_default_config(
offset);

Now we have to set the configuration as promised. We need to specify which pins are going to be in the SET group:

sm_config_set_set_pins(&c, 2, 1);

and in this case we have selected just one pin, GPIO 2, as the group starts at GPIO 2 and has just one pin. This means that our set instruction in the PIO program will toggle just pin GPIO 2. We also have to set the GPIO function to GPIO_FUNC_PIO0 to let the processor know that the pin is being controlled by the PIO.

Instead of using the function from Gpio5:

gpio_set_function(2, GPIO_FUNC_PIO0);  

you can use the specific PIO function to do the same job:

pio_gpio_init(pio0, 2);

Finally we can load the configuration into the state machine and start it running:

pio_sm_init(pio0, sm, offset, &c);
pio_sm_set_enabled(pio0, sm, true);

If you now look at the output of GPIO 2 you will see a square wave.

The complete program is listed below, but remember that it needs the PIO header that the assembler creates:

#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "sqwave.pio.h"
int main() { uint offset = pio_add_program(pio0,
&squarewave_program); uint sm = pio_claim_unused_sm(pio0, true); pio_sm_config c = squarewave_program_get_default_config(offset);
sm_config_set_set_pins(&c, 2, 1);
pio_gpio_init(pio0, 2);
pio_sm_init(pio0, sm, offset, &c);
pio_sm_set_enabled(pio0, sm, true);
while(true){};
return 0;
}

The pico/stdlib.h and hardware/pio.h headers are supplied by the library. stdlib.h simply includes:

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include "piolib.h"

pio.h header doesn’t do anything and is only included for compatibility with Pico programs so it can be deleted.

To make this work you also need to let VS Code know where the PIO include files are. Add:

            "args": [
                "-fdiagnostics-color=always",
                "-I../piolib/include",
                "-g",
                "${file}",
                "../piolib/piolib.c",
                "../piolib/pio_rp1.c",
                "-o",
            "${fileDirname}/${fileBasenameNoExtension}"
            ],

to the args section of the C/C++: gcc build active file task in tasks.json . This assumes that the header files and PIO C files are in a folder called piolib in the same folder as the project folder.

Note: At the time of writing you cannot use piolib at the same time as Gpio5 as some of the functions they define are in common and hence clash. The simplest solution is to add Gpio5_ to the start of each gpio function in the Gpio5 library – this works but it means you cannot run a Pico program without modification.

If you try this out you will find that the program does produce a square wave, but it isn’t suitable as a Blinky example. At the moment the clock rate is set to produce a square wave at around 66 MHz and we need to bring down the frequency to something more reasonable so that we can see an LED blink on and off. Notice that the CM5’s PIO runs at 200MHz and this is faster than the standard Pico PIO, which means that timings will change when you port a program from the Pico to the CM5.

Notice that the PIO program runs as fast as the PIO clock allows it to run, but the C program runs slower than in the case of the Pico due to the fact that the PIO isn’t directly connected to the CPU running the C program. For programs that simply load the PIO with a program and some data this makes no difference, but for programs that interact with the running PIO program it can introduce timing problems.

In chapter but not in this extract

  • Clock Division and Timing
  • Writing Loops
  • Data to the PIO
  • Sharing GPIO lines
  • Out to the GPIO
  • Side Effects
  • Input
  • Edges
  • Advanced PIO
  • DHT22 Using the PIO – Counting
  • DHT22 Using the PIO – Sampling.
  • Complete Listing
  • A PIO DS18B20 Program
  • Complete Listing

 

Summary

  • The PIO and the state machine are special processors designed to interact with the outside world.

  • You can use a PIO attempt to implement any otherwise unsupported protocol.

  • The CM5 has one PIO with four state machines.

  • The GPIO lines associated with the PIO are determined by a set of groups – OUT, SET, IN and SETSIDE. GPIO lines also have to be set to PIO mode before they will work in any of the groups.

  • You can set the clock frequency that the PIO uses to execute instructions one per clock cycle.

  • The clock should be set to a frequency that is suitable for the sort of pulses the PIO is working with.

  • It is easy to toggle a GPIO line, but slightly harder to make it slow enough to flash an LED. To do this you need to implement a busy wait loop.

  • The OSR and ISR are used to send data to and receive data from the GPIO lines.

  • There are two FIFO stacks which can be used to send data to the OSR and ISR from the processor.

  • Every instruction can change the state of GPIO lines in the SETSIDE group as a side effect of its execution.

  • Working with edges isn’t natural for the state machine, but it can be achieved using wait instructions.

  • It is possible to implement the DHT22 protocol as defined in the data sheet using a PIO by using counting loops to time each pulse.

  • A better use of the PIO is to notice that the protocol can be decoded by testing the state of the line a fixed time after the rising edge.

  • With a shift in viewpoint, it is just possible to squeeze a 1-Wire bus protocol into a 32-instruction PIO program.

Raspberry Pi Compute Module 5
IoT In C
Using Linux
Drivers and Gpio5

By Harry Fairhead

CIoTCM5360
Buy from Amazon.

Contents

  1. The CM5 For The IoT
  2. Setting Up the CM5
  3. C and Visual Studio Code
  4. Drivers: A First Program
  5. The GPIO Character Drive
  6. GPIO Using I/O Control
  7. GPIO Events
  8. GPIO Hardware With Gpio5 
  9. Some Electronics
  10. The Device Tree
  11. Pulse Width Modulation
        
    Extract: PWM Using GPIO5
  12. SPI Devices
  13. I2C Driver and Gpio5
  14. Sensor Drivers – Linux IIO & hwmon
  15. 1-Wire Bus
  16. The PIO
        
    Extract: Getting Started With PIO ***NEW!!!
  17. Going Further With Drivers
  18. Almost Real-Time Linux
  19. Appendix I Gpio5

 

 

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

Banner


Swift 6.2 Adds WebAssembly Support
17/06/2025

Swift 6.2 has been released with features to enhance performance, concurrency, and interoperability with other languages like C++, Java, and JavaScript. It also adds support for WebAssembly.



Ktor 3.2 Adds HTMX Support
26/06/2025

Ktor 3.2 has been released with new modules for dependency injection and HTMX. This version also adds support for Gradle version catalogs. 


More News

pico book

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Wednesday, 04 June 2025 )