GPIO для чайников (Часть 4)

  • Автор
-Почему в кинотеатрах так медленно гаснет свет?
-Потому, что киномеханик очень медленно вынимает вилку из розетки.


Знакомимся с широтно-импульсной модуляцией.

Ранее мы научились с помощью изменения состояния порта GPIO управлять светодиодом. Мы научились управлять длительностью и частотой импульсов, благодаря чему получили различные световые эффекты. Убедились в том, что если изменять состояние порта со звуковой частотой, то можно получать различные
звуки, освоили частотную модуляцию…

А что получится, если мы будем изменять уровень порта со звуковой частотой, но вместо динамика подключим нашего старого подопытного друга - светодиод?

Проведите эксперимент. Измените нашу программу blink.c так, чтобы светодиод загорался и гас 200 раз в секунду, с частотой 200 Гц. Для этого достаточно изменить параметры функции delay(). Чтобы узнать, какие задержки нужно ввести, достаточно рассчитать период колебания Т. Т=1/f. А т.к. f у нас равна 200Гц, то Т=1/200=0,005 секунды, или 5 миллисекунд. Вот за эти 5 миллисекунд мы должны успеть включить светодиод и выключить его 1 раз. Так, как 5 на 2 не делится нацело, примем время свечения светодиода в 2 мС, а время несвечения в 3мС. 2+3=5, т.е. полный период одного колебания так и останется 5мС. Теперь изменим программу: заменим delay(500), на delay(2) и delay(3) для горящего и не горящего
светодиода соответственно.

Скомпилируем программу и запустим. Если у вас всё ещё в схеме установлен динамик, то вы услышите низкий звук, а если динамик заменить светодиодом, то вы увидите непрерывно горящий светодиод. На самом деле светодиод конечно моргает, но делает он это на столько быстро, что глаз уже не замечает это моргание и воспринимает
его как непрерывное свечение. Но светит диод вроде бы не так ярко, как он у нас горел раньше. Можете для сравнения запустить нашу самую первую программу, где светодиод горел постоянно, и сравнить яркость светодиода в обоих случаях. Давайте разберёмся, почему так происходит и как это можно использовать.

Помните, в самой первой части мы рассчитывали токоограничивающий резистор для питания светодиода? Мы знаем, что у светодиода есть рабочий ток, при котором он светится наиболее ярко. Если этот ток уменьшать, то яркость светодиода будет тоже уменьшаться. А когда мы начинаем быстро включать и выключать светодиод, то
его яркость свечения становится зависимой от среднего тока (Iср) за период колебания. Для импульсного (П-образного) сигнала, который мы генерируем на выходе порта GPIO, средний ток будет пропорционален отношению t1 к t2. А именно: Iср=Iн x t1/t2, где Iн- номинальный ток светодиода, который мы благодаря резистору установили в 10мА. При номинальном токе светодиод светится наиболее ярко. А в нашем случае Iср=10 х 2/3 = 6,7мА. Мы видим, что ток стал меньше, поэтому и светодиод стал гореть менее ярко. В этой формуле отношение t1/t2 называется коэффициентом заполнения импульса D.

Чем этот коэффициент больше, тем больше будет среднее значение тока.  Мы можем изменять этот коэффициент от 0 до 1, или от 0% до 100%. А значит, мы можем и менять средний ток в этих пределах. Получается, что таким способом мы можем регулировать яркость светодиода от максимальной, до полностью выключенного! И хотя напряжение на выводе нашего порта по-прежнему может быть лишь либо +3,3в, либо 0в, ток в нашей схеме может изменяться. И изменением этого тока мы легко можем управлять нашей Малинкой. Вот такой способ управления и называется Широтно-Импульсной модуляцией, или просто ШИМ. В английском языке это звучит как PWM, или Pulse-Width Modulation. ШИМ, это импульсный сигнал постоянной частоты с переменным коэффициентом заполнения. Используется и такое определение, как Импульсный сигнал постоянной частоты с переменной скважностью. Скважность S, это величина обратная коэффициенту заполнения и характеризует отношение периода импульса T к его длительности t1.
S=T/t1 = 1/D.


