Что такое ШИМ
Хорошее определение широтно-импульсной модуляции (ШИМ) заключается в самом его названии. Это означает модуляция (изменение) ширины импульса (не частоты). Чтобы лучше понять что такое ШИМ, давайте сначала посмотрим некоторые основные моменты.
Микроконтроллеры представляют собой интеллектуальные цифровые компоненты которые работают на основе бинарных сигналов. Лучшее представление бинарного сигнала – меандр (сигнал имеющий прямоугольную форму). Следующая схема объясняет основные термины, связанные с прямоугольным сигналом.
В ШИМ-сигнале время (период), и следовательно частота является всегда постоянной величиной. Изменяется только время включения и время выключения импульса (скважность). Используя данный метод модуляции, мы можем получить необходимое нам напряжение.
Единственное различие между меандром и ШИМ-сигналом заключается в том, что у меандра время включения и отключения равны и постоянны (50% скважность), в то время как ШИМ-сигнал имеет переменную скважность.
Меандр может рассматриваться как частный случай ШИМ сигнала, который имеет 50% рабочий цикл (период включения = период отключения).
Допустим, мы имеим напряжение питания 50 вольт и нам необходимо запитать какую-либо нагрузку, работающую от 40 вольт. В этом случае хороший способ получения 40В из 50В — это использовать так называемый понижающий чоппер (прерыватель).
ШИМ сигнал, генерируемый чеппером, поступает на силовой узел схемы (тиристор, полевой транзистор), который в свою очередь управляет нагрузкой. Этот ШИМ-сигнал может легко генерироваться микроконтроллером, имеющим таймер.
Требования к ШИМ-сигналу для получения с помощью тиристора 40В из 50В: подача питания, на время = 400мс и выключение на время = 100мс (с учетом периода ШИМ сигнала равного 500 мс).
В общих словах это можно легко объяснить следующим образом: в основном, тиристор работает как переключатель. Нагрузка получает напряжение питания от источника через тиристор. Когда тиристор находится в выключенном состоянии, нагрузка не подключена к источнику, а когда тиристор находится в открытом состоянии, нагрузка подключается к источнику.
Этот процесс включения и выключения тиристора осуществляется посредством ШИМ сигнала.
Соотношение периода ШИМ-сигнала к его длительности называется скважность сигнала, а обратная к скважности величина именуется коэффициентом заполнения.
Если коэффициент заполнения равен 100, то в этом случае у нас сигнал постоянный.
Таким образом, скважность импульсов (рабочий цикл) может быть вычислен с использованием следующей формулы:
Используя выше приведенные формулы, мы можем рассчитать время включения тиристора для получения необходимого нам напряжения.
Умножая скважность импульсов на 100, мы можем представить это в процентном соотношении. Таким образом, процент скважность импульсов прямо пропорционален величине напряжения от исходного. В приведенном выше примере, если мы хотим получить 40 вольт от 50 вольт источника питания, то это может быть достигнуто путем генерации сигнала со скважность 80%. Поскольку 80% из 50 вместо 40.
Для закрепления материала, решим следующую задачу:
- рассчитаем длительность включения и выключения сигнала, имеющего частоту 50 Гц и скважность 60%.
Полученный ШИМ волны будет иметь следующий вид:
Один из лучших примеров применения широтно-импульсной модуляции является использование ШИМ для регулировки скорости двигателя или яркости свечения светодиода.
Этот прием изменения ширины импульса, чтобы получить необходимый рабочий цикл называется “широтно-импульсная модуляция”.
источник: www.circuitstoday.com
Источник: http://fornk.ru/2587-chto-takoe-shim/
PICSim.js – широтно-импульсная модуляция
Published: Сб. 14 Январь 2017By Oleg Mazko
In Embedded.
tags: picsim ШИМ
В предыдущем примере светодиод либо горит либо нет т.е. работает в т.н. ключевом (цифровом) режиме. Широтно-импульсная модуляция (ШИМ) в микроконтроллерах – очень простой и эффективный способ преобразования цифрового сигнала в аналоговый с очень высоким КПД.
Задавая скважность программно можно менять среднее напряжение на выходе ШИМ и тем самым формировать аналоговый сигнал любой формы. Высокий КПД ШИМ также позволяет эффективно управлять и мощной нагрузкой – моторами, нагревательными элементами.
К недостаткам ШИМ стоит в первую очередь высокий уровень помех, для устранения которых можно использовать даже самую простую интегрирующую RC-цепочку.
Наиболее простой реализацией ШИМ на микроконтроллере является программная реализация, однако также программный метод получения ШИМ сигнала очень требователен к частоте работы микроконтроллера и занимает достаточно много процессорного времени. В связи с этим большинство современных микроконтроллеров имеет на борту отдельный периферийных модуль аппаратного ШИМ, работающего параллельно с основной программой, но обо всём по порядку.
hex | picsim.js
/*
xc8 –chip=16f648A pwm.c
*/ #include #pragma config WDTE = OFF #define DUTY_MAX 12 #define _DUTY_CYCLE_RB0(value)
do {
for (unsigned char i = 0; i < DUTY_MAX; i++) { RB0 = i < value; } } while(0) int main() { PORTB = TRISB = 0; while(1) { if (!RA1) { _DUTY_CYCLE_RB0(DUTY_MAX/4); } else if (!RA2) { _DUTY_CYCLE_RB0(DUTY_MAX/3); } else if (!RA3) { _DUTY_CYCLE_RB0(DUTY_MAX/2); } else if (!RA4) { _DUTY_CYCLE_RB0(DUTY_MAX*2/3); } // Do something useful // for (unsigned char i=0; --i;); } return 0; }
Каждая кнопка RA1-RA4 при нажатии устанавливает свою яркость светодиода RB0. Поскольку частота переключения RB0 достаточно большая, человеческий глаз видит среднее значение яркости. Точно расчитать частоту переключения RB0 затруднительно т.к.
тут многое зависит от компилятора – нужно смотреть в ассемблерный листинг. Также очевидно что любая дополнительная логика в // Do something useful окажет сильное влияние на итоговую скважность импульсов на выходе RB0.
Аппаратный ШИМ лишен всех этих недостатков.
Для работы модуля ШИМ требуется один таймер и модуль CCP1. На блок-схеме PIC16f648 аппаратный модуль CCP1 на ноге RB3 поэтому там и будет гореть светодиод.
hex | picsim.js
/*
xc8 –chip=16f648A pwm.c
*/ #include #pragma config WDTE = OFF // ignore two least significant bits PWMxDCL
#define _DUTY_CYCLE(value)
CCPR1L = (4*(249+1)*value)>>2 int main() { // http://microchip.wikidot.com/8bit:10bitpwm // 0b01 Prescaler is 4; 0b1xx TMR2 on T2CON = 0b00000101; // Desired PWM frequency 250Hz // PR2=[(Fosc)/(4∗TMR2Prescale∗PWMFrequency)]−1 // PR2=(1Mhz/(4*4*250Hz))-1=249 PR2 = 249; // 0b11xx – PWM mode; // assert log(1e6/249)/log(2) > 8 bit CCP1CON = 0b00001100; PORTB = TRISB = 0; while(1) { if (!RA1) { _DUTY_CYCLE(1/4); } else if (!RA2) { _DUTY_CYCLE(1/3); } else if (!RA3) { _DUTY_CYCLE(1/2); } else if (!RA4) { _DUTY_CYCLE(2/3); } // Do something useful for (unsigned char i=0; –i;); } return 0;
}
Аппаратный ШИМ не расходует ресурсы процессора и логика // Do something useful никак не влияет на яркость свечения светодиодов. Для уменьшения энергопотребления можно спокойно уменьшить тактовую частоту микроконтроллера, просто пересчитав значения регистров ШИМ.
К тому же частоту аппаратного ШИМ в отличие от программного можно совершенно точно рассчитать на этапе проектирования системы.
Далее прерывания.
Источник: http://proiot.ru/blog/posts/2017/01/14/picsimjs-shirotno-impulsnaia-moduliatsiia/
Raspberry Pi – ШИМ и Сервопривод
Raspberry Pi имеет несколько путей реализации PWM (ШИМ) (Широтно-импульсной модуляции). Мы рассмотрим как реализовать, ШИМ программно, и задействуем для генерации ШИМ аппаратные ресурсы Raspberry Pi. Сначала будем менять яркость светодиода, а затем научимся управлять сервоприводом.
Что такое PWM (ШИМ)?
Широтно-импульсная модуляция (ШИМ) – это импульсный сигнал постоянной частоты и переменной скважности, то есть отношение длительности импульса к периоду его следования. С помощью задания скважности (длительности импульсов) можно менять среднее значение напряжения на выходе ШИМ. Подробнее о ШИМ читайте на Википедия.
Программная реализация ШИМ
Подключим светодиод к GPIO23 как указано на схеме:
Напишем скрипт pwm_soft.py:
nano ./pwm_soft.py
Текст скрипта:
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(23, GPIO.OUT)
p = GPIO.PWM(23, 50) # channel=23 frequency=50Hz
p.start(0)
try: while 1: for dc in range(0, 101, 5): p.ChangeDutyCycle(dc) time.sleep(0.1) for dc in range(100, -1, -5): p.ChangeDutyCycle(dc) time.sleep(0.1)
except KeyboardInterrupt: pass
p.stop()
GPIO.cleanup()
Запустим его:
python ./pwm_soft.py
Светодиод будет плавно загораться и плавно гаснуть.
Программная реализация ШИМ позволяет сформировать ШИМ-сигнал на любому выводе. В этом примере мы используем RPi.GPIO для программной генерации ШИМ сигнала. А это значит, что тратятся вычислительные ресурсы микрокомпьютера.
Если микрокомпьютер будет отвлекаться на другие задачи, ШИМ сигнал будет искажаться и не будет стабильным. Это не принципиально, если ШИМ применяется для управления яркостью светодиода. Но может стать неприемлемым, когда ШИМ применяется для формирования управляющего сигнала.
Например, при управлении сервоприводами программная реализация ШИМ не может стабильно удерживать сервоприводы в заданном положении. Это заметно на видео ниже.
Raspberry Pi имеет техническую возможность использовать аппаратный ресурс для генерации ШИМ.
Генерирование ШИМ сигнала с использованием аппаратных ресурсов Raspberry Pi
Проект WiringPi – это библиотека, которая содержит утилиты для простого доступа к GPIO. Она позволяет настроить аппаратные модули для специальных выходов ШИМ. Установим wiringPi:
sudo apt-get install git-core
git clone git://git.drogon.net/wiringPi
cd wiringPi
./build
cd ..
Подключим светодиод к GPIO18 как указано на схеме:
Первый выход ШИМ заведен на GPIO18, другие каналы ШИМ задействованы на аудио-выходе. Выполним следующие команды для формирования на GPIO18 ШИМ сигнала. Настраиваем первый канал PWM (GPIO18):
gpio mode 1 pwm
Задаем скважность от 0 до 1024:
gpio pwm 1 500
Светодиод должен светиться вполсилы. Поэкспериментируйте с ШИМ. Попробуйте задать следующие значения:
gpio pwm 1 10
gpio pwm 1 1023
Выключаем ШИМ:
gpio unexport 1
или
gpio unexportall
Генерирование аппаратного ШИМ сигнала на Python
Чтобы использовать ШИМ в Python надо установили WiringPi-Python:
sudo apt-get install python-dev python-setuptools
git clone https://github.com/WiringPi/WiringPi-Python
cd WiringPi-Python
git submodule update –init
python setup.py install
cd ..
Напишем срипт pwm.py:
nano pwm.py
Текст скрипта:
import time
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)
led(0)
while 1: for dc in range(0, 1023, 5): led(dc) time.sleep(0.01) for dc in range(1024, 0, -5): led(dc) time.sleep(0.01)
Запустим его:
python ./pwm.py
Светодиод будет плавно загораться и плавно гаснуть. Аппаратная реализация ШИМ обеспечивает более стабильный результат. К сожалению аппаратный выход в Raspberry Pi только один.
Но существует еще пара методов генерирования ШИМ. Через DMA, и использование внешнего PWM контроллера (ШИМ контроллера).
Эти методы рассмотрим ниже для управления сервоприводами, поскольку на светодиодах разница не будет заметна.
Управление сервоприводом
О сервоприводах и характеристиках управляющего сигнала я писал ранее в статье Управление сервоприводом (сервомашинкой) с помощью микроконтроллера ATMega.
Обычно сервоприводы используют питание 5В. Маломощный сервопривод можно питать от Raspberry Pi. Но если привод потребляет достаточно большой ток, или Вам нужно подключить несколько сервомашинок, лучше не нагружать Raspberry Pi и использовать отдельный источник питания. Схема подключения сервопривода:
Управление сервоприводом с помощью программно сформированного ШИМ
Сначала попробуем формировать ШИМ для управления сервоприводом программно. Создадим скрипт servo.py:
nano servo.py
Текст скрипта:
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
GPIO.setup(17,GPIO.OUT)
p=GPIO.PWM(17,50)
p.start(7.5)
try: while True: p.ChangeDutyCycle(7.5) print “Left” time.sleep(1) p.ChangeDutyCycle(12.5) print “Center” time.sleep(1) p.ChangeDutyCycle(2.5) print “Right” time.sleep(1)
except KeyboardInterrupt: p.stop() GPIO.cleanup()
Запустим servo.py:
python ./servo.py
Я намеренно вставил функции print в код. Наличие этих функций оказывает описанную ранее проблему нестабильности программно сформированного ШИМ. Сервомашинка НЕ фиксируется в заданном положении и дергается. Если удалить инструкции print, проблема уменьшается или вообще исчезает.
Управление сервоприводом с помощью ШИМ, сформированного через DMA
Устанавливаем RPIO:
apt-get install python-setuptools
easy_install -U RPIO
Создаем срипт servo_dma.py:
nano ./servo_dma.py
Текст скрипта:
import time
from RPIO import PWM
servo = PWM.Servo()
# Set servo on GPIO17 to 900.s (0.9ms)
servo.set_servo(17, 900)
# Set servo on GPIO17 to 2000.s (2.0ms)
#servo.set_servo(17, 2000)
try: while True: servo.set_servo(17, 750) print “Left” time.sleep(1) servo.set_servo(17, 1500) print “Center” time.sleep(1) print “Right” servo.set_servo(17, 2500) time.sleep(1)
except KeyboardInterrupt: # Clear servo on GPIO17 servo.stop_servo(17)
Запустим его
python ./servo_dma.py
Теперь сервопривод работает стабильно. То есть, для управления сервоприводами о программном ШИМ желательно вообще забыть.
Подробности использования RPIO читайте здесь: http://pythonhosted.org/RPIO/pwm_py.html#examples
На этом видео видно разницу между различными способами генерирования ШИМ сигнала:
Servoblaster
Существует проект Servoblaster, который тоже работает через DMA. С помощью него можно управлять до 8 сервоприводами. Servoblaster устанавливается как демон и позволяет управлять сервоприводами через файлы устройств.
То есть, управлять сервами можно через файловую систему.
Это позволяет управлять сервами, используя любой язык программирования или из командной строки и не требует установки дополнительных модулей таких как RPIO, который мы устанавливали ранее для Python.
Установим Servoblaster:
git clone http://github.com/richardghirst/PiBits.git
cd PiBits/ServoBlaster/user
make
sudo make install
Проверяем установился ли Servoblaster корректно:
ls /dev | grep servoblaster
Должны увидеть:
servoblaster
servoblaster-cfg
Можно просмотреть конфиг Servoblaster-а:
cat /dev/servoblaster-cfg
Выводы на которые можно подключать сервоприводы приведены в следующей таблице:
Servo numberGPIO numberPin in P1 header4 | P1-7 | |
1 | 17 | P1-11 |
2 | 18 | P1-12 |
3 | 21/27 * | P1-13 |
4 | 22 | P1-15 |
5 | 23 | P1-16 |
6 | 24 | P1-18 |
7 | 25 | P1-22 |
*– RaspberryPi B и RaspberryPi B Revision 2 имеют разницу в распиновке. Подробнее здесь: http://www.avislab.com/blog/raspberry-pi-install/
Допустимые значения положения зависят от вашего сервопривода. В большинстве случаев они лежат в диапазоне от 80 до 249. Напишем следующий срипт для управления сервоприводом:
nano ./servo_blaster.py
Текст скрипта:
import time
# Servo Channel 1 => GPIO 17
servoChannel = 1
def setServo(servoChannel, position): servoStr =”%u=%u
” % (servoChannel, position) with open(“/dev/servoblaster”, “wb”) as f: f.write(servoStr)
if __name__ == '__main__': val = 50 direction = 1 while True: #print val setServo(servoChannel, val) time.sleep(.01) if val == 249: direction -= 1 elif val == 50: direction = 1 val += direction
Запустим его:
python ./servo_blaster.py
Есть одна особенность Servoblaster. Пока он запущен, он занимает 8 указанных в таблице выходов и под другие цели Вы их уже не сможете задействовать. Попробуйте запустить ранее написанные скрипты:
python ./servo.py
python ./servo_dma.py
Сервомашинка не работает как следует.
Попробуем остановить демон Servoblaster и повторить попытку. Останавливаем Servoblaster с помощью команды:
sudo killall servod
Проверяем нет теперь servod в запущенных процессах:
ps ax | grep servod
Повторяем запуск скриптов:
python ./servo.py
python ./servo_dma.py
Все работает как следует. Запускаем servod командой:
/usr/local/sbin/servod –idle-timeout=2000
Возникает вопрос: а что делать, если надо задействовать лишь несколько каналов, а не все 8? Servoblaster можно конфигурировать благодаря следующим опциям:
–pcm tells servod to use PCM rather than PWM hardware to implement delays –idle-timeout=Nms tells servod to stop sending servo pulses for a given output N milliseconds after the last update –cycle-time=Nus Control pulse cycle time in microseconds, default 20000us –step-size=Nus Pulse width increment step size in microseconds, default 10us –min={N|Nus|N%} specifies the minimum allowed pulse width, default 50 steps or 500us –max={N|Nus|N%} specifies the maximum allowed pulse width, default 250 steps or 2500us –invert Inverts outputs –dma-chan=N tells servod which dma channel to use, default 14 –p1pins= tells servod which pins on the P1 header to use
–p5pins= tells servod which pins on the P5 header to use
Источник: http://www.avislab.com/blog/raspberry-pi-pwm_ru/
Шим на raspberry pi – популярная робототехника
Как я уже писал, в рамках проекта РоботКласс мы создали учебного мобильного робота на базе микрокомпьютера Raspberry PI. Как и в любом подобном проекте, для управления двигателями здесь используется метод ШИМ.
Мои изыскания касательно ШИМ для Raspberry PI я и приведу в этой короткой статье.Самый простой способ получить ШИМ на выходе GPIO – это программный генератор импульсов. Данный метод хорош, если не хочется разбираться с установкой каких-либо драйверов и разного софта на Linux.
Достаточно просто написать цикл, который будет каждые N миллисекунд выдавать на нужный GPIO импульс требуемой ширины. Представителем именно такой простой реализации является python-модуль pizypwm. Скачать его можно на гитхабе: https://github.
com/aboudou/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 и вообще программных ШИМ? А в том, что этот самый программный генератор импульсов ест ваш вычислительный ресурс. Другими словами, цикл генерации будет соперничать с прочим кодом, от чего будет страдать и стабильность ШИМ и стабильность выполнения всего остального. Нестабильность ШИМ может выражаться, например, в дергании сервоприводов.
Чтобы минимизировать влияние генератора ШИМ на выполнение основного управляющего кода, нужно передать его функции аппаратной части. Для этого, можно использовать встроенный в Raspberry PI аппаратный генератор, доступ к которому осуществляется, например, через библиотеку wiringpi. Сама библиотека для Python и инструкция по её установке есть на гитхабе:
https://github.com/WiringPi/WiringPi-Python
К сожалению, Raspberry PI имеет только один GPIO вывод с поддержкой ШИМ. Номер этого вывода 12 в нотации GPIO, и 18 – в нотации BCM (я обычно пользуюсь BCM). Ниже представлена простая программа, которая с помощью wiringpi инициализирует ШИМ на 18-м выводе, и устанавливает на нем сигнал со скважностью 0,5. Такой сигнал должен зажечь светодиод примерно на 50% яркости.
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)
Другой вариант реализации ШИМ позволяет использовать до 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:
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.
Источник: http://www.poprobot.ru/home/raspberrypi-pwm
Программный ШИМ — DRIVE2
Здесь я покажу, как настоящие зад██ты программисты делают программный ШИМ.
Обычно, чтобы сделать широтно-импульсную модуляцию на каком-либо порту, когда аппаратный ШИМ по той, или иной причине недоступен, используют один из двух подходов:
1.
Шим в циклеТут подход предельно прост: на выходе устанавливается высокий уровень, затем запускается цикл, часто пустой, выдерживающий паузу высокого уровня, затем на порту устанавливается низкий уровень и ещё одна пауза, для удержания низкого уровня. Что-то вроде этого:for(;;) {set_port_high();for (int i = 0; i < duty_cycle; i++) {}set_port_low();for (int i = duty_cycle; i < cycle_len; i++) {}}
Плюс такого подхода, что скорость работы ШИМа достаточно высока, поскольку каждая итерация в цикле ожидания занимает 4-6 тактов, то можно добиться достаточно высокой частоты ШИМ.
Минусы очевидны: программа не может производить никаких действий, пока выводится ШИМ. Если требуется выводить ШИМ на несколько каналов, это усложняет циклы, и снижает скорость. Если требуется совершать посторонние действия в цикле, это повлияет на точность установки ШИМ. Трудно точно предсказать частоту работы ШИМ, которая сильно зависит от того, как подобный код будет скомпилирован.
2. Шим в прерыванииЗдесь подход такой: настраивается периодически срабатывающее прерывание, например, по переполнению таймера, или по сравнению, если таймер работает в режиме CTC.
В прерывании ведётся увеличение счётчика, в зависимости от значения которого, устанавливается тот или иной уровень на ШИМ-выходе.
Например так:ISR(TIMER0_COMPA_vect) {pwm_counter++;if (pwm_conter >= cycle_len) pwm_counter = 0;if (pwm_counter < duty_cycle1) set_port1_high(); else set_port1_low();if (pwm_counter < duty_cycle2) set_port2_high(); else set_port2_low();}
Плюсы — т.к. это прерывание, то оно вызывается в строго предсказуемые моменты времени, значит частота ШИМ известна заранее и достигается большая точность установки ШИМ. Основное ПО может свободно делать свои дела.
Минусы — на вход в обработчик прерывания и выход из него тратится значительная часть процессорного времени. Работа прерывания может занимать более 60 тактов процессора. Если вызов прерывания происходит слишком часто, то это оставляет мало времени на работу основной части ПО:
В результате скорость ШИМ на несколько порядков ниже частоты работы ядра. Например, при частоте 9,6 МГц, и 7 битном ШИМ (счёт от 0 до 127), скорость ШИМ будет меньше 1 кГц.
Оптимизируем!Одна из очевидных оптимизаций, это не вызывать прерывание в те моменты, когда изменение уровня на порту не предполагается.
Например, можно запустить таймер, где один цикл таймера будет соответствовать одному циклу ШИМ, в прерываниях по переполнению и по сравнению менять соответствующим образом уровни на выводах.
Другой вариант, запускать прерывание по сравнению и при каждом срабатывание перенастраивать регистр сравнения на следующее время срабатывания.
В таком подходе можно поднять частоту ШИМ, т.к. время между прерываниями уже не так важно, даже если два подряд срабатывающих прерывания совсем не оставят времени для основного ПО, то в периоды паузы ПО успеет отыграться. Однако, обработчик прерывания должен успеть войти, запланировать следующий запуск и выйти. Чем больше выходов задействовано, тем сложнее получается обработчик и время его работы больше. В любом случае, разрешающая способность такого ШИМ, относительно скорости ядра, остаётся сравнительно небольшой.
Поскольку большая часть времени тратится на запуск и покидание обработчика прерывания, то очевидным подходом будет не выходить из обработчика, если рядом ожидается очередное действие.
Но это не упрощает процедуру планирования следующего запуска, а значит всё равно разрешающая способность остаётся довольно низкой.
Но! Можно заметить, что изменения логических уровней на портах и запуски обработчика происходят в определённые моменты цикла таймера. И от цикла к циклу они не изменяются. А значит, их можно рассчитать один раз заранее.
Планируем заранее!Можно заметить, что в цикле ШИМ с N выводами происходит не более N+1 действий: первое действие устанавливает высокие уровни на всех выводах, а последующие N действий устанавливают низкие уровни на тех или иных выводах.Иначе говоря, мы создаём микрокод — массив из описаний действий.
Каждое из действий это:1) перечень логических уровней, которые нужно вывести на порт;2) значение таймера, на которое нужно настроиться для следующего действия.
Зная, как много времени требуется на запуск прерывания, можно просчитать заранее: нужно ли настраиваться на вызов следующего прерывания, или же дождаться нужного значения таймера не выходя из прерывания. Поэтом добавим ещё флаг:
3) признак, что значение таймера нужно ожидать, не выходя из прерывания.
Наконец, мы хотим, чтобы выдаваемые значения ШИМ можно было менять. Но чего точно не хотим, чтобы они менялись посреди цикла ШИМ, или начали выполняться в момент построения микрокода.
Для этого у нас будет два массива с микрокодом: в одном содержится микрокод, выполняющийся в данный момент, а другой мы используем для подготовки нового микрокода.Осталось обеспечить переключение микрокода в безопасные моменты.
У нас есть переменная, которая хранит указатель на следующую операцию, которую нужно выполнить. Заведём ещё одну, показывающую, на какой из двух массивов с микрокодом следует переключиться, когда исполнение текущего дойдёт до конца.
Последнюю операцию в микрокоде пометим специальным образом, показывающим, нужно ли брать адрес очередной операции из текущего микрокода, или же адрес следующей операции нужно прочитать из этой переменной.Для этого заведём специальный флаг:
4) признак, что это не последняя операция в микрокоде (т.е. команда продолжать выполнять текущий микрокод)
ПримерчикТут два примера для ATtiny13A. Один — простая демонстрация с использованием 5 ШИМ выводов. Другой отображает температуру на RGB светодиоде. Замер температуры происходит путём сравнения скоростей осцилляторов системного генератора и сторожевого таймера.
Исходники тут:
aterlux.ru/files/SoftPWM.zip (5 ШИМ выводов)
aterlux.ru/files/SoftPWMthermo.zip (замер температуры сравнением скоростей осцилляторов)
Не буду расписывать детали реализации, все комментарии есть в исходниках. Расскажу только то что получилось:1) до 6 ШИМ выходов, с разрешающей способностью в 16 тактов процессора. Т.е. между двумя изменениями уровней может быть всего 16 тактов процессора.2) ШИМ в 128 шагов. Т.е.
вывод значения 0 – «постоянный низкий уровень», 128 – «постоянный высокий уровень». Т.е. частота 1 / 2048 от частоты процессора, и при скорости 9,6 МГц это будет 4,6 кГц.3) Смена уровней на выводах, при одинаковых значениях коэффициентов заполнения, происходит строго одновременно.
Можно также настроить использование нескольких выводов от одного коэффициента, при этом настроить инверсию на некоторые, и добиться работы в противофазе.
4) Можно настроить управление регистром не PORTB, а DDRB, тогда будет работа ШИМ в режиме «открытого стока», как это сделано в примере с измерением температуры.
Использование библиотечкиВ библиотеке три функции:
swpwm_start() — запускает ШИМ.
swpwm_stop() — останавливает ШИМ.
swpwm_update() — компилирует заданные значения коэффициентов заполнения в микрокод.
Для указания коэффициентов заполнения есть массив swpwm[].Т.е. принцип работы такой:Запускаем swpwm_start(), задаём коэффициенты, записывая значения в swpwm[] и применяем их вызовом swpwm_update().
Всё остальное, в исходном коде.
Источник: https://www.drive2.ru/b/484782649219481995/