sonyps4.ru

Ардуино изменение частоты шим выводов. Схема для демонстрации широтно-импульсной модуляции в Arduino

И попробуем выполнить новую задачу. Думаю, что все видели новогодние витринные гирлянды, в которых плавно мигают светодиоды. Допустим, что мы хотим сделать нечто подобное.
Мы уже рассматривали функцию digitalWrite() и знаем, что значение, которое она записывает, может быть двух вариантов - высокий или низкий уровень. В данном случае нам поможет функция analogWrite(). "Формулировки" функций различаются только начальными приставками, поэтому их легко запомнить.

Функция analogWrite(), так же как и digitalWrite(), содержит в скобках два аргумента и работает по тому же словесному принципу: "куда, что". Главным различием является возможность записи широкого диапазона значений вместо привычного LOW или HIGH. Это и позволит нам регулировать яркость светодиода. Главное замечание, которое необходимо учитывать, это то, что данная функция работает только на определенных контактах. Эти контакты обозначены символом "~". Этот символ означает, что это PWM-контакт. PWM (pulse-width modulation) звучит по-русски как ШИМ (широтно-импульсная модуляция). Принцип работы основан на изменении длительности импульса. Графически это можно изобразить так:

Давайте попробуем разобраться как это работает, рассмотрев простой пример. Для этого необходимо подключить светодиод к PWM-контакту через резистор номиналом 150 Ом и "зашить" в Arduino простенькую программу. Схема подключения и код скетча представлены ниже:


void setup()
{
pinMode(led,OUTPUT);
}

void loop()
{
for(int i=0; i<=255; i++)
{
analogWrite(led,i);
delay(10);
}
for(int i=255; i>=0; i--)
{
analogWrite(led,i);
delay(10);
}
}


Думаю, что в целом код понятен, но необходимо уделить немного внимания циклу for(). Существует такое понятие как разрешение. Поскольку мы работаем с 8-битным разрешением (это будет рассмотрено несколько позднее), то минимальному значению будет соответствовать 0, а максимальному - 255. В конце каждой итерации мы установили временную задержку в 10мс.

Давайте вернемся к схеме из предыдущего урока и попробуем сделать аналогичную гирлянду с использованием функции analogWrite().


int buttonPin = 2;
int pins = {3,5,6,9,10,11};

boolean lastButton = LOW;
boolean currentButton = LOW;
boolean enable = false;

void setup()
{
pinMode(buttonPin, INPUT);
for(int mode = 0; mode <= 5; mode++) pinMode(pins, OUTPUT);
}

boolean debounce(boolean last)
{
boolean current = digitalRead(buttonPin);
if(last != current)
{
delay(5);
current = digitalRead(buttonPin);
}
return current;
}

void loop()
{
currentButton = debounce(lastButton);
if(lastButton == LOW && currentButton == HIGH)
{
enable = !enable;
}

If(enable == true)
{
for (int i=0; i<=5; i++)
{
for (int brightness = 0; brightness <= 255; brightness++)
{
delay(1);
}
delay(40);
}
for (int i=0; i<=5; i++)
{
for (int brightness = 255; brightness >= 0; brightness--)
{
analogWrite(pins[i], brightness);
delay(1);
}
delay(40);
}
}

If(enable == false)
{
for(int i = 0; i <= 5; i++) digitalWrite(pins[i], LOW);
}

LastButton = currentButton;
}


Визуально скетч стал несколько сложнее. На самом деле здесь все просто и давайте в этом разберемся. Нам необходимо идентифицировать все подключенные светодиоды, но вместо привычного int led мы используем массив, каждый элемент которого является PWM-контактом на Arduino. В теле функции void setup() мы тоже поступили хитрым образом. "Перечислять" все контакты мы доверили циклу for(), с каждой итерацией которого производится конфигурация соответствующего контакта на OUTPUT. Переходим к функции void loop(). Функция debounce() и начальное условие if() остается без изменений. У нас по-прежнему идет проверка уровней двух переменных: предыдущее значение (изначально LOW) и текущее состояние кнопки. При выполнении этих условий значение переменной enable инвертируется. Учитывая это, мы добавили еще два простых условия if(). Если enable = true, то гирлянда включается, плавностью "перетекания" которой управляет цикл for(). Если же enable = false, то все светодиоды выключены. По окончанию условий переменная lastButton принимает текущее состояние кнопки.
Тестируя нашу программу, мы заметили, что все работает не должным образом. Помните, в прошлом уроке мы сделали поправку, что при большом значении временной задержки кнопка срабатывает по её истечению? В прошлом примере, при включенной гирлянде, суммарная задержка в теле функции void loop() составляла 85мс. Это давало нам возможность успеть "попасть" в определенной отрезок времени. В данном скетче, при том же условии, задержка отличается в несколько раз. Возможно, при желании выключить гирлянду напрашивается слово "прервать". Это и будет являться решением данной задачи!

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

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

ШИМ

