Подключение матрицы
Я уже писал о том, как подключать светодиодную матрицу в случае использования одного красного цвета. Для добавления зеленого нам нужно просто включить в схему еще один сдвиговый регистр 74HC595, и посадить его на шину SPI последовательно.
Вспомним расположение ног матрицы:
1-8 - это аноды, ra-rh - красные катоды, ga-gh - зеленые катоды. На каждую из этих групп нам понадобится по одному регистру (всего 3 штуки). В прошлый раз наша установка выглядела так:
видно, что ноги зеленых катодов не задействованы. Теперь пришел их черед! Добавляем еще один регистр и подключаем его к зеленым катодам вот так:
Для большей наглядности я таки-нарисовал схему на этот раз:
На графической схеме синими проводами подключен "анодный" регистр, фиолетовыми - "красный" регистр, а белыми - "зеленый" регистр. Если вглядеться, как они сидят на шине SPI (внимание на желтые провода), можно увидеть, что ближе всего к Arduino "красный" регистр, далее за ним "зеленый", а потом "анодный". Это нам нужно запомнить, чтобы в программе посылать байты в правильном порядке. Сначала на придется посылать "анодный" байт, затем "зеленый" байт, а в конце - "красный", поскольку байты проталкиваются сквозь регистры.
Первый опыт
Напишем простенькую программу, которая будет набегом менять цвет точек. Сначала лучше покажу, как она работает, а потом мы разберем код:
Код:
#include <SPI.h> //подключаем библиотеку SPI
enum { reg = 9 }; //выбираем линию SS регистра на 9-м пине Arduino
void setup(){
SPI.begin(); //инициализируем SPI
//переводим выбранный для передачи пин в режим вывода
pinMode(reg, OUTPUT);
}
static uint8_t spiByte=0xFF;
void loop(){
for (int i=0;i<8;i++){
spiByte=spiByte ^ (1 << i); //рассчитываем анодный байт
digitalWrite(reg, LOW); //начинаем передачу по SPI
SPI.transfer(0xFF); //передаем анодный байт
SPI.transfer(spiByte); //передаем зеленый байт
SPI.transfer(0xFF ^ spiByte); //передаем красный байт
digitalWrite(reg, HIGH); //заканчиваем передачу по SPI
delay(80); //пауза
}
}
enum { reg = 9 }; //выбираем линию SS регистра на 9-м пине Arduino
void setup(){
SPI.begin(); //инициализируем SPI
//переводим выбранный для передачи пин в режим вывода
pinMode(reg, OUTPUT);
}
static uint8_t spiByte=0xFF;
void loop(){
for (int i=0;i<8;i++){
spiByte=spiByte ^ (1 << i); //рассчитываем анодный байт
digitalWrite(reg, LOW); //начинаем передачу по SPI
SPI.transfer(0xFF); //передаем анодный байт
SPI.transfer(spiByte); //передаем зеленый байт
SPI.transfer(0xFF ^ spiByte); //передаем красный байт
digitalWrite(reg, HIGH); //заканчиваем передачу по SPI
delay(80); //пауза
}
}
Слева от каждой колонки я написал цифры, которые соответствуют зеленому байту. Ну и получается так, что при каждом шаге цикла строка
spiByte=spiByte ^ (1 << i);
11111111 ^ 1 = 11111110
11111110 ^ 10 = 11111100
11111100 ^ 100 = 11111000
и так далее.
Динамическая индикация
Займемся более интересными вещами - напишем букву на матрице. Пусть это будет красная буква N на зеленом фоне.
Нам надо создать такую штуку, которая называется битовая маска. Нарисуем поле 8 на 8 и раскрасим его в 2 цвета, как мы представляем себе букву N:
Нам понадобится отдельно красная маска и зеленая. Начнем с красной. Берем первую колонку и начинаем снизу вверх записывать 0, если клетка красная, и 1, если зеленая. Для удобства сразу запишем код для IDE Arduino, в которой бинарные числа записываются с префиксом 0b:
0b00000000,
0b00000000,
0b11111001,
0b11110011,
0b11100111,
0b11001111,
0b00000000,
0b00000000
0b00000000,
0b11111001,
0b11110011,
0b11100111,
0b11001111,
0b00000000,
0b00000000
Теперь сделаем зеленую маску:
0b11111111,
0b11111111,
0b00000110,
0b00001100,
0b00011000,
0b00110000,
0b11111111,
0b11111111
0b11111111,
0b00000110,
0b00001100,
0b00011000,
0b00110000,
0b11111111,
0b11111111
Общая идея динамической индикации в матрице в том, что в определенный момент времени мы включаем светодиоды, подключенные к одному и тому же аноду (т.е. в одной колонке), и поочередно перебираем аноды. Если делать это медленно, то буква будет мерцать. Если мы увеличим частоту полного обновления матрицы до 50 герц, то глаз не увидит моргания, и картинка будет казаться статичной.
Собственно код:
#include <SPI.h> //подключаем библиотеку SPI
enum { reg = 9 }; //выбираем линию SS регистра на 9-м пине Arduino
void setup(){
SPI.begin(); //инициализируем SPI
pinMode(reg, OUTPUT);
}
static uint8_t red[8] = { //красная маска
0b00000000,
0b00000000,
0b11111001,
0b11110011,
0b11100111,
0b11001111,
0b00000000,
0b00000000
},
green[8] = { //зеленая маска
0b11111111,
0b11111111,
0b00000110,
0b00001100,
0b00011000,
0b00110000,
0b11111111,
0b11111111
};
void loop(){
for(int i=0; i<8; i++){
digitalWrite(reg, LOW); //начинаем передачу по SPI
SPI.transfer(1 << i); //выбираем анод
SPI.transfer(green[i]); //передаем зеленый байт
SPI.transfer(red[i]); //передаем красный байт
digitalWrite(reg, HIGH); //заканчиваем передачу по SPI
delay(2); //пауза
}
}
enum { reg = 9 }; //выбираем линию SS регистра на 9-м пине Arduino
void setup(){
SPI.begin(); //инициализируем SPI
pinMode(reg, OUTPUT);
}
static uint8_t red[8] = { //красная маска
0b00000000,
0b00000000,
0b11111001,
0b11110011,
0b11100111,
0b11001111,
0b00000000,
0b00000000
},
green[8] = { //зеленая маска
0b11111111,
0b11111111,
0b00000110,
0b00001100,
0b00011000,
0b00110000,
0b11111111,
0b11111111
};
void loop(){
for(int i=0; i<8; i++){
digitalWrite(reg, LOW); //начинаем передачу по SPI
SPI.transfer(1 << i); //выбираем анод
SPI.transfer(green[i]); //передаем зеленый байт
SPI.transfer(red[i]); //передаем красный байт
digitalWrite(reg, HIGH); //заканчиваем передачу по SPI
delay(2); //пауза
}
}
Почему пауза 2? Посчитаем частоту обновления матрицы как 1/(0,002*8)=62,5 Гц. Этого хватает, чтобы глазу свечение казалось непрерывным. Если поставить 3, то частота будет 1/(0,003*8)=41,(6) - уже меньше.
Анимация
Нарисуем нашу Феррари:
и аккуратно запишем две битовые маски для нее, заполнив первые восемь байт единицами, чтобы тачка как бы выезжала с правого краю:
static uint8_t red[40] = { //красная маска
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0b11001111,
0b11000111,
0b11100111,
0b11110111,
0b11110111,
0b11100111,
0b10000011,
0b10000101,
0b10000110,
0b10010110,
0b10000000,
0b10000110,
0b10000110,
0b10010110,
0b11100000,
0b11110011,
0b11110011,
0b11100001,
0b11000001,
0b11111111,
0b11011111,
0b11111111,
0b11110111,
0b01001001,
0b10101011,
0b01011011,
0b11100101,
0b11011111,
0b11110111,
0b11111111,
0b11110111,
0b11101111
},
green[40]={ //зеленая маска
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0b11110111,
0b11111111,
0b10011111,
0b00001111,
0b00001111,
0b10011111,
0b11111111,
0b11111011,
0b11111001,
0b11111001,
0b11111111,
0b11111001,
0b11111001,
0b11111001,
0b10011111,
0b00001111,
0b00001111,
0b10011111,
0b11111111,
0b11011111,
0b10101111,
0b10000111,
0b00001011,
0b10110111,
0b01010100,
0b10100100,
0b10011011,
0b11100111,
0b11101111,
0b11100111,
0b11101111,
0b11111111
};
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0b11001111,
0b11000111,
0b11100111,
0b11110111,
0b11110111,
0b11100111,
0b10000011,
0b10000101,
0b10000110,
0b10010110,
0b10000000,
0b10000110,
0b10000110,
0b10010110,
0b11100000,
0b11110011,
0b11110011,
0b11100001,
0b11000001,
0b11111111,
0b11011111,
0b11111111,
0b11110111,
0b01001001,
0b10101011,
0b01011011,
0b11100101,
0b11011111,
0b11110111,
0b11111111,
0b11110111,
0b11101111
},
green[40]={ //зеленая маска
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0b11110111,
0b11111111,
0b10011111,
0b00001111,
0b00001111,
0b10011111,
0b11111111,
0b11111011,
0b11111001,
0b11111001,
0b11111111,
0b11111001,
0b11111001,
0b11111001,
0b10011111,
0b00001111,
0b00001111,
0b10011111,
0b11111111,
0b11011111,
0b10101111,
0b10000111,
0b00001011,
0b10110111,
0b01010100,
0b10100100,
0b10011011,
0b11100111,
0b11101111,
0b11100111,
0b11101111,
0b11111111
};
int timer, timerPrev=0; //для подсчета времени
int shift=0; //текущая величина сдвига
int len=40; //длина изображения
int shift=0; //текущая величина сдвига
int len=40; //длина изображения
Действуем вот таким макаром:
void loop(){
timer = millis(); //засекам время
//если прошло 80 мс после прошлого фрейма, включаем следующий
if (timer-timerPrev>80){
shift++; //увеличиваем сдвиг на 1
if (shift==len)shift=0;
timerPrev=timer;
}
for(int i=0; i<8; i++){
digitalWrite(reg, LOW);
SPI.transfer(1 << i);
SPI.transfer(green[i+shift > len-1 ? i+shift-len : i+shift]);
SPI.transfer(red[i+shift > len-1 ? i+shift-len : i+shift]);
digitalWrite(reg, HIGH);
delay(2);
}
}
timer = millis(); //засекам время
//если прошло 80 мс после прошлого фрейма, включаем следующий
if (timer-timerPrev>80){
shift++; //увеличиваем сдвиг на 1
if (shift==len)shift=0;
timerPrev=timer;
}
for(int i=0; i<8; i++){
digitalWrite(reg, LOW);
SPI.transfer(1 << i);
SPI.transfer(green[i+shift > len-1 ? i+shift-len : i+shift]);
SPI.transfer(red[i+shift > len-1 ? i+shift-len : i+shift]);
digitalWrite(reg, HIGH);
delay(2);
}
}
Ваши вопросы оставляйте в комментариях или присылайте по почте. Успехов в ваших проектах!
Далее по теме Arduino и SPI
Very interesting!
ОтветитьУдалитьHow to do with multiple modules? Thank you
I already made a comment but I got no answer!
ОтветитьУдалить