ecvillarosa
Posts: 3
Joined: Fri May 11, 2018 2:35 pm
Contact: Skype

Writing Multiple GPIO Pins AT ONCE

Wed May 12, 2021 4:40 am

I just started devling in MicroPython and one of the things I am looking for in MicroPython's library is the WRITING of multiple GPIO pins AT ONCE, as emphasized below.

Sample circuit: CC 7-Segment display driven by RPi PICO:
GP0 -> Segment a
GP1 -> Segment b
GP2 -> Segment c
GP3 -> Segment d
GP4 -> Segment e
GP5 -> Segment f
GP6 -> Segment g
GP7 -> Dp

Need to know if the below is possible in MicroPython:
<RP2040 GPIO Register> = %00000110 #display digit 1 on 7-Segment

Accessing the GPIO in PIC Microcontrollers is straight forward. For example, PortB Register is an 8-bit register assigned to physical microcontroller pins, see below.
/-----------------------------------------------------------------------\
PORTB | RB7 | RB6 | RB5 | RB4 | RB3 | RB2 | RB1 | RB0 |
\------------------------------------------------------------------------/
Let's assume same wiring is followed, displaying digit 1 on the 7-segment would just be as simple as below.

PortB = %00000110

Is there such function or implementation that facilitates the above intention in MicroPython? I did checking the details of RP2040 registers in the datasheet and can't find a detailed explanation of the chip's registers

hippy
Posts: 9900
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Writing Multiple GPIO Pins AT ONCE

Wed May 12, 2021 10:13 am

You can peek and poke anywhere in the memory address space from MicroPython ...

Code: Select all

from machine import mem32, mem16, mem8

def Peek32(adr):
  return mem32[adr]
  
def Poke32(adr, val):
  mem32[adr] = val
So all you need is the address of the GPIO register to poke. I believe there are address locations which will set, clear or toggle bits leaving others unchanged which makes things easier. I can't recall the details but the RP2040 datasheet should provide those.

horuable
Posts: 118
Joined: Sat Mar 06, 2021 12:35 am

Re: Writing Multiple GPIO Pins AT ONCE

Wed May 12, 2021 10:30 am

The addresses you're looking for are 0xd0000004 for peek and 0xd0000010 for poke. Both functions allow you to read/write all of the Picos pins at once since there's no division into ports.


As hippy mentioned you can avoid doing read-modify-write by using Atomic Register Access. It is achieved by adding an offset to the register you want to change:
  1. XOR = 0x1000
  2. SET = 0x2000
  3. CLEAR = 0x3000


Oops, scratch that. While generally true for most registers, GPIO output has its own registers that don't follow this rule:
  1. GPIO_OUT_XOR = 0xd000001c
  2. GPIO_OUT_SET = 0xd0000014
  3. GPIO_OUT_CLR = 0xd0000018
I don't understand why it doesn't use standard Atomic Register Access, but that's just how it is I guess.

hippy
Posts: 9900
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Writing Multiple GPIO Pins AT ONCE

Wed May 12, 2021 11:52 am

Found my example code ...

Code: Select all

from machine import mem32
import time

LED = 25

SIO_BASE     = 0xD0000000

GPIO_OUT     = SIO_BASE + 0x010
GPIO_OUT_SET = SIO_BASE + 0x014
GPIO_OUT_CLR = SIO_BASE + 0x018
GPIO_OUT_XOR = SIO_BASE + 0x01C

GPIO_OE      = SIO_BASE + 0x020
GPIO_OE_SET  = SIO_BASE + 0x024
GPIO_OE_CLR  = SIO_BASE + 0x028
GPIO_OE_XOR  = SIO_BASE + 0x02C

# Make LED pin an output
mem32[GPIO_OE_SET] = 1 << LED

# Flash by setting all bits to a specific value
for n in range(5):
    mem32[GPIO_OUT] = 1 << LED # On  - Set bit to 1
    time.sleep(0.5)
    mem32[GPIO_OUT] = 0 << LED # Off - Set bit to 0
    time.sleep(0.5)
time.sleep(2)

# Flash by setting and clearing a specific bit
for n in range(5):
    mem32[GPIO_OUT_SET] = 1 << LED # On  - Set this bit
    time.sleep(0.25)
    mem32[GPIO_OUT_CLR] = 1 << LED # Off - Clear this bit
    time.sleep(0.25)
time.sleep(2)
   
# Flash by toggling a specific bit
for n in range(10):
    mem32[GPIO_OUT_XOR] = 1 << LED # Toggle this bit
    time.sleep(0.125)
time.sleep(2)
You can emulate PICmicro constructs using ...

Code: Select all

RB0_PIN = 0 # RB0 on GPIO0 ... RB7 on GPIO7

def TrisB(bits):
    mem32[GPIO_OE_CLR]  = ((bits & 0xFF)       ) << RB0_PIN
    mem32[GPIO_OE_SET]  = ((bits & 0xFF) ^ 0xFF) << RB0_PIN

def PortB(bits):
    mem32[GPIO_OUT_CLR] = ((bits & 0xFF) ^ 0xFF) << RB0_PIN
    mem32[GPIO_OUT_SET] = ((bits & 0xFF)       ) << RB0_PIN

TrisB(0b00000000) # TrisB = %00000000 - All outputs
PortB(0b00000110) # PortB = %00000110 - display digit 1

ecvillarosa
Posts: 3
Joined: Fri May 11, 2018 2:35 pm
Contact: Skype

Re: Writing Multiple GPIO Pins AT ONCE

Wed May 12, 2021 4:21 pm

Thank you guyz for the tips. That is exactly what I am looking for. Really appreciate it much. I'll give a try tomorrow for same circuit and will update on the status.

I've spent some time today browsing the web and found the below sample code to be an alternative solution to the problem. However, related libraries and functions are written in C and unfortunately I am not a C guy. Is there any means to utilize these functions in MicroPython?
https://github.com/raspberrypi/pico-exa ... 7segment.c

Also, I find the following links valuable in dealing with low-level registry access, however, all are in C and written for Raspberry Pi, and not for PICO.
http://www.pieter-jan.com/node/15
viewtopic.php?t=82752

It would have been great if PICO team could document same as below PDF for RP2040 microcontroller. The PDF details all the registry mappings of hardware peripherals. At present, I still cannot find same compilation for RP2040.
https://www.raspberrypi.org/documentati ... herals.pdf

fdufnews
Posts: 329
Joined: Fri Oct 07, 2011 5:37 pm

Re: Writing Multiple GPIO Pins AT ONCE

Wed May 12, 2021 8:48 pm

It would have been great if PICO team could document same as below PDF for RP2040 microcontroller.
It's here https://datasheets.raspberrypi.org/rp20 ... asheet.pdf

horuable
Posts: 118
Joined: Sat Mar 06, 2021 12:35 am

Re: Writing Multiple GPIO Pins AT ONCE

Thu May 13, 2021 5:30 pm

To be more specific almost every section of the datasheet has a subsection named List of Registers where you can find addresses and definitions of registers. For the registers discussed here, you have to go to SIO section and scroll down until you find the List of Registers since for some reason it doesn't appear to be indexed.
ecvillarosa wrote: Is there any means to utilize these functions in MicroPython?
Not directly (obviously), but if you write your MicroPython equivalents, then sure.
Here's my somewhat similar version, that just counts up. Adding buttons is up to you.

Code: Select all

from machine import Pin, mem32
from time import sleep_ms

def gpio_set_mask(mask):
    mem32[0xd0000014] = mask

def gpio_clr_mask(mask):
    mem32[0xd0000018] = mask


FIRST_GPIO = 2

bits = [0x3f,
        0x06,
        0x5b,
        0x4f,
        0x66,
        0x6d,
        0x7d,
        0x07,
        0x7f,
        0x6f]

# Set pins to output
for i in range(7):
    Pin(i + FIRST_GPIO, Pin.OUT, value = 1)

val = 0
while True:
    mask = bits[val % 10] << FIRST_GPIO
    gpio_clr_mask(mask)
    sleep_ms(500)
    gpio_set_mask(mask)
    val += 1

Notice that I've swapped gpio_set_mask and gpio_clr_mask since I'm not setting outputs to inverted mode as it is done in the linked code.

Return to “MicroPython”