使用 MAX7219 驅動 8x8 矩陣式顯示器

除了用於驅動七段顯示器,MAX7219 另一種常見的應用為驅動 8x8 矩陣式 LED 顯示器。此教學文章將介紹如何使用單顆 MAX7219 驅動 8x8 矩陣式顯示器,以及如何串聯 (daisy-chain) 數個 MAX7219 以驅動多個 8x8 顯示矩陣。為了降低電路複雜度,本文使用 8x8 矩陣式顯示模組 (內建 MAX7219) 作為範例使用元件。

目錄

  • 目錄

  • 七段顯示器與矩陣顯示器的對應關係

  • 硬體接線

  • 範例一:使用單個顯示器

  • 範例二:串連多個顯示器

  • 範例三:字串跑馬燈

  • 結論

七段顯示器與矩陣顯示器的對應關係

雖然七段顯示器與 8x8 矩陣式顯示器在外型上有極大的差異,但本質上兩者是相同的東西:皆為內含多顆 LED 的顯示裝置。一個七段顯示器含有 8 顆 LED (筆畫部分使用 7 顆 LED、小數點使用 1 顆 LED),而且根據之前的教學,得知 MAX7219 可同時驅動 8 組七段顯示器,也就是說,MAX7219 最多可驅動 8x8 = 64 顆 LED,而這個數字,正好就是 8x8 矩陣式顯示器內所具有的 LED 數目。因此,讓 MAX7219 以原始資料模式運作,即可用於驅動 8x8 矩陣顯示器。所以,如何將七段顯示器對應至矩陣顯示器?接下來的文章將以實際範例搭配對應程式碼進行說明。

上圖為矩陣顯示器的示意圖。當使用 MAX7219 的原始資料模式時 (若不熟悉 MAX7219 運作模式,可參考之前的教學範例),矩陣顯示器的每個欄位可對應到一個 Digit 暫存器,也就是說,第 0 欄對應到 Digit 0、第 1 欄對應到 Digit 1... 以此類推。而矩陣的每一則對應到七段顯示器中的不同筆畫,從顯示器最上方開始,第 0 列對應到 Digit 暫存器資料的第 0 個位元 (LSB)、第 1 列對應到 Digit 暫存器的第 1 個位元... 依序下去,直到第 7 列對應到 Digit 暫存器的 MSB (第 7 位元)。以實際範例說明,例如要在矩陣顯示器上顯示數字 "7" 的圖像:

根據圖上的位元資訊,可知 "7" 的點陣圖樣可被表示為:

const byte bitmap_7[] =
{
  // define the dot pattern from column 0 ~ column 7
  B00000000, B00000000, B01100001, B00010001, B00001001, B00000111, B00000000, B00000000
};

而這些資訊就是後續會送進 Digit 暫存器作為顯示用的原始資料。

硬體接線

需使用 LinkIt 7697 的下列五隻腳位:

  • 5V

  • GND

  • P15

  • P16

  • P17

分別將它們連接至顯示模組上的:

  • VCC

  • GND

  • CLK

  • CS

  • DIN

接線示意圖如下:

範例一:使用單個顯示器

第一個範例將依照前面章節介紹的點陣圖定義方式,在單個 8x8 矩陣式顯示器上顯示不同字元。下面的 sketch 程式碼會驅動顯示器顯示 "Hello, LinkIt7697" 字樣,並呈現如同影片中的效果:

這段程式碼是以之前七段顯示器教學的 sketch 為基礎,修改為驅動矩陣顯示器的版本:

//
// Use one MAX7219 to control a 8x8 dot matrix
// http://www.icshop.com.tw/product_info.php/products_id/13181
//
// MAX7219 datasheet: https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf
//

// the MAX7219 address map (datasheet table 2)
#define MAX7219_DECODE_REG      (0x09)
#define MAX7219_INTENSITY_REG   (0x0A)
#define MAX7219_SCANLIMIT_REG   (0x0B)
#define MAX7219_SHUTDOWN_REG    (0X0C)
#define MAX7219_DISPLAYTEST_REG (0x0F)
#define MAX7219_DIGIT_REG(pos)  ((pos) + 1)
#define MAX7219_COLUMN_REG(pos) MAX7219_DIGIT_REG(pos)

// shutdown mode (datasheet table 3)
#define MAX7219_OFF             (0x0)
#define MAX7219_ON              (0x1)

// pin 13 of MAX7219 (CLK)
const int clock_pin = 15;
// pin 12 of MAX7219 (LOAD)
const int data_latch_pin = 16;
// pin 1 of MAX7219 (DIN)
const int data_input_pin = 17;

// number of columns of the display matrx
#define NUM_OF_COLUMNS  (8)
// for each character bitmap, it consumes 4 bytes
#define BYTE_PER_MAP    (4)

// matrix pattern for "Hello,LinkIt7697"
const byte char_pattern[] =
{
  B01111111, B00001000, B00001000, B01111111,   // H
  B00111000, B01010100, B01010100, B00011000,   // e
  B01000001, B01111111, B01000000, B00000000,   // l
  B01000001, B01111111, B01000000, B00000000,   // l
  B00111000, B01000100, B01000100, B00111000,   // o
  B10110000, B01110000, B00000000, B00000000,   // ,
  B01111111, B01000000, B01000000, B01000000,   // L
  B01001000, B01111010, B01000000, B00000000,   // i
  B01111100, B00000100, B00000100, B01111000,   // n
  B01111111, B00010000, B00101000, B01000100,   // k
  B01000001, B01111111, B01000001, B00000000,   // I
  B00001000, B01111110, B01001000, B00000000,   // t
  B01100001, B00010001, B00001001, B00000111,   // 7
  B00111110, B01001001, B01001001, B00110000,   // 6
  B00000110, B01001001, B01001001, B00111110,   // 9
  B01100001, B00010001, B00001001, B00000111,   // 7
  B00000000, B00000000, B00000000, B00000000    // space
};

#define DISPLAY_STR_LENGTH  (sizeof(char_pattern) / BYTE_PER_MAP)

// update the register value of MAX7219
void set_register(byte address, byte value)  
{
  digitalWrite(data_latch_pin, LOW);
  shiftOut(data_input_pin, clock_pin, MSBFIRST, address);
  shiftOut(data_input_pin, clock_pin, MSBFIRST, value);
  digitalWrite(data_latch_pin, HIGH);
}

void clear_matrix()
{
  // clear the dot matrix
  for (int i = 0; i < NUM_OF_COLUMNS; i++)
  {
    set_register(MAX7219_COLUMN_REG(i), B00000000);
  }
}

void init_max7219()
{
  // disable test mode. datasheet table 10
  set_register(MAX7219_DISPLAYTEST_REG, MAX7219_OFF);
  // set medium intensity. datasheet table 7
  set_register(MAX7219_INTENSITY_REG, 0x1);
  // turn off display. datasheet table 3
  set_register(MAX7219_SHUTDOWN_REG, MAX7219_OFF);
  // drive 8 digits. datasheet table 8
  set_register(MAX7219_SCANLIMIT_REG, 7);
  // no decode mode for all positions. datasheet table 4
  set_register(MAX7219_DECODE_REG, B00000000);

  // clear matrix display
  clear_matrix();
}

void setup()  
{
  // init pin states
  pinMode(clock_pin, OUTPUT);
  pinMode(data_latch_pin, OUTPUT);    
  pinMode(data_input_pin, OUTPUT);

  // init MAX2719 states
  init_max7219();
}

unsigned int char_index = 0;

void loop()  
{
  int i;
  
  // turn off display first
  set_register(MAX7219_SHUTDOWN_REG, MAX7219_OFF);

  // display one bitmap
  for (i = 0; i < BYTE_PER_MAP; i++)
  {
    // starting from column 2
    set_register(MAX7219_COLUMN_REG(2 + i), char_pattern[char_index * BYTE_PER_MAP + i]);
  }

  // turn on display
  set_register(MAX7219_SHUTDOWN_REG, MAX7219_ON);

  // step to the next character
  char_index++;
  // wrap around the character if needed
  if (char_index >= DISPLAY_STR_LENGTH)
  {
    char_index = 0;
  }
  
  delay(666);
}

程式說明

不同於七段顯示器,當使用 MAX7219 驅動矩陣式顯示器時,程式所要控制的是每個「欄位」的內容、而非每個「數字」的內容。所以為了增加可讀性,程式中將 Digit 暫存器的名稱改為 Column 暫存器:

#define MAX7219_COLUMN_REG(pos) MAX7219_DIGIT_REG(pos)

並定義顯示器及圖形等相關基本參數:

// number of columns of the display matrx
#define NUM_OF_COLUMNS  (8)
// for each character bitmap, it consumes 4 bytes
#define BYTE_PER_MAP    (4)

// matrix pattern for "Hello,LinkIt7697"
const byte char_pattern[] =
{
  B01111111, B00001000, B00001000, B01111111,   // H
  B00111000, B01010100, B01010100, B00011000,   // e
  ...
  B00000000, B00000000, B00000000, B00000000    // space
};

#define DISPLAY_STR_LENGTH  (sizeof(char_pattern) / BYTE_PER_MAP)

注意

由於每個字元只使用 4x8 個像素,所以定義點陣圖資訊時,只定義了其中 4 個欄位,其他欄位則保持空白、不顯示任何東西。

為了顯示自訂的點陣圖,MAX7219 需將每個 Column 暫存器皆設定為以原始資料模式運作:

// no decode mode for all positions. datasheet table 4set_register(MAX7219_DECODE_REG, B00000000);

接著依序將點陣圖資訊送進各個 Column 暫存器,進而組成欲顯示的圖形:

// turn off display firstset_register(MAX7219_SHUTDOWN_REG, MAX7219_OFF); // display one bitmapfor (i = 0; i < BYTE_PER_MAP; i++){  // starting from column 2  set_register(MAX7219_COLUMN_REG(2 + i), char_pattern[char_index * BYTE_PER_MAP + i]);} // turn on displayset_register(MAX7219_SHUTDOWN_REG, MAX7219_ON);

由於只使用 8x8 矩陣中的 4x8 點陣範圍 (並置中顯示),所以程式碼從顯示器的第 2 欄開始更新暫存器資訊,並且一共更新 BYTE_PER_MAP 個欄位。此外,由於顯示器「每一」的內容對應到點陣圖資料的「一個 byte」,因此 char_pattern 陣列中每張點陣圖的起始位置,都會是以 BYTE_PER_MAP 為倍數間隔開來。

範例二:串連多個顯示器

MAX7219 支援以串連模式 (daisy-chain) 連接數個 MAX7219 以驅動多個顯示器。當多個 MAX7219 IC 串連在一起時,送進第一顆 IC 的資料會在 16 個時脈訊號 (cycle) 後輸出至下一顆 IC;這個數字剛好與 MAX7219 收取一筆指令所花費的時間相同,因此這也是為何 MAX7219 被稱作為 shift register 的原因:當 IC 本身花 16 個時脈訊號的時間、將資料收進內部的 16-bit 儲存空間後,若還有更多的資料持續送進來,則 IC 會依「先進先出」的順序,開始將內部的 16-bit 資料以 bit 為單位往外輸出。所以,當欲傳送指令給串連在一起的 MAX7219 時,開發者需依「從遠到近」的順序發出給各個 MAX7219 的指令,才能正確地讓指令到達其所對應的 MAX7219。以下用動畫說明此傳輸過程:

此動畫中有三個 MAX7219 串聯在一起,所以若要傳送指令到所有的 MAX7219,則需要 16x3 = 48 個時脈訊號的時間。傳送流程一開始 LOAD 訊號被拉低,使 MAX7219 進入等待指令更新的狀態;在前 16 個時脈訊號中,開發板送出給 MAX7219 #2 (接線上離開發板最遠的 MAX7219) 的指令進入首個節點,接下來的 16 個時脈訊號,再送出給 MAX7219 #1 的指令。此時原本存在首個節點中的指令會被往後推出進到下一個節點。在最後一組的 16 個時脈訊號裡,開發板送出給 MAX7219 #0 的指令,並觸發原本存在首兩個節點中的指令繼續向後推進至最終的目的地。當所有指令都到達正確位置後,將 LOAD 訊號拉高,讓 MAX7219 依收到的指令進行動作。此流程即說明了當串聯在一起的 MAX7219 需接收新指令時,資料傳遞的順序及運作機制。但如果串連在一起的 MAX7219 中,只有單個 MAX7219 需要更新指令、其他的不需新指令,又該如何進行呢?

MAX7219 裡有一個特殊的暫存器,稱為 No-Op 暫存器 (位址為 0,請參閱 datasheet 中的 Table 2)。當 MAX7219 在 LOAD 訊號為 HIGH 時看到 No-Op 指令,將不會進行任何動作、狀態也不會改變。下面的動畫展示「只有 MAX7219 #1 需收取新指令、其他節點皆不改變狀態」的狀況:

在接下來的多個矩陣顯示器串連範例中,會將前一個範例擴充為五個顯示器的組態,並透過使用 No-Op 指令,讓每個顯示器單獨顯示與上例相同的圖像:

硬體接線

將每個顯示模組的 DOUT 腳位接至下個顯示模組的 DIN 腳,而其他腳位則直接連接到下個模組的相同腳位即可。

注意

當串連多個矩陣顯示器時,請確保 5V 供電源能提供足夠的電流輸出,以免顯示模組因供電不足而無法正常運作。

以驅動單個顯示器的程式碼為基礎,將之修改為可驅動串連顯示器的版本。首先,先定義 No-Op 指令:

#define MAX7219_NOOP_REG        (0x00)

接著將 set_register() 擴充為 set_all_registers() 以及 set_single_register(),分別用來對串連在一起的 MAX7219 下指令。set_all_registers() 的作用為對所有 MAX7219 皆發出相同的指令,而 set_single_register() 則是針對單一 MAX7219 進行指令更新 (其他的 MAX7219 則不改變狀態):

// update a specific register value of all MAX7219svoid set_all_registers(byte address, byte value){  digitalWrite(data_latch_pin, LOW);   for (int i = 0; i < NUM_OF_MATRIXES; i++)  {    shiftOut(data_input_pin, clock_pin, MSBFIRST, address);    shiftOut(data_input_pin, clock_pin, MSBFIRST, value);  }    digitalWrite(data_latch_pin, HIGH);} // only update the register in one MAX7219void set_single_register(int index, byte address, byte value){  // only process within valid index range  if (index >= 0 && index < NUM_OF_MATRIXES)  {    digitalWrite(data_latch_pin, LOW);     for (int i = NUM_OF_MATRIXES - 1; i >= 0; i--)    {      // for specified MAX7219, access the desired register      if (i == index)      {        shiftOut(data_input_pin, clock_pin, MSBFIRST, address);          }      else      {        // send No-Op operation to all other MAX7219s (the value is "don't-care" for No-Op command)        shiftOut(data_input_pin, clock_pin, MSBFIRST, MAX7219_NOOP_REG);      }            shiftOut(data_input_pin, clock_pin, MSBFIRST, value);    }      digitalWrite(data_latch_pin, HIGH);  }}

綜合上述改動,並利用這兩個 API 取代原本驅動單個顯示器範例中的 set_register() API 後,完整的程式範例如下:

//
// Use one MAX7219 to control 5 8x8 dot matrixes
// http://www.icshop.com.tw/product_info.php/products_id/13181
//
// MAX7219 datasheet: https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf
//

// the MAX7219 address map (datasheet table 2)
#define MAX7219_DECODE_REG      (0x09)
#define MAX7219_INTENSITY_REG   (0x0A)
#define MAX7219_SCANLIMIT_REG   (0x0B)
#define MAX7219_SHUTDOWN_REG    (0X0C)
#define MAX7219_DISPLAYTEST_REG (0x0F)
#define MAX7219_DIGIT_REG(pos)  ((pos) + 1)
#define MAX7219_COLUMN_REG(pos) MAX7219_DIGIT_REG(pos)
#define MAX7219_NOOP_REG        (0x00)

// shutdown mode (datasheet table 3)
#define MAX7219_OFF             (0x0)
#define MAX7219_ON              (0x1)

// pin 13 of MAX7219 (CLK)
const int clock_pin = 15;
// pin 12 of MAX7219 (LOAD)
const int data_latch_pin = 16;
// pin 1 of MAX7219 (DIN)
const int data_input_pin = 17;

// number of columns of the display matrx
#define NUM_OF_COLUMNS  (8)
// for each character bitmap, it consumes 4 bytes
#define BYTE_PER_MAP    (4)
// define the number of chained matrixes
#define NUM_OF_MATRIXES (5)

// matrix pattern for "Hello,LinkIt7697"
const byte char_pattern[] =
{
  B01111111, B00001000, B00001000, B01111111,   // H
  B00111000, B01010100, B01010100, B00011000,   // e
  B01000001, B01111111, B01000000, B00000000,   // l
  B01000001, B01111111, B01000000, B00000000,   // l
  B00111000, B01000100, B01000100, B00111000,   // o
  B10110000, B01110000, B00000000, B00000000,   // ,
  B01111111, B01000000, B01000000, B01000000,   // L
  B01001000, B01111010, B01000000, B00000000,   // i
  B01111100, B00000100, B00000100, B01111000,   // n
  B01111111, B00010000, B00101000, B01000100,   // k
  B01000001, B01111111, B01000001, B00000000,   // I
  B00001000, B01111110, B01001000, B00000000,   // t
  B01100001, B00010001, B00001001, B00000111,   // 7
  B00111110, B01001001, B01001001, B00110000,   // 6
  B00000110, B01001001, B01001001, B00111110,   // 9
  B01100001, B00010001, B00001001, B00000111,   // 7
  B00000000, B00000000, B00000000, B00000000    // space
};

#define DISPLAY_STR_LENGTH  (sizeof(char_pattern) / BYTE_PER_MAP)

// update a specific register value of all MAX7219s
void set_all_registers(byte address, byte value)
{
  digitalWrite(data_latch_pin, LOW);

  for (int i = 0; i < NUM_OF_MATRIXES; i++)
  {
    shiftOut(data_input_pin, clock_pin, MSBFIRST, address);
    shiftOut(data_input_pin, clock_pin, MSBFIRST, value);
  }
  
  digitalWrite(data_latch_pin, HIGH);
}

// only update the register in one MAX7219
void set_single_register(int index, byte address, byte value)
{
  // only process for valid index range
  if (index >= 0 && index < NUM_OF_MATRIXES)
  {
    digitalWrite(data_latch_pin, LOW);

    for (int i = NUM_OF_MATRIXES - 1; i >= 0; i--)
    {
      // for specified MAX7219, access the desired register
      if (i == index)
      {
        shiftOut(data_input_pin, clock_pin, MSBFIRST, address);
    
      }
      else
      {
        // send No-Op operation to all other MAX7219s (the value is "don't-care" for No-Op command)
        shiftOut(data_input_pin, clock_pin, MSBFIRST, MAX7219_NOOP_REG);
      }
      
      shiftOut(data_input_pin, clock_pin, MSBFIRST, value);
    }
  
    digitalWrite(data_latch_pin, HIGH);
  }
}

void clear_matrix()
{
  // clear the dot matrix
  for (int i = 0; i < NUM_OF_COLUMNS; i++)
  {
    set_all_registers(MAX7219_COLUMN_REG(i), B00000000);
  }
}

void init_max7219()
{
  // disable test mode. datasheet table 10
  set_all_registers(MAX7219_DISPLAYTEST_REG, MAX7219_OFF);
  // set medium intensity. datasheet table 7
  set_all_registers(MAX7219_INTENSITY_REG, 0x1);
  // turn off display. datasheet table 3
  set_all_registers(MAX7219_SHUTDOWN_REG, MAX7219_OFF);
  // drive 8 digits. datasheet table 8
  set_all_registers(MAX7219_SCANLIMIT_REG, 7);
  // no decode mode for all positions. datasheet table 4
  set_all_registers(MAX7219_DECODE_REG, B00000000);

  // clear matrix display
  clear_matrix();
}

void setup()  
{
  // init pin states
  pinMode(clock_pin, OUTPUT);
  pinMode(data_latch_pin, OUTPUT);    
  pinMode(data_input_pin, OUTPUT);

  // init MAX2719 states
  init_max7219();
}

unsigned int char_index = 0;
unsigned int matrix_index = 0;

void loop()  
{
  int i;
  
  // turn off display first
  set_all_registers(MAX7219_SHUTDOWN_REG, MAX7219_OFF);

  // display one bitmap
  for (i = 0; i < BYTE_PER_MAP; i++)
  { 
    // starting from column 2
    set_single_register(matrix_index, MAX7219_COLUMN_REG(2 + i), char_pattern[char_index * BYTE_PER_MAP + i]);
  }

  // turn on display
  set_all_registers(MAX7219_SHUTDOWN_REG, MAX7219_ON);

  // step to the next character
  char_index++;
  // wrap around the character if needed
  if (char_index >= DISPLAY_STR_LENGTH)
  {
    char_index = 0;

    // move to the next matrix
    matrix_index++;

    if (matrix_index >= NUM_OF_MATRIXES)
    {
      clear_matrix();
      matrix_index = 0;
    }
  }
  
  delay(400);
}

此例使用了 5 個顯示器模組,若要更改模組數目,可直接修改第 34 行的定義:

// define the number of chained matrixes
#define NUM_OF_MATRIXES (5)

重點回顧

  • 若要對所有的 MAX7219 下同樣的指令,請使用 set_all_registers() API,就如同 init_max7219() 以及 clear_matrix() 皆為相同的概念。

  • 若只要對特定的 MAX7219 下指令,請使用 set_single_register() API,即可讓其他 MAX7219 不受影響。

範例三:字串跑馬燈

結合前面所述的概念,最後一個範例將展示如何進一步使用 5 個矩陣顯示器產生常見的跑馬燈效果:

完整的範例程式碼如下:

//
// Use multiple 8x8 dot matrix modules to display a marquee string
// http://www.icshop.com.tw/product_info.php/products_id/13181
//
// MAX7219 datasheet: https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf
//

// the MAX7219 address map (datasheet table 2)
#define MAX7219_DECODE_REG      (0x09)
#define MAX7219_INTENSITY_REG   (0x0A)
#define MAX7219_SCANLIMIT_REG   (0x0B)
#define MAX7219_SHUTDOWN_REG    (0X0C)
#define MAX7219_DISPLAYTEST_REG (0x0F)
#define MAX7219_COLUMN_REG(pos) ((pos) + 1)

// shutdown mode (datasheet table 3)
#define MAX7219_OFF             (0x0)
#define MAX7219_ON              (0x1)

// pin 13 of MAX7219 (CLK)
const int clock_pin = 15;
// pin 12 of MAX7219 (LOAD)
const int data_latch_pin = 16;
// pin 1 of MAX7219 (DIN)
const int data_input_pin = 17;

// number of columns of the display matrx
#define NUM_OF_COLUMNS  (8)

// define the number of chained matrixes
#define NUM_OF_MATRIXES (5)

// matrix pattern for "Hello,LinkIt7697"
const byte char_pattern[] =
{
  B00000000, B00000000, B01111111, B00001000, B00001000, B01111111, // H
  B00000000, B00000000, B00111000, B01010100, B01010100, B00011000, // e
  B00000000, B00000000, B01000001, B01111111, B01000000, B00000000, // l
  B00000000, B00000000, B01000001, B01111111, B01000000, B00000000, // l
  B00000000, B00000000, B00111000, B01000100, B01000100, B00111000, // o
  B00000000, B00000000, B10110000, B01110000, B00000000, B00000000, // ,
  B00000000, B00000000, B01111111, B01000000, B01000000, B01000000, // L
  B00000000, B00000000, B01001000, B01111010, B01000000, B00000000, // i
  B00000000, B00000000, B01111100, B00000100, B00000100, B01111000, // n
  B00000000, B00000000, B01111111, B00010000, B00101000, B01000100, // k
  B00000000, B00000000, B01000001, B01111111, B01000001, B00000000, // I
  B00000000, B00000000, B00001000, B01111110, B01001000, B00000000, // t
  B00000000, B00000000, B01100001, B00010001, B00001001, B00000111, // 7
  B00000000, B00000000, B00111110, B01001001, B01001001, B00110000, // 6
  B00000000, B00000000, B00000110, B01001001, B01001001, B00111110, // 9
  B00000000, B00000000, B01100001, B00010001, B00001001, B00000111, // 7
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000  // space
};

#define DISPLAY_COLUMN_LENGTH (sizeof(char_pattern))

// update a specific register value of all MAX7219s
void set_all_registers(byte address, byte value)
{
  digitalWrite(data_latch_pin, LOW);

  for (int i = 0; i < NUM_OF_MATRIXES; i++)
  {
    shiftOut(data_input_pin, clock_pin, MSBFIRST, address);
    shiftOut(data_input_pin, clock_pin, MSBFIRST, value);
  }
  
  digitalWrite(data_latch_pin, HIGH);
}

void clear_all_matrix()
{
  // clear the dot matrix
  for (int i = 0; i < NUM_OF_COLUMNS; i++)
  {
    set_all_registers(MAX7219_COLUMN_REG(i), B00000000);
  }
}

void init_all_max7219()
{
  // disable test mode. datasheet table 10
  set_all_registers(MAX7219_DISPLAYTEST_REG, MAX7219_OFF);
  // set medium intensity. datasheet table 7
  set_all_registers(MAX7219_INTENSITY_REG, 0x1);
  // turn off display. datasheet table 3
  set_all_registers(MAX7219_SHUTDOWN_REG, MAX7219_OFF);
  // drive 8 digits. datasheet table 8
  set_all_registers(MAX7219_SCANLIMIT_REG, 7);
  // no decode mode for all positions. datasheet table 4
  set_all_registers(MAX7219_DECODE_REG, B00000000);

  // clear matrix display
  clear_all_matrix();
}

void setup()  
{
  // init pin states
  pinMode(clock_pin, OUTPUT);
  pinMode(data_latch_pin, OUTPUT);    
  pinMode(data_input_pin, OUTPUT);

  // init MAX2719 states
  init_all_max7219();
}

unsigned int starting_column_index = 0;

void loop()  
{
  int i, j;
  int target_column;

  // turn off display first
  set_all_registers(MAX7219_SHUTDOWN_REG, MAX7219_OFF);
  
  // update all columns
  for (i = 0; i < NUM_OF_COLUMNS; i++)
  {
    digitalWrite(data_latch_pin, LOW);
    
    // update all matrixes
    for (j = NUM_OF_MATRIXES - 1; j >= 0; j--)
    {
      // jump to the correct column position for each matrix
      target_column = starting_column_index + NUM_OF_COLUMNS * j + i;
      //  make sure the target column is within the bitmap array
      target_column %= DISPLAY_COLUMN_LENGTH;
      
      shiftOut(data_input_pin, clock_pin, MSBFIRST, MAX7219_COLUMN_REG(i));
      shiftOut(data_input_pin, clock_pin, MSBFIRST, char_pattern[target_column]);
    }

    // latch the data pin to make the register data takes effect
    digitalWrite(data_latch_pin, HIGH);
  }
  
  // turn on display
  set_all_registers(MAX7219_SHUTDOWN_REG, MAX7219_ON);

  // step to the next column
  starting_column_index++;
  // go back to the first column of the string bitmap to be displayed
  if (starting_column_index >= DISPLAY_COLUMN_LENGTH)
  {
    starting_column_index = 0;
  }

  delay(200);
}

結論

接續前篇的七段顯示器教學,本文完整地介紹了另一種 MAX7219 應用:不只用於驅動七段顯示器,利用原始資料模式的操作,MAX7219 也可用於驅動多個 LED、或是驅動矩陣式 LED。藉由使用 shift register,可大幅簡化建置 LED 顯示應用時的佈線及控制複雜度。

Last updated