Are the unused GPIO pins available with the PICO display pack firmware activated?

I have the display up and running with the examples without issue, however my intention was to use the display to provide information about the primary function, which is driving LEDs with PWM on the GPIO pins.

The documentation on the Pimoroni site suggests just the SPI, LED, 4 switches and a couple of other pins are in use, leaving plenty of GPIO for my purposes. On trying to use them, I get some odd results such as fixed output high, driving the display backlight, incorrect RGB LED pins and other pins reacting to the RGB LED configuration.

Before I invest more time into my potential user error, can I confirm that the GPIO pins are available and supported for use with the Pimoroni custom firmware in place? A THG review suggests that perhaps they are not, but there is nothing conclusive elsewhere I can find.

Thanks for any info.

Just in case you didnā€™t see it, the pinout listing what pins are used is at the bottom of the product page.
Pico Display Pack ā€“ Pimoroni

Hi @alphanumeric

Thank you for checking. I was basing my GPIO use on this diagram, which suggests there are a number of pins free. As observed on another post it seems it is inaccurate in that RGB LED are actually pins 10-12 not 9-11.

I have done some further testing and found the below, not sure if anyone else can replicate or if I potentially have a hardware/firmware fault.

Setup:
I have a pot feeding into GPIO 28 to populate the PWM duty cycles for 7 LEDs
LEDS are being driven by GPIO 0, 1, 2, 3, 5, 26 and 27

The other GPIOs exhibit the following behaviour when I try to use them as PWM LED outputs:
GPIO4 - duty value affects display pack backlight
GPIO22 - drives red LED on the display pack
GPIO21 - seems to fight with GPIO 5, they flicker badly on duty = 0 on GPIO21
GPIO10 fights with GPIO26
GPIO11 fights with GPIO27
GPIO9 goes high duty cycle when 0 is written to PWM pin duty (observed by LED not code)

Iā€™m either doing something stupid (very possible), hardware is broken, firmware is somehow not loaded right or the GPIOs arenā€™t meant to be used with this pack in place. Just not sure which yet, will test on another pico as I have a few, but only one display pack.

