ATtiny2313 + MAX7219 матрица 8×8

Ранее в 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)

Comments

Добавить комментарий

Войти с помощью: