Опубликовано

Управляемый ночник на трехцветном светодиоде

По материалам 2 главы книги Дж. Блума «Изучаем Arduino: инструменты и методы технического волшебства»

https://bhv.ru/product/izuchaem-arduino-instrumenty-i-metody-tehnicheskogo-volshebstva-2-e-izd-per-s-angl/

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

Для этого проекта мы возьмем трехцветный светодиод с общим анодом. Это означает, что наш светодиод имеет в целом четыре вывода. Три вывода подключены к катодам светодиодов разных цветов, а четвертый подключен к анодам всех трех светодиодов. Подключите выводы катодов трехцветного светодиода к трем контактам ШИМ платы Arduino через последовательные токоограничивающие резисторы, как показано в монтажной схеме на рис. 2.8. Как и в случае с одноцветным красным светодиодом, используйте резисторы номиналом 220 Ом.

Рис. 2.8. Монтажная схема ночника на трехцветном светодиоде (Рисунок создан в программе Fritzing)

Поскольку все три составляющих светодиода имеют общий анод, для индивидуального управления ими к плате Arduino нужно подключать их катоды, а не анод, как было в случае с одноцветным красным светодиодом в предыдущих проектах. Контакт платы Arduino, работающий в режиме вывода, поставляет ток двумя способами:

  • Когда на контакте установлен высокий уровень, этот контакт поставляет ток в режиме истока. Иными словами, ток из источника питания Arduino истекает из этого контакта, протекает через подключенную к контакту нагрузку (которой в данном случае является светодиод) и втекает в шину земли.
  • Когда на контакте установлен низкий уровень, этот контакт поставляет ток в режиме стока. Иными словами ток из источника питания входит в контакт и далее на внутреннюю землю платы.

Поэтому, когда анод светодиода подключен к положительному напряжению 5 В, а его катод — к контакту платы Arduino, работающему в режиме вывода, логика управления этим светодиодом инвертируется. Чтобы обеспечить протекание тока из шины питания напряжением 5 В через резистор и светодиод и далее через контакт платы Arduino на внутреннюю землю платы, на этот контакт нужно подавать низкий управляющий уровень. Когда же на этот контакт подается высокий уровень, на нем будет такой же потенциал (5 В), как и на аноде светодиода. В результате ток не будет протекать, и светодиод выключится.

Можно разработать такую программу, чтобы при каждом нажатии кнопки с противодребезговой защитой циклически переключать цвета трехцветного светодиода. Для этого будет полезной дополнительная функция для задания следующего состояния цветового цикла трехцветного светодиода. В программе, код которой приведен в листинге 2.6, определено в общем семь цветовых состояний трехцветного светодиода, плюс одно выключенное состояние. Посредством функции analogWrite() можно создавать любые комбинации основных цветов, чтобы получить соответствующий общий цвет. Основной цикл loop() этой программы отличается от цикла в предыдущем примере только тем, что вместо переключения между двумя состояниями светодиода, в нем при каждом нажатии кнопки инкрементируется счетчик состояния светодиода, значение которого сбрасывается на ноль после прохождения через все доступные опции. Загрузите этот код в плату Arduino с подключенной к ней схемой ночника и любуйтесь его работой. Состояния цветов можно модифицировать, изменив значения аргумента цвета функции analogWrite().

Листинг 2.6. Программа rgb_nightlight.ino для управления трехцветным светодиодом

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
const int BLED=9; // Подключаем катод синего светодиода к контакту 9
 
const int GLED=10; // Подключаем катод зеленого светодиода к контакту 10
 
const int RLED=11; // Подключаем катод красного светодиода к контакту 11
 
const int BUTTON=2; // Кнопку подключаем к контакту 2
 
boolean lastButton = LOW; // Предыдущее состояние кнопки.
 
boolean currentButton = LOW; // Текущее состояние кнопки
 
int ledMode = 0; // Переключаемся в цикле между состояниями
 
// светодиода
 
void setup()
 
{
 
pinMode (BLED, OUTPUT); // Задаем режим вывода для контакта
 
// синего светодиода
 
pinMode (gLED, OUTPUT); // Задаем режим вывода для контакта
 
// зеленого светодиода
 
pinMode (RLED, OUTPUT); // Задаем режим вывода для контакта
 
// красного светодиода
 
pinMode (BUTTON, INPUT); // Задаем режим ввода для контакта кнопки
 
// (не обязательно)
 
}
 
/*
 
* Функция устранения дребезга контактов
 
* Передаем ей значение предыдущего состояния кнопки,
 
* и получаем от нее текущее значение состояния кнопки
 
* с устраненным дребезгом.
 
*/
 
