
Полная версия
Путь к пониманию микроконтроллеров: на основе Arduino
Увеличение яркости светодиода состоит из повторяющихся команд analogWrite и delay. Для выполнения повторяющихся действий нужное количество раз используется цикл for (подробное описание цикла в подразделе 2.2.3).
void setup () {
pinMode (3, OUTPUT);
}
void loop () {
for (int i=0; i <=255; i = i+1) {
analogWrite (3,i);
delay (10);
}
}
Теперь светодиод будет включаться плавно с шагом равным 1.
Для плавного выключения светодиода нужно действовать в обратном порядке, задавая яркость от 255 до 0 с нужным шагом.
for (int i=255; i> = 0; i = i-1) {
analogWrite (3,i);
delay (10);
}
Для плавного включения и последующего плавного выключения светодиода следует расположить второй цикл сразу после первого. Если необходимо, чтобы светодиод был дольше включен или дольше выключен — после цикла, вне его фигурных скобок, нужно использовать delay. Пример кода ниже:
void setup () {
pinMode (3, OUTPUT);
}
void loop () {
for (int i=0; i <=255; i = i+1) {
analogWrite (3,i);
delay (10);
}
delay (100);
for (int i=255; i> = 0; i = i-1) {
analogWrite (3,i);
delay (10);
}
delay (100);
}
*** Если вы впервые знакомитесь с Arduino и впервые читаете книгу — рекомендую пропустить объяснение ниже и перейти сразу к окончанию этого подраздела — к «задачи для закрепления материала», а к тексту ниже вернуться после изучения 1 — 3 разделов.
Следующий шаг — изменение функций Wiring на использование регистров (подробнее в подразделе 3.7 этой книги).
void setup () {
DDRD |= (1 <<3); // Назначение 3 цифрового вывода Arduino выходом.
TCCR2A=0b00100011; //Включение ШИМ на 3 выводе Arduino
TCCR2B=0b00000011; // Установка частоты для 3 вывода Arduino — 976 Гц
}
void loop () {
for (int i=0; i <=255; i=i+1) {
OCR2B=i; // подача напряжения на 3 вывод Arduino
delay (10);
}
for (int i=255; i> = 0; i = i-1) {
OCR2B=i; // подача напряжения на 3 вывод Arduino
delay (10);
}
delay (100);
}
Задачи для закрепления материала:
Задачи следует решать с использованием функций Wiring.
(Дополнительно) Решение задач с использованием регистров.
1. Начальные состояния — первый светодиод не светится, второй светится. Первый светодиод плавно зажигается, после этого второй светодиод плавно затухает. После первый светодиод плавно затухает, а второй плавно зажигается. Описанные алгоритм выполняется только один раз.
2. Два светодиода. Одновременно первый светодиод плавно зажигается, а второй плавно затухает, после одновременно первый светодиод плавно затухает, а второй плавно зажигается. Описанный алгоритм выполняется два раза.
3. Два светодиода. Одновременно первый светодиод плавно зажигается, а второй плавно затухает, после одновременно первый светодиод плавно затухает, а второй плавно зажигается. Описанный алгоритм выполняется многократно, пока на Arduino поступает напряжение питания.
Что мы изучили в подразделе 1.4:
1. Широтно-импульсную модуляцию;
2. Функцию analogWrite ();
3. Цикл for (подробнее в подразделе 2.2.3);
4. Основы работы с таймерами через регистры (подробнее в подразделе 3.7).
1.5 Управление группой светодиодов по входному параметру
Оптимизация кода
Есть модули, которые включают в себя несколько светодиодов.
Примером является семисегментный индикатор — модуль индикации, который может отобразить любую цифру (от 0 до 9) и некоторые буквы.
Каждый сегмент индикатора можно представить как отдельный светодиод.
Управляется каждый сегмент так же независимо, у каждого сегмента есть свой вывод.
Поэтому задача управления семисегментным индикатором сводится к формированию цифр через управление несколькими светодиодами.
Необходимо сразу определиться с нумерацией сегментов, чтобы в ходе решения задачи не было ошибок.
Вид семисегментного индикатора представлен ниже.

