Hiển thị hình ảnh với LED MATRIX 8x8

Xin chào các bạn, bài viết hôm nay của mình sẽ giới thiệu về cách hiển thị hình ảnh trên LED MATRIX 8x8 với cổng Serial.

Nội dung chính

  • Cách điều khiển LED MATRIX 8x8.
  • Ôn lại cách sử dụng IC 74HC595.

Phần cứng 

  • Arduino UNO R3.
  • 1 LED MATRIX 8x8. (mình sử dụng loại row anode).
  • 2 IC 74HC595.
  • 8 điện trở 560 om
  • Dây dẫn (nhiều mới đủ nhé!^^).

Giới thiệu

LED MATRIX 8x8 đơn giản chỉ là 64 con LED được sắp xếp với nhau theo dạng ma trận, thành 8 hàng và 8 cột, tức là 16 chân. Vì mỗi loại LED MATRIX có sơ đồ chân riêng nên các bạn hãy tra cứu datasheet của nó để có thể lắp mạch chính xác nhé! (mà kiểm tra từng chân của LED matrix cũng biết được laugh).

Trong bài viết này mình sử dụng LED matrix "row anode", có nghĩa là các chân điều khiển hàng của ma trận chính là cực dương của LED. Đây là hình minh họa:

Để LED MATRIX hoạt động, chúng ta chỉ cần cấp dòng điện vào các chân ROWs và nối các chân COLUMNS với GND.

Về IC 74HC595, đã có 1 bài viết hướng dẫn rất chi tiết, các bạn có thể tham khảo ở Điều khiển 8 đèn LED sáng theo ý muốn

Lắp mạch

Mình sẽ giải thích cách lắp mạch ngay:

  • Để điều khiển LED matrix, mình sử dụng 2 con IC 74HC595, 1 để điều khiển 8 chân row, 1 để điều khiển 8 chân column.
  • Trước mỗi chân row, mình có gắn thêm 1 con trở 560 om để hạn dòng cho LED.
  • LED matrix gồm 8 chân row: từ row 0-->row 7; 8 chân column: từ column 0 --> column 7. Trong sơ đồ mạch phía trên, 8 chân ở top của LED matrix (từ trái qua phải) gồm: column 0, column 1, row 1, column 7, row 3, column 2, column 4, row 0. 8 chân ở bottom của LED matrix (từ trái qua phải) gồm: row 4, row 6, column 6, column 5, row 7, column 3, row 5, row 2.
  • Ở đây mình lắp IC 74HC595 control columns nối tiếp theo sau IC 74HC595 control rows, do đó, chân 14 của IC 74HC595 control columns sẽ nối với chân 9 của IC 74HC595 control rows.
  • Chân 11 (CLOCK), 12 (LATCH) của 2 IC nối với nhau, và nối với pin 10,11 của Arduino.
  • Chân 14 của IC 74HC595 control rows sẽ nối với pin 12 của Arduino.
  • Chân số 10 và 16 của 2 IC sẽ nối VCC, chân số 8 và 13 sẽ nối GND.

Lập trình