boolean debounce(boolean last)
 
{
 
boolean current = digitalRead(BUTTON); // Считываем текущее
 
// состояние кнопки.
 
if (last != current) // если оно иное, чем предыдущее...
 
{
 
delay(5); // Ждем 5 мс
 
current = digitalRead(BUTTON); // Считываем состояние снова
 
}
 
return current; // Возвращаем текущее
 
// состояние кнопки
 
}
 
/*
 
* Выбор состояния светодиода
 
* Передаем функции номер состояния светодиода и устанавливаем
 
* его соответственно
 
* Обратите внимание на то, что поскольку наш трехцветный светодиод
 
* имеет общий анод, чтобы включать составляющие его светодиоды,
 
* на их катоды нужно подавать низкий (LOW) уровень.
 
*/
 
void setMode(int mode)
 
{
 
// КРАСНЫЙ
 
if (mode == 1)
 
{
 
digitalWrite(RLED, LOW);
 
digitalWrite(GLED, HIGH);
 
digitalWrite(BLED, HIGH);
 
}
 
// ЗЕЛЕНЫЙ
 
else if (mode == 2)
 
{
 
digitalWrite(RLED, HIGH);
 
digitalWrite(GLED, LOW);
 
digitalWrite(BLED, HIGH);
 
}
 
// СИНИЙ
 
else if (mode == 3)
 
{
 
digitalWrite(RLED, HIGH);
 
digitalWrite(GLED, HIGH);
 
digitalWrite(BLED, LOW);
 
}
 
// ФИЛОЛЕТОВЫЙ (КРАСНЫЙ+СИНИЙ)
 
else if (mode == 4)
 
{
 
analogWrite(RLED, 127);
 
analogWrite(GLED, 255);
 
analogWrite(BLED, 127);
 
}
 
// ЗЕЛЕНОВАТО-ГОЛУБОЙ (СИНИЙ+ЗЕЛЕНЫЙ)
 
else if (mode == 5)
 
{
 
analogWrite(RLED, 255);
 
analogWrite(GLED, 127);
 
analogWrite(BLED, 127);
 
}
 
// ОРАНЖЕВЫЙ (ЗЕЛЕНЫЙ+КРАСНЫЙ)
 
else if (mode == 6)
 
{
 
analogWrite(RLED, 127);
 
analogWrite(GLED, 127);
 
analogWrite(BLED, 255);
 
}
 
// БЕЛЫЙ (ЗЕЛЕНЫЙ+КРАСНЫЙ+СИНИЙ)
 
else if (mode == 7)
 
{
 
analogWrite(RLED, 170);
 
analogWrite(GLED, 170);
 
analogWrite(BLED, 170);
 
}
 
// ОТКЛЮЧЕН (mode = 0)
 
else
 
{
 
digitalWrite(RLED, LOW);
 
digitalWrite(GLED, LOW);
 
digitalWrite(BLED, LOW);
 
}
 
}
 
void loop()
 
{
 
currentButton = debounce(lastButton); // Считываем состояние
 
// кнопки с устраненным дребезгом
 
if (lastButton == LOW && currentButton == HIGH) // если кнопка
 
// была нажата...
 
{
 
ledMode++; // Инкрементируем значение LED
 
}
 
lastButton = currentButton; // Сбрасываем значение кнопки
 
// Если прошли по всему циклу цветовых состояний,
 
// обнуляем счетчик
 
if (ledMode == 8) ledMode = 0;
 
setMode(ledMode); // Изменяем значение состояния светодиода
 
}

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

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

Примечание

Светодиод RGB

Конструктивно трехцветный светодиод представляет собой три цветных светодиода: красный (R), зеленый (G) и синий (B), смонтированных в общем корпусе. Различные оттенки цвета получаются путем смешения 3-х базовых цветов (модель RGB). Поскольку светодиоды расположены очень близко друг к другу, наши глаза видят результат сочетания цветов, а не три цвета по отдельности. Для регулировки интенсивности каждого светодиода можно использовать сигнал ШИМ.

Внешний вид, назначение контактов

Имейте в виду, что существуют два вида светодиодов RGB: с общим анодом (рис. L1.1, справа) и с общим катодом (рис. L1.1, слева) , которые имеют разную схему подключения общего вывода: к минусу (–) или к плюсу (+).

04-01-1-rgb

Рис. L1.1. RGB-светодиод с общим катодом (слева) и с общим анодом (справа)

Опубликовано

ЖК-дисплей 1602 с модулем I2C

По материалам руководства для набора “Умный дом на базе Arduino. Большой набор + КНИГА

