ШИМ на Raspberry PI

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

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

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

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

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

from RPi import GPIOfrom time import sleepfrom pizypwm import *ledPin = 11led_pwm = PiZyPwm(100, ledPin, GPIO.BCM)led_pwm.start(100)power = 0dim_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:    passexcept:    raiseled_pwm.stop()GPIO.cleanup()

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

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

WiringPI

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

https://github.com/WiringPi/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 можно тут:

https://github.com/sarfata/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.