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

Управление несколькими цветными светодиодами одним контактом платы Arduino

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

Arduino. Большая книга рецептов, 3-издание

ЗАДАЧА

Требуется управлять цветом нескольких RGB-светодиодов, используя один контакт платы Arduino.

РЕШЕНИЕ

Для решения этой задачи требуется использовать специальный модуль RGB-светодиодов со встроенным микроконтроллером — кольцо NeoPixel. Подключение модуля NeoPixel и потенциометра для управления его цветом к плате Arduino показано на рис. 7.7.

C:\Users\acer\Documents\#Electronics\Магазин электроники\фрагменты из книг\2788. Arduino Cookbook. Recipes to Begin, Expand, and Enhance\pic\07\7.7.png

Рис. 7.7. Подключение модуля NeoPixel и потенциометра к плате Arduino

При использовании платы Arduino с питанием 3,3 В как потенциометр, так и положительный вывод модуля NeoPixel следует подключить к контакту 3V3 платы Arduino, а не к контакту 5V.

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

Листинг 7.4. Работа с модулем RGB-светодиодов NeoPixels

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
/*
* Скетч SimplePixel
* Меняет свет модуля RGB-светодиодов в зависимости от значения
сигнала датчика
*/
#include <Adafruit_NeoPixel.h>
const int sensorPin = A0; // Номер контакта для подключения датчика
const int ledPin = 6; // Номер контакта для подключения модуля RGB-светодиодов
const int count = 8; // Количество светодиодов в модуле
// Объявляем экземпляр объекта модуля RGB-светодиодов
Adafruit_NeoPixel leds = Adafruit_NeoPixel(count, ledPin, NEO_GRB + NEO_KHZ800);
 
void setup()
{
leds.begin(); // Инициализируем экземпляр модуля светодиодов
for (int i = 0; i < count; i++)
{
leds.setPixelColor(i, leds.Color(0,0,0)); // Выключаем все светодиоды модуля
}
leds.show(); // Обновляем состояние модуля новыми значениями
// отдельных RGB-светодиодов (все выключены)
}
 
void loop()
{
static unsigned int last_reading = -1;
int reading = analogRead(sensorPin);
if (reading != last_reading)
{
// Если значение входного сигнала изменилось,
// сопоставляем новое значение диапазону цветов модуля NeoPixel
unsigned int mappedSensorReading = map(reading, 0, 1023, 0, 65535);
// Обновляем состояние отдельных RGB-светодиодов модуля
// с небольшой задержкой между ними, чтобы создать эффект скольжения
for (int i = 0; i < count; i++)
{
leds.setPixelColor(i, leds.gamma32(
leds.ColorHSV(mappedSensorReading, 255, 128)));
leds.show();
delay(25);
}
last_reading = reading;
}
}

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

Скетч решения управляет модулем NeoPixel компании Adafruit, содержащим восемь RGB-светодиодов, расположенных в виде кольца. Скетч можно использовать с другими версиями модуля NeoPixel, содержащими большее количество RGB-светодиодов (пикселов), присвоив переменной numOfLeds соответствующее значение. Но в таком случае следует иметь в виду, что каждый светодиод может потреблять до 60 мА тока (если задать для него белый цвет на полную яркость). Порт USB может запитать до восьми светодиодов, но для большего количества питание на модуль NeoPixel необходимо подавать с отдельного мощного источника питания напряжением 5 В. При этом шину «земли» этого источника питания необходимо подключить к шине «земли» платы Arduino. Кроме того, для плат Arduino с напряжением питания 3,3 В, напряжение питания модуля NeoPixel не должно превышать 3,7 В, поскольку напряжение сигнала данных модуля NeoPixel должно быть примерно равно напряжению питания модуля. При использовании для модуля NeoPixel отдельного источника питания, необходимо установить конденсатор емкостью 1000 мкФ между положительной и отрицательной («землей») шинами источника питания для защиты светодиодов модуля. При этом следует обеспечить правильную полярность подключения этого конденсатора.

В скетче объявляется переменная leds объекта модуля NeoPixel с помощью следующей строки кода:

1
Adafruit_NeoPixel leds = Adafruit_NeoPixel(count, ledPin, NEO_GRB + NEO_KHZ800);

В результате исполнения этой строки в памяти платы создается структура для хранения цвета каждого светодиода и взаимодействия с модулем. В параметрах объявления указывается количество светодиодов в модуле (count), номер контакта платы Arduino (ledPin), к которому подключается линия управления модуля, а также тип используемого модуля (NEO_GRB + NEO_KHZ800). При использовании другого светодиодного модуля нужно проверить информацию в документации библиотеки и используемого модуля, чтобы узнать, не требуется ли применить иные параметры.

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

Цвет отдельных светодиодов устанавливается с помощью метода (функции) led.setPixelColor(). В параметрах функции передается номер требуемого светодиода (нумерация которых начинается со значения 0) и требуемый цвет. Данные на светодиод передаются посредством функции led.show(). Прежде чем вызывать функцию led.show(), можно изменить значения цвета нескольких светодиодов, которые потом будут применены одновременно с этой функцией. Значения, которые не были изменены, останутся прежними. При создании экземпляра объекта Adafruit_NeoPixel все значения цветов инициализируются значением 0.

Библиотека Adafruit NeoPixel содержит собственную функцию ColorHSV() для преобразования цвета спектра в значения RGB. Функции передаются три параметра: цвет спектра, насыщенность цвета и яркость, в указанном порядке. Результат, возвращенный функцией ColorHSV(), обрабатывается функцией gamma32(), которая преобразовывает его, чтобы компенсировать разницу между представлением цвета компьютерами и его восприятием людьми.

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

Если используемый модуль не поддерживается библиотекой Adafruit

В ранних светодиодных модулях использовалась микросхема WS2811, на смену которой пришли разные другие версии — например, WS2812, WS2812B и APA102. Если ваш светодиодный модуль не поддерживается библиотекой Adafruit, попробуйте использовать библиотеку Fast LED (http://fastled.io).

Совместимая с Arduino плата Teensy 3.x и более поздние ее версии (https://www.pjrc.com/teensy) может управлять восемью модулями и благодаря своему эффективному аппаратному и программному обеспечению способна реализовывать высококачественную анимацию.

Как уже упоминалось ранее, пикселы доступны на рынке как в виде отдельных светодиодов, так и установленные на гибких лентах на разных расстояниях друг от друга (указывается в количестве светодиодов на метр или фут). Компания Adafruit выпускает под торговым именем NeoPixel широкий диапазон светодиодных модулей на печатных платах разных форм-факторов: круги, короткие полоски или панели.

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

Более подробная информация по модулям NeoPixel приводится в руководстве Uberguide компании Adafruit (https://oreil.ly/zgAVa).

Веб-страница библиотеки OctoWS2811 (https://oreil.ly/yxGBM) поддерживает работу со светодиодными модулями платы Teensy и содержит удачные схемы подключения источников питания для большого количества светодиодов, а также программу movie2serial на языке Processing, которая извлекает данные из файла видео, указанного в программе, и отображает это видео на панели из светодиодных полос.

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

Светодиодная матрица 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.