Гениально определение ШИМ сформулировано в Википедии , поэтому я просто скопипащу его оттуда: "ШИМ - приближение желаемого сигнала (многоуровневого или непрерывного) к действительным бинарным сигналам (с двумя уровнями - вкл / выкл ), так, что, в среднем, за некоторый отрезок времени, их значения равны. <...> ШИМ есть импульсный сигнал постоянной частоты и переменной скважности, то есть отношения периода следования импульса к его длительности. С помощью задания скважности (длительности импульсов) можно менять среднее напряжение на выходе ШИМ . "


Теперь разберемся, что это значит. Пусть есть обычный такой прямоугольный сигнал:




Он имеет фиксированную частоту и скважность 50%. Это означает, что половину периода напряжение максимально, а другую половину оно равно нулю. Проинтегрировав этот сигнал за период, мы увидим, что его энергия равна половине максимальной. Это будет эквивалентно тому, как если бы мы все время подавали половину напряжения.


Если у нас максимальное напряжение равно 5 В, то напряжение, получаемое на выходе ШИМ равно скважность умножить на 5 В (и делить на 100% чтобы формал-nazi не привязывались):


Arduino позволяет записать на ШИМ-выход значение от 0 до 255, а это значит, что мы можем получить напряжение с дискретностью примерно 20 мВ.


Трехцветный светодиод

Вот он, четырехногий красавец:


Самая длинная нога - это общий анод, а все остальные - это катоды, каждый отвечает за свой цвет: (смотрим на рисунок) самая нижняя - красный, вторая сверху - зеленый, самая верхняя - синий.

Если подать на длинную ногу +5В, а на все остальные 0В, то получится белый свет (умоляю, предохраняйтесь - ставьте ограничивающие резисторы!). Насколько он белый, можно судить по следующему видео:


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

ШИМ на Arduino

Частота ШИМ на Arduino - примерно 490 Гц. На плате Arduino UNO выводы, которые могут быть использованы для ШИМ - 3,5,6, 9, 10 и 11. На плате к этому есть подсказка - шелкографией перед номерами ШИМ-выводов есть тильда или диез.

Нет ничего проще, чем управлять ШИМ на Arduino! Для этого используется одна единственная функция analogWrite(pin, value) , где pin - номер вывода, а value - значение от 0 до 255. При этом ничего не надо писать в void setup() !

Подробнее про это на английском языке можно почитать и .

Совсем немного работаем

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

Соберем незамысловатую схему:


И напишем незамысловатый код:

//обзываем выводы соответственно цвету
int REDpin = 9;
int GREENpin = 10;
int BLUEpin = 11;

void setup (){}

void loop (){
for (int value = 0 ; value <= 255; value +=1) {
//яркость красного уменьшается
analogWrite (REDpin, value);
//яркость зеленого увеличивается
analogWrite (GREENpin, 255-value);
//синий не горит
analogWrite (BLUEpin, 255);
//пауза
delay (30);
}

for (int value = 0 ; value <= 255; value +=1) {
//красный не горит
analogWrite (REDpin, 255);
//яркость зеленого уменьшается
analogWrite (GREENpin, value);
//яркость синего увеличивается
analogWrite (BLUEpin, 255-value);
//пауза
delay (30);
}

for (int value = 0 ; value <= 255; value +=1) {
//яркость красного увеличивается
analogWrite (REDpin, 255-value);
//зеленый не горит
analogWrite (GREENpin, 255);
//яркость синего уменьшается
analogWrite (BLUEpin, value);
//пауза
delay (30);
}
}

Месяца 3 назад, как и многие горе-электроники, купил себе на мой тогдашний взгляд самую навороченную микропроцессорную плату из семейства Arduino, а именно Seeeduino Mega, на базе процессора Atmega1280. Побаловавшись всласть вращающимся сервоприводом и моргающим светодиодом, встал вопрос: «зачем же я её купил?».

Я работаю одним из ведущих конструкторов на одном крупном военном Зеленоградском заводе, и в данный момент веду проект по разработке метрологического средства измерения. В данной задаче существует бесконечное множество проблем, которые требуют индивидуального решения. Одной из таких задач является управление шаговым двигателем без шумов и с шагом не 1.8 градуса, как сказано в документации шагового двигателя, а до 0.0001 градуса. Казалось бы, задача сложна и нерешабельна, но, повозившись немного со схемами управления, пришёл к выводу, что всё реально и возможно. Требуется только генерация двух сигналов специфичной формы и со сдвигом фаз и частотой изменения напряжения до 1 МГц. (Подробное исследование шагового мотора и раскрытие всех тайн управления напишу в следующей статье) Сразу же в голове стали появляться проблески надежды, что я не зря потратил 1500 рублей на свою красненькую Seeeduino, и я, набравшись энтузиазма, начал разбираться.

Первоначальный ужас:

Подключив микропроцессорную плату к осцилографу, и написав цикл digitalWrite(HIGH), и ниже digitalWrite(LOW), на осцилографе обнаружил довольно унылый меандр с частотой 50Гц. Это кошмар. Это крах, подумал я, на фоне требуемых 1Мгц.
Далее, через осцилограф, я изучил еще несколько скоростей выполнения:
AnalogRead() - скорость выполнения 110 мкс.
AnalogWrite() - 2000 мкс
SerialPrintLn() - при скорости 9600 около 250мкс, а при максимальной скорости около 3мкс.
DigitalWrite() - 1800мкс
DigitalRead() - 1900мкс

На этом я, всплакнув, чуть не выкинул свою Seeeduino. Но не тут-то было!

Глаза боятся, руки делают!

Не буду рассказывать свои душевные муки и описывать три долгих дня изучения, лучше сразу скажу всё как есть!
Подняв всю возможную документацию на Arduino и на процессор Atmega1280, исследовав опыт зарубежных коллег , хочу предложить несколько советов, как заменять чтение/запись:
Улучшаем AnalogRead()
#define FASTADC 1

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

Void setup() {
int start ;
int i ;

#if FASTADC
// set prescale to 16
sbi(ADCSRA,ADPS2) ;
cbi(ADCSRA,ADPS1) ;
cbi(ADCSRA,ADPS0) ;
#endif

Serial.begin(9600) ;
Serial.print("ADCTEST: ") ;
start = millis() ;
for (i = 0 ; i < 30000 ; i++)
analogRead(0) ;
Serial.print(millis() - start) ;
Serial.println(" msec (30000 calls)") ;
}

Void loop() {
}

Результат: скорость 18,2 мкс против бывших 110 мкс .
Кстати, максимальная скорость АЦП Атмеги как раз 16мкс. Как вариант - использовать другую микросхему, заточенную именно под АЦП, которая позволит уменьшить скорость до 0,2мкс (читать ниже, почему)

Улучшаем digitalWrite()
Каждая Arduino/Seeeduino/Feduino/Orduino/прочаяduino имеет порты. Каждый порт - 8 бит, которые сначала надо настроить на запись. Например, на моей Seeeduino PORTA - c 22 по 30 ножку. Теперь всё просто. Управляем с 22 по 30 ножки с помощью функций
PORTA=B00001010 (битовая, ножки 23 и 25 - HIGH)
или
PORTA=10 (десятичная, всё так же)
Результат = 0,2мкс против 1800мкс , которые достигаются обычным digitalWrite()
Улучшаем digitalRead()
Практически то же самое, что и в улучшении с digitalWrite(), но теперь настраиваем ножки на INPUT, и используем, например:
if (PINA==B00000010) {...} (если на ножке 23 присутствует HIGH, а на 22 и 24-30 присутствует LOW)
Результат выполнения этого if() - 0.2мкс против 1900мкс , которые достигаются обычным digitalRead()
Улучшаем ШИМ модулятор, или analogWrite()
Итак, есть данные, что digitalRead() исполняется 0,2мкс, и ШИМ модулятор имеет дискретность 8 разрядов, минимальное время переключения ШИМ 51,2мкс против 2000 мкс.
Используем следующий код:
int PWM_time=32; //Число, которое мы как бы хотим записать в analogWrite(PIN, 32)
for (int k=0;kFor (int k=0;k<256-PWM_time) PORTA=B00000000;

Вот и получили ШИМ с частотой 19кГц против 50Гц.

Подведём итоги

digitalWrite() было 1800мкс , стало 0,2мкс
digitalRead() было 1900мкс , стало 0,2мкс
analogWrite() было 2000мкс , стало 51,2мкс
analogRead() было 110мкс , стало 18,2мкс , а можно до 0,2мкс

Широтно-импульсная модуляция (pulse width modulation, PWM) часто используется в цифровой технике. В Arduino есть специальная функция для формирования этого сигнала.
Временная диаграмма ШИМ-сигнала:

analogWrite

Скважность это отношение периода следования импульсов к их длительности. На рисунке – чем больше мгновенное значение управляющего сигнала, тем меньше скважность импульсов ШИМ-сигнала. Этот способ модуляции широко применяется в технике ввиду того, что позволяет произвольное среднедействующее значение средствами цифровых устройств. Этот способ применим в тех случаях, когда необходимо, например, плавно регулировать яркость свечения лампы, температуры нагревателя, скорость вращения мотора постоянного тока и так далее.
Arduino имеет встроенную функцию для формирования ШИМ-сигнала – analogWrite(pin,value) . У этой функции два параметра: pin – номер вывода, на котором формируется сигнал; value – относительное значение скважности импульсов.
Value может принимать значения от 0 до 255. Причем максимальная скважность получается при 0 (а среднедействующее значение минимально), а минимальная при 255 (среднедействующее максимально). Функция analogWrite может работать только с выводами 9, 10 и 11 (на нашей плате с микроконтроллером Atmega8). Соответственно, только такие значения может принимать переменная pin .
Если использовать эту функцию для управления другим выводом (0-8, 12, 13), то при значения скважности от 0 до 127 на выводе будет низкий уровень напряжения, а при 128-255 – высокий.

Подготовка к работе

Мы будем очень рады, если вы поддержите наш ресурс и посетите магазин наших товаров .



Загрузка...