В 10-й главе книги Дж. Блума «Изучаем Arduino: инструменты и методы технического волшебства» подробно описано применение LCD-дисплея в проектах Arduino (стр. 202).

При разработке больших проектов на Arduino Uno очень часто для подключения различных устройств не хватает выводов. Например, для подключения одного LCD-дисплея в проекте из книги Дж. Блума (стр. 205, рис. 10.2) задействовано 6 цифровых выводов (D2 ÷ D7) и два вывода питания платы Arduino.

Чтобы сократить количество выводов для подключения LCD-дисплеев, можно использовать специальный интерфейсный модуль IIC/I2C (рис. L2.1), с помощью которого обмен данными между LCD-дисплеем (ЖКД) и Arduino осуществляется по последовательному протоколу I2C (Inter-Integrated Circuit), который подробно описан в 8-й главе книги Дж. Блума.

В этом случае будут задействованы только 4 вывода Arduino:

  • GND (Земля),
  • VCC (Питание +5V),
  • A4 (SDA, Serial Data — последовательные данные),
  • A5 (SCL, Serial Clock — cигнал последовательного тактирования).

Внешний вид, назначение контактов

Удобно приобрести модуль LCD-дисплея, на котором модуль IIC/I2C уже установлен (см. рис. L2.1). На модуле LCD-дисплея расположен потенциометр для управления контрастом дисплея.

L2-1-LCD

Рис. L2.1. Плата ЖКД 1602 с модулем I2C

Схема подключения

04-02-2-lcd

Рис. L2.2. Схема подключения LCD-дисплея с модулем I2C

Основные характеристики

Наименование Значение
Напряжение, В От 4,5 до 5,5
Ток, мА 1,5
Ток подсветки дисплея, мА 120
Напряжение подсветки дисплея, В От 4,1 до 4,3

Программный код

  1. Загрузите библиотеку LiquidCrystal I2C by Frank de Brabander для работы Arduino с LCD-дисплеем по протоколу I2C. Для этого откройте Менеджер библиотек, выполнив команду Инструменты | Управлять библиотеками. Справа вверху в строке поиска введите LiquidCrystal I2C. В открывшемся списке выберите LiquidCrystal I2C by Frank de Brabander. Нажмите кнопку Установка.
  2. Загрузите скетч из листинга L2.1. Адрес шины I2C по умолчанию: 0x27.

Примечание

Библиотеку также можно скачать в виде ZIP-файла по ссылке https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library и подключить в среду разработки Arduino IDE с помощью команды Скетч | Подключить библиотеку | Добавить .ZIP библиотеку….

Листинг L2.1. Тест LCD-экрана

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <Wire.h>
 
#include <LiquidCrystal_I2C.h>
 
//задаем адрес экрана 0x27, 16 символов, 2 строки
 
LiquidCrystal_I2C lcd(0x27, 16, 2);
 
void setup() {
 
lcd.init(); // Инициализируем экран включаем подсветку
 
lcd.backlight();
 
//Устанавливаем положение курсора для первой строки.
 
lcd.home();
 
//выводим строку 1
 
lcd.print("String 1 Test");
 
//выводим строку 2
 
lcd.setCursor(0, 1);
 
lcd.print("String 2 Test");
 
}
 
void loop() {
 
}

Библиотека LiquidCrystal_I2C не поддерживает кириллические символы. Для их отображения по адресу https://github.com/ssilver2007/LCD_1602_RUS скачайте и установите библиотеку LCD_1602_RUS (автор Сергей Сироткин). При этом основная библиотека LiquidCrystal_I2C должна быть уже установлена. Имейте в виду, что максимально возможно отображение восьми уникальных по начертанию русских символов (например: Ж, Д, И, Ю и т. п.).

Загрузите скетч из листинга L2.2 — теперь символы кириллицы должны отображаться корректно.

Листинг L2.2. Тест кириллических шрифтов на LCD-экране

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <LCD_1602_RUS.h>
 
LCD_1602_RUS lcd(0x27, 16, 2);
 
void setup()
 
{
 
lcd.init();
 
lcd.backlight();
 
lcd.setCursor(0, 0);
 
lcd.print("Первая строка");
 
lcd.setCursor(0, 1);
 
lcd.print("Вторая строка");
 
}
 
void loop()
 
{
 
}
Опубликовано

Светодиодная матрица 8×8 с драйвером MAX7219

По материалам руководства для набора “Умный дом на базе Arduino. Большой набор + КНИГА

Светодиодная матрица представляет собой набор светодиодов, сгруппированных в квадратную или прямоугольную матрицу. Каждый светодиод имеет отрицательный вывод (катод), который подключен к «земле», и положительный (анод), подключенный к источнику питания. Аноды и катоды соединены так, что образуют столбцы и строки. Таким образом, подавая питание от Arduino на определенные строки и столбцы, мы можем «зажигать» нужный светодиод.

