Top.Mail.Ru

Raspberry Pi. PWM. Генерация ШИМ-сигнала.

Рассмотрев работу с портами ввода-вывода GPIO в качестве обычных входов и выходов, нельзя не затронуть и вопрос использования Raspberry Pi для генерации ШИМ (PWM).

ШИМ-сигналы могут использоваться, к примеру, для управления серво-приводами или двигателями постоянного тока, либо для контроля яркости светодиодов. И это только несколько самых основных, сразу возникающих в памяти, применений. Таким образом, необходимость в генерации ШИМ возникает довольно часто и в самых разных проектах.

Итак, ШИМ-сигнал представляет из себя последовательность импульсов с постоянной частотой следования, но разной длительностью. Давайте рассмотрим наглядный пример:

ШИМ сигнала

Поскольку частота импульсов постоянна, то значит и период имеет фиксированную величину. А вот длительность импульса может меняться, собственно так и происходит модуляция. Рассмотрим два разных сигнала, использующихся для включения/отключения светодиодов:

Пример PWM сигналов

Пусть светодиоды загораются при высоком уровне сигнала. Период в обоих случаях идентичен, а вот длительность импульсов второго сигнала больше в 2 раза. Соответственно, светодиод во втором случае будет гореть ярче, чем в первом.

Строго говоря, светодиод будет мигать, то есть часть периода гореть, а часть периода - нет. Но при высокой частоте следования импульсов глазу будут не заметны эти мигания, поэтому на практике получим 2 светодиода с разной яркостью. То есть, чем большую часть периода диод горит - тем ярче будет в результате светить.

ШИМ-сигналы характеризуют параметром, который называется - коэффициент заполнения (duty cycle). Он вычисляется по формуле:

D = \frac{\tau}{T}\medspace * \medspace 100\%

Здесь \tau - длительность импульса, а T - период сигнала.

Вернемся к рассмотренным выше сигналам - для первого случая длительность импульса составляет \frac{4}{10} периода сигнала, а значит D = 40\%. Аналогично, для второго случая D = 80\%.

На этом заканчиваем лирическое отступление на тему ШИМ и переходим к практической реализации на Raspberry Pi.

Вспоминаем таблицу распределения функций портов GPIO. Я сейчас пользуюсь Raspberry Pi 4, для любой другой модификации можно найти аналогичную информацию в документации. Итак, для генерации ШИМ могут использоваться следующие выводы:

GPIO ШИМ Raspberry Pi

Задействуем два порта в нашем примере - GPIO12 и GPIO13. При этом пусть коэффициент заполнения будет одинаковым, а частота второго сигнала (GPIO13) - в 2 раза больше.

Для работы с PWM на Python используем все тот же модуль RPi.GPIO, установить который в случае отсутствия можно командой:

sudo apt-get install python-rpi.gpio

Создаем файл pwm_test.py:

import RPi.GPIO as GPIO
import time

GPIO_PWM_0 = 12
GPIO_PWM_1 = 13
WORK_TIME = 10
DUTY_CYCLE = 50
FREQUENCY = 100

GPIO.setmode(GPIO.BCM)

GPIO.setup(GPIO_PWM_0, GPIO.OUT)
GPIO.setup(GPIO_PWM_1, GPIO.OUT)

pwmOutput_0 = GPIO.PWM(GPIO_PWM_0, FREQUENCY)
pwmOutput_1 = GPIO.PWM(GPIO_PWM_1, 2 * FREQUENCY)

pwmOutput_0.start(DUTY_CYCLE)
pwmOutput_1.start(DUTY_CYCLE)

time.sleep(WORK_TIME)

pwmOutput_0.stop()
pwmOutput_1.stop()
GPIO.cleanup()

Разбираем написанное. Первым делом подключаем модули и объявляем переменные:

import RPi.GPIO as GPIO
import time

GPIO_PWM_0 = 12
GPIO_PWM_1 = 13
WORK_TIME = 10
DUTY_CYCLE = 50
FREQUENCY = 100
  • GPIO_PWM_0 и GPIO_PWM_1 - номера выводов GPIO, которые мы используем для генерации ШИМ
  • WORK_TIME - время выполнения программы. В этом примере не будем анализировать действия пользователя для определения момента выхода из программы. Просто включаем ШИМ и по истечении времени WORK_TIME (10 секунд) заканчиваем работу.
  • DUTY_CYCLE - коэффициент заполнения, одинаковый для обоих каналов и составляющий 50%.
  • FREQUENCY - частота сигнала (100 Гц) первого канала. На втором установим в 2 раза больше, как и планировали.

