Introduction

GPIO Limitations

  • GPIO can be used turn an LED on or off
  • GPIO can be used turn a motor on or off
  • But what if we need to control the speed of the motor?
  • Or control the brightness of the LED?

Solution: Pulse Train

  • Instead of just turning the LED on or off
  • To control the brightness of a LED it needs to be switched on and off rapidly
  • Since the power is only applied to LED half the time, the LED will glow with half the brightness
figures/pwm-dutycycle-train.png

Solution: Pulse Train (Contd.)

  • Here we are generating a period wave form, where ON time is equal to OFF time
  • The brightness of the LED, can be further controlled by changing the ON time in the periodic waveform
figures/pwm-signal.png

Pulsed GPIOs

  • The GPIOs can be pulsed high and low by controlling the delay between them.
  • Instead of doing this in software, it can be delegated to hardware controller
  • PWM Controller: Hardware to generate periodic waveform, where the on time can be programmed
  • The signal generated by PWM Controller is called PWM signal

Pulse Width Modulation

Overview

  • Using PWM signal it is possible to generate analog variations with digital signals.
  • The PWM signal can be used to control the power delivered to a load, by controlling the on-time of the PWM signal.

PWM Signal Parameters

  • Period is the time taken by PWM signal to complete one cycle, which involves both on and off time.
    PWM Period = ON Time + OFF Time
  • The Duty Cycle is defined as the ratio of ON Time of the signal to the period of the signal.
    Duty Cycle = (ON Time / Period) * 100
figures/pwm-params.png

PWM Applications

  • PWM signals are used to dynamically control the power generated from a source.
  • PWM signals are generally used for motor speed control, LED brightness control, power supplies and wave form generation.

PWM Controller

  • The hardware which can be configured to generate PWM signals is called as PWM controller.
  • The width of the signal generated by controller can be configured through software by modifying PWM registers.
figures/pwm-controller.png

PWM Controller

  • PWM controller resides in the memory bus of the SOC.
figures/pwm-arm.png

PWM Grouping

  • Set of PWM signals are grouped into a single controller and as such there could be several PWM controler in a SOC.
  • Each PWM signal in a PWM controller is called a PWM channel.

ZKit PWM Mappings

PWM Sequence of PWM3 Channel No in PWM3 Device

PWM16

0

Backlight

PWM17

1

DC Motor

PWM18

2

Buzzer

PWM in Linux

  • In Linux the PWM interfaces can be accessed from userspace application through sysfs.
  • The access to sysfs goes to generic PWM driver, which inturn calls the processor specific PWM controller driver.

Sysfs PWM

Using PWMs with the sysfs interface

  • Inside sysfs in path /sys/class/pwm we have files which allows to control PWM channels.
  • For each PWM controller a folder sys/class/pwm/pwmchip<base> is created
  • Where base is the base channel number for that controller
/sys/class/pwm/
| -- pwmchip<base>
     | -- pwm<no>
          | -- period
          | -- duty_cycle
          ` -- enable

Using PWMs with the sysfs interface

  • For each channel in a PWM Chip we would find a directory /sys/../pwmchip<base>/pwm<chno>.
  • Inside the channel directory we have files period, duty_cycle & enable.
/sys/class/pwm/
| -- pwmchip<base>
     | -- pwm<no>
          | -- period
          | -- duty_cycle
          ` -- enable

Peizo Buzzer

Buzzer Overview

  • It works on the inverse principle of peizo electric effect ie, electrical energy is converted into mechanical energy.
  • When an oscillating digital signal is provided, the buzzer stretch or compress, producing sound.
  • The tone of the sound can be modified by varying frequency of the signal.
  • PWM can be interfaced to Buzzer to create varied tone.

Buzzer Circuitry

figures/buzzer.png

PWM Access

PWM Channel Request

  • The processor pins can be mapped for PWM channels through sysfs
$ echo 2 > /sys/class/pwm/pwmchip16/export
  • You would find /sys/class/pwm/pwmchip16/pwm2 available.
  • PWM channel can be freed as shown below
$ echo 2 > /sys/class/pwm/pwmchip16/unexport

PWM Signal Control

  • The period of the PWM can be controller as
$ echo 1000000 > /sys/class/pwm/pwmchip16/pwm2/period
  • The duty cyle can be updated as
$ echo 500000 > /sys/class/pwm/pwmchip16/pwm2/duty_cycle
  • The PWM channel can be enabled / disabled as
$ echo 1 > /sys/class/pwm/pwmchip16/pwm2/enable

Programming for Buzzer

Following code will control the tone of buzzer.

import time

ch = "2"
pwm_path = "/sys/class/pwm/pwmchip16/"
export_path = pwm_path+"export"
unexport_path = pwm_path+"unexport"
period_path = pwm_path+"pwm"+ch+"/period"
duty_path = pwm_path+"pwm"+ch+"/duty_cycle"
enable_path = pwm_path+"pwm"+ch+"/enable"

export = open(export_path, "w")
export.write(ch)
export.close()

