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

除了用於[驅動七段顯示器](https://cavedu.gitbook.io/linkit-7697/linkit-7697-arduino-ide-kai-fa-zhi-nan/zhou-bian-yuan-jian-lian-jie-jiao-xue/shi-yong-max7219-qu-dong-qi-duan-xian-shi-qi)，MAX7219 另一種常見的應用為驅動 8x8 矩陣式 LED 顯示器。此教學文章將介紹如何使用單顆 MAX7219 驅動 8x8 矩陣式顯示器，以及如何串聯 (daisy-chain) 數個 MAX7219 以驅動多個 8x8 顯示矩陣。為了降低電路複雜度，本文使用 [8x8 矩陣式顯示模組](https://robotkingdom.com.tw/product/max7219dotmatrix/) (內建 MAX7219) 作為範例使用元件。

### 目錄 <a href="#driving8x8dotmatriceswithmax7219-mu-lu" id="driving8x8dotmatriceswithmax7219-mu-lu"></a>

* 目錄
* 七段顯示器與矩陣顯示器的對應關係
* 硬體接線
* 範例一：使用單個顯示器
* 範例二：串連多個顯示器
* 範例三：字串跑馬燈
* 結論

### 七段顯示器與矩陣顯示器的對應關係 <a href="#driving8x8dotmatriceswithmax7219-qi-duan-xian-shi-qi-yu-ju-zhen-xian-shi-qi-de-dui-ying-guan-xi" id="driving8x8dotmatriceswithmax7219-qi-duan-xian-shi-qi-yu-ju-zhen-xian-shi-qi-de-dui-ying-guan-xi"></a>

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

![](https://3972650740-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FY4gduUSLWOCI23CXUWej%2Fuploads%2FF9SQlh9HTTvEIZYFugft%2Fmapping.png?alt=media\&token=cbfc6703-13c8-4b55-8993-6597d2855ce9)

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

![](https://3972650740-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FY4gduUSLWOCI23CXUWej%2Fuploads%2FD2xot03axeeHOyFrO76N%2F7example.png?alt=media\&token=6952526a-0389-46a0-8138-145be8a434d6)

根據圖上的位元資訊，可知 "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

接線示意圖如下：

![](https://3972650740-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FY4gduUSLWOCI23CXUWej%2Fuploads%2FLoeLQZLHjUSpBJjOK6DC%2F1matrix.png?alt=media\&token=0bbf9a74-fcaf-439d-af4d-01e2005cdee3)\
完成硬體線路連接後，接下來的文章將示範如何在顯示器上顯示圖案。

### 範例一：使用單個顯示器 <a href="#driving8x8dotmatriceswithmax7219-fan-li-yi-shi-yong-chan-ge-xian-shi-qi" id="driving8x8dotmatriceswithmax7219-fan-li-yi-shi-yong-chan-ge-xian-shi-qi"></a>

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

{% embed url="<https://youtu.be/PaKMkrsWESs>" %}

這段程式碼是以之前七段顯示器教學的 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);
}
```

#### 程式說明 <a href="#driving8x8dotmatriceswithmax7219-cheng-shi-shui-ming" id="driving8x8dotmatriceswithmax7219-cheng-shi-shui-ming"></a>

不同於七段顯示器，當使用 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)
```

{% hint style="danger" %}
注意

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

為了顯示自訂的點陣圖，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* 為倍數間隔開來。

### 範例二：串連多個顯示器 <a href="#driving8x8dotmatriceswithmax7219-fan-li-er-chuan-lian-duo-ge-xian-shi-qi" id="driving8x8dotmatriceswithmax7219-fan-li-er-chuan-lian-duo-ge-xian-shi-qi"></a>

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。以下用動畫說明此傳輸過程：&#x20;

{% embed url="<https://youtu.be/JS7W9CcV_0I>" %}

此動畫中有三個 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](https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf) 中的 Table 2)。當 MAX7219 在 **LOAD** 訊號為 HIGH 時看到 No-Op 指令，將不會進行任何動作、狀態也不會改變。下面的動畫展示「只有 MAX7219 #1 需收取新指令、其他節點皆不改變狀態」的狀況：

{% embed url="<https://youtu.be/SMH45vnRbjI>" %}

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

{% embed url="<https://youtu.be/kUAiqULx69c>" %}

硬體接線

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

![](https://3972650740-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FY4gduUSLWOCI23CXUWej%2Fuploads%2FgiBxplCdqzqZ31R8VZ1P%2F5_matrixes.png?alt=media\&token=a6cf61db-4519-44c9-84d9-d95f3d215413)

{% hint style="danger" %}
注意

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

以驅動單個顯示器的程式碼為基礎，將之修改為可驅動串連顯示器的版本。首先，先定義 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)
```

#### 重點回顧 <a href="#driving8x8dotmatriceswithmax7219-zhong-dian-hui-gu" id="driving8x8dotmatriceswithmax7219-zhong-dian-hui-gu"></a>

* 若要對**所有**的 MAX7219 下**同樣**的指令，請使用 *set\_all\_registers()* API，就如同 *init\_max7219()* 以及 *clear\_matrix()* 皆為相同的概念。
* 若只要對**特定**的 MAX7219 下指令，請使用 *set\_single\_register()* API，即可讓其他 MAX7219 不受影響。

### 範例三：字串跑馬燈 <a href="#driving8x8dotmatriceswithmax7219-fan-li-san-zi-chuan-pao-ma-deng" id="driving8x8dotmatriceswithmax7219-fan-li-san-zi-chuan-pao-ma-deng"></a>

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

{% embed url="<https://youtu.be/_yaaAefPgbU>" %}

完整的範例程式碼如下：

```
//
// 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);
}
```

### 結論 <a href="#driving8x8dotmatriceswithmax7219-jie-lun" id="driving8x8dotmatriceswithmax7219-jie-lun"></a>

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