Внешний вид, назначение контактов

Для управления матрицей 8×8 требуются 16 контактов (рис. L3.1, слева). Однако задействовать 16 контактов Arduino UNO для вывода символов на матрицу очень расточительно. Лучшим решением является использование специального расширителя выводов на базе микросхемы MAX7219. В этом случае для управления матрицей достаточно пяти контактов. Такие модули выпускаются в различном исполнении (рис. L3.1, справа).

04-03-1-8x8

Рис. L3.1. Светодиодная матрица 8×8 с модулем MAX7219

Схема подключения

04-03-2-8x8

Рис. L3.2. Схема подключения cветодиодной матрицы 8×8 с модулем MAX7219

Программный код

  1. Загрузите библиотеку LedControl для работы Arduino с модулем MAX7219. Для этого откройте Менеджер библиотек, выполнив команду Инструменты | Управлять библиотеками. Справа вверху в строке поиска введите LedControl. В открывшемся списке выберите LedControl by Eberhard Fahle. Нажмите кнопку Установка.
  2. Загрузите скетч из листинга L3.1 — на матрице появится смайлик, изменяющий «мимику» через каждую секунду.

Листинг L3.1. Вывод динамического смайла на светодиодную матрицу

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include "LedControl.h"
 
#include "binary.h"
 
/*
 
DIN подключен к пину 11
 
CLK подключен к пину 13
 
CS подключен к пину 8
 
*/
 
LedControl matr=LedControl(11,13,8,1);
 
// Счастливый смайл
 
byte hf[8]= {B00111100,B01000010,B10011001,B10100101,B10000001,B10100101,B01000010,B00111100};
 
// Нейтральный смайл
 
byte nf[8]= {B00111100,B01000010,B10000001,B10111101,B10000001,B10100101,B01000010,B00111100};
 
// Печальный смайл
 
byte sf[8]= {B00111100,B01000010,B10100101,B10011001,B10000001,B10100101,B01000010,B00111100};
 
void setup() {
 
matr.shutdown(0,false); //Включаем светодиодную матрицу
 
matr.setIntensity(0,8); // Установка яркости на среднее значение
 
matr.clearDisplay(0); // Очистка матрицы
 
Serial.begin(9600);
 
}
 
void loop(){
 
//Вывод счастливого смайла
 
matr.setRow(0,0,hf[0]);
 
matr.setRow(0,1,hf[1]);
 
matr.setRow(0,2,hf[2]);
 
matr.setRow(0,3,hf[3]);
 
matr.setRow(0,4,hf[4]);
 
matr.setRow(0,5,hf[5]);
 
matr.setRow(0,6,hf[6]);
 
matr.setRow(0,7,hf[7]);
 
delay(1000); //задержка 1 с
 
//Вывод нейтрального смайла
 
matr.setRow(0,0,nf[0]);
 
matr.setRow(0,1,nf[1]);
 
matr.setRow(0,2,nf[2]);
 
matr.setRow(0,3,nf[3]);
 
matr.setRow(0,4,nf[4]);
 
matr.setRow(0,5,nf[5]);
 
matr.setRow(0,6,nf[6]);
 
matr.setRow(0,7,nf[7]);
 
delay(1000); //задержка 1 с
 
//Вывод печального смайла
 
matr.setRow(0,0,sf[0]);
 
matr.setRow(0,1,sf[1]);
 
matr.setRow(0,2,sf[2]);
 
matr.setRow(0,3,sf[3]);
 
matr.setRow(0,4,sf[4]);
 
matr.setRow(0,5,sf[5]);
 
matr.setRow(0,6,sf[6]);
 
matr.setRow(0,7,sf[7]);
 
delay(1000); //задержка 1 с
 
}

Вы можете самостоятельно создавать собственные изображения для вывода на матрицу 8×8. Изображение можно смоделировать на обычном листке в клетку, в табличном редакторе MS Excel или онлайн. Например, на ресурсе http://arduino.on.kg/matrix-font можно создать и получить программный код собственного изображения или выбрать готовый символ (рис. L3.3).

04-03-3-8x8

Рис. L3.3. Моделирование изображения на матрице 8×8

Опубликовано

Управление светодиодной матрицей с использованием драйвера дисплея MAX72xx

Раздел из главы 7 “Управление светодиодными устройствами вывода данных” из книги “Arduino. Большая книга рецептов, 3-е изд.” (авторы Джепсон Брайан, Марголис Майкл, Уэлдин Николас Роберт)

ЗАДАЧА