Ну а нам, для закрепления наших знаний, остаётся написать программку, которая будет плавно зажигать и гасить наш светодиод. Процесс изменения яркости свечения называется диммированием.

У меня получилось вот так:
//
dimmer.c
// Программа плавно изменяет яркость светодиода
// Светодиод подключён к порту Р1_03#include <bcm2835.h>#define PIN RPI_GPIO_P1_03
int main()
{   
if (!bcm2835_init()) return 1;      

bcm2835_gpio_fsel(PIN,BCM2835_GPIO_FSEL_OUTP);  
 //Устанавливаем порт Р1_03 на выводunsigned int t_on, t_off;
// t_on продолжительность включённого состояния= t1, а t_off- выключенного =t2

int d = 100, i, j, flag=0; // d- коэффициент заполнения в процентах, i и j, вспомогательные переменные для организации циклов, flag- если =0 светодиод затухает, если =1 разгорается

int a=10; // количество полных рабочих циклов
while (a)
{
for (j=100; j!=0; j--) //изменяем коэффициент заполнения от 100% до 0%
{
t_on=50*d; //находим t1
t_off=50*(100-d);  //находим t2
if (flag==0) d=d-1; // если светодиод затухает, уменьшаем коэффициент заполнения
if (flag==1) d=d+1; // если светодиод разгорается, увеличиваем коэффициент заполнения

for (i=10; i!=0; i--) //передаём 10 импульсов на светодиод с рассчитанными параметрами t1 и t2        
  {             
bcm2835_gpio_write(PIN, LOW);             
delayMicroseconds(t_on);             
bcm2835_gpio_write(PIN, HIGH);             
delayMicroseconds(t_off);         
}

if (d==0) flag=1; // если светодиод выключен, начинаем его включать
if (d==100) flag=0; // если светодиод достиг максимума свечения, начинаем его гасить 
}

a--;
}
return (!bcm2835_close ()); // Выход из программы



Сохраняем программу под именем dimmer.c, компилируем и запускаем.

Как видите, теперь наш светодиод медленно гаснет и медленно разгорается. Вот так и работает ШИМ. Широтно-импульсная модуляция используется во многих областях. Это и управление яркостью свечения ламп и светодиодов, управление сервоприводами, регулирование напряжения в импульсных источниках питания (которые например, стоят в вашем компьютере), в цифро-аналоговых и аналого-цифровых преобразователях и т.д. К стати, если вернуться к нашей схеме с динамиком, то при помощи ШИМ можно управлять громкостью сигнала, а изменяя частоту- его тоном.

Помните старый анекдот из предисловия к этой части, о киномеханике, медленно вытягивающим вилку из розетки? Теперь то мы знаем, что этому киномеханику, чтобы плавно погасить свет, нужно наоборот очень быстро вставлять и вытаскивать вилку из розетки. 

На этом мы и закончим данный урок. Остаётся лишь добавить, что ШИМ настолько часто используется в различных приложениях, что производители процессорного оборудования часто встраивают ШИМ-контроллер непосредственно в процессор. Т.е. вы процессору задаёте параметры требуемого вам сигнала, а процессор уже сам, без вашей помощи выдаёт нужный вам сигнал. При этом, нисколько не тратя программных ресурсов на генерацию этого сигнала. Bcm2835 тоже имеет встроенный аппаратный ШИМ. И этот ШИМ является альтернативной функцией порта GPIO 18, или P1-12. Чтобы воспользоваться аппаратными ШИМ мы должны установить порт P1-12 в режим ALT5 и задать процессору параметры. Но это уже совсем другая история…

Комментарии:

MrKVO7
Здравствуйте. Очень полезная информация, на русском языке такого еще не встречал. Вот только у меня вопрос. Как GPIO работает на вход? Хотелось бы узнать об этом по подробнее, желательно также с примерами. Заранее спасибо.
RomanNV5
Смотрите 2-ю часть, там про использование порта в режиме ввода.
Я не правильно выразился. Можно ли на вход подать сигнал не верхнего уровня, а на пример 1 В или 1,5 В и определит ли его RPI? И можно ли на выходе получить значение из диапазона 0-3,3 В?
Через резистивный делитель к примеру можно любое напряжение на выходе получить.

1.5В на входе должен по идее принять за лог.1
RomanNV5
GPIO- это цифровой порт, а значит он умеет оперировать только цифровыми сигналами. Он может быть либо в состоянии "1", либо в "0". Промежуточные значения он не может обработать достоверно и каждый раз вы будете получать непредсказуемый результат. Поэтому, при сопряжении устройств с различными уровнями логики, вы обязательно должны их согласовывать. Работа с GPIO ничем не отличается от работы с другими логическими устройствами. Так что смело набирайте в гугле "согласование логических уровней" и выбирайте для себя наиболее подходящий вам способ. Самый универсальный вариант, это применение специализированных микросхем- трансляторов уровней. Например в вашем случае подойдёт ADG3301, или другие из этой линейки. Можно выполнить согласование и через операционный усилитель, или даже через обыкновенный транзистор. Нам просто нужно усилить сигнал уровнем 1в, до 3,3в. Т.е. нам необходимо расчитать усилитель с коэффициентом усиления 3,3.
Обратное преобразование с 3,3в к 1в очень легко можно выполнить резистивным делителем напряжения из 2-х резисторов.
RomanNV5
Что касается второго вашего вопроса, то опять-же, в логических уровнях нет промежуточных значений, а значит на выходе нельзя получить промежуточные значения напряжений.
Для решения этой задачи применяются цифро-аналоговые преобразователи. Так же можно применить ШИМ, а к выходу порта подключить интегрирующую цепочку из резистора и конденсатора, а к её выходу- операционный усилитель. Тогда изменяя скважность импульсов, вы сможите на выходе усилителя получать любые напряжения. Без усилителя на выходе интегрирующего звена вы получите напряжение в диапазоне от 0 до 3,3в. Но работать такая схема будет лишь на высокоомной нагрузке. Поэтому в любом случае лучше поставить операционный усилитель, даже если он будет работать в режиме повторителя с коэффициентом усиления 1.

В общем все этивопросы напрямую относятся к схемотехнике и одинаково решаются для всех логических устройств, в том числе и для Raspberry.
MrKVO4
Все понял куда копать. Нашел пример с АЦП на микросхеме MCP3008 (http://learn.adafruit.com/reading-a-analog-in-and-controlling-audio-volume-with-the-raspberry-pi).
Вы говорили, что в RPI имеется встроенный аппаратный ШИМ, а встроенного АЦП или ЦАП случайно нет?
Нет. ЦАП и АЦП в нём нету.
I am mehanic1
Все гораздо проще и рациональнее можно сделать...Вот пример http://www.airspayce.com/mikem/bcm2835/pwm_8c-example.html через реализованный через библиотечный CLOCK_DIVIDER.. Можно задать любую частоту работы для устройства...надо обновится до версии bcm2835 1.30...
spectoronis
В этом примере используете 18 порт, он является плюсовым как я понял, а в 1 части со свтеодиодом был использован отрицательный порт. Вопрос такой, как узнать или где можно посмотреть сколько еще имеется на raspberry портов таких как 18?
Lamer
Странно, что никто не заметил.
"средний ток будет пропорционален отношению t1 к t2. А именно: Iср=Iн x
t1/t2, где Iн- номинальный ток светодиода, который мы благодаря
резистору установили в 10мА. При номинальном токе светодиод светится
наиболее ярко. А в нашем случае Iср=10 х 2/3 = 6,7мА."
А если я установлю время свечения 3 мс, а время несвечения 2 мс, у меня соотношение t1/t2 будет 3/2, значит ток будет в Icp=10 x 3/2 = 15 мА?  Если время несвечения уменьшать еще, то ток будет продолжать расти?
Может, все-таки, t1 на Т надо делить? Тем более тогда обратная скважности величина получится.

Для написания комментариев необходимо зарегистрироваться