GPIO & PWM on Raspberry Pi Pico W

GPIO & PWM on Raspberry Pi Pico W

Control LEDs, read buttons, use ADC, and generate PWM with MicroPython.

GPIO in MicroPython

All GPIO operations on the Pico W use the machine module:

from machine import Pin, ADC, PWM

The Pico W has 26 multi-function GPIO pins exposed on the header. Each can be configured as digital input, digital output, or connected to peripherals (SPI, I2C, UART, ADC, PWM).

Digital Output

from machine import Pin

led = Pin(15, Pin.OUT)   # Configure GPIO 15 as output

led.on()         # Set HIGH (3.3 V)
led.off()        # Set LOW (0 V)
led.toggle()     # Flip current state
led.value(1)     # Same as on()
led.value(0)     # Same as off()

Digital Input

from machine import Pin

btn = Pin(14, Pin.IN, Pin.PULL_UP)   # Input with pull-up resistor

state = btn.value()   # Returns 1 (high) or 0 (low)

# With pull-up: reads 1 when open, 0 when button connects pin to GND
if btn.value() == 0:
    print("Button pressed!")

Pull options:

  • Pin.PULL_UP ߀” internal pull-up, reads 1 when nothing connected
  • Pin.PULL_DOWN ߀” internal pull-down, reads 0 when nothing connected
  • No pull (omit) ߀” floating input, unreliable without external resistor

Interrupts

from machine import Pin

def button_pressed(pin):
    print("Interrupt! Pin:", pin)

btn = Pin(14, Pin.IN, Pin.PULL_UP)
btn.irq(trigger=Pin.IRQ_FALLING, handler=button_pressed)

IRQ_FALLING fires when the pin transitions from HIGH to LOW (button pressed with pull-up). IRQ_RISING fires on LOW߆’HIGH.

Analog Input (ADC)

The Pico W has 3 ADC channels plus one for internal temperature:

ADC GPIO Notes
ADC0 GPIO 26 External input
ADC1 GPIO 27 External input
ADC2 GPIO 28 External input
ADC3 (4) Internal CPU temperature sensor
from machine import ADC

adc = ADC(26)              # ADC on GPIO 26
raw = adc.read_u16()       # Returns 0߀“65535 (16-bit scaled)
voltage = raw * (3.3 / 65535)

Reading the Internal Temperature

from machine import ADC

sensor = ADC(4)            # Internal temperature sensor
raw = sensor.read_u16()
voltage = raw * (3.3 / 65535)
temp_c = 27 - (voltage - 0.706) / 0.001721
print(f"CPU temp: {temp_c:.1f} ?°C")

PWM Output

Any GPIO pin supports PWM on the Pico W. Each PWM slice covers two pins and they share frequency.

from machine import PWM, Pin

pwm = PWM(Pin(15))
pwm.freq(1000)          # 1000 Hz frequency
pwm.duty_u16(32768)     # 50% duty cycle (0߀“65535)

# Fade LED in
for duty in range(0, 65535, 256):
    pwm.duty_u16(duty)
    sleep_ms(5)

pwm.deinit()            # Release the PWM pin when done

Servo Control

A servo expects a 50 Hz PWM signal with pulse width 1߀“2 ms:

from machine import PWM, Pin
from time import sleep

servo = PWM(Pin(15))
servo.freq(50)

def set_angle(angle):
    # Map 0߀“180 degrees to 1߀“2 ms pulse (at 50 Hz = 20 ms period)
    min_duty = int(65535 * 1 / 20)    # 1 ms / 20 ms
    max_duty = int(65535 * 2 / 20)    # 2 ms / 20 ms
    duty = min_duty + int((max_duty - min_duty) * angle / 180)
    servo.duty_u16(duty)

set_angle(0)
sleep(1)
set_angle(90)
sleep(1)
set_angle(180)