period = open(period_path, "w")
period.write("1000000")
period.close()

duty_cycle = open(duty_path, "w")
duty_cycle.write("500000")
duty_cycle.flush()

enable = open(enable_path, mode="w")
enable.write("1")
enable.flush()

time.sleep(5)

duty_cycle.write("0")
duty_cycle.close()

enable.write("0")
enable.flush()
enable.close()

unexport = open(unexport_path, "w")
unexport.write(ch)
unexport.close()

PWM Class

from os.path import exists

__pwm_path = "/sys/class/pwm/pwmchip16/pwm{}"
__pwm_export_path = "/sys/class/pwm/pwmchip16//export"
__pwm_unexport_path = "/sys/class/pwm/pwmchip16/unexport"
__pwm_period_path = "/sys/class/pwm/pwmchip16/pwm{}/period"
__pwm_dutycycle_path = "/sys/class/pwm/pwmchip16/pwm{}/duty_cycle"
__pwm_enable_path = "/sys/class/pwm/pwmchip16/pwm{}/enable"

def cat(file):
    file = open(file, "r")
    value = file.read()
    file.close()
    return value

def echo(value, file):
    file = open(file, "w")
    file.write(value)
    file.close()

def pwm_export_channel(channel):
    if not exists(__pwm_path.format(channel)):
        echo(str(channel), __pwm_export_path)

def pwm_unexport_channel(channel):
    if exists(__pwm_path.format(channel)):
        echo(str(channel), __pwm_unexport_path)

def pwm_set_period(channel, period):
    echo(str(period), __pwm_period_path.format(channel))

def pwm_set_dutycycle(channel, dutycycle):
    echo(str(dutycycle), __pwm_dutycycle_path.format(channel))

def pwm_enable(channel):
    echo(str(1), __pwm_enable_path.format(channel))

def pwm_disable(channel):
    echo(str(0), __pwm_enable_path.format(channel))

Playing Tone

#!/usr/bin/python
import time
from pwm import *

BUZZER = 2
pwm_export_channel(BUZZER)

melody = [379218, 379218, 100000, 379218,
          100000, 477782, 379218, 100000,
          318878, 100000, 100000, 100000,
          637754, 100000, 100000, 100000,
          477782, 100000, 100000, 637754,
          100000, 100000, 75814,  100000,
          100000, 568182, 100000, 506072,
          100000, 536192, 568182, 100000,
          637754, 379218, 379218,
          284092, 100000, 35792, 318878,
          100000, 379218, 100000, 477782,
          100000, 379218, 100000, 477782,
          425712, 506072, 100000, 100000,
          379218, 379218, 100000, 379218,
          100000, 477782, 379218, 100000,
          318878, 100000, 100000, 100000,
          637754, 100000, 100000, 100000,
          284092, 100000, 35792, 318878,
          100000, 379218, 100000, 477782,
          100000, 379218, 100000, 477782,
          425712, 506072, 100000, 100000,
          ]

note = [0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.111, 0.111, 0.111,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.111, 0.111, 0.111, 0.111,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083,
        0.083, 0.083, 0.083, 0.083]

count = 0
while count < 83:
    pwm_set_period(BUZZER, melody[count])
    duty = melody[count]//2
    pwm_set_dutycycle(BUZZER, duty)
    pwm_enable(BUZZER)
    time.sleep(note[count])
    pwm_set_dutycycle(BUZZER, 0)
    time.sleep(note[count])
    count += 1

pwm_unexport_channel(BUZZER)

DC Motor

Overview

  • The speed of the DC Motor is proportional to the supply voltage provided to it.
  • Speed control can be acheived by varying the average voltage delivered to the motor.
  • The motor is rapidly switched on and off, where by controlling the on-time it is possible to control the speed of the motor.
  • To control the speed of the DC-motors the voltage to the DC-motor has to be modulated through PWM signals.
  • DC motor’s speed and direction of rotation can be controlled varying the voltage levels and polarity.

Voltage Variation

Voltage variation with duty cycle

Motor Control

  • Change the speed of the motor every 5 seconds
    • Low Speed: Set duty cycle to 30% for 5 seconds
    • Hight Speed: Set duty cycle to 100% for 5 seconds
  • Speed change can be observed
    • By feeling the change in vibration
    • By observing the wobling of the disk, at low speed

Motor Control Code

#!/usr/bin/python
from pwm import *
import time

MOTOR = 1

pwm_export_channel(MOTOR)

period_ns = 10000000
pwm_set_period(MOTOR, period_ns)

while True:
    for duty in [30, 100]:
        duty_ns = int(period_ns * duty / 100)
        pwm_set_dutycycle(MOTOR, duty_ns)

        pwm_enable(MOTOR)
        time.sleep(5)
        pwm_disable(MOTOR)

pwm_unexport_channel(MOTOR)

Try Out

  • Modify the DC motor code, to control instead the LCD backlight brightness instead
  • Modify the duty cycle steps, to 30%, 70% and 100%