The Raspberry Pi’s GPIO pins allow you to send PWM signals through them, so I thought “wait can’t we just drive servos directly?” Turns out you can. But if you’re trying to power 5v servos through the Pi’s 5V pins then you’re in for a bad time for all but micro servos (~9g or under). Servos draw a lot of power and usually require 4.8, 6, or 7.4 volts. They’ll typically need more amps than your Pi can provide, and the pi’s power pins aren’t protected which puts you at risk for burning out your SBC.

What can we use PWM for on the GPIO pins then? Remember that the Pi is a 3.3v device. So GPIO pins can send 0v or 3.3v and logic levels leverage this for high/low (e.g anything under 2.7v is low, higher is high)

Say you have an analog voltmeter that reads values from 0v to 3.3v. What if you want to write a program that points to different values along this scale?

You can use PWM! For a given operating frequency, you switch the signal off for x% of the time to get “x% voltage.” For example if you’re operating at 100Hz then your period is 1/100 of a second (period is just how long 1 operation cycle takes). Then if you want your voltmeter to read halfway (1.65v) then you simply send pulses for half the period (1/2 * 1/100 = 1/200 seconds). 1/200 seconds == 20 milliseconds or 20,000 usec.

Now you could do the math and specify a specific usec pulse, but most libraries abstract that for you and just provide access to the duty cycle, which is how much of the cycle to be “on duty” (when it’s on). So if you’re operating at 100Hz you can specify 50 as the duty cycle (50Hz that is) and your PWM signal will send 1/2 voltage, so 3.3v * 1/2 = 1.65v.

How to use PWM to control a voltmeter

Using Python and the GPIO library from Raspberry Pi:

  1. Setup a pin to be an output. GPIO.setup(<pin number>, GPIO.OUT)
  2. Use pwm_pin = GPIO.PWM(<pin number>, <Hz>) to set up pulse-width modulation.
  3. pwm_pin.start(50) // Start with duty cycle = 50.
  4. pwm_pin.ChangeDutyCycle(60) // Change duty cycle so that voltage is max.
  5. pwm_pin.ChangeFrequency(1000) // Change frequency.
  6. Then turn it all off:
    pwm_pin.stop()
    GPIO.cleanup()
    

This approach will work for LEDs (brightness) and other analog electronics that take a voltage input as well.

Max Frequency on Raspberry Pi

Apparently the theoretical hardware limit for frequency on a RPi GPIO Pin is 19.2 MHz but it’s much lower in practice because of software overhead and whatnot.