I have a Display Pack. Post the code youā€™d like me to run on it and Iā€™ll post back what I find out. Three ` before and after you code will wrap it on code tags.

I can see that where the pins seem to be conflicting above they match possible configured pins for the same function on the I2C buses. Not sure if maybe there is some I2C configuration turned on in the display pack init. Those libraries are definitely beyond me working out from inspection.

@alphanumeric most of the weird behaviour above would require setting up physical LEDs and clearly doing something it doesnā€™t like that I donā€™t know how detrimental it would be to the hardware.

However, it would be helpful to know if GPIO 4 and 22 drive the backlight and red LED channel for you as well.

This code flashes the backlight and LED on the display pack alternately on and off each second for me. It would at least prove there is undocumented pin function which would suggest that GPIO use may be unsupported or at least at your own risk with the current documentation.

import utime
from machine import Pin, PWM
import picodisplay

buf = bytearray(picodisplay.get_width() * picodisplay.get_height() * 2)
picodisplay.init(buf)
picodisplay.set_backlight(1.0)

picodisplay.set_pen(0, 255, 0)                    
picodisplay.clear()                               
picodisplay.update()                              

ext_led1 = PWM(Pin(4))
ext_led1.freq(1000)
ext_led2 = PWM(Pin(22))
ext_led2.freq(1000)

while True:
    ext_led1.duty_u16(0)
    utime.sleep(1)
    ext_led1.duty_u16(65000)
    utime.sleep(1)
    ext_led2.duty_u16(0)
    utime.sleep(1)
    ext_led2.duty_u16(65000)
    utime.sleep(1)

Just ran it on my Display, screen lights up green on and off and the LED blinks red, alternately. One turns on off and then the other.

1 Like

I think you will find that the RGB LED is already set up and expecting PWM control with r,g,b in the range 0-255.

Just use
display.set_led(r,g,b)

It is all set up for you in PWM not boolean 0/1 True/False

That is what I did in the tutorial
Pimoroni Pico Display Workout : 3 Steps - Instructables
Hope this helps

# Pimoroni Pico Display Using the RGB LED
# Tony Goodhew 1st March 2021
# Tested with Pimoroni UF2 Ver: 0.0.7

import picodisplay as display 
import utime, random, math
from machine import Pin
width = display.get_width()
height = display.get_height()
display_buffer = bytearray(width * height * 2)
display.init(display_buffer)
# Set the backlight to 90% - Needed on Display
display.set_backlight(0.9)

def blk():
    display.set_pen(0,0,0)
    display.clear()
    display.update()
    
def title(msg,r,g,b):
    blk()
    display.set_pen(r,g,b)
    display.text(msg, 20, 10, 200, 4)
    display.update()
    utime.sleep(2)
    blk()
    
blk()    
display.set_pen(255,255,0)
title("Blink",0,0,200)
display.set_pen(255,255,255)
for i in range(5):
    display.set_led(0,255,0)
    utime.sleep(0.5)
    display.set_led(0,0,0)
    utime.sleep(0.5)

display.set_pen(255,255,0)
title("Control",0,0,200)    
display.set_pen(200,200,200)
display.text("  Press buttons A & B ", 5, 3, 230, 2)
display.text("Press Y button to halt", 5, 17, 230, 2)
display.update()

pot = 50
running = True
while running:    
    if display.is_pressed(0): # A pressed
        pot = pot + 1
        if pot > 100: pot = 100
    if display.is_pressed(1): # B pressed
        pot = pot - 1
        if pot < 0 : pot = 0    
    utime.sleep(0.02)
    
    display.update()
    display.set_pen(pot,pot,pot) # grey to white
    display.circle(120,140,50)
    # Calculate rainbow colour
    if pot < 50: # Blue -> Cyan -> Green
        r = 0
        g = int(pot * 2)
        b = int(100 - pot * 2)   
    else:       # Green -. Yellow -> Red
        r = int((pot-50)*2)
        g = int(100 - ((pot-50)*2))
        b = 0
    display.set_led(r,g,b)
    percent = pot
    display.set_pen(0,0,0)
    display.rectangle(0,30,200,40)
    display.set_pen(200,0,0)
    display.text(str(pot), 100, 30, 200, 4)
    display.update()
    if display.is_pressed(3): # Y button is pressed ?
        running = False
      
# Tidy up
blk()
display.set_led(0,0,0) # Set the LED to black
display.set_pen(200,0,0)
led = Pin(25, Pin.IN, Pin.PULL_DOWN) # Normal state
display.text("All Done!", 55, 40, 200, 3)
display.update()
utime.sleep(2)
blk()

Hi @Tonygo2

I am trying to drive external LEDs on new GPIO pins, I was just trying to test there was an undocumented further driver for the red LED on the board on another pin which suggests the documented pinout needs updating or that indeed other GPIOs are not supported for use as they may conflict in addition to the documented functions.

I have started building the C toolchains to experiment with the C library to see if this gives me more reliable GPIO access on the spare pins.

Just tried all the non-screen pins with a ā€˜Blinkā€™ script:

import picodisplay as display 
import utime, random, math
from machine import Pin
width = display.get_width()
height = display.get_height()
display_buffer = bytearray(width * height * 2)
display.init(display_buffer)
# Set the backlight to 90% - Needed on Display
display.set_backlight(0.9)
p = 28
led = Pin(p, Pin.OUT)

def blk():
    display.set_pen(0,0,0)
    display.clear()
    display.update()
    
blk()
# Blink external LED    
for i in range(3):
    led.value(1)
    utime.sleep(0.5)
    led.value(0)
    utime.sleep(0.5)

0,1,2,3,4,5,6,9,21,22,26,27 &28 work as expected and blink
including 9!
Pin 7 (LED R) flashes the RGB LED green in opposite value to the external LED
Pin 8 blinks RGB LED blue after a stutter while external blinks opposite value.

This is all really weird.

Iā€™m going to avoid pins 7,8 and 9 and use the built in PWM method to control the REG LED.

I believe I have an explanation.

Iā€™ve spent a couple of weekends getting windows visual studio to connect over picoprobe SWD for C/C++ to get a lower level understanding of the hardware and software driving it (I thought it might be the python library causing conflicts and wanted to rule that out). Excellent fun, I highly recommend if you like a puzzle and donā€™t actually want to get any useful output for some timeā€¦

It turns out that my understanding of the PWM implementation on the Pico was lacking and the C configuration highlighted this.

To drive a pin with PWM in C, you enable the pin for PWM and then you have to identify the slice driving that pin:

gpio_set_function(22, GPIO_FUNC_PWM);
uint slice_num = pwm_gpio_to_slice_num(22);

Then drive that slice appropriate with a duty cycle (I think this is a 25% duty cycle, but still getting to grips with C)

pwm_set_wrap(slice_num, 4);
pwm_set_chan_level(slice_num, PWM_CHAN_A, 1);

At no point do you directly drive a pin with a duty cycle as you appear to in the micropython abstraction.

There are 8 PWM slices, each with 2 channels, meaning 16 PWM outputs are possible, however there is a hardcoded mapping of PWM Slice/Channel to pin. Which means if you already have say, a display pack tied to certain pins using PWM, you cannot use the same Slice/Channel combination elsewhere without a conflict, which is exactly the behaviour I was seeing.

I looked up the mappings here: Hardware id list - Sim Innovations Wiki

As you can see, the pairs of GPIOs conflicting in my previous post all share the same PWM Slice/Channel.

So it looks like I can support 7 LEDs maximum without conflicting PWM, but I suspect my SPI and UART requirements may conflict further. I will probably look into PIO or the 2nd core to perform PWM in software instead for some of the LEDs

An interesting journey though, I hope this is helpful for someone else in the future.

1 Like

Whoosh, right over my head, but very well done and deduced. Bravo. =)

so far over my head I thought it was a UFO ;)

Way beyond my coding grade but I wonder if the ā€˜slicesā€™ are state machines?

The slices are independent execution cores of some type on the silicon. Dedicated PWM hardware you address and are mapped to pins you can switch on for PWM. A bit like a UART or dedicated SPI control.

Whether or not you could describe them as state machines in the logical sense Iā€™m not sure, probably non-programmable state machines dedicated to one function. But they are separate from the 8 ā€œstate machinesā€ you can control for the programmable IO for things like custom hardware interfaces, extra PWM controls etc. i.e. configuring pins for PWM in hardware does not take from the pool of 8 state machines objects you can spin up using Python or C/C++ with their assembler style programming, at least to my understanding.

On the product page it states the following.

There is also an onboard RGB LED (ideal to use an activity indicator!) which is also PWMed (with gamma correction) on pins LED_R , LED_G , and LED_B . If you want to use the LED pins for something else there are three cuttable traces on the underside of the board.

One would assume there should be a way to disable the Displays LED code to use those Pins?

@hel