호타리
2025. 4. 3. 19:23
2025.04.03
DOTMATRIX 제어
- LED를 매트릭스 형태로 배치하여 문자와 기호를 표시할 수 있도록 만들어진 출력 장치
- 8x8 크기가 흔히 사용됨
- 행과 열 제어를 위해 각각 8개, 총 16개 제어선 필요
- 제어 회로 / 칩이 포함된 모듈도 흔히 사용
<dotmatrix.c>
#include "main.h"
extern SPI_HandleTypeDef hspi2;
void dotmatrix_main_test();
void init_dotmatrix(void);
int dotmatrix_main(void);
int dotmatrix_main_func(void);
uint8_t allon[] = { // allon 문자 정의
0b11111111,
0b11111111,
0b11111111,
0b11111111,
0b11111111,
0b11111111,
0b11111111,
0b11111111
};
uint8_t smile[] = { // 스마일 문자 정의
0b00111100,
0b01000010,
0b10010101,
0b10100001,
0b10100001,
0b10010101,
0b01000010,
0b00111100 };
uint8_t hart[] = { // hart
0b00000000, // hart
0b01100110,
0b11111111,
0b11111111,
0b11111111,
0b01111110,
0b00111100,
0b00011000
};
uint8_t one[] =
{0b00011000,
0b00111000,
0b00011000,
0b00011000,
0b00011000,
0b00011000,
0b01111110,
0b01111110};
uint8_t my_name[] =
{0B01111010,
0B00001010,
0B00001010,
0B11111010,
0B00100010,
0B10101110,
0B10000010,
0B11111110};
uint8_t col[4]={0,0,0,0};
void dotmatrix_main_test()
{
//dotmatrix_main();
while (1)
{
for (int i=0; i < 8; i++)
{
col[0] = ~(1 << i); // 00000001 --> 11111110
col[1] = hart[i];
HAL_SPI_Transmit(&hspi2, col, 2, 1);
GPIOB->ODR &= ~GPIO_PIN_13; // latch핀을 pull-down
GPIOB->ODR |= GPIO_PIN_13; // latch핀을 pull-up
HAL_Delay(1);
}
}
}
uint8_t number_data[20][10] =
{
{
0b01110000, //0
0b10001000,
0b10011000,
0b10101000,
0b11001000,
0b10001000,
0b01110000,
0b00000000
},
{
0b01000000, //1
0b11000000,
0b01000000,
0b01000000,
0b01000000,
0b01000000,
0b11100000,
6 // 점 0b00000110
},
{
0b01110000, //2
0b10001000,
0b00001000,
0b00010000,
0b00100000,
0b01000000,
0b11111000,
6
},
{
0b11111000, //3
0b00010000,
0b00100000,
0b00010000,
0b00001000,
0b10001000,
0b01110000,
6
},
{
0b00010000, //4
0b00110000,
0b01010000,
0b10010000,
0b11111000,
0b00010000,
0b00010000,
6
},
{
0b11111000, //5
0b10000000,
0b11110000,
0b00001000,
0b00001000,
0b10001000,
0b01110000,
6
},
{
0b00110000, //6
0b01000000,
0b10000000,
0b11110000,
0b10001000,
0b10001000,
0b01110000,
6
},
{
0b11111000, //7
0b10001000,
0b00001000,
0b00010000,
0b00100000,
0b00100000,
0b00100000,
6
},
{
0b01110000, //8
0b10001000,
0b10001000,
0b01110000,
0b10001000,
0b10001000,
0b01110000,
6
},
{
0b01111010,
0b00001010,
0b00001010,
0b00110010,
0b01000010,
0b01111110,
0b01000010,
0b01111110
},
{
0b00000000, // hart
0b01100110,
0b11111111,
0b11111111,
0b11111111,
0b01111110,
0b00111100,
0b00011000
}
};
unsigned char display_data[8]; // 최종 8x8 출력할 데이터
unsigned char scroll_buffer[50][8] = {0}; // 스코롤할 데이타가 들어있는 버퍼
int number_of_character = 11; // 출력할 문자 갯수
// 초기화 작업
// 1. display_data에 number_data[0]에 있는 내용 복사
// 2. number_data를 scroll_buffer에 복사
// 3. dotmatrix의 led를 off
void init_dotmatrix(void)
{
for (int i=0; i < 8; i++)
{
display_data[i] = number_data[i];
}
for (int i=1; i < number_of_character+1; i++)
{
for (int j=0; j < 8; j++) // scroll_buffer[0] = blank
{
scroll_buffer[i][j] = number_data[i-1][j];
}
}
}
// scroll 문자 출력 프로그램
int dotmatrix_main(void)
{
static int count=0; // 컬럼 count
static int index=0; // scroll_buffer의 2차원 index값
static uint32_t past_time=0; // 이전 tick값 저장
uint32_t now = HAL_GetTick(); // 1ms
// 1.처음시작시 past_time=0; now: 500 --> past_time=500
if (now - past_time >= 500) // 500ms scroll
{
past_time = now;
for (int i=0; i < 8; i++)
{
display_data[i] = (scroll_buffer[index][i] >> count) |
(scroll_buffer[index+1][i] << 8 - count);
}
if (++count == 8) // 8칼람을 다 처리 했으면 다음 scroll_buffer로 이동
{
count =0;
index++; // 다음 scroll_buffer로 이동
if (index == number_of_character+1) index=0;
// 11개의 문자를 다 처리 했으면 0번 scroll_buffer를 처리 하기위해 이동
}
}
/*
0b00000000, // hart
0b01100110,
0b11111111,
0b11111111,
0b11111111,
0b01111110,
0b00111100,
0b00011000
*/
for (int i=0; i < 8; i++)
{
// 공통 양극 방식
// column에는 0을 ROW에는 1을 출력해야 해당 LED가 on된다.
col[0] = ~(1 << i); // 00000001 --> 11111110
col[1] = display_data[i];
HAL_SPI_Transmit(&hspi2, col, 2, 1);
GPIOB->ODR &= ~GPIO_PIN_13; // latch핀을 pull-down
GPIOB->ODR |= GPIO_PIN_13; // latch핀을 pull-up
HAL_Delay(1);
}
}
// scroll 문자 출력 프로그램
int dotmatrix_main_func(void)
{
static int count=0; // 컬럼 count
static int index=0; // scroll_buffer의 2차원 index값
static uint32_t past_time=0; // 이전 tick값 저장
init_dotmatrix();
while(1)
{
uint32_t now = HAL_GetTick(); // 1ms
// 1.처음시작시 past_time=0; now: 500 --> past_time=500
if (now - past_time >= 500) // 500ms scroll
{
past_time = now;
for (int i=0; i < 8; i++)
{
display_data[i] = (scroll_buffer[index][i] >> count) |
(scroll_buffer[index+1][i] << 8 - count);
}
if (++count == 8) // 8칼람을 다 처리 했으면 다음 scroll_buffer로 이동
{
count =0;
index++; // 다음 scroll_buffer로 이동
if (index == number_of_character+1) index=0;
// 11개의 문자를 다 처리 했으면 0번 scroll_buffer를 처리 하기위해 이동
}
}
for (int i=0; i < 8; i++)
{
// 공통 양극 방식
// column에는 0을 ROW에는 1을 출력해야 해당 LED가 on된다.
col[0] = ~(1 << i); // 00000001 --> 11111110
col[1] = display_data[i];
HAL_SPI_Transmit(&hspi2, col, 2, 1);
GPIOB->ODR &= ~GPIO_PIN_13; // latch핀을 pull-down
GPIOB->ODR |= GPIO_PIN_13; // latch핀을 pull-up
HAL_Delay(1);
}
}
return 0;
}
<실행 결과>
LED 매트릭스
- 행에 해당하는 핀에 HIGH, 열에 해당하는 핀에 LOW를 켬
- 핀 번호와 행 / 열 번호와는 무관함
공통 행 양극 / 음극 방식 제어
행 단위 스캔
열 단위 스캔
행 단위 스캔의 저항
열 단위 스캔의 저항
동작 원리
led-matrix-font-generator 사이트
https://www.riyas.org/2013/12/online-led-matrix-font-generator-with.html
이름 만들기
{
0b01001010, // 박
0b01001010,
0b01111011,
0b01001010,
0b01111010,
0b00000000,
0b01111110,
0b00000010
},
{
0b00010001, // 성
0b00100001,
0b01010111,
0b10001001,
0b00000001,
0b00111100,
0b01000010,
0b00111100
},
{
0b00010000, // 호
0b01111100,
0b00000000,
0b00111000,
0b01000100,
0b00111000,
0b00010000,
0b01111100
},
SPI 통신을 사용하지 않고 LED CONTROL
#include "main.h"
void dotmatrix_main_test();
void init_dotmatrix(void);
int dotmatrix_main(void);
int dotmatrix_main_func(void);
uint8_t allon[] = { // allon 문자 정의
0b11111111,
0b11111111,
0b11111111,
0b11111111,
0b11111111,
0b11111111,
0b11111111,
0b11111111
};
uint8_t smile[] = { // 스마일 문자 정의
0b00111100,
0b01000010,
0b10010101,
0b10100001,
0b10100001,
0b10010101,
0b01000010,
0b00111100
};
uint8_t hart[] = { // hart
0b00000000, // hart
0b01100110,
0b11111111,
0b11111111,
0b11111111,
0b01111110,
0b00111100,
0b00011000
};
uint8_t one[] ={
0b00011000,
0b00111000,
0b00011000,
0b00011000,
0b00011000,
0b00011000,
0b01111110,
0b01111110
};
uint8_t my_name[] ={
0B01111010,
0B00001010,
0B00001010,
0B11111010,
0B00100010,
0B10101110,
0B10000010,
0B11111110
};
uint8_t fuxku[] = {
0b00000000,
0b00000000,
0b11111000,
0b11111000,
0b11111111,
0b11111000,
0b01110000,
0b00000000
};
uint8_t apple[] = {
0b01111000,
0b10000100,
0b10000100,
0b01001000,
0b01001010,
0b10000101,
0b10110100,
0b01001000
};
uint8_t col[4]={0,0,0,0};
void dotmatrix_main_test()
{
uint8_t temp;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10 | GPIO_PIN_13 | GPIO_PIN_15, 0);
HAL_Delay(10);
while (1)
{
for (int i=0; i < 8; i++)
{
col[0] = ~(1 << i); // 00000001 --> 11111110
col[1] = hart[i];
// HAL_SPI_Transmit(&hspi2, col, 2, 1);
for (int j = 0; j < 2; j++)
{
for (int k = 0; k < 8; k++)
{
temp = col[j];
if (temp & (1 << k))
{
HAL_GPIO_WritePin(GPIOB, SER_74HC595_Pin, 1);
}
else
{
HAL_GPIO_WritePin(GPIOB, SER_74HC595_Pin, 0);
}
HAL_GPIO_WritePin(GPIOB, CLK_74HC595_Pin, 1); // clk을 상승에서
HAL_GPIO_WritePin(GPIOB, CLK_74HC595_Pin, 0); // 하강으로
}
}
GPIOB->ODR &= ~GPIO_PIN_13; // latch핀을 pull-down
GPIOB->ODR |= GPIO_PIN_13; // latch핀을 pull-up
HAL_Delay(1);
}
}
}
엘레베이터 버튼, 화면 구현
<dotmatrix.c>
#if 1
#include "main.h"
#include "button.h"
void init_dotmatrix(void);
int dotmatrix_main_func(void);
uint8_t smile[] = { // 스마일 문자 정의
0b00111100,
0b01000010,
0b10010101,
0b10100001,
0b10100001,
0b10010101,
0b01000010,
0b00111100
};
uint8_t up_arrow_data[20][10] =
{
{
0b00011000,
0b00111100,
0b01111110,
0b11111111,
0b00111100,
0b00111100,
0b00111100,
0b00000000
},
{
0b00011000,
0b00111100,
0b01111110,
0b11111111,
0b00111100,
0b00111100,
0b00111100,
0b00000000
}
};
uint8_t down_arrow_data[20][10] =
{
{
0b00000000,
0b00111100,
0b00111100,
0b00111100,
0b11111111,
0b01111110,
0b00111100,
0b00011000
},
{
0b00000000,
0b00111100,
0b00111100,
0b00111100,
0b11111111,
0b01111110,
0b00111100,
0b00011000
}
};
uint8_t col[2] = {0, 0};
uint8_t number_data[20][10] =
{
{
0b01001010, // 박
0b01001010,
0b01111011,
0b01001010,
0b01111010,
0b00000000,
0b01111110,
0b00000010
},
{
0b00010001, // 성
0b00100001,
0b01010111,
0b10001001,
0b00000001,
0b00111100,
0b01000010,
0b00111100
},
{
0b00010000, // 호
0b01111100,
0b00000000,
0b00111000,
0b01000100,
0b00111000,
0b00010000,
0b01111100
},
{
0b00111100, // smile
0b01000010,
0b10100101,
0b10000001,
0b10100101,
0b10011001,
0b01000010,
0b00111100
}
};
void direct(uint8_t* data, uint8_t len)
{
uint8_t temp;
for (int j = 0; j < len; j++)
{
for (int k = 0; k < 8; k++)
{
temp = data[j];
if (temp & (1 << k))
HAL_GPIO_WritePin(GPIOB, SER_74HC595_Pin, 1);
else
HAL_GPIO_WritePin(GPIOB, SER_74HC595_Pin, 0);
// CLK 펄스: 상승 -> 하강
HAL_GPIO_WritePin(GPIOB, CLK_74HC595_Pin, 1);
HAL_GPIO_WritePin(GPIOB, CLK_74HC595_Pin, 0);
}
}
}
unsigned char display_data[8]; // 최종 8x8 출력할 데이터
unsigned char scroll_buffer[50][8] = {0}; // 스코롤할 데이타가 들어있는 버퍼
int number_of_character = 4; // 출력할 문자 갯수
// 초기화 작업
// 1. display_data에 number_data[0]에 있는 내용 복사
// 2. number_data를 scroll_buffer에 복사
// 3. dotmatrix의 led를 off
void init_dotmatrix(void)
{
for (int i=0; i < 8; i++)
{
display_data[i] = number_data[i];
}
for (int i=1; i < number_of_character+1; i++)
{
for (int j=0; j < 8; j++) // scroll_buffer[0] = blank
{
scroll_buffer[i][j] = number_data[i-1][j];
}
}
}
// scroll 문자 출력 프로그램
int dotmatrix_main_func(void)
{
static int count = 0;
static int index = 0;
static uint32_t past_time = 0;
typedef enum {
IDLE_STATE,
UP_ARROW_STATE,
DOWN_ARROW_STATE
} DotMatrixState;
static DotMatrixState state = IDLE_STATE;
static uint32_t arrowStateStart = 0;
static uint32_t arrowFrameTimer = 0;
const uint32_t SCROLL_DELAY = 500;
const uint32_t ARROW_DURATION = 30000;
const uint32_t ARROW_FRAME_PERIOD = 200;
init_dotmatrix();
static GPIO_PinState lastBtnState = GPIO_PIN_RESET;
HAL_Delay(50);
lastBtnState = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_0);
static uint8_t up_arrow_disp[8];
static int up_currentFrame = 0;
static int up_nextRow = 0;
static uint8_t down_arrow_disp[8];
static int down_currentFrame = 0;
static int down_nextRow = 0;
while (1)
{
uint32_t now = HAL_GetTick();
GPIO_PinState btnState = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_0);
switch (state)
{
case IDLE_STATE:
if (now - past_time >= SCROLL_DELAY)
{
past_time = now;
for (int i = 0; i < 8; i++)
{
display_data[i] = (scroll_buffer[index][i] >> count) |
(scroll_buffer[index+1][i] << (8 - count));
}
if (++count == 8)
{
count = 0;
index++;
if (index == number_of_character + 1)
index = 0;
}
}
for (int i = 0; i < 8; i++)
{
col[0] = ~(1 << i);
col[1] = display_data[i];
direct(col, 2);
GPIOB->ODR &= ~GPIO_PIN_13;
GPIOB->ODR |= GPIO_PIN_13;
HAL_Delay(1);
}
if (btnState == GPIO_PIN_SET && lastBtnState == GPIO_PIN_RESET)
{
state = UP_ARROW_STATE;
arrowStateStart = now;
arrowFrameTimer = now;
for (int i = 0; i < 8; i++)
{
up_arrow_disp[i] = up_arrow_data[up_currentFrame][i];
}
up_nextRow = 0;
}
break;
case UP_ARROW_STATE:
if (btnState == GPIO_PIN_SET && lastBtnState == GPIO_PIN_RESET)
{
state = DOWN_ARROW_STATE;
arrowStateStart = now;
arrowFrameTimer = now;
for (int i = 0; i < 8; i++)
{
down_arrow_disp[i] = down_arrow_data[down_currentFrame][i];
}
down_nextRow = 0;
}
else if (now - arrowStateStart >= ARROW_DURATION)
{
state = IDLE_STATE;
past_time = now;
}
else
{
if (now - arrowFrameTimer >= ARROW_FRAME_PERIOD)
{
for (int i = 0; i < 7; i++)
{
up_arrow_disp[i] = up_arrow_disp[i+1];
}
up_arrow_disp[7] = up_arrow_data[(up_currentFrame + 1) % 2][up_nextRow];
up_nextRow++;
if (up_nextRow >= 8)
{
up_nextRow = 0;
up_currentFrame = (up_currentFrame + 1) % 2;
}
arrowFrameTimer = now;
}
for (int i = 0; i < 8; i++)
{
col[0] = ~(1 << i);
col[1] = up_arrow_disp[i];
direct(col, 2);
GPIOB->ODR &= ~GPIO_PIN_13;
GPIOB->ODR |= GPIO_PIN_13;
HAL_Delay(1);
}
}
break;
case DOWN_ARROW_STATE:
if (btnState == GPIO_PIN_SET && lastBtnState == GPIO_PIN_RESET)
{
state = UP_ARROW_STATE;
arrowStateStart = now;
arrowFrameTimer = now;
for (int i = 0; i < 8; i++)
{
up_arrow_disp[i] = up_arrow_data[up_currentFrame][i];
}
up_nextRow = 0;
}
else if (now - arrowStateStart >= ARROW_DURATION)
{
state = IDLE_STATE;
past_time = now;
}
else
{
if (now - arrowFrameTimer >= ARROW_FRAME_PERIOD)
{
for (int i = 7; i > 0; i--)
{
down_arrow_disp[i] = down_arrow_disp[i-1];
}
down_arrow_disp[0] = down_arrow_data[(down_currentFrame + 1) % 2][7 - down_nextRow];
down_nextRow++;
if (down_nextRow >= 8)
{
down_nextRow = 0;
down_currentFrame = (down_currentFrame + 1) % 2;
}
arrowFrameTimer = now;
}
for (int i = 0; i < 8; i++)
{
col[0] = ~(1 << i);
col[1] = down_arrow_disp[i];
direct(col, 2);
GPIOB->ODR &= ~GPIO_PIN_13;
GPIOB->ODR |= GPIO_PIN_13;
HAL_Delay(1);
}
}
break;
}
lastBtnState = btnState;
HAL_Delay(10);
}
return 0;
}
#endif
#if 0
#include "main.h"
void dotmatrix_main_test();
void init_dotmatrix(void);
int dotmatrix_main(void);
int dotmatrix_main_func(void);
uint8_t allon[] = { // allon 문자 정의
0b11111111,
0b11111111,
0b11111111,
0b11111111,
0b11111111,
0b11111111,
0b11111111,
0b11111111
};
uint8_t smile[] = { // 스마일 문자 정의
0b00111100,
0b01000010,
0b10010101,
0b10100001,
0b10100001,
0b10010101,
0b01000010,
0b00111100
};
uint8_t hart[] = { // hart
0b00000000, // hart
0b01100110,
0b11111111,
0b11111111,
0b11111111,
0b01111110,
0b00111100,
0b00011000
};
uint8_t one[] ={
0b00011000,
0b00111000,
0b00011000,
0b00011000,
0b00011000,
0b00011000,
0b01111110,
0b01111110
};
uint8_t my_name[] ={
0B01111010,
0B00001010,
0B00001010,
0B11111010,
0B00100010,
0B10101110,
0B10000010,
0B11111110
};
uint8_t fuxku[] = {
0b00000000,
0b00000000,
0b11111000,
0b11111000,
0b11111111,
0b11111000,
0b01110000,
0b00000000
};
uint8_t apple[] = {
0b01111000,
0b10000100,
0b10000100,
0b01001000,
0b01001010,
0b10000101,
0b10110100,
0b01001000
};
uint8_t col[4]={0,0,0,0};
void dotmatrix_main_test()
{
uint8_t temp;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10 | GPIO_PIN_13 | GPIO_PIN_15, 0);
HAL_Delay(10);
while (1)
{
for (int i=0; i < 8; i++)
{
col[0] = ~(1 << i); // 00000001 --> 11111110
col[1] = hart[i];
// HAL_SPI_Transmit(&hspi2, col, 2, 1);
for (int j = 0; j < 2; j++)
{
for (int k = 0; k < 8; k++)
{
temp = col[j];
if (temp & (1 << k))
{
HAL_GPIO_WritePin(GPIOB, SER_74HC595_Pin, 1);
}
else
{
HAL_GPIO_WritePin(GPIOB, SER_74HC595_Pin, 0);
}
HAL_GPIO_WritePin(GPIOB, CLK_74HC595_Pin, 1); // clk을 상승에서
HAL_GPIO_WritePin(GPIOB, CLK_74HC595_Pin, 0); // 하강으로
}
}
GPIOB->ODR &= ~GPIO_PIN_13; // latch핀을 pull-down
GPIOB->ODR |= GPIO_PIN_13; // latch핀을 pull-up
HAL_Delay(1);
}
}
}
#endif
<실행 결과>
https://youtube.com/shorts/rBA0nT-45P8