The Pico In MicroPython: Direct To The Hardware
Written by Harry Fairhead & Mike James   
Monday, 13 December 2021
Article Index
The Pico In MicroPython: Direct To The Hardware
Single-Cycle IO Block

Single-Cycle IO Block

At this point you might think that we are ready to access the state of the GPIO lines for general input and output. This isn’t quite the whole story. To accommodate the fact that the processor has two cores, and to make access faster to important devices, there is a special connection, the SIO or Single-cycle IO Block, between the cores and, among other things, the GPIO. The SIO connects directly to the two cores and they can perform single-cycle 32‑bit reads and writes to any register in the SIO. Notice that the SIO is not connected via the general address bus. You can see the general structure of the SIO in the diagram below. You can find out about the other devices it connects to from the documentation - our focus is on the GPIO lines.

 SIO

Notice that the GPIO lines are multipurpose and the SIO only has control when they are being used as GPIO lines. In this sense the SIO is just another peripheral that can take control of a GPIO line.

The SIO provides a set of registers that makes using the GPIO much faster and much easier. The basic registers are:

GPIO_OUT Sets all GPIO lines to high or low

GPIO_IN Reads all GPIO lines

GPIO_OE Sets any GPIO line to output driver or high impedance

There are also three registers – SET, CLR and XOR - that make working with GPIO_OUT and GPIO_OE easier. Each of these can be thought of as a mask that sets, clears or XORs bits in the corresponding register.

For example, GPIO_OUT_SET can be used to set just those bits in GPIO_OUT that correspond to the positions that are set high.

The locations of these registers are as offsets from 0xd0000000:

Offset

Name

Description

0x004

GPIO_IN

GPIO Input value

0x010

GPIO_OUT

GPIO output value

0x014

GPIO_OUT_SET

GPIO output value set

0x018

GPIO_OUT_CLR

GPIO output value clear

0x01c

GPIO_OUT_XOR

GPIO output value XOR

0x020

GPIO_OE

GPIO output enable

0x024

GPIO_OE_SET

GPIO output enable set

0x028

GPIO_OE_CLR

GPIO output enable clear

0x02c

GPIO_OE_XOR

GPIO output enable XOR

 

Blinky Revisited

Now we can re-write Blinky yet again, but this time using direct access to the SIO GPIO registers.

from machine import mem32,Pin
from time import sleep_ms 
led=Pin(25,mode=Pin.OUT)  
addrSIO = 0xd0000000
while True:
    mem32[addrSIO + 0x014] = 1 << 25
    sleep_ms(500)
    mem32[addrSIO + 0x018] = 1 << 25
    sleep_ms(500)

This program uses the standard MicroPython class to set the GPIO line to SIO control and output. If you think that this is cheating, it is an exercise to set the line correctly using the GPIO control register and the SIO.

This example is a demonstration rather than being useful, but there are some very useful functions we can write using our knowledge of how the GPIO lines are controlled. MicroPython is limited to controlling a single GPIO line at a time, but the hardware can change or read multiple GPIO lines in a single register operation. For example:

def gpio_get():
    return mem32[0xd0000000+0x010]

Here the get function simply reads the GPIO_OUT register which has a single bit for the output state of each GPIO line. Notice that GPIO lines set to output reflect their last written-to state.

A set function simply writes the mask to the GPIO_OUT_SET register

def gpio_set(mask):
	mem32[0xd0000000+0x014] = mask

A clear function is just as easy and this just writes to the GPIO_OUT_CLR register:

def gpio_clear(mask):
 	mem32[0xd0000000+0x18C] = mask

You can easily create functions for reset and other logical operations on all of the GPIO lines in one operation, but a single mask value function is usually sufficient:

def gpio_set(value,mask):  
	mem32[0xd0000000+0x01C] = 
(machine.mem32[0xd0000000+0x010])^value & mask

