> For the complete documentation index, see [llms.txt](https://cavedu.gitbook.io/linkit-7697/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cavedu.gitbook.io/linkit-7697/linkit-7697-arduinoide/zhou-bian-yuan-jian-lian-jie-jiao-xue/shi-yong-max7219-qu-dong-8x8-ju-zhen-shi-xian-shi-qi.md).

# 使用 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 矩陣顯示器。所以，如何將七段顯示器對應至矩陣顯示器？接下來的文章將以實際範例搭配對應程式碼進行說明。

![](/files/5G5M3f3R6UaujSxdbByc)

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

![](/files/F7VTHp5BFoiVoqCxf8N7)

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

接線示意圖如下：

![](/files/KRUvmBmXbfTxh7wMZETE)\
完成硬體線路連接後，接下來的文章將示範如何在顯示器上顯示圖案。

### 範例一：使用單個顯示器 <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** 腳，而其他腳位則直接連接到下個模組的相同腳位即可。

![](/files/ycWGru0cvvnhQIlaJmWh)

{% 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>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://cavedu.gitbook.io/linkit-7697/linkit-7697-arduinoide/zhou-bian-yuan-jian-lian-jie-jiao-xue/shi-yong-max7219-qu-dong-8x8-ju-zhen-shi-xian-shi-qi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
