События‎ > ‎

ШИМ на Raspberry PI

Как я уже писал, в рамках проекта РоботКласс мы создали учебного мобильного робота на базе микрокомпьютера Raspberry PI. Как и в любом подобном проекте, для управления двигателями здесь используется метод ШИМ. Мои изыскания касательно ШИМ для Raspberry PI я и приведу в этой короткой статье.

Программный ШИМ

Самый простой способ получить ШИМ на выходе GPIO - это программный генератор импульсов. Данный метод хорош, если не хочется разбираться с установкой каких-либо драйверов и разного софта на Linux. Достаточно просто написать цикл, который будет каждые N миллисекунд выдавать на нужный GPIO импульс требуемой ширины. Представителем именно такой простой реализации является python-модуль pizypwm. Скачать его можно на гитхабе: https://github.com/aboudou/pizypwm

Для примера, рассмотрим программу, которая заставляет светодиод плавно разжигаться и гаснуть.

Программный ШИМ с помощью pizypwm

from RPi import GPIO
from time import sleep

from pizypwm import *

ledPin = 11
led_pwm = PiZyPwm(100, ledPin, GPIO.BCM)
led_pwm.start(100)

power = 0
dim_up = range(0,80)
dim_up.reverse()
dim_down = range(0,80)

try:
    while True:
        for power in dim_up:
            led_pwm.changeDutyCycle(power)
            sleep(0.01)
        for power in dim_down:
            led_pwm.changeDutyCycle(power)
            sleep(0.01)

except KeyboardInterrupt:
    pass

except:
    raise

led_pwm.stop()
GPIO.cleanup()

В чем минусы pizypwm и вообще программных ШИМ? А в том, что этот самый программный генератор импульсов ест ваш вычислительный ресурс. Другими словами, цикл генерации будет соперничать с прочим кодом, от чего будет страдать и стабильность ШИМ и стабильность выполнения всего остального. Нестабильность ШИМ может выражаться, например, в дергании сервоприводов.

Аппаратный ШИМ

WiringPI

Чтобы минимизировать влияние генератора ШИМ на выполнение основного управляющего кода, нужно передать его функции аппаратной части. Для этого, можно использовать встроенный в Raspberry PI аппаратный генератор, доступ к которому осуществляется, например, через библиотеку wiringpi. Сама библиотека для Python и инструкция по её установке есть на гитхабе:

К сожалению, Raspberry PI имеет только один GPIO вывод с поддержкой ШИМ. Номер этого вывода 12 в нотации GPIO, и 18 - в нотации BCM (я обычно пользуюсь BCM). Ниже представлена простая программа, которая с помощью wiringpi инициализирует ШИМ на 18-м выводе, и устанавливает на нем сигнал со скважностью 0,5. Такой сигнал должен зажечь светодиод примерно на 50% яркости.

Аппаратный ШИМ с помощью wiringpi

import wiringpi

# GPIO pin 12 = BCM pin 18 = wiringpi pin 1
led_pin  = 1

wiringpi.wiringPiSetup()
wiringpi.pinMode(led_pin, 2)
wiringpi.pwmWrite(led_pin, 0)

def led(led_value):
    wiringpi.pwmWrite(led_pin, led_value)

# значение должно быть от 0 до 1024
led(512)

PI-Blaster

Другой вариант реализации ШИМ позволяет использовать до 8 (а в некоторых библиотеках и до 15) выводов на Raspberry PI. Делается это с помощью того же аппаратного ШИМ-генератора, но совместно с DMA (прямой доступ к памяти), что позволяет добиться распределения генерируемого сигнала между несколькими GPIO выводами, без участия центрального процессора. Лично я использую библиотеку pi-blaster, которая, в свою очередь, основана на известном проекте ServoBlaster. Скачать pi-blaster можно тут:

В отличие от предыдущих методов, работать с pi-blaster чуть тяжелее. Чтобы сменить значение ШИМ, нужно записать его в специальный файл. А чтобы сменить выводы, на которые выводится ШИМ, потребуется пересобрать само приложение. Но обо всем по-порядку.

1. Установка

Скачиваем приложение из указанного репозитория, и распаковываем при необходимости. Среди полученного списка файлов находим pi-blaster.c, в котором можно указать список задействованных в генерации, GPIO выводов. По-умолчанию, этот список имеет вид: 

static uint8_t pin2gpio[] = {
4, // P1-7
17, // P1-11
18, // P1-12
21, // P1-13
22, // P1-15
23, // P1-16
24, // P1-18
25, // P1-22
};

Можно изменить номера выводов или заблокировать лишние с помощью комментария "//". Учтите только, что номера выводов указаны в нотации GPIO, а не BCM. 

После того как список должным образом отредактирован, выполняем команды:

sudo make
sudo make install

Затем запускаем демона (но он должен сам запуститься уже после предыдущей команды):

sudo ./pi-blaster

2. Удаление

Чтобы выключить демона, и удалить его из автозагрузки, выполняем:

sudo make uninstall

Также можно убить самого демона командой: 

sudo kill -9 номер_процесс

для чего предварительно потребуется узнать номер процесса с помощью команды ps ax.

3. Использование

После установки pi-blaster, в папке dev появится специальный программный FIFO буфер: /dev/pi-blaster. 
Чтобы изменить значение ШИМ на нужном выводе, просто записываем в этот буфер выражение вида:

номер_канала = значение

где номер канала - номер от 0 до 7, указывающий на тот или иной вывод из списка доступных GPIO (тот что мы редактировали на шаге №1).
значение - действительное число от 0.0 до 1.0, задающее скважность ШИМ.

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

Аппаратный ШИМ с помощью pi-blaster

class PWM:
    def __init__( self, pin ):
        self.pin = pin

    def set( self, value ):
        cmd = 'echo "%d=%.2f" > /dev/pi-blaster' % ( channel_map[self.pin], value/100. )
        os.system(cmd)

led = PWM(25)
led.set(0.5)

Итог

Из всех трех рассмотренных подходов, для своих коварных проектов на Raspberry PI я выбрал полу-аппаратный генератор ШИМ, использующий DMA (pi-blaster). Он достаточно легко ставится и используется, но имеет небольшой минус, связанный с необходимостью пересобирать его каждый раз, когда требуется поменять список используемых для ШИМ выводов. Именно из-за такой отрицательной черты, для учебных целей я все-таки рекомендую чисто программную реализацию pizypwm.


Comments