Далее настроим режим нумерации портов, чтобы номера соответствовали названию сигнала (например, GPIO12, GPIO13), а не порядковому номеру на разъеме, а также настроим нужные нам порты:

GPIO.setmode(GPIO.BCM)

GPIO.setup(GPIO_PWM_0, GPIO.OUT)
GPIO.setup(GPIO_PWM_1, GPIO.OUT)

Создаем объекты pwmOutput_0 и pwmOutput_1 для работы с каналами PWM. В качестве аргументов указываем последовательно - номер порта GPIO и частоту следования импульсов:

pwmOutput_0 = GPIO.PWM(GPIO_PWM_0, FREQUENCY)
pwmOutput_1 = GPIO.PWM(GPIO_PWM_1, 2 * FREQUENCY)

Запускаем генерацию функцией start() - аргументом является требуемая величина коэффициента заполнения (duty cycle):

pwmOutput_0.start(DUTY_CYCLE)
pwmOutput_1.start(DUTY_CYCLE)

Ожидаем 10 секунд и останавливаем процесс:

time.sleep(WORK_TIME)

pwmOutput_0.stop()
pwmOutput_1.stop()
GPIO.cleanup()

На этом базовый пример закончен, запускаем его на выполнение:

python pwm_test.py

В результате получаем сигналы ШИМ:

ШИМ на плате Raspberry Pi

Коэффициент заполнения одинаковый (50% - длительность импульса равна половине периода), а частота во втором случае в 2 раза выше, собственно, как и должно быть 👍

Давайте реализуем еще один небольшой пример - будем менять коэффициент заполнения, а, соответственно, и длительность импульса в цикле программы. При этом период импульсов будет оставаться постоянным. Создаем файл pwm_test_dynamic.py и пишем код:

import RPi.GPIO as GPIO
import time

GPIO_PWM_0 = 12
FREQUENCY = 100
DELAY_TIME = 0.02

GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_PWM_0, GPIO.OUT)

pwmOutput_0 = GPIO.PWM(GPIO_PWM_0, FREQUENCY)
pwmOutput_0.start(0)

try:
    while True:
        for dutyCycle in range(0, 101, 1):
            pwmOutput_0.ChangeDutyCycle(dutyCycle) 
            sleep(DELAY_TIME)
except KeyboardInterrupt:
    pwmOutput_0.stop()
    GPIO.cleanup()
    print('exiting')

Аналогичным образом настраиваем GPIO12 и запускаем PWM с коэффициентом заполнения 0 и частотой 100 Гц:

GPIO_PWM_0 = 12
FREQUENCY = 100
DELAY_TIME = 0.02

GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_PWM_0, GPIO.OUT)

pwmOutput_0 = GPIO.PWM(GPIO_PWM_0, FREQUENCY)
pwmOutput_0.start(0)

В основном цикле программы while True каждые 0.02 с (DELAY_TIME) изменяем duty cycle в диапазоне от 0 до 100:

while True:
	for dutyCycle in range(0, 101, 1):
		pwmOutput_0.ChangeDutyCycle(dutyCycle) 
		sleep(DELAY_TIME)

При нажатии пользователем Ctrl + C завершаем выполнение программы:

pwmOutput_0.stop()
GPIO.cleanup()
print('exiting')

Запускаем:

python pwm_test_dynamic.py

И получаем:

Генерация работает как положено, так что на этом заканчиваем разбор ШИМ на Raspberry Pi, до новых статей!

Подписаться
Уведомить о
guest

7 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Павел
Павел
3 лет назад

частоту так же можно менять в процессе выполнения программы?

Алекс
Алекс
2 лет назад

в строке "sleep(DELAY_TIME)" ошибка, там должно быть "time.sleep(DELAY_TIME)"
иначе будет вот это
File "pwm_test_dynamic.py", line 14, in <module>
  sleep(DELAY_TIME)
NameError: name 'sleep' is not defined

Елена
Елена
11 месяцев назад

Здравствуйте. Частота следования импульсов при проверке осциллографом не получается постоянной - при установке в программе 1000, она колеблется от 860 до 890 примерно. Читала что зависит это от частоты процессора самой Raspberry - она меняется в процессе работы. Подскажите, пожалуйста, возможно ли добиться постоянной частоты следования импульсов.

Елена
Елена
Ответ на комментарий  Aveal
11 месяцев назад

Спасибо, действительно дает ожидаемые 1000 импульсов в сек. или около того.

7
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x