STM32 UART Debug command

아래 이미지와 같이 uart를 사용하여 디버그 메시지 확인 및 사용자가 특정 기능을 확인하기 위한 커맨드를 입력할 수 있다. 이것은 개발할 때 반드시 필요한 기능이기 때문에 uart 관련 전용 파일로 만들어 다른 프로그램에서 쉽게 적용할 수 있도록 코딩하였다.

사용자 입력을 받으려면 uart data를 인터럽트로 받는 것보다 폴링 방식으로 data를 수신하는 것이 편하지만, 이 코드에서는 인터럽트 방식을 사용하였다. 따라서 아래 이미지와 같이 MCU를 설정하도록 한다.

STM32CubeIDE는 MCU 설정에 맞게 소스코드를 생성하는데, 이 글에서는 UART 관련 사항을 uart.h / uart.c 파일로 관리할 것이다. 우선 main.c 파일에서 MX_USART2_UART_Init() 함수를 제거하고 uart.h / uart.c 파일로 옮겨야 하며, 전체 코드는 아래 압축파일을 받아서 확인이 가능하다.

BoardTest.zip
0.64MB

위 코드에서 표준출력을 uart로 출력하기 위해 __io_putchar() 함수를 재정의해야 하며, 사용자 입력을 받아서 처리하는 인터럽스 콜백인 HAL_UART_RxCpltCallback() 함수를 아래와 같이 처리하였다.

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART2)
    {
        char cdata[4], len = 1;
       
        memset(cdata, 0x00, 4);
        switch(data)
        {
            case '\r':
            case '\n':
                len = 2;
                sprintf(cdata, "\r\n");

                buff[length++] = '\r';
                buff[length++] = '\n';
                break;

            case '\b':
            case 0x7F:
                len = 3;
                sprintf(cdata, "\b \b");

                if(length > 0)
                    buff[length--] = '\0';
                break;

            default:
                cdata[0] = data;
                buff[length++] = data;
                break;
        }

        HAL_UART_Transmit_IT(huart, (uint8_t *)cdata, len);
        HAL_UART_Receive_IT(huart, &data, 1);
    }
}

전역변수 buff의 사용자 입력 데이터를 저장하는데, 백스페이스인 경우 buff에 저장된 데이터를 지우는 처리해야 하며, 사용자 입력 데이터를 화면에 출력되도록 uart로 데이터를 전송해야 한다.

 

아래 drv_uart_rx_buffer() 함수는 사용자가 리턴하기 전까지 사용자 입력 데이터를 받아오는 함수이며, 이것을 처리할 동안은 인터럽트가 발생하지 않게 비활성화 시켜야 한다.

int drv_uart_rx_buffer(uint8_t *buf, uint8_t size)
{
    int ret_value = 0;

    while(buff[length-2] != '\r' && buff[length-1] != '\n')
    {
        HAL_Delay(10);
        continue;
    }

    __HAL_UART_DISABLE_IT(&huart2, UART_IT_RXNE);
    if(size < length)
        length = size;

    length -= 2;   
    memcpy(buf, buff, length);
    ret_value = length;

    memset(buff, 0x00, sizeof(buff));
    length = 0;

    __HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);
    HAL_UART_Receive_IT(&huart2, &data, 1);
    return ret_value;
}

마지막으로 main.c에서 사용자 입력 데이터에 따라 특정 기능이 실행되도록 아래와 같이 코드를 작성하면 된다.

int main(void)
{
	char buf[MAX_UART_RX_BUFFER];

	HAL_Init();
	SystemClock_Config();

	MX_GPIO_Init();
	if(drv_uart_init() != 0)
		Error_Handler();

	display_help();
	while (1)
	{
		memset(buf, 0x00, MAX_UART_RX_BUFFER);
		drv_uart_tx_buffer((uint8_t *)">", 1);
		if(drv_uart_rx_buffer((uint8_t *)buf, MAX_UART_RX_BUFFER) == 0)
			continue;

		if(strncmp(buf, "help", strlen("help")) == 0)
			display_help();
		else if(strncmp(buf, "led_info", strlen("len_info")) == 0)
		{
			...
		}
		else if(strncmp(buf, "led_togg", strlen("led_togg")) == 0)
		{
			...
		}
	}
}

콘솔 화면에 '>'를 출력하기 위해 printf가 아닌 drv_uart_tx_buffer() 함수를 사용한 이유는 printf를 사용할 경우 IO 버퍼로 인해 바로 바로 출력되지 않아, drv_uart_tx_buffer() 함수를 만들어 uart로 바로 바로 전송하도록 하였다.