Ранее в http://rcl-radio.ru/?p=98749 был описан пример использования восьми разрядного семисегментного индикатора с последовательным интерфейсом на базе драйвера MAX7219. В этой статье будет аналогичный проект, но только с использованием светодиодной матрицы 8х8 на базе драйвера MAX7219. У меня в наличии имеется модуль который содержит сразу 4 ячейки матрицы 8х8, что позволяет его использовать например для создания простых часов. К качестве управления матрицей будет использован микроконтроллер ATtiny2313.
ATtiny2313 достаточно простой и бюджетный микроконтроллер, имеет всего 2048 байт внутрисистемной энергонезависимой ФЛЭШ-памяти и 128 байт внутренней SRAM памяти. Небольшой объем памяти накладывает определенные ограничения в использовании микроконтроллера, но его достаточно для выполнения простых операций. Для программирования ATtiny2313 можно использовать среду программирования Arduino IDE.
Поддержка Arduino IDE — http://rcl-radio.ru/?p=94074
MAX7219 — драйвер восьми разрядного цифрового LED индикатора с последовательным интерфейсом. Драйвер может управлять восемью семисегментными индикаторами с точкой, либо отдельно 64 светодиодами в LED панелях с общим катодом.
Драйвер MAX7219 управляется по трехпроводной последовательной шине Microwire (3-Wire). Драйвер допускают каскадирование для управления большим числом индикаторов. Каждый из разрядов индикатора имеет независимую адресацию и его содержимое может быть обновлено без необходимости перезаписи всего индикатора. ИС MAX7219 также позволяют пользователю определять режим декодирования каждого разряда.
Для начала необходимо собрать схему показанную на рисунке:
Так же если у Вас нет в наличии ATtiny2313, то можно использовать плату Arduino Nano или UNO, при этом подключение MAX7219 будет иметь следующую распиновку:
PD2 | D2 (ARDUINO) |
PD3 | D3 (ARDUINO) |
PD4 | D4 (ARDUINO) |
Управляется MAX7219 через 16 битные регистры, старшим битом вперёд. В регистре первые 8 бит отводится под адрес регистра (фактически используются только 4-е младшие бита), остальные под 8 бит под данные.
Регистры управления драйвером MAX7219
Описание регистров:
- 0х00 — не используется
- 0х01 … 0х08 — регистры знакомест (вывод данных на индикаторы, в таблице слева указаны биты в режиме декодирования, справа показаны биты при отключенном режиме декодирования)
- 0х09 — режим декодирования (0х00 — отключен режим декодирования, 0xFF — режим декодирования активен)
- 0х0А — интенсивность свечения (яркость 16 значений)
- 0х0B — выбор кол-ва отображаемых знакомест
- 0x0C — спящий режим (0 — OFF, 1 — ON)
- 0x0F — тест
Для активации работы драйвера MAX7219 необходимо в регистрах указать следующие данные:
- адрес 0x0F, данные 0x00 – тест выключен
- адрес 0x0С, данные 0x01 – выйти из сна
- адрес 0x0B, данные 0x07 – кол-во знакомест 8
- адрес 0x09, данные 0x00 – дешифратор отключен
- адрес 0x0A, данные 0x04 – яркость уровень 4 из 16
Плата MAX7219 с индикаторами помимо питания имеет три контакта: DIN, CS, CLK
При логической единице на входе CS драйвер MAX7219 начинает воспринимать команды, на вход CLK подаются синхроимпульсы, а на вход DIN биты данных, которые считываются при нарастающем фронте синхроимпульса.
При использовании матрицы режим декодирования должен быть отключен, в регистре 0x09 должен быть 0. Так же при использовании несколько драйверов MAX7219, отправлять пакеты данных нужно столько раз сколько у Вас драйверов. Сам пакет состоит из одной строки в которой содержится по 2 байта на один драйвер. Например если матрица имеет 4 драйвера, то необходимо 4 раза отправить данные для регистра 0х01 для верхней строки, потом для так же для всех оставшихся 7 строк. Так же следует учитывать, что перед отправкой пакета данных нужно отпустить линию CS только один раз, а при окончании отправки данных поднять линию CS. Линии CS и CLK всех драйверов в матрице соединены вместе, а выход DOUT соединен со входом DIN в каждом следующем драйвере.
Далее подробно рассмотрим пример тестового запуска матрицы. Для примера выведем на матрицу следующую картинку:
#define DIN PD2 #define CS PD3 #define CLK PD4 void setup() { delay(2); DDRD |= (1 << DIN) | (1 << CS) | (1 << CLK); max7219(0x0F, 0, 0, 0, 0);// тест выкл. max7219(0x0C, 1, 1, 1, 1);// вкл. индик. max7219(0x0A, 1, 1, 1, 1);// яркость max7219(0x09, 0, 0, 0, 0);// дешифраторы выкл. max7219(0x0B, 7, 7, 7, 7);// кол-во разрядов cl(); delay(1000); } void loop() { max7219(1, 0xFF, 0xFF, 0xFF, 0xFF); max7219(2, 0x80, 0x00, 0x00, 0x01); max7219(3, 0x80, 0x00, 0x00, 0x01); max7219(4, 0x80, 0xFF, 0xFF, 0x01); max7219(5, 0x80, 0xFF, 0xFF, 0x01); max7219(6, 0x80, 0x00, 0x00, 0x01); max7219(7, 0x80, 0x00, 0x00, 0x01); max7219(8, 0xFF, 0xFF, 0xFF, 0xFF); delay(1000); } void max7219(byte reg_n, byte h1, byte h2, byte h3, byte h4){ PORTD &=~(1 << CS);WriteBit16(reg_n,h1);WriteBit16(reg_n,h2);WriteBit16(reg_n,h3);WriteBit16(reg_n,h4);PORTD |=(1 << CS); } void WriteBit16(byte reg, byte data){ for(char i = 7; i >= 0; i--){ PORTD &= ~(1 << CLK); if(((reg >> i) & 1) == 1){PORTD |= (1 << DIN);}else{PORTD &= ~(1 << DIN);} PORTD |=(1 << CLK);} for(char i = 7; i >= 0; i--){ PORTD &= ~(1 << CLK); if(((data >> i) & 1) == 1){PORTD |= (1 << DIN);}else{PORTD &= ~(1 << DIN);} PORTD |=(1 << CLK);} PORTD &= ~(1 << CLK);PORTD |= (1 << DIN); } void cl(){for(char i=1;i<=8;i++){max7219(i, 0, 0, 0, 0);}}
Скетч использует 824 байт (40%) памяти устройства. Всего доступно 2048 байт.
Глобальные переменные используют 9 байт (7%) динамической памяти, оставляя 119 байт для локальных переменных. Максимум: 128 байт.
Отправка данных:
void WriteBit16(byte reg, byte data){ for(char i = 7; i >= 0; i--){ PORTD &= ~(1 << CLK); if(((reg >> i) & 1) == 1){PORTD |= (1 << DIN);}else{PORTD &= ~(1 << DIN);} PORTD |=(1 << CLK);} for(char i = 7; i >= 0; i--){ PORTD &= ~(1 << CLK); if(((data >> i) & 1) == 1){PORTD |= (1 << DIN);}else{PORTD &= ~(1 << DIN);} PORTD |=(1 << CLK);} PORTD &= ~(1 << CLK);PORTD |= (1 << DIN); }
Показанный выше код реализует работу шины SPI, как видно сначала идет запись адреса регистра, а после запись данных в драйвер MAX7219. При этом линия CS в этом коде не задействована.
Для 4-х драйверов необходимо отправить сразу четыре пакета для одной строки, следующий код реализует эту возможность:
void max7219(byte reg_n, byte h1, byte h2, byte h3, byte h4){ PORTD &=~(1 << CS);WriteBit16(reg_n,h1);WriteBit16(reg_n,h2);WriteBit16(reg_n,h3);WriteBit16(reg_n,h4);PORTD |=(1 << CS);}
Как видно, в этом коде уже используется линия CS, один раз в начале отправки пакетов данных и один раз после завершения отправки пакетов.
Функция max7219 реализуем управление светодиодами одной строки состоящей их 32 светодиодов, для управления всеми светодиодами матрицы, эту функцию необходимо использовать 8 раз указывая свой номер строки.
Инициация матрицы:
max7219(0x0F, 0, 0, 0, 0);// тест выкл. max7219(0x0C, 1, 1, 1, 1);// вкл. индик. max7219(0x0A, 1, 1, 1, 1);// яркость max7219(0x09, 0, 0, 0, 0);// дешифраторы выкл. max7219(0x0B, 7, 7, 7, 7);// кол-во разрядов
Так же как при управлениями строками матрицы, все настройки драйвера необходимо отправлять столько раз, сколько у Вас используется драйверов. Перед инициацией матрицы рекомендуется делать задержку в 2 мкс.
Сам рисунок показанный на картинке выводит следующий код:
max7219(1, 0xFF, 0xFF, 0xFF, 0xFF); max7219(2, 0x80, 0x00, 0x00, 0x01); max7219(3, 0x80, 0x00, 0x00, 0x01); max7219(4, 0x80, 0xFF, 0xFF, 0x01); max7219(5, 0x80, 0xFF, 0xFF, 0x01); max7219(6, 0x80, 0x00, 0x00, 0x01); max7219(7, 0x80, 0x00, 0x00, 0x01); max7219(8, 0xFF, 0xFF, 0xFF, 0xFF);
Так как одна строка матрицы воспринимается как одно целое, то управлять одной строкой указывая четыре байта не очень удобно, намного проще указывать один 32 разрядный байт.
#define DIN PD2 #define CS PD3 #define CLK PD4 void setup() { delay(2); DDRD |= (1 << DIN) | (1 << CS) | (1 << CLK); max7219_L(0x0F, 0);// тест выкл. max7219_L(0x0C, 0x01010101);// вкл. индик. max7219_L(0x0A, 0x01010101);// яркость max7219_L(0x09, 0);// дешифраторы выкл. max7219_L(0x0B, 0x07070707);// кол-во разрядов cl(); delay(1000); } void loop() { max7219_L(1, 0xFFFFFFFF); max7219_L(2, 0x80000001); max7219_L(3, 0x80000001); max7219_L(4, 0x80FFFF01); max7219_L(5, 0x80FFFF01); max7219_L(6, 0x80000001); max7219_L(7, 0x80000001); max7219_L(8, 0xFFFFFFFF); delay(100); } void max7219_L(byte reg_n, long h){ byte h1 = ((h & 0xFF000000) >> 24); byte h2 = ((h & 0x00FF0000) >> 16); byte h3 = ((h & 0x0000FF00) >> 8); byte h4 = (h & 0x000000FF); PORTD &=~(1 << CS);WriteBit16(reg_n,h1);WriteBit16(reg_n,h2);WriteBit16(reg_n,h3);WriteBit16(reg_n,h4);PORTD |=(1 << CS); } void WriteBit16(byte reg, byte data){ for(char i = 7; i >= 0; i--){ PORTD &= ~(1 << CLK); if(((reg >> i) & 1) == 1){PORTD |= (1 << DIN);}else{PORTD &= ~(1 << DIN);} PORTD |=(1 << CLK);} for(char i = 7; i >= 0; i--){ PORTD &= ~(1 << CLK); if(((data >> i) & 1) == 1){PORTD |= (1 << DIN);}else{PORTD &= ~(1 << DIN);} PORTD |=(1 << CLK);} PORTD &= ~(1 << CLK);PORTD |= (1 << DIN); } void cl(){for(char i=1;i<=8;i++){max7219_L(i, 0);}}
Используя для управления строкой один байт, проще выводить цифры и символы, делать бегущую строку.
max7219_L(1, 0xFFFFFFFF); max7219_L(2, 0x80000001); max7219_L(3, 0x80000001); max7219_L(4, 0x80FFFF01); max7219_L(5, 0x80FFFF01); max7219_L(6, 0x80000001); max7219_L(7, 0x80000001); max7219_L(8, 0xFFFFFFFF);
Работает функция max7219_L аналогично функции max7219 из первого примера кода, но в функции max7219_L число LONG перед оправкой в драйвер разбирается на четыре 8-битных байта:
void max7219_L(byte reg_n, long h){ byte h1 = ((h & 0xFF000000) >> 24); byte h2 = ((h & 0x00FF0000) >> 16); byte h3 = ((h & 0x0000FF00) >> 8); byte h4 = (h & 0x000000FF); PORTD &=~(1 << CS);WriteBit16(reg_n,h1);WriteBit16(reg_n,h2);WriteBit16(reg_n,h3);WriteBit16(reg_n,h4);PORTD |=(1 << CS);}
Для примера использования матрицы основанной на 4-х драйверах MAX7219, соберем простые часы:
// ATtiny2313 20 MHz #define DIN PD2 #define CS PD3 #define CLK PD4 byte d0,d1,d2,d3,i1,bb,i3; unsigned int i2; byte hh,mm; byte dat[10][8] = {{B00111100,B01100110,B01101110,B01111110,B01110110,B01100110,B00111100,B00000000}, {B00011000,B00111000,B00011000,B00011000,B00011000,B00011000,B00011000,B00000000}, {B00111100,B01100110,B00000110,B00011100,B00110000,B01100000,B01111110,B00000000}, {B00111100,B01100110,B00000110,B00011100,B00000110,B01100110,B00111100,B00000000}, {B00001110,B00011110,B00110110,B01100110,B01111110,B00000110,B00000110,B00000000}, {B01111110,B01100000,B01111100,B00000110,B00000110,B01100110,B00111100,B00000000}, {B00011100,B00110000,B01100000,B01111100,B01100110,B01100110,B00111100,B00000000}, {B01111110,B00000110,B00000110,B00001100,B00011000,B00011000,B00011000,B00000000}, {B00111100,B01100110,B01100110,B00111100,B01100110,B01100110,B00111100,B00000000}, {B00111100,B01100110,B01100110,B00111110,B00000110,B00001100,B00111000,B00000000}}; void setup() { TCCR1A = 0; TCCR1B = 0; OCR1A = 31250; // 0.1 s TCCR1B |= (1 << WGM12); TCCR1B |= (1 << CS11) | (1 << CS10); // 64 TIMSK |= (1 << OCIE1A); delayMicroseconds(2000); DDRD |= (1 << DIN) | (1 << CS) | (1 << CLK); PORTB |= (1 << 2) | (1 << 3); max7219_L(0x0F, 0);// тест выкл. max7219_L(0x0C, 0x01010101);// вкл. индик. max7219_L(0x0A, 0x01010101);// яркость max7219_L(0x09, 0);// дешифраторы выкл. max7219_L(0x0B, 0x06060606);// кол-во разрядов } void loop() { d0=hh/10; d1=hh%10; d2=mm/10; d3=mm%10; if(((PINB >> 2) & 1) == 0){mm++; if(mm>59){mm = 0;}del();} if(((PINB >> 3) & 1) == 0){hh++; if(hh>23){hh = 0;}del();} if(i1 <= 5)bb = 3; else bb = 0; max7219_L(1, (unsigned long)dat[d0][0]<<24 | (unsigned long)dat[d1][0]<<17 | (unsigned int)dat[d2][0]<<7 | dat[d3][0]); max7219_L(2, (unsigned long)dat[d0][1]<<24 | (unsigned long)dat[d1][1]<<17 | (unsigned int)dat[d2][1]<<7 | dat[d3][1] | (int long)bb << 15); max7219_L(3, (unsigned long)dat[d0][2]<<24 | (unsigned long)dat[d1][2]<<17 | (unsigned int)dat[d2][2]<<7 | dat[d3][2] | (int long)bb << 15); max7219_L(4, (unsigned long)dat[d0][3]<<24 | (unsigned long)dat[d1][3]<<17 | (unsigned int)dat[d2][3]<<7 | dat[d3][3]); max7219_L(5, (unsigned long)dat[d0][4]<<24 | (unsigned long)dat[d1][4]<<17 | (unsigned int)dat[d2][4]<<7 | dat[d3][4] | (int long)bb << 15); max7219_L(6, (unsigned long)dat[d0][5]<<24 | (unsigned long)dat[d1][5]<<17 | (unsigned int)dat[d2][5]<<7 | dat[d3][5] | (int long)bb << 15); max7219_L(7, (unsigned long)dat[d0][6]<<24 | (unsigned long)dat[d1][6]<<17 | (unsigned int)dat[d2][6]<<7 | dat[d3][6]); i3=0; }//loop void max7219_L(byte reg_n, unsigned long h){ byte h1 = h >> 24; byte h2 = h >> 16; byte h3 = h >> 8; byte h4 = h; PORTD &=~(1 << CS);WriteBit16(reg_n,h1);WriteBit16(reg_n,h2);WriteBit16(reg_n,h3);WriteBit16(reg_n,h4);PORTD |=(1 << CS); } void WriteBit16(byte reg, byte data){ for(char i = 7; i >= 0; i--){ PORTD &= ~(1 << CLK); if(((reg >> i) & 1) == 1){PORTD |= (1 << DIN);}else{PORTD &= ~(1 << DIN);} PORTD |=(1 << CLK);} for(char i = 7; i >= 0; i--){ PORTD &= ~(1 << CLK); if(((data >> i) & 1) == 1){PORTD |= (1 << DIN);}else{PORTD &= ~(1 << DIN);} PORTD |=(1 << CLK);} PORTD &= ~(1 << CLK);PORTD |= (1 << DIN); } ISR(TIMER1_COMPA_vect){ i2++;i1++; if(i1 > 9){i1 = 0;} if(i2 > 599){mm++;i2 = 0;} if(mm > 59){hh++;mm = 0;} if(hh > 23){hh = 0;} } void del(){while(i3<199){i3++;delayMicroseconds(2000);}}
Скетч использует 2044 байт (99%) памяти устройства. Всего доступно 2048 байт.
Глобальные переменные используют 100 байт (78%) динамической памяти, оставляя 28 байт для локальных переменных. Максимум: 128 байт.
Скетч часов содержит массив цифр от 0 до 9:
byte dat[10][8] = {{B00111100,B01100110,B01101110,B01111110,B01110110,B01100110,B00111100,B00000000}, {B00011000,B00111000,B00011000,B00011000,B00011000,B00011000,B00011000,B00000000}, {B00111100,B01100110,B00000110,B00011100,B00110000,B01100000,B01111110,B00000000}, {B00111100,B01100110,B00000110,B00011100,B00000110,B01100110,B00111100,B00000000}, {B00001110,B00011110,B00110110,B01100110,B01111110,B00000110,B00000110,B00000000}, {B01111110,B01100000,B01111100,B00000110,B00000110,B01100110,B00111100,B00000000}, {B00011100,B00110000,B01100000,B01111100,B01100110,B01100110,B00111100,B00000000}, {B01111110,B00000110,B00000110,B00001100,B00011000,B00011000,B00011000,B00000000}, {B00111100,B01100110,B01100110,B00111100,B01100110,B01100110,B00111100,B00000000}, {B00111100,B01100110,B01100110,B00111110,B00000110,B00001100,B00111000,B00000000}};
Каждая строка массива описывает одну цифру. Создавать свои различные символы очень просто, достаточно байты массива расположить не в строку, а в столбик, после чего все станет понятно. Например цифра 5:
B01111110 -******- B01100000 -**----- B01111100 -*****-- B00000110 -----**- B00000110 -----**- B01100110 -**--**- B00111100 --****-- B00000000 --------
В часах для отсчета времени используется таймер_1, переполнение которого происходит с интервалом 0,1 сек. Настройки таймера рассчитаны на работу с тактовой частотой в 20 МГц. Если у Вас другая тактовая частота, то необходимо подобрать параметр регистра сравнения OCR1A.
Делитель таймера установлен на 64, для определения параметра регистра сравнения воспользуйтесь следующим примером:
F = 20 000 000 / 64 / 31250 = 10 Гц (Т = 0,1 сек)
Установка времени часов осуществляется двумя кнопками (HH:MM)
На такой матрице GPS часики бы сделать)
Как раз и матрица с приёмником neo-6 есть…