PIO explained PICOMITE

Author Message

Joined: 05/03/2018
Location: Netherlands
Posts: 2745
Posted: 10:24pm 10 Dec 2022      

The RP2040 is a microcontroller around a dual ARM M0 core. The ARM cores can communicate with the outside world using peripherals. Many of these peripherals are common function blocks, such as TIMERS, I2C, SPI ADC's and individual Inputs and Outputs. The TIMERS are often used for PWM.

MMBasic has it's dedicated way of dealing with the peripherals. Input and Output and PWM, I2C, SPI and ADC are well supported from the language.

The RP2040 however has one peripheral that is not a common function block: the PIO. The PIO is something that the Raspberry Pi designers added to make the chip very versatile, since it can be freely configured.

The PIO can manipulate IO pins of the RP2040 chip without any intervention of the ARM cores Each PIO has 4 state machines that can run simple programs to manipulate IO pins. Stating that: it can run programs. But not like a microprocessor. It has a very limitted instructions set, it cannot even increment a value or add 2 numbers.

When the ARM (MMBasic) assigns a pin to the PIO, it looses direct control over that pin.
The ARM has only limitted control over what the PIO does. The main interface between the PIO and the ARM are the FIFO's. And it can start/stop execution of each statemachine in the PIO (there is more, but that will come in a later chapter).

Block diagram of a PIO: the 4 state machines and the FIFO's are shown. As is the access to the IO.

There is much documentation in the RP2040 datasheet (whole of chapter 3) on the PIO, that I often consult. You can find the datasheet on the Raspberry Pi web page.

