아두이노 #54 32x8 LED Matrix Font 데이터 참조로 문자 표시 및 메모리 문제 해결
Lucy Archive
Lucy 2023
2020. 12. 2. 02:19

Arduino : MAX7219 32x8 LED Matrix Text Display Using Font Data

지난 포스트에서는 32x8 LED Matrix를 구동하는 방법에 대해 설명하였습니다. 이번 포스트는 Font 데이터를 사용하여 원하는 문자를 LED Matrix에 출력하는 방법과 메모리 문제 해결하는 방법을 소개합니다. LED Matrix 기본 사용법은 지난 포스트들을 참조해주세요.

하드웨어 구성

준비물

본 예제에 사용된 부품은 아래와 같습니다.

  • Arduino Uno x 1EA
  • MAX7219 32x8 LED Dot Matrix x 1EA
  • JumperWires

하드웨어 연결

Arduino Uno 와 32x8 LED Matrix는 아래의 회로도와 같이 연결하였습니다. Arduino Uno의 13, 11, 10번 핀을 각각 LED Matrix 모듈의 SCK, DIN, CS 핀에 연결하였습니다. 

연결 회로도
연결 예시

Font Data 사용하여 문자 출력

코드

아래의 코드는 폰트가 저장된 font 배열을 참조하여 선언부의 text 문자열 변수에 저장된 문자를 출력합니다. 선언부의 String text = "Ver2" 코드에 "Ver2"대신 출력할 문자열을 수정하면 됩니다.

String text = "Ver2"; /* Input String to Display */

코드에 사용된 Font 데이터는 아래의 링크를 참조하였습니다.

참조링크 : https://github.com/dhepper/font8x8/blob/master/font8x8_basic.h

#define SCK 13
#define DIN 11
#define LATCH 10

#define SIZE 4 /* Module Number */

String text = "Ver2"; /* Input String to Display */
uint8_t buff[SIZE][8]; /* Display Data Buffer */