Необходимо каждому сегменту присвоить позицию. Допустим, сегмент А — 2, B — 3, C — 4, D — 5, E — 6, F — 7, G — 8, dp — 1.
Сегмент «dp» является дополнительным и присутствует не на всех модулях семисегментного индикатора.
Почему он под номером 1? Для реализации данной задачи нам не понадобится символ точки («dp»). Цифровые выводы 0 и 1 также использовать не будем. При такой нумерации символ A, под номером 2, можно подключить к цифровому выводу Arduino 2. Символ В (3) к выводу 3 и так далее. Номер символа будет соответствовать номеру вывода Arduino. Такое распределение будет удобно и исключит вероятность ошибки, хотя, разумеется, подключать выводы сегментов можно и к другим выводам Arduino.
Распишем, на какие сегменты нужно подать напряжение, чтобы отобразить каждую из цифр.
0: A (2),B (3),C (4),D (5),E (6),F (7).
1: B (3), C (4).
2: A (2), B (3), G (8), E (6), D (5).
3: A (2), B (3), G (8), C (4), D (5).
4: F (7), G (8), B (3), C (4).
5: A (2), F (7), G (8), C (4), D (5).
6: A (2), F (7), G (8), C (4), D (5), E (6).
7: A (2), B (3), C (4).
8: A (2), B (3), C (4), D (5), E (6), F (7), G (8).
9: A (2), B (3), C (4), D (5), F (7), G (8).
Цифры можно отображать по очереди, для этого представим список, представленный выше, с помощью функций digitalWrite и delay (скетч промежуточный!).
void loop () {
//0
digitalWrite (2,1);
digitalWrite (3,1);
digitalWrite (4,1);
digitalWrite (5,1);
digitalWrite (6,1);
digitalWrite (7,1);
delay (50);
//1
digitalWrite (3,1);
digitalWrite (4,1);
delay (50);
//2
digitalWrite (2,1);
digitalWrite (3,1);
digitalWrite (5,1);
digitalWrite (6,1);
digitalWrite (8,1);
delay (50);
//3
digitalWrite (2,1);
digitalWrite (3,1);
digitalWrite (4,1);
digitalWrite (5,1);
digitalWrite (8,1);
delay (50);
//4
digitalWrite (3,1);
digitalWrite (4,1);
digitalWrite (7,1);
digitalWrite (8,1);
delay (50);
//5
digitalWrite (2,1);
digitalWrite (4,1);
digitalWrite (5,1);
digitalWrite (7,1);
digitalWrite (8,1);
delay (50);
//6
digitalWrite (2,1);
digitalWrite (4,1);
digitalWrite (5,1);
digitalWrite (6,1);
digitalWrite (7,1);
digitalWrite (8,1);
delay (50);
//7
digitalWrite (2,1);
digitalWrite (3,1);
digitalWrite (4,1);
delay (50);
//8
digitalWrite (2,1);
digitalWrite (3,1);
digitalWrite (4,1);
digitalWrite (5,1);
digitalWrite (6,1);
digitalWrite (7,1);
digitalWrite (8,1);
delay (50);
//9
digitalWrite (2,1);
digitalWrite (3,1);
digitalWrite (4,1);
digitalWrite (5,1);
digitalWrite (7,1);
digitalWrite (8,1);
delay (50);
}
В результате выполнения этого скетча меняющиеся цифры мы не увидим, потому, что на семисегментном индикаторе отобразится 0, а команд для того, чтобы «стереть» 0 не было. В итоге 1 и другие цифры будут «выводиться поверх» 0, а значит мы будем продолжать видеть 0.
Для решения проблемы можно после каждого delay вставить функции для снятия напряжения со всех выводов:
digitalWrite (2,0);
digitalWrite (3,0);
digitalWrite (4,0);
digitalWrite (5,0);
digitalWrite (6,0);
digitalWrite (7,0);
digitalWrite (8,0);
Но визуально это может выглядеть, как мерцание. Так как если смотреть на физический процесс, то мы включаем, выключаем, включаем, выключаем… и так от 0 до 9, а потом заново (так как void loop начинает выполняться повторно).
Решение достаточно простое, во время отображения каждой цифры, вместо подхода описанного выше, нужно подавать не только логические единицы, но и логические нули на необходимые выводы.
Тогда скетч примет следующий вид:
void loop () {
//0
digitalWrite (2,1);
digitalWrite (3,1);
digitalWrite (4,1);
digitalWrite (5,1);
digitalWrite (6,1);
digitalWrite (7,1);
digitalWrite (8,0);
delay (50);
//1
digitalWrite (2,0);
digitalWrite (3,1);
digitalWrite (4,1);
digitalWrite (5,0);
digitalWrite (6,0);
digitalWrite (7,0);
digitalWrite (8,0);
delay (50);
//2
digitalWrite (2,1);
digitalWrite (3,1);
digitalWrite (4,0);
digitalWrite (5,1);
digitalWrite (6,1);
digitalWrite (7,0);
digitalWrite (8,1);
delay (50);
//3
digitalWrite (2,1);
digitalWrite (3,1);
digitalWrite (4,1);
digitalWrite (5,1);
digitalWrite (6,0);
digitalWrite (7,0);
digitalWrite (8,1);
delay (50);
//4
digitalWrite (2,0);
digitalWrite (3,1);
digitalWrite (4,1);
digitalWrite (5,0);
digitalWrite (6,0);
digitalWrite (7,1);
digitalWrite (8,1);
delay (50);
//5
digitalWrite (2,1);
digitalWrite (3,0);
digitalWrite (4,1);
digitalWrite (5,1);
digitalWrite (6,0);
digitalWrite (7,1);
digitalWrite (8,1);
delay (50);
//6
digitalWrite (2,1);
digitalWrite (3,0);
digitalWrite (4,1);
digitalWrite (5,1);
digitalWrite (6,1);
digitalWrite (7,1);
digitalWrite (8,1);
delay (50);
//7
digitalWrite (2,1);
digitalWrite (3,1);
digitalWrite (4,1);
digitalWrite (5,0);
digitalWrite (6,0);
digitalWrite (7,0);
digitalWrite (8,0);
delay (50);
//8
digitalWrite (2,1);
digitalWrite (3,1);
digitalWrite (4,1);
digitalWrite (5,1);
digitalWrite (6,1);
digitalWrite (7,1);
digitalWrite (8,1);
delay (50);
//9
digitalWrite (2,1);
digitalWrite (3,1);
digitalWrite (4,1);
digitalWrite (5,1);
digitalWrite (6,0);
digitalWrite (7,1);
digitalWrite (8,1);
delay (50);
}
Теперь мы не включаем-выключаем индикатор, мы выключаем только отдельные сегменты, в таком случае на тех сегментах, которые должны будут включиться — уже есть напряжение. Мерцания не происходит.
Отображать цифры можно не только по возрастанию, но и по любому другому алгоритму. А можно отображать цифру в зависимости от входного параметра. Тогда, допустим, если некоторая переменная будет равна 5, то и индикатор покажет цифру 5.
В таком случае у нас будет десять (цифры от 0 до 9) заранее известных реакций (подать напряжение на нужные светодиоды для формирования заданной цифры и выключить напряжение с остальных светодиодов).
Мы можем сформировать скетч через конструкцию проверки условия if.
Если параметр равен 0, то вывести напряжение на следующие выводы…
Если параметр равен 1, то вывести напряжение на следующие выводы…
Можно сформировать скетч через вложенные конструкции:
Если параметр равен 0, то вывести напряжение на следующие выводы:…
Иначе если параметр равен 1, то вывести напряжение на следующие выводы:…
Но в данном случае, когда есть множество реакций (вывод цифр от 0 до 9) и один параметр, от значения которого зависит реакция, следует использовать конструкцию switch (подробное описание конструкции в подразделе 2.1.2).
«Switch» с Английского переводится, как «переключатель», так и мы будем переключать одну реакцию на другую при смене значения входного параметра.
Микроконтроллер должен понимать, от какого параметра зависит переключение, поэтому в круглых скобках после switch мы его записываем:
switch (a)
У конструкции switch есть свое тело выделяемое фигурными скобками:
switch (a) {
}
Переключаться микроконтроллер в конструкции switch будет между случаями, в которых переменная a равна тому или иному значению, поэтому в конструкции добавляем случаи (скетч промежуточный!):
switch (a) {
case 0 :
case 1:
case 2:
}
case — с Английского «случай» или «ящик», так и мы добавляем случаи или некие ящики с действиями.
То есть код сейчас можно прочитать так:
Переключаться в зависимости от значения переменной а: если переменная а равна 0, то делать… если переменная а равна 1, то делать…
Своих фигурных скобок у case нет, но микроконтроллеру все еще нужно объяснить, где рабочее тело case заканчивается, для этого используется команда break; позволяющая после выполнения всех действий не идти дальше, а переместиться к строчке с закрывающей фигурной скобкой рабочего тела switch.
Тогда код принимает следующий вид:
switch (a) {
case 0 :
//функции для случая, когда а==0
break;
case 1:
//функции для случая, когда а==1
break;
case 2:
//функции для случая, когда а==2
break;
…
}
Преимущество такой записи — наглядность, это уменьшает вероятность случайных ошибок связанных с размещением не в том месте фигурных скобок или частей кода.
В итоге код для возможности отображения цифр 0,1,2 будет выглядеть следующим образом:
switch (a) {
case 0 :
digitalWrite (2,1);
digitalWrite (3,1);
digitalWrite (4,1);
digitalWrite (5,1);
digitalWrite (6,1);
digitalWrite (7,1);
digitalWrite (8,0);
delay (50);
break;
case 1:
digitalWrite (2,0);
digitalWrite (3,1);
digitalWrite (4,1);
digitalWrite (5,0);
digitalWrite (6,0);
digitalWrite (7,0);
digitalWrite (8,0);
delay (50);
break;
case 2:
digitalWrite (2,1);
digitalWrite (3,1);
digitalWrite (4,0);
digitalWrite (5,1);
digitalWrite (6,1);
digitalWrite (7,0);
digitalWrite (8,1);
delay (50);
break;
}
В случае, если предполагается, что переменная может принимать множество значений, в результате которых должна быть одна и та же реакция, или возможно нестандартное поведение, на которое нужно реагировать определенным образом, следует использовать «default:», который является аналогом else.
Код в таком случае будет выглядеть следующим образом.
switch (a) {
case 0 :
//функции для случая, когда а==0
break;
case 1:
//функции для случая, когда а==1
break;
case 2:
//функции для случая, когда а==2
break;
…
default:
break;
}
Обратите внимание, что после default: мы не пишем никакое значение, потому что код внутри default выполняется, если не подошёл ни один из case.
То есть, если переменная равна какому-то значению, но его нет среди значений case, то будут выполнены функции между default: и break;
В текущем коде после default будет логично выключить напряжение на всех выводах отвечающих за сегменты индикатора.
Тогда код примет следующий вид:
switch (a) {
case 0 :
//функции для случая, когда а==0
break;
case 1:
//функции для случая, когда а==1
break;
case 2:
//функции для случая, когда а==2
Конец ознакомительного фрагмента.
Текст предоставлен ООО «Литрес».
Прочитайте эту книгу целиком, купив полную легальную версию на Литрес.
Безопасно оплатить книгу можно банковской картой Visa, MasterCard, Maestro, со счета мобильного телефона, с платежного терминала, в салоне МТС или Связной, через PayPal, WebMoney, Яндекс.Деньги, QIWI Кошелек, бонусными картами или другим удобным Вам способом.