const int DATA = 12;// pin 12 của Arduino nối với pin DATA của 74HC595
const int CLOCK = 10;//pin 10 của Arduino nối với pin CLOCK của 74HC595
const int LATCH = 11;//pin 11 của Arduino nối với pin LATCH của 74HC595
/* hàng và cột của LED matrix*/
int row[] = {1, 2, 4, 8, 16, 32, 64, 128};
int column[] = {128, 64, 32, 16, 8, 4, 2, 1};
/*biểu diễn các ký tự chữ và số ở dạng HEX*/
unsigned int characterHEX[][8] = {
{0x18,0x3C,0x66,0x66,0x7E,0x66,0x66,0x66},//A
{0x78,0x64,0x68,0x78,0x64,0x66,0x66,0x7C},//B
{0x3C,0x62,0x60,0x60,0x60,0x62,0x62,0x3C},//C
{0x78,0x64,0x66,0x66,0x66,0x66,0x64,0x78},//D
{0x7E,0x60,0x60,0x7C,0x60,0x60,0x60,0x7E},//E
{0x7E,0x60,0x60,0x7C,0x60,0x60,0x60,0x60},//F
{0x3C,0x62,0x60,0x60,0x66,0x62,0x62,0x3C},//G
{0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x66},//H
{0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x7E},//I
{0x7E,0x18,0x18,0x18,0x18,0x18,0x1A,0x0C},//J
{0x62,0x64,0x68,0x70,0x70,0x68,0x64,0x62},//K
{0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x7E},//L
{0xC3,0xE7,0xDB,0xDB,0xC3,0xC3,0xC3,0xC3},//M
{0x62,0x62,0x52,0x52,0x4A,0x4A,0x46,0x46},//N
{0x3C,0x66,0x66,0x66,0x66,0x66,0x66,0x3C},//O
{0x7C,0x62,0x62,0x7C,0x60,0x60,0x60,0x60},//P
{0x38,0x64,0x64,0x64,0x64,0x6C,0x64,0x3A},//Q
{0x7C,0x62,0x62,0x7C,0x70,0x68,0x64,0x62},//R
{0x1C,0x22,0x30,0x18,0x0C,0x46,0x46,0x3C},//S
{0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18},//T
{0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x3C},//U
{0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x18},//V
{0x81,0x81,0x81,0x81,0x81,0x99,0x99,0x66},//W
{0x42,0x42,0x24,0x18,0x18,0x24,0x42,0x42},//X
{0xC3,0x66,0x3C,0x18,0x18,0x18,0x18,0x18},//Y
{0x7E,0x02,0x04,0x08,0x10,0x20,0x40,0x7E},//Z
{0x3C,0x66,0x66,0x6E,0x76,0x66,0x66,0x3C},//0
{0x18,0x38,0x58,0x18,0x18,0x18,0x18,0x7E},//1
{0x3C,0x66,0x66,0x0C,0x18,0x30,0x7E,0x7E},//2
{0x7E,0x0C,0x18,0x3C,0x06,0x06,0x46,0x3C},//3
{0x0C,0x18,0x30,0x6C,0x6C,0x7E,0x0C,0x0C},//4
{0x7E,0x60,0x60,0x7C,0x06,0x06,0x46,0x3C},//5
{0x04,0x08,0x10,0x38,0x6C,0x66,0x66,0x3C},//6
{0x7E,0x46,0x0C,0x18,0x18,0x18,0x18,0x18},//7
{0x3C,0x66,0x66,0x3C,0x66,0x66,0x66,0x3C},//8
{0x3C,0x66,0x66,0x36,0x1C,0x08,0x10,0x20},//9
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},// khoảng trắng
{0x00,0x66,0xFF,0xFF,0x7E,0x3C,0x18,0x00}// hình trái tim, kí hiệu là '&'
};
/* ký tự đại diện để biểu diễn chữ và số trên matrix*/
char character[] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7','8','9',' ','&'};
void setup()
{
  Serial.begin(9600);// Serial với baudrate 9600
  /* 3 pins DATA, CLOCK, LATCH cần phải để OUTPUT*/
  pinMode(DATA,OUTPUT);
  pinMode(CLOCK,OUTPUT);
  pinMode(LATCH,OUTPUT);
  /* in ra cổng Serial "ENTER A STRING"*/
  Serial.println("ENTER A STRING");
}
/* hàm nhấp nháy chữ*/
/* image là ký tự cần hiển thị,
   times là số lần nhấp nháy,
   on, off là độ dài của hiệu ứng*/
void blinkImage(unsigned int image[],int times,int on,int off)
{
  for(int i=0;i<times;i++)
  {
    displayImage(image,on);// hiển thị
    
    clearImage(off);// xóa  
  }
}
/*hàm hiển thị chữ lên LED matrix*/
/* image là ký tự cần hiển thị,
   duration là độ dài của hiệu ứng*/
void displayImage(unsigned int image[],int duration)
{
  for(int hold=0;hold<duration;hold++)
  {
    for(int a=0;a<8;a++)
    {
      digitalWrite(LATCH, LOW);
      shiftOut(DATA, CLOCK, MSBFIRST,~image[a]);//column
      shiftOut(DATA, CLOCK, MSBFIRST,row[a]);//row
      digitalWrite(LATCH, HIGH);
      delay(1);
    }
  }
}
/* hàm clear LED matrix*/
/* duration là độ dài của hiệu ứng clear*/
void clearImage(int duration)
{
  for(int hold=0;hold<duration;hold++)
  {
    for(int a=0;a<8;a++)
    {
      digitalWrite(LATCH, LOW);
      shiftOut(DATA, CLOCK, MSBFIRST,B11111111);//column
      shiftOut(DATA, CLOCK, MSBFIRST,row[a]);//row
      digitalWrite(LATCH, HIGH);
      delay(1);
    }
  }
}
/*hàm scroll image sang trái*/
/* image là ký tự cần hiển thị*/
void scrollImage(unsigned int image[])
{
  int shift, hold, a;//biến shift dùng để lưu số bit cần shiftOut
                     //biến hold dùng để điều chỉnh độ dài của hiệu ứng
                     //biến a dùng để lưu column và row hiện tại
  for(shift = 0; shift < 9; shift++)
  {
    for(hold = 0; hold < 30; hold++)
    {
      for(a = 0; a < 8; a++)
      {
        digitalWrite(LATCH, 0);
        /* dịch ký tự sang trái*/
        shiftOut(DATA,CLOCK,MSBFIRST,~(image[a]<<shift));//column
        shiftOut(DATA,CLOCK,MSBFIRST,row[a]);//row
        digitalWrite(LATCH, 1);
        delay(1);
      }
    }
  }
  
}
void loop()
{
   String string;// khai báo biến String object
   /* đọc dữ liệu từ cổng Serial */
   while(Serial.available() > 0)
   {
     char ch = Serial.read();
     string += ch;// lưu ký tự vừa nhận được vào biến string
     delay(5);// delay để đợi ký tự tiếp theo, KHÔNG THỂ THIẾU
   }
   Serial.println(string);// in string ra Serial monitor
   /* hiển thị ra LED matrix */
   while(Serial.available() == 0)
   {
     /*so sánh từng phần tử của string với 
     các ký tự đã được lưu trong mảng character[].
     Nếu ký tự xuất hiện trong string tồn tại 
     trong mảng character[] thì hiển thị ra LED matrix,
     nếu không tồn tại thì báo "invalid character"*/
     for(int k = 0;k < string.length();k++)
     {
       for(int i=0;i < sizeof(character);i++)
       {
         if(string.charAt(k) == character[i])
         {
           //bỏ "//" nếu muốn sử dụng hàm blinkImage()
           //blinkImage(characterHEX[i],1,30,30);
           scrollImage(characterHEX[i]);
           break;
         }
         /* nếu ko tồn tại ký tự xuất hiện trong string*/
         if((i == (sizeof(character) - 1)) && (string.charAt(k) != character[i]))
         {
           Serial.print(string.charAt(k));
           Serial.println(":invalid character");
         }
       }
       /*kiểm tra xem có dữ liệu mới hay không*/
       if(Serial.available() > 0)
       break;
     }
     delay(300);
   }
}

Giải thích

//hàng và cột của LED matrix
int row[] = {1, 2, 4, 8, 16, 32, 64, 128};
int column[] = {128, 64, 32, 16, 8, 4, 2, 1};

=> dùng để kiểm soát hàng và cột của matrix.

1 = B00000001 => hàng 0

2 = B00000010 => hàng 1

4 = B00000100 => hàng 2

....

128 = B10000000 => hàng 7

tương tự đối với cột.

- Tạo ký tự. Để biểu diễn chữ 'I' ra LED matrix, ta sẽ tạo 1 mảng sau:

I[] = {

B01111110, 

B00011000, 

B00011000, 

B00011000, 

B00011000,

B00011000, 

B00011000, 

B01111110};

những vị trí có số 1 sẽ là những điểm sẽ sáng trên LED matrix. Để ngắn gọn, ta có thể chuyển mảng trên về dạng số HEX: B0111 1110 => 0111 = 7 (DEC) = 7 (HEX); 1110 = 14 (DEC) = E (HEX); ==> B01111110 = 0x7E;

tương tự ta sẽ được: I[] = {0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x7E};

==> trong đoạn code trên mình chỉ mới tạo 26 chữ cái (có mấy cái hơi xấu) và 10 chữ số, các bạn có thể chỉnh sửa cho nó đẹp hơn và sáng tạo ra những hình ảnh khác nhé!

- hàm displayImage()

/*hàm hiển thị chữ lên LED matrix*/
/* image là ký tự cần hiển thị,
   duration là độ dài của hiệu ứng*/
void displayImage(unsigned int image[],int duration)
{
  for(int hold=0;hold<duration;hold++)
  {
    for(int a=0;a<8;a++)
    {
      digitalWrite(LATCH, LOW);
      shiftOut(DATA, CLOCK, MSBFIRST,~image[a]);//column
      shiftOut(DATA, CLOCK, MSBFIRST,row[a]);//row
      digitalWrite(LATCH, HIGH);
      delay(1);
    }
  }
}

=> Tại 1 thời điểm, chúng ta chỉ có thể điều khiển các LEDs của 1 row, do đó,  trong thực tế, hình ảnh ta thấy được trên LED matrix là do "hiện tượng lưu ảnh" của mắt, khi chúng ta quét các hàng của matrix với tốc độ rất nhanh. Để làm được việc này, sau mỗi lần shiftOut, ta chỉ delay 1/1000 giây, và lặp lại "duration" lần sau khi đã shiftOut hết 8 bit. Giá trị của biến duration càng lớn thì thời gian của hiệu ứng càng dài.

==> Ta sử dụng bitwise NOT (~) khi shiftOut columns, vì để bật 1 LED, row cần được nối VCC (set 1), column cần được nối GND (set 0).

==> Nếu sử dụng LSBFIRST, ta có thể lật ngược ký tự so với ban đầu.

/*hàm scroll image sang trái*/
/* image là ký tự cần hiển thị*/
void scrollImage(unsigned int image[])
{
  int shift, hold, a;//biến shift dùng để lưu số bit cần shiftOut
                     //biến hold dùng để điều chỉnh độ dài của hiệu ứng
                     //biến a dùng để lưu column và row hiện tại
  for(shift = 0; shift < 9; shift++)
  {
    for(hold = 0; hold < 30; hold++)
    {
      for(a = 0; a < 8; a++)
      {
        digitalWrite(LATCH, 0);
        /* dịch ký tự sang trái*/
        shiftOut(DATA,CLOCK,MSBFIRST,~(image[a]<<shift));//column
        shiftOut(DATA,CLOCK,MSBFIRST,row[a]);//row
        digitalWrite(LATCH, 1);
        delay(1);
      }
    }
  }
}

==> Chúng ta sử dụng "<<" và ">>" để dịch ký tự sang trái và phải. Khi dịch phải, cần đặt biến ở dạng unsigned để tránh tình trạng "sign extension". Các bạn có thể tham khảo thêm ở BIT MATH - Các phép toán thao tác trên bit

 

==> Biến shift sẽ lưu giữ số bit cần dịch trái (phải).

Đây là clip LED matrix của mình

Lời kết

Mọi thắc mắc hay sai sót các bạn hãy còm men nhé laugh. Chúc các bạn thành công!

 

lên
27 thành viên đã đánh giá bài viết này hữu ích.
Chuyên mục: 
Hướng dẫn sử dụng các loại module

Nếu bạn đang muốn thực hiện hóa ý tưởng của mình mà không biết dùng loại module nào? Hãy tham khảo các module trong danh sách sau

Các dự án được truyền cảm hứng

Làm xe điều khiển từ xa qua Wifi, điều khiển bằng app Android và PC(Java)

Ước mơ từ nhỏ của mình là làm được một con xe điều khiển (mà phải điều khiển bằng điện thoại cơ mới thích,hehe), nhưng nó vẫn chỉ là ước mơ thôi vì mình không học điện tử mình sợ không làm được, cho đến khi biết tới Arduino thì mình thấy mọi chuyện cứ đơn giản dần. Và sau khi đọc các bài viết chia sẻ của cộng đồng Arduino.vn mình đã có được một con xe điều khiển (chỉ có chức năng đi, quẹo trái, phải, bật tắt đèn). Về phần cứng của xe thì không có gì nhiều để nói cả nhưng mình muốn chia sẻ cho các bạn Source code app điều khiển trên Android mà mình tự viết, các bạn có thể sửa để điều khiển qua Bluetooth hoặc bổ sung thêm tính năng cho nó

lên
22 thành viên đã đánh giá bài viết này hữu ích.
Các bài viết cùng tác giả

Sử dụng cảm biến khoảng cách HC-SR04

Xin chào, hôm nay mình sẽ giới thiệu về cách sử dụng cảm biến khoảng cách HC-SR04. Chúng ta sẽ cùng nhau tìm hiểu về nguyên lý hoạt động của HC-SR04. và cách sử dụng với Arduino.

lên
16 thành viên đã đánh giá bài viết này hữu ích.

Debounce cho nút nhấn bằng tụ điện

Xin chào, bài đăng của mình sẽ nói về kỹ thuật DEBOUNCE cho nút bấm. Đây là kỹ thuật rất đơn giản và hiệu quả.

lên
13 thành viên đã đánh giá bài viết này hữu ích.