ЗАДАЧА
Требуется управлять светодиодной матрицей, задействовав для этого как можно меньшее количество контактов платы 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}; // Номера контактов для управления светодиодами
Рис. 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.
Вам могут понадобиться
-
4-х разрядный семисегментный индикатор на базе драйвера TM1637
120₽96₽ -
Arduino. Большая книга рецептов, 3-е изд.
1925 ₽
1578 ₽ -
Умный дом на базе Arduino. Большой набор + КНИГА
15568 ₽
12454 ₽ -
Светодиодная матрица 8*8 с модулем MAX7219
140₽112₽