ШИМ

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 #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 и задать процессору параметры.

Но это уже совсем другая история…

Тэги:

 

Автор:

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

  • MrKVO7
    01.10.2012 в 12:32 ответ

    Здравствуйте. Очень полезная информация, на русском языке такого еще не встречал. Вот только у меня вопрос. Как GPIO работает на вход? Хотелось бы узнать об этом по подробнее, желательно также с примерами. Заранее спасибо.

  • RomanNV5
    01.10.2012 в 13:00 ответ

    Смотрите 2-ю часть, там про использование порта в режиме ввода.

    • MrKVO4
      02.10.2012 в 05:19 ответ

      Я не правильно выразился. Можно ли на вход подать сигнал не верхнего уровня, а на пример 1 В или 1,5 В и определит ли его RPI? И можно ли на выходе получить значение из диапазона 0-3,3 В?

      • Mike3
        03.10.2012 в 19:34 ответ

        Через резистивный делитель к примеру можно любое напряжение на выходе получить. 1.5В на входе должен по идее принять за лог.1

  • RomanNV5
    02.10.2012 в 08:43 ответ

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

  • RomanNV5
    02.10.2012 в 08:52 ответ

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

  • MrKVO4
    02.10.2012 в 11:09 ответ

    Все понял куда копать. Нашел пример с АЦП на микросхеме MCP3008 (http://learn.adafruit.com/reading-a-analog-in-and-controlling-audio-volume-with-the-raspberry-pi). Вы говорили, что в RPI имеется встроенный аппаратный ШИМ, а встроенного АЦП или ЦАП случайно нет?

    • RomanNV5
      02.10.2012 в 12:32 ответ

      Нет. ЦАП и АЦП в нём нету.

  • I am mehanic1
    17.10.2013 в 22:38 ответ

    Все гораздо проще и рациональнее можно сделать...Вот пример http://www.airspayce.com/mikem/bcm2835/pwm_8c-example.html через реализованный через библиотечный CLOCK_DIVIDER.. Можно задать любую частоту работы для устройства...надо обновится до версии bcm2835 1.30...

  • spectoronis
    23.04.2015 в 18:12 ответ

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

    • mshock
      17.07.2017 в 08:49 ответ

      http://raspberrypi.ru/gpio_pinout_pi3/

  • Lamer
    17.09.2019 в 09:40 ответ

    Странно, что никто не заметил. "средний ток будет пропорционален отношению 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 на Т надо делить? Тем более тогда обратная скважности величина получится.

Ваш комментарий

Авторизуйтесь для отправки комментария

© Сообщество пользователей RaspberryPi 2021