uint8_t font[128][8] = {
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0000 (nul)
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0001
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0002
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0003
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0004
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0005
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0006
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0007
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0008
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0009
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000A
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000B
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000C
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000D
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000E
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000F
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0010
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0011
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0012
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0013
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0014
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0015
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0016
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0017
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0018
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0019
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001A
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001B
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001C
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001D
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001E
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001F
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0020 (space)
  { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00},   // U+0021 (!)
  { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0022 (")
  { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00},   // U+0023 (#)
  { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00},   // U+0024 ($)
  { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00},   // U+0025 (%)
  { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00},   // U+0026 (&)
  { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0027 (')
  { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00},   // U+0028 (()
  { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00},   // U+0029 ())
  { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00},   // U+002A (*)
  { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00},   // U+002B (+)
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06},   // U+002C (,)
  { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00},   // U+002D (-)
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00},   // U+002E (.)
  { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00},   // U+002F (/)
  { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00},   // U+0030 (0)
  { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00},   // U+0031 (1)
  { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00},   // U+0032 (2)
  { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00},   // U+0033 (3)
  { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00},   // U+0034 (4)
  { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00},   // U+0035 (5)
  { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00},   // U+0036 (6)
  { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00},   // U+0037 (7)
  { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00},   // U+0038 (8)
  { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00},   // U+0039 (9)
  { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00},   // U+003A (:)
  { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06},   // U+003B (//)
  { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00},   // U+003C (<)
  { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00},   // U+003D (=)
  { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00},   // U+003E (>)
  { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00},   // U+003F (?)
  { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00},   // U+0040 (@)
  { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00},   // U+0041 (A)
  { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00},   // U+0042 (B)
  { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00},   // U+0043 (C)
  { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00},   // U+0044 (D)
  { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00},   // U+0045 (E)
  { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00},   // U+0046 (F)
  { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00},   // U+0047 (G)
  { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00},   // U+0048 (H)
  { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0049 (I)
  { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00},   // U+004A (J)
  { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00},   // U+004B (K)
  { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00},   // U+004C (L)
  { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00},   // U+004D (M)
  { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00},   // U+004E (N)
  { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00},   // U+004F (O)
  { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00},   // U+0050 (P)
  { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00},   // U+0051 (Q)
  { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00},   // U+0052 (R)
  { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00},   // U+0053 (S)
  { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0054 (T)
  { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00},   // U+0055 (U)
  { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00},   // U+0056 (V)
  { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00},   // U+0057 (W)
  { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00},   // U+0058 (X)
  { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00},   // U+0059 (Y)
  { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00},   // U+005A (Z)
  { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00},   // U+005B ([)
  { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00},   // U+005C (\)
  { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00},   // U+005D (])
  { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00},   // U+005E (^)
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF},   // U+005F (_)
  { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0060 (`)
  { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00},   // U+0061 (a)
  { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00},   // U+0062 (b)
  { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00},   // U+0063 (c)
  { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00},   // U+0064 (d)
  { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00},   // U+0065 (e)
  { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00},   // U+0066 (f)
  { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F},   // U+0067 (g)
  { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00},   // U+0068 (h)
  { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0069 (i)
  { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E},   // U+006A (j)
  { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00},   // U+006B (k)
  { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+006C (l)
  { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00},   // U+006D (m)
  { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00},   // U+006E (n)
  { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00},   // U+006F (o)
  { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F},   // U+0070 (p)
  { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78},   // U+0071 (q)
  { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00},   // U+0072 (r)
  { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00},   // U+0073 (s)
  { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00},   // U+0074 (t)
  { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00},   // U+0075 (u)
  { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00},   // U+0076 (v)
  { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00},   // U+0077 (w)
  { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00},   // U+0078 (x)
  { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F},   // U+0079 (y)
  { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00},   // U+007A (z)
  { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00},   // U+007B ({)
  { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00},   // U+007C (|)
  { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00},   // U+007D (})
  { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+007E (~)
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}    // U+007F
};

void setup(){
  pinMode(SCK, OUTPUT);
  pinMode(DIN, OUTPUT);
  pinMode(LATCH, OUTPUT);

  Serial.begin(115200);

  write_Max7219(0x09, 0x00); // Decode Mode - No decode for digits
  write_Max7219(0x0a, 0x01); // Intensity - 1/32
  write_Max7219(0x0b, 0x07); // Scan Limit - All Output Port Enable
  write_Max7219(0x0c, 0x01); // Shutdown - Normal Operation
  write_Max7219(0x0f, 0x00); // Display Test

  delay(50);

  for(int i = 0 ; i < SIZE ; i++){
    for(int j = 0 ; j < 8 ; j++){
      buff[i][j] = font[text.charAt(i)][j];
    }
  }
}

void loop(){
  for(int rowNumber = 1 ; rowNumber < 9 ; rowNumber++){
    digitalWrite(LATCH,LOW);
    for( int columnData = 0 ; columnData < SIZE ; columnData++){
        shiftOut(DIN, SCK, MSBFIRST, rowNumber);
        shiftOut(DIN, SCK, LSBFIRST, buff[columnData][rowNumber - 1]);
    }
    digitalWrite(LATCH,HIGH);
  }
  while(1){;}
}

void write_Max7219(uint8_t address, uint8_t data){
  digitalWrite(LATCH,LOW);
  for(int i = 0 ; i < SIZE ; i++){
    shiftOut(DIN, SCK, MSBFIRST, address);
    shiftOut(DIN, SCK, MSBFIRST, data);
  }
  digitalWrite(LATCH,HIGH);
}

주요 코드 설명

선언부에 선언된 buff 배열은 LED Matrix에 출력할 데이터를 저장하는 버퍼입니다.

uint8_t buff[SIZE][8]; /* Display Data Buffer */

void setup() 함수내에서 아래의 for문은 text변수에 저장된 문자열을 font배열에서 도트 데이터를 찾아 buff 배열에 저장합니다.

for(int i = 0 ; i < SIZE ; i++){
  for(int j = 0 ; j < 8 ; j++){
    buff[i][j] = font[text.charAt(i)][j];
  }
}

void loop()에서 for문은 buff에 저장된 데이터를 MAX7219에 전송합니다. 데이터를 전송이 완료되면 동작을 멈춥니다.

for(int rowNumber = 1 ; rowNumber < 9 ; rowNumber++){
  digitalWrite(LATCH,LOW);
  for( int columnData = 0 ; columnData < SIZE ; columnData++){
      shiftOut(DIN, SCK, MSBFIRST, rowNumber);
      shiftOut(DIN, SCK, LSBFIRST, buff[columnData][rowNumber - 1]);
  }
  digitalWrite(LATCH,HIGH);
}
while(1){;}

실행결과

위 코드를 실행하면 아래 그림과 같이 text 문자열 변수에 저장된 Ver2 문자를 출력합니다.

코드 실행 결과

문제점

(위 코드를 수정/개선해야 할 곳은 많지만) 위 코드를 빌드하면 아래와 같이 아두이노의 동적 메모리(SRAM)를 61%나 사용합니다. 아두이노로 이 기능만 사용하면 문제가 되지 않지만, 추가적인 다른 기능을 추가하게 되면 SRAM 부족으로 예상할 수 없는 문제들이 발생할 수 있습니다.

LED Matrix 메모리 문제 해결

LED Matrix, LCD와 같은 표시 장치는 많은 메모리를 차지합니다. 위 코드에서 LED Matrix에 폰트 데이터만 추가했을 뿐인데 동적 메모리를 61%나 차지하는 것을 확인하였습니다. 이 문제를 해결하기 위한 방법으로 읽기만 사용하는 데이터를 FLASH로 저장하는 방법을 사용할 수 있습니다. 기본 사용법과 적용 예시는 하단의 링크를 참조해주세요.

참조 링크

동적 메모리 용량 무족 문제 PROGRMEM F() 메크로 사용

 

아두이노 강좌 #27 동적 메모리 용량 부족 문제 PROGMEM F() 메크로로 해결

Arduino : PROGMEM F() Macro 아두이노 우노의 SRAM 은 2KB 로 메모리 문제에서 자유로울 수 없습니다. 특히, LED Matrix, OLED 와 같은 디스플레이 장치를 사용하거나, SD Card 장치 라이브러리를 사용하는 경우..

juahnpop.tistory.com

2차원 배열 PROGMEM 매크로 사용 방법

 

아두이노 강좌 #28 2차원 배열 PROGMEM 매크로 사용 방법

Arduino : PROGMEM 2D Array 지난 포스트에서는 아두이노의 SRAM 부족 방법을 해결하기 위해 PROGMEM 와 F() 매크로를 사용하여 전역 변수와 문자열을 FLASH MEMORY 에 저장하여 사용하는 방법을 소개하였습니

juahnpop.tistory.com

수정된 코드

위 코드에서 font 데이터 선언 부 uint8_t font[128][8]const byte font[128][8] PROGMEM로 수정합니다. 이 코드의 의미는 font배열 데이터를 FLASH에 저장하여 사용한다는 뜻입니다.

void setup() 함수 내의 for문을 아래와 같이 수정합니다. 이 코드에 의미는 FLASH에 저장된 메모리로부터 데이터를 읽어 온다는 의미입니다.

for(int i = 0 ; i < SIZE ; i++){
  for(int j = 0 ; j < 8 ; j++){
    buff[i][j] = pgm_read_byte(&(font[text.charAt(i)][j]));
  }
}

코드 실행 결과

위 2가지 코드를 수정 후 선언부의 text 변수에 "Ver3"를 입력 후 아두이노 업로드를 하면 아래 사진과 같이 정상적으로 출력이 됩니다.

코드 수정 후 실행 결과

빌드 후 메모리 아두이노의 동적 메모리 사용량을 확인하면 기존 코드 61% 에서 11%로 감소된 것을 확인할 수 있습니다.

아두이노 동적 메모리 사용률


마무리

이번 포스트에서 Font Data를 사용하여 MAX7219 32x8 LED Dot Matrix 문자 출력하는 방법에 대해 소개하였습니다. 추가로 동적 메모리 용량을 많이 차지하는 문제를 해결하기 위한 방법도 안내하였습니다. 이후 포스트는 LED Matrix 스크롤하는 방법 및 외부 통신으로 제어하는 방법에 대해 포스트를 작성해나갈 예정입니다.

끝까지 읽어 주셔서 감사합니다.^^

관련포스트

👉 아두이노 LED Matrix 관련글 목록 보기

👉 아두이노 디스플레이 장치 관련글 목록 보기

👉 아두이노 관련글 전체 목록 보기