이런거 저런거 그런거
4개 LED Matrix (8x32) 컨트롤 하기 본문
8x8 한개에 더 나아가 동일한 Matrix가 4개 붙어있는 부품을 샀다.
모델명 : SZH-EKAD-115 (MAX7219 아두이노 8X32 도트 매트릭스 모듈)
이 부품 역시 SPI 통신을 통해 컨트롤 할 수 있으며 Daisy-Chain방식을 통해 핀을 더 할당하지 않고도 4개의 MAX7219를 컨트롤 하여 4개의 Matrix Led를 컨트롤 할 수 있다.
그렇다면 궁금한 부분은
[아두이노] --- SPI ---> [Matrix 3] --- SPI ---> [Matrix 2] --- SPI ---> [Matrix 1] --- SPI ---> [Matrix 0]
위와 같이 연결되어있다고 했을 때 [Matrix 0]의 Led만 컨트롤 하고 싶을 경우 데이터를 어떻게 보내야 하는가 이다.
이와 관련하여 이해하기 쉬운 동영상 설명을 찾았다.
https://www.youtube.com/watch?v=SMH45vnRbjI
즉, SPI로 연결된 MAX7219 갯수만큼 데이터를 보내야 한다.
보내기 전, 후로 CS핀을 컨트롤 하며 CS핀을 High로 컨트롤 하는 순간 보낸 데이터가 적용된다.
보내는 데이터의 순서는 MAX7219갯수 x 2 byte(address + data)를 보내야 하며 각 MAX7219는 2byte 큐와 같다고 생각하면 된다. 따라서 예를 들면
[아두이노] --- SPI ---> [Matrix 3] --- SPI ---> [Matrix 2] --- SPI ---> [Matrix 1] --- SPI ---> [Matrix 0] |
[Matrix 1] 를 컨트롤 하고 싶다면 아래와 같이 하면 된다는 것이다.
Control_Matrix_1(Addr, Value) {
CS-Pin LOW
Send No-Op, 0x00 // For Matrix 0
Send Addr, Value // For Matrix 1
Send No-Op, 0x00 // For Matrix 2
Send No-Op, 0x00 // For Matrix 3
CS-Pin HIGH
}
여기서 No-Op란 Address 0x00에 해당한다.
(보내는 Signal은 Address + Data, 총 2 byte여야 하므로 No-Op를 보낸 후 1byte의 데이터(아무거나)를 보내준다)
추후 조도, 온도, 습도를 표시하는데도 사용하기 위해
4개의 Matrix Led를 컨트롤 하기 위해 Class로 만들어서 좀 보기 좋게(?) 작성하기로 했다.
이번 역시 Library를 사용하지 않는 방향으로...
아래는 4개의 Matrix Led에 숫자를 차례대로 증가시키면서 출력하는 샘플코드다.
(추가로 밝기도 0 ~ 0xF 까지 차례대로 증가하도록 설정한다.)
/*
* MAX7219 Control Register Address&Default Value
*/
#define NO_OP_ADDR (0x00)
#define NO_OP_VAL (0x00)
#define DECODE_MODE_ADDR (0x09)
#define DECODE_MODE_VAL (0x00)
#define INTENSITY_ADDR (0x0A) //Bright Setting (0x00 ~ 0x0F)
#define INTENSITY_VAL (0x00)
#define SCAN_LIMIT_ADDR (0x0B) //How many use digit led (line count in matrix led device)
#define SCAN_LIMIT_VAL (0x07)
#define POWER_DOWN_MODE_ADDR (0x0C)
#define POWER_DOWN_MODE_VAR (0x01)
#define TEST_DISPLAY_ADDR (0x0F)
#define TEST_DISPLAY_VAL (0x00)
class MultiMatrix {
/*
* Definitions
*/
public:
static const int MODULE_COUNT = 4;
enum Flip {
FLIP_NONE = 0,
FLIP_H,
FLIP_V,
FLIP_HV,
FLIP_MAX,
};
private:
/* LED Pattern */
const char NUMBER[10][3] = {
{0x1f, 0x11, 0x1f}, //0
{0x00, 0x00, 0x1f}, //1
{0x1d, 0x15, 0x17}, //2
{0x15, 0x15, 0x1f}, //3
{0x07, 0x04, 0x1f}, //4
{0x17, 0x15, 0x1d}, //5
{0x1f, 0x15, 0x1d}, //6
{0x03, 0x01, 0x1f}, //7
{0x1f, 0x15, 0x1f}, //8
{0x17, 0x15, 0x1f} //9
};
const int NUMBER_AREA_MASK = 0x1F;
/*
* Member Values
*/
private:
int clk, cs, din;
enum Flip flip;
unsigned char display_buffer[MODULE_COUNT][8];
/*
* Member Functions
*/
public:
MultiMatrix(void);
~MultiMatrix() {};
int init(enum Flip flip, int clkPin, int csPin, int dinPin);
int draw(int idx, int row, int col, int state);
int draw_number(int idx, int number);
int set_bright(unsigned char bright);
int set_bright(int idx, unsigned char bright);
void update(void);
private:
void send_data(int idx, uint8_t addr, uint8_t value);
void send_same_data(uint8_t addr, uint8_t value);
};
MultiMatrix::MultiMatrix(void)
{
int i;
for (i = 0; i < MODULE_COUNT; i++) {
memset(this->display_buffer[i], 0x00, 8);
}
}
void MultiMatrix::send_data(int idx, uint8_t addr, uint8_t value)
{
int i;
digitalWrite(this->cs, LOW);
for (i = 0; i < MODULE_COUNT; i++) {
if (i == idx) {
//send Data
shiftOut(this->din, this->clk, MSBFIRST, addr);
shiftOut(this->din, this->clk, MSBFIRST, value);
} else {
//send No-Op
shiftOut(this->din, this->clk, MSBFIRST, NO_OP_ADDR);
shiftOut(this->din, this->clk, MSBFIRST, NO_OP_VAL);
}
}
digitalWrite(this->cs, HIGH);
}
void MultiMatrix::send_same_data(uint8_t addr, uint8_t value)
{
int i;
digitalWrite(this->cs, LOW);
for (i = 0; i < MODULE_COUNT; i++) {
//send Data
shiftOut(this->din, this->clk, MSBFIRST, addr);
shiftOut(this->din, this->clk, MSBFIRST, value);
}
digitalWrite(this->cs, HIGH);
}
int MultiMatrix::init(enum Flip flip, int clkPin, int csPin, int dinPin)
{
int i;
this->flip = flip;
//set pins
this->clk = clkPin;
this->cs = csPin;
this->din = dinPin;
pinMode(clkPin, OUTPUT);
pinMode(csPin, OUTPUT);
pinMode(dinPin, OUTPUT);
//setting modules to initial value
for (i = 0; i < MODULE_COUNT; i++) {
send_data(i, DECODE_MODE_ADDR, DECODE_MODE_VAL);
send_data(i, INTENSITY_ADDR, INTENSITY_VAL);
send_data(i, SCAN_LIMIT_ADDR, SCAN_LIMIT_VAL);
send_data(i, POWER_DOWN_MODE_ADDR, POWER_DOWN_MODE_VAR);
send_data(i, TEST_DISPLAY_ADDR, TEST_DISPLAY_VAL);
}
return 0;
}
int MultiMatrix::draw(int idx, int row, int col, int onOff)
{
if (idx >= MODULE_COUNT)
return -1;
if (row < 0 || 7 < row)
return -1;
if (col < 0 || 7 < col)
return -1;
if (onOff)
this->display_buffer[idx][col] |= ((0x1) << row);
else
this->display_buffer[idx][col] &= ~((0x1) << row);
}
int MultiMatrix::draw_number(int idx, int number)
{
int i;
unsigned char data[8] = {0,};
if (idx >= MODULE_COUNT)
return -1;
if (number < 0 || 100 < number)
return -1;
/*
* Clear Buffer
*/
for (i = 0; i < 8; i++) {
this->display_buffer[idx][i] &= ~(NUMBER_AREA_MASK);
}
/*
* Draw
*/
this->display_buffer[idx][1] |= NUMBER[number / 10][0];
this->display_buffer[idx][2] |= NUMBER[number / 10][1];
this->display_buffer[idx][3] |= NUMBER[number / 10][2];
this->display_buffer[idx][5] |= NUMBER[number % 10][0];
this->display_buffer[idx][6] |= NUMBER[number % 10][1];
this->display_buffer[idx][7] |= NUMBER[number % 10][2];
return 0;
}
int MultiMatrix::set_bright(unsigned char bright)
{
int i;
if (bright < 0x0 || 0xF < bright)
return -1;
send_same_data(INTENSITY_ADDR, (uint8_t)bright);
return 0;
}
int MultiMatrix::set_bright(int idx, unsigned char bright)
{
if (bright < 0x0 || 0xF < bright)
return -1;
send_data(idx, INTENSITY_ADDR, (uint8_t)bright);
return 0;
}
void MultiMatrix::update(void)
{
int i, j;
for (i = 0; i < 8; i++) {
digitalWrite(this->cs, LOW);
for (j = 0; j < MODULE_COUNT; j++) {
//- send address
shiftOut(this->din, this->clk, MSBFIRST, i + 1);
//- send data
switch (this->flip) {
case FLIP_NONE:
shiftOut(this->din, this->clk, MSBFIRST, this->display_buffer[j][i]);
break;
case FLIP_H:
shiftOut(this->din, this->clk, MSBFIRST, this->display_buffer[j][7 - i]);
break;
case FLIP_V:
shiftOut(this->din, this->clk, LSBFIRST, this->display_buffer[j][i]);
break;
case FLIP_HV:
shiftOut(this->din, this->clk, LSBFIRST, this->display_buffer[j][7 - i]);
break;
}
}
digitalWrite(this->cs, HIGH);
}
}
/*
* Application Codes
*/
#define MAX7219_CLK (10)
#define MAX7219_CS (11)
#define MAX7219_DIN (12)
MultiMatrix led;
void setup() {
led.init(MultiMatrix::FLIP_HV, MAX7219_CLK, MAX7219_CS, MAX7219_DIN);
}
void loop() {
static int i;
if (i > 100) {
i = 0;
}
led.draw_number((i % 4), i);
led.set_bright((i % 4), i % 0xF);
led.update();
i++;
delay(500);
}
뭔가 이번엔 길다.....
위 코드 중 일부인 MultiMatrix Class의 멤버함수만 설명해보자면,
public:
MultiMatrix(void);
~MultiMatrix() {};
int init(enum Flip flip, int clkPin, int csPin, int dinPin);
int draw(int idx, int row, int col, int state);
int draw_number(int idx, int number);
int set_bright(unsigned char bright);
int set_bright(int idx, unsigned char bright);
void update(void);
private:
void send_data(int idx, uint8_t addr, uint8_t value);
void send_same_data(uint8_t addr, uint8_t value);
- init()
- flip : 출력 방향을 설정한다. 8x8기준으로 8x32 자재는 Matrix Led 모듈이 거꾸로 달려있어 HV Flip 을 해 줘야 한다. H Flip의 경우 buffer array를 거꾸로 읽는 방법으로 지원했으며, V Flip의 경우 기존 MSBFIRST를 LSBFIRST로 변경하여 간단히 해결하였다.(결국 둘 다 어디서부터 읽어 갈거냐에 대한 설정)
즉, 위 두 개를 적용하면 글자를 360도 거꾸로 돌릴 수 있는 것이다. - clkPin, csPin, dinPin : SPI통신을 위한 3개의 핀
- flip : 출력 방향을 설정한다. 8x8기준으로 8x32 자재는 Matrix Led 모듈이 거꾸로 달려있어 HV Flip 을 해 줘야 한다. H Flip의 경우 buffer array를 거꾸로 읽는 방법으로 지원했으며, V Flip의 경우 기존 MSBFIRST를 LSBFIRST로 변경하여 간단히 해결하였다.(결국 둘 다 어디서부터 읽어 갈거냐에 대한 설정)
- draw() : Led OnOff를 idx에 해당하는 Matrix Led의 버퍼에 설정한다. (아두이노에서 제일 먼 Matrix Led가 0번에 해당한다.)
- draw_number() : 숫자를 해당 idx에 해당하는 Matrix Led의 버퍼에 그린다. 00 ~ 99 까지 지원하며 그리는 위치는 Row 0 ~ 4로 총 5개 Row을 사용한다.
- set_bright() : 전체 Led에 대한 밝기 또는 특정 Matrix에 대한 밝기 설정 (밝기 설정 범위 : 0x0 ~ 0xF)
- update() : 화면 업데이트 (draw()나 draw_number() 호출만으로 Led가 업데이트 되지 않으며 update()함수가 불러야 실제로 Led가 컨트롤 된다.)
- send_data() : 특정 matrix led에 데이터를 보낸다.
- send_same_data() : 전체 matrix led에 대해 동일한 데이터를 보낸다. (ex> 밝기 전체 설정)
위 설명과 같이 클래스 내부에는 display buffer가 존재하며 draw(), draw_number()는 버퍼를 대상으로만 작업한다.
그리고 설정한 내용이 Led에 표시되려면 update()를 호출해야한다.
MultiMatrix의 display_buffer 변수와 실제 Led Matrix를 매칭 시켜보면 아래와 같다.
'이것저것 해보기 > 아두이노 가지고 놀기' 카테고리의 다른 글
조도센서값을 도트 매트릭스에 표시하기 (0) | 2019.09.22 |
---|---|
Matrix LED를 좀 쉽게 컨트롤하는 방법(MAX7219) (0) | 2019.09.21 |
8x8 Led Matrix를 전체 컨트롤 해 보기 (0) | 2019.09.17 |
8x8 LED 매트릭스 한 줄 컨트롤 해보기 (0) | 2019.09.08 |
8x8 LED 매트릭스에서 LED 하나 깜박거리게 해보기 (0) | 2019.09.08 |