Требуется управлять светодиодной матрицей 8×8, используя минимальное количество контактов платы Arduino.

РЕШЕНИЕ

Подобно решению из разд. 7.13, количество контактов для управления светодиодной матрицей можно уменьшить, применив микросхему драйвера дисплея. В этом решении используется популярная микросхема драйвера светодиодного дисплея MAX7219 или MAX7221. Подключение драйвера дисплея к плате Arduino и светодиодной матрицы к драйверу показано на рис. 7.16, а в листинге 7.19 приводится скетч для работы с этой схемой.

Подключение драйвера дисплея MAX72xx к плате Arduino для управления светодиодной матрицей 8×8Рис. 7.16. Подключение драйвера дисплея MAX72xx к плате Arduino для управления светодиодной матрицей 8×8

В скетче используется библиотека с обширными возможностями MD_MAX72XX, которая поддерживает отображение на дисплее текста и фигур, а также выполнение различных трансформаций отображаемого содержимого. Библиотека MD_MAX72XX устанавливается с помощью Менеджера библиотек среды Arduino IDE (подробная информация по установке библиотек приведена в разд. 16.2).

Листинг 7.19. Управление светодиодной матрицей
с помощью драйвера дисплея MAX72xx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/*
 
Скетч 7219 Matrix demo
 
*/
 
#include <MD_MAX72xx.h>
 
// Контакты платы Arduino для управления драйвером 7219
 
#define LOAD_PIN 2
 
#define CLK_PIN 3
 
#define DATA_PIN 4
 
// Конфигурируем устройство
 
#define MAX_DEVICES 1
 
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
 
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN, CLK_PIN, LOAD_PIN, MAX_DEVICES);
 
void setup()
 
{
 
mx.begin();
 
}
 
void loop()
 
{
 
mx.clear(); // Очищаем дисплей
 
// Отображаем строки и столбцы
 
for (int r = 0; r < 8; r++)
 
{
 
for (int c = 0; c < 8; c++)
 
{
 
mx.setPoint(r, c, true); // Выключаем каждый светодиод модуля
 
delay(50);
 
}
 
// Проходим в цикле по всем возможным уровням яркости
 
for (int k = 0; k <= MAX_INTENSITY; k++)
 
{
 
mx.control(MD_MAX72XX::INTENSITY, k);
 
delay(100);
 
}
 
}
 
}

Обсуждение работы решения и возможных проблем

В начале скетча создается экземпляр mx объекта MD_MAX72XX, которому в параметрах передается тип устройства, номера контактов для данных загрузки и сигнала тактирования, а также максимальное количество устройств (при последовательном соединении модулей). В главном цикле loop() выполняется очистка дисплея, а затем с помощью функции setPoint() включаются пикселы (светодиоды) матрицы. Включив строку матрицы, скетч проходит в цикле через все возможные уровни яркости, а затем переходит к обработке следующей строки.

В скетче указаны номера контактов для зеленых светодиодов двухцветной светодиодной матрицы 8×8 компании Adafruit (артикул 458). При использовании другой светодиодной матрицы обратитесь к справочному листку на нее, чтобы определить контакты для ее строк и столбцов. Скетч также будет работать и с одноцветной матрицей, поскольку он использует только один из двух цветов матрицы. Если обнаружится, что матрица отображает текст в обратном направлении или не в ожидаемой ориентации, можно попробовать исправить эту ошибку, изменив тип устройства в строке:

1
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW с PAROLA_HW

на GENERIC_HW, ICSTATION_HW или FC16_HW. В примерах библиотеки MD_MAX72XX содержится тестовый скетч MD_MAX72xx_HW_Mapper, который выполняет тестирование и помогает определить, какой тип устройства использовать.

Резистор R1 на рис. 7.16 ограничивает максимальный ток, который может протекать через светодиод. В табл. 7.4 приведены данные номиналов токоограничивающих резисторов из справочного листка драйвера MAX72xx для нескольких значений прямого напряжения светодиода и величины протекающего через него тока.

Таблица 7.4. Номиналы токоограничивающих резисторов (из справочного листка драйвера MAX72xx)

Ток Прямое напряжение светодиода
1,5 В 2,0 В 2,5 В 3,0 В 3,5 В
40 мА 12 кОм 12 кОм 11 кОм 10 кОм 10кОм
30 мА 18 кОм 17 кОм 16 кОм 15 кОм 14 кОм
20 мА 30 кОм 28 кОм 26 кОм 24 кОм 22 кОм
10 мА 68 кОм 64 кОм 60 кОм 56 кОм 51 кОм

