호타리 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