This writes to the GPIO_OUT_XOR register, but it writes a combination of a mask and a value. The mask gives the GPIO lines that need to be changed and the value gives the state they are to be set to. For example, if mask is 0111 and value is 0100 then value & mask is 0100. If this is XORed with the current state of the lines – e.g. 0101, in this case the result is 0001, which changes the state of only GP0 to a zero. Thus we have set lines GP2, GP1 and GP0 as specified in the mask to the corresponding bits in the value, i.e. 0100. Notice that this process sets the lines selected in the mask to either a zero or a one as determined by the bits in value.

As demonstrated in Chapter 4, the value,mask function can be used to set GPIO lines simultaneously:

from machine import Pin
import machine
def gpio_get():
    return machine.mem32[0xd0000000+0x010]
def gpio_set(value,mask):
     machine.mem32[0xd0000000+0x01C]=
(machine.mem32[0xd0000000+0x010])^value & mask
pin=Pin(22,Pin.OUT) pin=Pin(21,Pin.OUT) value1=1<<22 | 0<<21 value2=0<<22 | 1<<21 mask=1<<22 | 1<<21 while True: gpio_set(value1,mask) gpio_set(value2,mask)

This sets lines GP21 and GP22 to 01 and 10 on each pass through the loop:

SIOlogic

 

In Chapter But Not In This Extract

  • Example I - Events
  • Example II PAD - Pull, Drive and Schmitt
  • Digging Deeper 

Summary

  • All of the peripherals, including the GPIO lines, are controlled by registers – special memory locations that you write and read to configure and use the hardware.

  • Exactly where the registers are positioned in address space is given in the documentation as a base address used for all of the similar registers and an offset that has to be added to the base to get the address of a particular register.

  • The SIO block provides a more convenient way to access the GPIO lines and it has a different set of addresses and registers to the GPIO lines.

  • With knowledge of how things work, you can add functions that are missing from MicroPython such as events and PAD control.

  • Each GPIO line connects to the outside world via a PAD which has a number of configurable elements such as pull-up, slew rate and so on.

 SIO

Programming the Raspberry Pi Pico/W In MicroPython Second Edition

By Harry Fairhead & Mike James

picopython2e360

Buy from Amazon.

Contents

  • Preface
  • Chapter 1 The Raspberry Pi Pico – Before We Begin
  • Chapter 2 Getting Started
  • Chapter 3 Getting Started With The GPIO
  • Chapter 4 Simple Output
  • Chapter 5 Some Electronics
  • Chapter 6 Simple Input
             Extract: Simple Input 
  • Chapter 7 Advanced Input – Events and Interrupts
  • Chapter 8 Pulse Width Modulation
             Extract: PWM 
  • Chapter 9 Controlling Motors And Servos
             Extract: DC Motors
  • Chapter 10 Getting Started With The SPI Bus
  • Chapter 11 A-To-D and The SPI Bus 
  • Chapter 12 Using The I2C Bus
  • Chapter 13 Using The PIO   
  • Chapter 14 The DHT22 Sensor Implementing A Custom Protocol
             Extract: A PIO Driver For The DHT22  
  • Chapter 15 The 1‑Wire Bus And The DS1820
  • Chapter 16 The Serial Port
  • Chapter 17 Using The Pico W - WiFi
             Extract: HTTP Client 
             Extract: Sockets***NEW!
  • Chapter 18 Asyncio And Servers
  • Chapter 19 Direct To The Hardware
             Extract: Direct To The Hardware

Also of interest:

Raspberry Pico File System

<ASIN:B0BR8LWYMZ>

<ASIN:B0BL1HS3QD>

 

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


Rust 1.83 Improves Const Context Code Handling
12/12/2024

Rust 1.83 has been released with improvements to the handling of code running in const contexts.



Use Javascriptmas To Hone Your Webdev Skills
08/12/2024

Every day until December 24th MDN, in partnership with Scrimba, is releasing a daily challenge, which as the name suggests requires you to practice your JavaScript skills. Each solution you submi [ ... ]


More News

espbook

 

Comments




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



Last Updated ( Tuesday, 06 August 2024 )