Величина прямого напряжения зеленых светодиодов матрицы, представленной на рис. 7.16, составляет 2 вольта, а прямой ток — 20 мА. Согласно данным табл. 7.3, для этой комбинации напряжения и тока требуется резистор сопротивлением 38 кОм, но для надежности лучше использовать резистор номиналом 30 или 33 кОм. Конденсаторы емкостью 0,1 и 10 мкФ, подключенные параллельно линиям питания, требуются для того, чтобы предотвратить возникновение импульсных помех при включении и выключении светодиодов матрицы (дополнительная информация по таким конденсаторам приводится в разд. «Использование развязывающих конденсаторов» приложения 3).

Дополнительная информация

Подробная информация по микросхеме драйвера дисплея MAX72xx приведена в ее справочном листке (https://oreil.ly/IH7U7).

Опубликовано

Управление светодиодной матрицей с использованием чарлиплексирования

Раздел из главы 7 “Управление светодиодными устройствами вывода данных” из книги “Arduino. Большая книга рецептов, 3-е изд.” (авторы Джепсон Брайан, Марголис Майкл, Уэлдин Николас Роберт)

ЗАДАЧА

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

РЕШЕНИЕ

Одно из возможных решений этой задачи — использование чарлиплексирования. Чарлиплексирование — это особый вид мультиплексирования, позволяющий увеличить количество устройств (светодиодов), которыми можно управлять одной группой контактов. В листинге 7.13 приводится скетч для управления посредством чарлиплексирования шестью светодиодами, задействовав всего лишь три контакта платы Arduino. Подключение светодиодов показано на рис. 7.11 (методика вычисления значений токоограничивающих резисторов для светодиодов приводится в разд. 7.1).

Использование метода чарлиплексирования для управления шестью светодиодами с помошью трех контактов платы

Рис. 7.11. Использование метода чарлиплексирования для управления шестью светодиодами с помошью трех контактов платы

Листинг 7.13. Управление несколькими светодиодами методом чарлиплексирования

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/*
 
* Скетч Charlieplexing
 
* Последовательно включает и выключает три светодиода.
 
*/
 
int pins[] = {2,3,4}; // Номера контактов для управления светодиодами
 
// Следующие две строки кода вычисляют количество контактов
 
// и светодиодов на основе информации в массиве pins
 
const int NUMBER_OF_PINS = sizeof(pins)/ sizeof(pins[0]);
 
const int NUMBER_OF_LEDS = NUMBER_OF_PINS * (NUMBER_OF_PINS-1);
 
byte pairs[NUMBER_OF_LEDS/2][2] = { {2,1}, {1,0}, {2,0} }; // Сопоставляем
 
// контакты светодиодам
 
void setup()
 
{
 
// Ничего не делаем
 
}
 
void loop()
 
{
 
for(int i=0; i < NUMBER_OF_LEDS; i++)
 
{
 
lightLed(i); // Последовательно включаем каждый светодиод
 
delay(1000);
 
}
 
}
 
// Эта функция включает требуемый светодиод. Нумерация светодиодов
 
// начинается с 0
 
void lightLed(int led)
 
{
 
// Следующие четыре строки кода преобразовывают номер светодиода
 
// в номера контактов
 
int indexA = pairs[led/2][0];
 
int indexB = pairs[led/2][1];
 
int pinA = pins[indexA];
 
int pinB = pins[indexB];
 
// Выключаем все контакты, не подключенные к заданному светодиоду
 
for(int i=0; i < NUMBER_OF_PINS; i++)
 
{
 
if(pins[i] != pinA && pins[i] != pinB)
 
{
 
// Если этот контакт не является одним из требуемых
 
pinMode(pins[i], INPUT); // Задаем для него входной режим работы
 
digitalWrite(pins[i],LOW); // Отключаем повышающий резистор
 
}
 
}
 
// Теперь включаем контакты для требуемого светодиода
 
pinMode(pinA, OUTPUT);
 
pinMode(pinB, OUTPUT);
 
if( led % 2 == 0)
 
{
 
digitalWrite(pinA,LOW);
 
digitalWrite(pinB,HIGH);
 
}
 
else
 
{
 
digitalWrite(pinB,LOW);
 
digitalWrite(pinA,HIGH);
 
}
 
}

Обсуждение работы решения и возможных проблем

Этот метод мультиплексирования называется чарлиплексированием по имени Чарли Аллена (Charlie Allen) из компании Microchip Technology, Inc., который его разработал и опубликовал. Метод основан на том обстоятельстве, что светодиоды включаются только при правильном подключении: когда анод более положительный, чем катод. В табл. 7.3 приводится список комбинаций выходных сигналов на трех контактах управления и соответствующие состояния шести управляемых ими светодиодов (см. рис. 7.9). Обозначение L означает LOW (низкий уровень), H — HIGH (высокий уровень), а i — INPUT (входной режим работы). Контакт, находящийся во входном режиме работы, по сути, отключен от схемы.

Таблица 7.3. Комбинации уровней контактов и соответствующие состояния светодиодов

Контакты Светодиоды
4 3 2 1 2 3 4 5 6
L L L 0 0 0 0 0 0
L H i 1 0 0 0 0 0
H L i 0 1 0 0 0 0
i L H 0 0 1 0 0 0
i H L 0 0 0 1 0 0
L i H 0 0 0 0 1 0
H i L 0 0 0 0 0 1

Количество светодиодов можно увеличить до 12, добавив всего лишь еще один контакт управления. В таком случае первые шесть светодиодов подключаются так же, как и в предыдущем примере (см. рис. 7.11), а дополнительные шесть, как показано на рис. 7.12.

Для управления удвоенным количеством светодиодов предыдущий скетч (см. листинг 7.13) нужно модифицировать, добавив дополнительный контакт в массив pins:

byte pins[] = {2,3,4,5}; // Номера контактов для управления светодиодами

Использование метода чарлиплексирования для управления 12 светодиодами с помошью четырех контактов платыРис. 7.12. Использование метода чарлиплексирования для управления 12 светодиодами
с помошью четырех контактов платы

Также необходимо откорректировать строку кода, сопоставляющую контакты светодиодам, добавив в массив pairs дополнительные пары:

byte pairs[NUMBER_OF_LEDS/2][2] = { {0,1}, {1,2}, {0,2}, {2,3}, {1,3}, {0,3} };

Остальной код остается таким же, но в цикле loop() будут обрабатываться 12 светодиодов, т. к. их количество определяется по количеству элементов массива pins.

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

В листинге 7.14 приводится код скетча, в котором этот подход используется для создания линейного индикатора, включая последовательность светодиодов, количество которых пропорционально значению сигнала датчика, подаваемого на аналоговый контакт A0.

Листинг 7.14. Создание линейного индикатора с помощью модифицированного чарлиплексирования

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/* Скетч создания линейного индикатора с использованием чарлиплексирования
 
*/
 
byte pins[] = {2,3,4};
 
const int NUMBER_OF_PINS = sizeof(pins)/ sizeof(pins[0]);
 
const int NUMBER_OF_LEDS = NUMBER_OF_PINS * (NUMBER_OF_PINS-1);
 
byte pairs[NUMBER_OF_LEDS/2][2] = { {2,1}, {1,0}, {2,0} }; // Сопоставляем
 
// контакты светодиодам
 
int ledStates = 0; // Переменная для хранения состояний для 15 светодиодов
 
int refreshedLed; // Светодиод, состояние которого обновляется
 
void setup()
 
{
 
// Ничего не делаем
 
}
 
void loop()
 
{
 
const int analogInPin = 0; // Входной аналоговый контакт
 
// для подключения потенциометра
 
// Код из решения для создания линейного индикатора
 
int sensorValue = analogRead(analogInPin); // Считываем входной
 
// аналоговый сигнал
 
// Сопоставляем полученное значение количеству светодиодов
 
int ledLevel = map(sensorValue, 0, 1023, 0, NUMBER_OF_LEDS);
 
for (int led = 0; led < NUMBER_OF_LEDS; led++)
 
{
 
if (led < ledLevel )
 
{
 
setState(led, HIGH); // Включаем светодиоды ниже полученного уровня
 
}
 
else
 
{
 
setState(led, LOW); // Выключаем светодиоды выше уровня
 
}
 
}
 
ledRefresh();
 
}
 
void setState( int led, bool state)
 
{
 
bitWrite(ledStates,led, state);
 
}
 
void ledRefresh()
 
{
 
// При каждом вызове обновляем состояние другого светодиода
 
if( refreshedLed++ > NUMBER_OF_LEDS) // Переходим к следующему светодиоду
 
refreshedLed = 0; // Повторяем, начиная с первого светодиода,
 
// если все были обновлены
 
if( bitRead(ledStates, refreshedLed ) == HIGH)
 
lightLed( refreshedLed );
 
else
 
if(refreshedLed == 0) // Выключаем все светодиоды, если контакт 0 выключен
 
for(int i=0; i < NUMBER_OF_PINS; i++)
 
digitalWrite(pins[i],LOW);
 
}
 
// Эта функция идентична функции из скетча решения
 
// Она включает требуемый светодиод. Нумерация светодиодов
 
// начинается с 0
 
void lightLed(int led)
 
{
 
// Следующие четыре строки кода преобразовывают номер светодиода
 
// в номера контактов
 
int indexA = pairs[led/2][0];
 
int indexB = pairs[led/2][1];
 
int pinA = pins[indexA];
 
int pinB = pins[indexB];
 
// Выключаем все контакты, не подключенные к заданному светодиоду
 
for(int i=0; i < NUMBER_OF_PINS; i++)
 
{
 
if(pins[i] != pinA && pins[i] != pinB)
 
{
 
// Если этот контакт не является одним из требуемых
 
pinMode(pins[i], INPUT); // Задаем для него входной режим работы
 
digitalWrite(pins[i],LOW); // Отключаем повышающий резистор
 
}
 
}
 
// Теперь включаем контакты для требуемого светодиода
 
pinMode(pinA, OUTPUT);
 
pinMode(pinB, OUTPUT);
 
if( led % 2 == 0)
 
{
 
digitalWrite(pinA,LOW);
 
digitalWrite(pinB,HIGH);
 
}
 
else
 
{
 
digitalWrite(pinB,LOW);
 
digitalWrite(pinA,HIGH);
 
}
 
}

В скетче используются значения битов переменной ledStates для представления состояния светодиодов (0 — выключен, 1 — включен). Функция refresh() проверяет значение каждого бита и включает светодиоды для битов со значением 1. Вызов функции должен осуществляться быстро и постоянно, чтобы избежать мигания светодиодов.

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

Вместо явного вызова функции refresh() для обновления состояния светодиодов ее можно вызывать посредством прерывания. Прерывания по таймеру подробно рассматриваются в главе 18, но в листинге 7.15 приводится пример одного способа использования прерывания для обновления состояния светодиодов. Для создания прерываний в скетче задействована библиотека FrequencyTimer2, устанавливаемая с помощью Менеджера библиотек (установка библиотек сторонних разработчиков подробно рассматривается в разд. 16.2).

Листинг 7.15. Обновление состояния светодиодов с использованием прерывания

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <FrequencyTimer2.h> // Библиотека для создания прерывания
 
byte pins[] = {2,3,4};
 
const int NUMBER_OF_PINS = sizeof(pins)/ sizeof(pins[0]);
 
const int NUMBER_OF_LEDS = NUMBER_OF_PINS * (NUMBER_OF_PINS-1);
 
byte pairs[NUMBER_OF_LEDS/2][2] = { {2,1}, {1,0}, {2,0} };
 
int ledStates = 0; //Переменная для хранения состояний для 15 светодиодов
 
int refreshedLed; // Светодиод, состояние которого обновляется
 
void setup()
 
{
 
FrequencyTimer2::setPeriod(20000/NUMBER_OF_LEDS); // Задаем период
 
// Следующая строка кода указывает объекту FrequencyTimer2,
 
// какую функцию нужно вызывать (ledRefresh)
 
FrequencyTimer2::setOnOverflow(ledRefresh);
 
FrequencyTimer2::enable();
 
}
 
void loop()
 
{
 
const int analogInPin = 0; // Входной аналоговый контакт
 
// для подключения потенциометра
 
// Код из решения для создания линейного индикатора
 
int sensorValue = analogRead(analogInPin); // Считываем входной аналоговый сигнал
 
// Сопоставляем полученное значение количеству светодиодов
 
int ledLevel = map(sensorValue, 0, 1023, 0, NUMBER_OF_LEDS);
 
for (int led = 0; led < NUMBER_OF_LEDS; led++)
 
{
 
if (led < ledLevel )
 
{
 
setState(led, HIGH); // Включаем светодиоды ниже полученного уровня
 
}
 
else
 
{
 
setState(led, LOW); // Выключаем светодиоды выше уровня
 
}
 
}
 
// Состояние светодиода обновляется теперь не в цикле loop(),
 
// а при обработке прерывания, создаваемого FrequencyTimer2
 
}
 
// Остальной код такой же, что и в предыдущем примере

Объект FrequencyTimer2 устанавливает период прерывания величиной 1666 микросекунд (20 мс, разделенное на количество светодиодов, равное 12). Затем методу FrequencyTimer2::setOnOverflow указывается функция (ledRefresh), которую нужно вызывать при каждом активировании таймера. Библиотека FrequencyTimer2 совместима с ограниченным количеством плат: Arduino Uno (и, скорее всего, с большинством плат с микроконтроллером ATmega328), Arduino Mega и с несколькими версиями платы Teensy. Более подробная информация по этой библиотеке предлагается на веб-сайте PJRC (https://oreil.ly/e-KTE).

Дополнительная информация

Прерывания по таймеру более подробно рассматриваются в главе 18.