Ниже рассмотрим пример использования выходов с ШИМ (PWM) для управления яркостью светодиода.
Как-то, при изучении программирования Ардуино на просторах Интернета, наткнулся на такой вопрос пользователя Николая (орфография сохранена):
Здравствуйте))) Подскажите пожалуйста новечьку, Как можно реализовать такой скечь При нажатии на кнопку светодиод плавно загорается если нажать ещё раз на кнопку то светодиод плавно тухнет…?
Итак, нам необходимо сделать так, чтобы по нажатии на кнопку, медленно повышалась яркость светодиода до максимального, а при повторном нажатии на кнопку – медленно снижалась до полного гашения. Чтобы видеть законченность процесса изменения яркости – сделал сигнализацию через встроенный светодиод на pin13, поэтому в начальных настройках его гашу.
Для реализации такой программы потребуется светодиод VD1(HL1) (любой), ограничивающий резистор R1 на 330...2000 Ом, кнопка SB1 (любая), стягивающий резистор R2 на 10...18 кОм.
Последовательное соединение светодиода VD1(HL1) и R1 подключается между контактами Ардуино pin11 и GND. Можно использовать любой другой pin, из тех, что имеют возможность выводить ШИМ (PWM) – это контакты (на Arduino UNO) 3, 5, 6, 9, 10, 11, отмеченные на плате значком тильды ~.
Предыдущая схема рабочая, но можно построить схему более оптимально и убрать лишние детали. Так, если мы будем использовать инверсную логику, это когда сигнал от кнопки будет принимать по нажатии не высокий уровень HIGH, а низкий – LOW, то мы сможем использовать подтягивающий резистор внутри микроконтроллера, задав в настройке скетча команду:
pinMode(buttonPin, INPUT_PULLUP);
Эта команда пин, сконфигурированный как вход, подтягивает к +5V, тем самым мы убираем внешний стягивающий резистор R1. Если бы мы использовали несколько пинов как вход, то таким приёмом мы бы хорошо упростили схему. Поэтому новичкам рекомендую использовать инверсную логику, когда кнопки одним выводом подсоединяются к пинам микроконтроллера, а другим выводом – к GND.
Реализуем схему 1. Кнопка одним контактом подключена к +5V, а второй контакт к стягивающему резистору R1. От их соединения идёт подключение к контакту pin2. Второй свободный вывод резистора подключен к контакту GND.
Для макетирования удобно использовать макетные панельки (Breadboard).
Ниже приведён скетч схемы 1:
/*********************************************************** * 2018-02-26 Mr.ALB Тренировка в программировании Ардуино ***********************************************************/ // Переменные с пинами подключенных устройств int buttonPin = 2;// Подключаем кнопку SB1 на 2-й pin // Подключаем светодиод на 11-й pin, можно на любой с PWM int ledPin = 11; // Переменные для хранения состояния кнопки и светодиода boolean lastButton = LOW; boolean currentButton = LOW; int ledLevel = 0; // Настройка изменения яркости int deltaLevel = 10; // Шаг изменения яркости int sign = 1; // Знак шага int step_delay = 120;// Здержка в шаге изменения яркости светодиода // Настройка сигнализатора окончания изменения яркости int max_blink = 3; // Число морганий светодиода LED_BUILTIN int blink_delay = 500;// Задержка состояния светодиода на pin13 /*********************************************************** * Настроечная функция, выполняется один раз вначале ***********************************************************/ void setup() { pinMode(buttonPin, INPUT); pinMode(ledPin, OUTPUT); pinMode(LED_BUILTIN, OUTPUT); // Потушим светодиод на pin13 (LED_BUILTIN) digitalWrite(LED_BUILTIN, LOW); } /*********************************************************** * Функция постоянно повторяется * При нажатии на кнопку, светодиод медленно увеличивает яркость * При повторном нажатии на кнопку, светодиод медленно гаснет ***********************************************************/ void loop() { currentButton = debounce(lastButton);// Опрос кнопки if(lastButton == LOW && currentButton == HIGH) { if(ledLevel == 0) sign = 1; if(ledLevel == 255) sign = -1; // Изменяем яркость светодиода fn_stepBrightness(step_delay,deltaLevel,sign); // Сигнализируем об окончании изменения fn_blink(max_blink, blink_delay); } lastButton = currentButton;// Переписываем состояние кнопки } /*********************************************************** * Функция для подавления дребезга ***********************************************************/ boolean debounce(boolean last) { boolean current = digitalRead(buttonPin);// Читаем состояние кнопки if(last != current) { delay(5); current = digitalRead(buttonPin); } return current; } /*********************************************************** * Функция сигнализации окончания цикла * изменения яркости светодиода ***********************************************************/ void fn_blink(int blink_max, int blink_delay) { for(int i = 0; i < blink_max; i++) { digitalWrite(LED_BUILTIN, HIGH); delay(blink_delay); digitalWrite(LED_BUILTIN, LOW); delay(blink_delay); } } /*********************************************************** * Функция изменения яркости светодиода ***********************************************************/ void fn_stepBrightness(int step_delay, int deltaLevel, int sign) { for(int i = 0; i < (int(256 / deltaLevel) + 1); i++) { ledLevel = ledLevel + deltaLevel * sign; if(ledLevel > 255) ledLevel = 255; if(ledLevel < 0) ledLevel = 0; delay(blink_delay); analogWrite(ledPin, ledLevel); delay(step_delay); } }
Данный пример мною подробно закоментирован и, надеюсь, всё понятно. Конечно можно делать по другому, можно удалить лишнее и код будет меньше. Тут лишь вариант управления яркостью светодиода через ШИМ (PWM).
Можно на базе этого скетча сделать разные модификации, к примеру, по нажатии на кнопку – светодиод будет увеличивать яркость, а потом так же плавно её снижать до затухания. Можно поиграться с задержками и процесс разгорания и гашения светодиода убыстрить и т.п. вариации.
/*********************************************************** * При нажатии на кнопку: * светодиод медленно увеличивает яркость, * а потом медленно гаснет ***********************************************************/ void loop() { currentButton = debounce(lastButton);// Опрос кнопки if(lastButton == LOW && currentButton == HIGH) { fn_stepBrightness(step_delay,deltaLevel,1); // Зажигается fn_stepBrightness(step_delay,deltaLevel,-1);// Тухнет // Сигнализируем об окончании изменения fn_blink(max_blink, blink_delay); } lastButton = currentButton;// Переписываем состояние кнопки }
Для расширения возможностей этого примера, можно добавить ещё кнопку и по её нажатию можно будет выбирать режим работы светодиода: 1) как в базовом варианте, 2) как в дополнительном, т.е. совместить обе модификации в одной реализации. А если кому и этого мало , то можно добавить ещё кнопочку или пару, и управлять через них задержками изменения яркости светодиода (гирлянды светодиодов, при использовании внешних источников).
Рассмотрим отличия скетча 2 от первого варианта. Основное отличие в инверсной логике, т.е. срабатывание кнопки тогда, когда на контакте пина 2 будет логическое LOW. Далее изменены типы переменных, значение которых не более 255 принимают тип byte. Для знака sign увеличения яркости или её снижения выбран тип boolean.
Эта оптимизация кода позволила уменьшить его на 76 байт. В этой небольшой программе как бы не существенно, но когда программа увеличивается, то бывает важен каждый байт, поэтому лучше сразу писать код более оптимальным, чем потом стараться его уменьшить.
/*********************************************************** 2018-02-26 Mr.ALB Тренировка в программировании Ардуино Скетч 1 2022-05-17 Mr.ALB Использование инверсной логики Скетч 2 ПОДКЛЮЧЕНИЕ: R2 - pin11 HL1 A - R2 HL1 k - GND SB1 - pin2 и GND 2022-05-17 Скетч 1 использует 1660 байт 2022-05-17 Скетч 2 использует 1584 байт разница: 1660 - 1584 = 76 ***********************************************************/ // Определение отладки, если не нужно, то закомментировать // строку ниже //#define DEBUG // Отладка #define BT_PIN 2 // Подключаем кнопку SB1 на 2-й pin #define LED_PIN 11 // Подключаем светодиод на 11-й pin, ; // можно на любой с PWM #define MAX_BLINK 3 // Число морганий светодиода LED_BUILTIN // Переменные для хранения состояния кнопки и светодиода bool lastButton = HIGH; bool currentButton = HIGH; int ledLevel = 0; // Настройка изменения яркости byte deltaLevel = 10; // Шаг изменения яркости bool sign = true; // Знак шага int step_delay = 120;// Здержка в шаге изменения яркости светодиода // Настройка сигнализатора окончания изменения яркости int blink_delay = 300;// Задержка состояния светодиода на pin13 /*********************************************************** Настроечная функция, выполняется один раз вначале ***********************************************************/ void setup() { #ifdef DEBUG Serial.begin(9600); #endif pinMode(BT_PIN, INPUT_PULLUP); pinMode(LED_PIN, OUTPUT); pinMode(LED_BUILTIN, OUTPUT); // Потушим светодиод на pin13 (LED_BUILTIN) digitalWrite(LED_BUILTIN, LOW); } /*********************************************************** Функция постоянно повторяется При нажатии на кнопку, светодиод медленно увеличивает яркость При повторном нажатии на кнопку, светодиод медленно гаснет ***********************************************************/ void loop() { currentButton = debounce(lastButton); // Опрос кнопки if (lastButton == HIGH && currentButton == LOW) { fnBlink(1, blink_delay); // Сигнал нажатия кнопки if (ledLevel == 0) sign = true; // Знак на увеличение if (ledLevel == 255) sign = false; // Знак на уменьшение // Изменяем яркость светодиода fnStepBrightness(step_delay, deltaLevel, sign); // Сигнализируем об окончании изменения fnBlink(MAX_BLINK, blink_delay); } lastButton = currentButton; // Переписываем состояние кнопки } /*********************************************************** Функция для подавления дребезга ***********************************************************/ boolean debounce(boolean last) { boolean current = digitalRead(BT_PIN);// Читаем состояние кнопки if (last != current) { delay(10); current = digitalRead(BT_PIN); } return current; } /*********************************************************** Функция сигнализации окончания цикла изменения яркости светодиода blink_max - сколько мигнуть blink_delay - задержка в мс ***********************************************************/ void fnBlink(byte blink_max, unsigned int blink_delay) { for (byte i = 0; i < blink_max; i++) { digitalWrite(LED_BUILTIN, HIGH); delay(blink_delay); digitalWrite(LED_BUILTIN, LOW); delay(blink_delay); } } /*********************************************************** Функция изменения яркости светодиода step_delay - Задержка в мс на шаг deltaLevel - На сколько изменить за шаг sign - Знак уменьшение/увеличение ***********************************************************/ void fnStepBrightness ( unsigned int step_delay, byte deltaLevel, bool sign ) { for (byte i = 0; i < (byte(256 / deltaLevel) + 1); i++) { ledLevel = ledLevel + deltaLevel * ((sign == true) ? 1 : -1); #ifdef DEBUG Serial.print(i); Serial.print("\t ledLevel="); Serial.println(ledLevel); #endif // Ограничиваем диапазон значений ledLevel = constrain(ledLevel, 0, 255); analogWrite(LED_PIN, ledLevel); delay(step_delay); } }
В приведённом выше скетче использована функция debounce() для устранения дребезга кнопки, при её нажимании. Взял где-то в Интернете или в примерах в Arduino IDE.
Программы для повторения: