Skip to main content

PWM

A pin only has two states: fully on or fully off. PWM gets you something in between by switching the pin on and off so fast that whatever it drives only notices the average. The name stands for pulse-width modulation.

PiGPIO 18GNDresistorLEDhighlowwrite(25, 100)write(50, 100)write(75, 100)

The duty cycle is how much of the time the pin is on. At 25%, the pin is high for a quarter of every pulse, and an LED looks dim. At 75%, it looks much brighter. Nothing about the voltage changed: your eye just averages the flashes.

The frequency is how many pulses happen each second. An LED only needs them fast enough that you can't see a flicker, so 1,000 a second is plenty. A servo expects exactly 50.

The Pi's hardware makes the pulses by itself, so they stay steady while your program gets on with something else.

What uses it​

  • LEDs, for brightness
  • Servos, where the width of each pulse sets the angle
  • DC motors, for speed, through an amplifier circuit or a driver board
  • Passive buzzers, where the frequency sets the pitch

Fade an LED​

This uses the same LED and 330 Ω resistor as the GPIO page, on a different pin, because only four pins can do PWM.

12141Pin 1 is at the SD card end
  • Pin 12GPIO 18To the resistor, then the LED's long leg
  • Pin 14GNDTo the LED's short leg
Add the Raspberry Pi library first

Pi and its devices come from a library, not from the language, so this code won't build without it. In IntelliJ, choose Tools | Nylon | Add Nylon Library… and pick raspberry-pi. That adds one line to your project's build.markup.code:

(+dependencies : RepositoryDependency coordinate={"com.grandtheorem:raspberry-pi:0.1.0"} /)

Every file starts with its namespace. Pi is the board, and nylon.time gives us sleep.

namespace fade

import grandtheorem.rpi.Pi
import namespace nylon.time

Ask for PWM on GPIO 18 at 1,000 pulses a second. It starts low, with a duty of 0.

entrypoint.default main() {
let led = Pi.pwm(18, 1000)

write(level, 100) holds the pin high for level parts out of 100 of every pulse. Counting up fades the LED in.

for (3 times) {
for (level in 0 thru 100) {
led.write(level, 100)
sleep(10.milliseconds())
}

Counting back down fades it out.

for (level in 0 thru 100) {
led.write(100 - level, 100)
sleep(10.milliseconds())
}
}

stop() holds the pin low.

led.stop()
}

The LED fades in and out three times, about two seconds each, and the program ends.

Move a servo​

A hobby servo is a small motor with its own control circuit. It reads the width of each pulse and turns to match: about 1.5 milliseconds is the middle, with 1 and 2 milliseconds near either end. A small one such as the SG90 is a good first servo.

46121Pin 1 is at the SD card end
  • Pin 45 VTo the servo's red wire
  • Pin 6GNDTo the servo's brown or black wire
  • Pin 12GPIO 18To the servo's orange or yellow wire
warning

Only the signal wire goes to a GPIO pin. A servo's motor needs far more current than a pin can supply, so its power comes from the 5 V pin. That's fine for a small servo like the SG90, but anything bigger needs its own power supply, with its ground joined to the Pi's.

The start is the same as before.

namespace servo

import grandtheorem.rpi.Pi
import namespace nylon.time

A servo expects 50 pulses a second. That makes each one 20 milliseconds long, or 20,000 microseconds.

entrypoint.default main() {
let servo = Pi.pwm(18, 50)

Using 20000 as the scale makes each step one microsecond, so the first number is simply the pulse width. 1,000 microseconds turns to one end.

servo.write(1000, 20000)
sleep(1.seconds())

1,500 microseconds goes to the middle.

servo.write(1500, 20000)
sleep(1.seconds())

2,000 microseconds goes to the other end. stop() ends the pulses, so the servo stops holding its position.

servo.write(2000, 20000)
sleep(1.seconds())

servo.stop()
}

The servo swings to one end, the middle and the other end, a second at each.

Good to know​

Four pins, two channels. PWM only works on GPIO 12, 13, 18 and 19, and any other pin is refused. GPIO 12 and 18 share one channel, and GPIO 13 and 19 share the other. Two pins on one channel carry the same pulses, so to run two different signals, use one pin from each.

Pick whichever scale suits you. write(1, 4) and write(25, 100) both mean 25%. Only the ratio matters.

Servos differ. Their limits aren't exact, and a servo pushed past its end stop buzzes and strains. If yours does, bring the widths in towards 1,500.

note

These programs ran on a Raspberry Pi 3 B+ and a Raspberry Pi 4, as this page shows them.