The RP2040 has 2 PIO blocks (so in total 8 state machines) but we will only experiment with PIO block 1, since PIO block 0 is used for generating VGA video in the VGA picomite ("boot to basic computer" on Geoff's website). And the VGA picomite is a perfect platform to learn through execersize.

One thing the PIO has in common with MMbasic is that you need to provide 2 things:

1/ The configuration
  (in normal MMbasic this is the OPTION list, and the SETPIN commands)
  The PIO configuration from MMBasic uses the PIO INIT MACHINE command where it provides each state machine with the IO map and execution speed and other parameters (details later)
MMBasic provides helper functions for the configuration. Many configuration parameters are defaulted in MMBasic, and we will use that.
2/ The program
  (in normal MMbasic this the the basic program, and occasionally the CSUB's).
  Writing a program into the PIO uses the PIO PROGRAM command. MMBasic does not directly support Mnemonics (text describing the instruction) so we will manually create the hex codes that the PIO will execute.
Running the PIO program with PIO START, and stopping it with PIO STOP
Above these commands can be executed inside the MMBasic program, so MMBAsic keeps control over the PIO's.

Before we can configure a PIO state machine we need to know a bit about it's capabilities. We'll start with the simple things, and will dive deeper each post.


The state machines can SET a pin high and low. They can shift serial data OUT and IN. The capaitals show the actual command... SET, OUT, IN. So IN does not read the state of a pin, it shifts a data bit IN. The shift in and shift out can shift multiple bits to equal number of SUCCESSIVE pins. A 32 bit FIFO value can be shifted out to 32 pins, making it a 32 bit write to IO. Finally the state machine can read the status of a pin (but there is not direct command for it).

In the configuration you assign a number of pins to the state machine. As mentioned above, the state machine groups pins, so successive pins should be considered (i.e. SET output pins are GP1,GP2,GP3).
There are configuration fields for IN, OUT, and SET (and SIDE SET, later more about this). You can assign the same pin for multiple functions (i.e. GP0 is used for OUT and SET, which is handy if you want to build a UART in PIO).

The program for the complete PIO

The statemachines execute 16 bit code. A PIO has 32 words (16 bit) of program memory. That program memory can be used for 1 state machine, but also for 2,3 or 4 state machines. They share program memory. One beautifull thing is that all 4 state machines can run the same program AT THE SAME TIME. In case you write a SPI output program, you essentially have 4 completely independent SPI outputs if you want.
The state machines each have their own execution speed.

Let's do out first program, start simple... blink (fast)

Our first program is a LED blink program. The LED will blink very fast to keep things simple (666Hz) and in a later thread we will slow it down. We want the LED to connect to GP0.

First we have to attach the GP0 pin to the PIO (PIO1 in this case).

Now the PIO must take control of the GP0 pin

We want to be able to SET the GP0 pin high and SET the GP0 pin low to blink the LED. That is done in the pincontrol of PIO INIT MACHINE. There is a helper function for pincontrol: PINCTRL that has successive fields for :
a/ the number of SIDE SET pins (not used here, but needs to be filled in, use 0)
b/ the number of SET pins      (we used 1 pin, GP0, so this must be a 1)
c/ the number of OUT pins      (not used here)
d/ the lowest pin for IN pins  (not usede here)
e/ the lowest pin for SIDE SET (not used here)
f/ the lowest pin for SET      (must be set to GP0)
g/ the lowest pin for OUT      (not used here)

so PINCTRL (0,1,,,,gp0,)

This tells the state machine there is 1 SET pin it can use, and the SET pin is GP0.
If you change the "1" to a "2" the state machine can use GP0 and GP1 (2 succesive pins).

Now we can start writing the PIO state machine program

We want to do following

 SET PIN to output
The state machines have a SET instruction:

The instruction can SET up to 5 bits (the DATA field) in multiple destinations (DESTINATION field). One of them being the IO pins (DESTINATION = 000). The DELAY/SIDE SET field will be ignored for now (value 00000). More information about the SET instruction can be found in the RP2040 datasheet (chapter 3.4.10), which I highly recomend reading.

So the instruction will read 111 00000 000 DATA

Since we have 1 IO pin configured we can fill DATA with either 00000 (set pin to 0) or 00001 (set pin to 1). The other bits have no function with our configuration. In case we woudl assigne 2 IO pins, as hinted above, we could write 2 pins with 00000, 00010, 00001, 00011.

Our program takes shape...

111 00000 000 00001  = 1110 0000 0000 0001 = &hE001  SET PIN HIGH
111 00000 000 00000  = 1110 0000 0000 0000 = &hE000  SET PIN LOW

What about setting the pin to output mode... We use the same command. Only the DESTINATION field must be 100 to set the PIN DIRECTION to 1

111 00000 100 00001  = 1110 0000 1000 0001 = &hE081  SET PIN output

Now we need to jump back to the beginning. Let us use the JMP instruction...

The instruction can under certain conditions to an absolute address (ADDRESS field). Since there are 5 bits in this field, and there are 32 program memory steps, we can jump directly everywhere. If we choose 000 for the CONDITION field, the jump is unconditional (always). The DELAY/SIDE SET field is ignored (00000). Now we only need to know the address.

000 00000 000 ADDRESS  &h0000+ADSRESS  JMP ADDRESS

OK lets put this in PIO memory, starting at address line 0.

0     &hE081   SET PIN OUT
1     &hE001   SET PIN 1
2     &hE000   SET PIN 0
3     &h0001   JMP 1  

Finally we have to determine at what speed the state machine must run. The lowest frequency it can run at CPU speed 126MHz is 2kHz.

So here we have our first program. We use state machine 0 of PIO 1.

'disconnect ARM from GP0
setpin gp0,pio1

'configure pio1
p=Pio(pinctrl 0,1,,,,gp0,)
f=2000 'Hz

'pio program
'line   code  comment
' 0     E081  GP0 output
' 1     E001  pin high
' 2     E000  pin low
' 3     0001  jmp 1

'program pio1
pio program line 0,&hE081
pio program line 1,&hE001
pio program line 2,&hE000
pio program line 3,&h0001

'write the configuration: PIO, state machine, frequency,pincontrol,,,start address
PIO init machine 1,0,f,p,,,0

'start the pio1 code
PIO start 1,0

An LED connected to GP0 will blink very fast (which appears a dim).
Try this and play with it. Next time we will make some things simpler, and easier.

Happy experimenting....

Edited 2022-12-11 09:33 by Volhout
If nothing goes right ... turn left