아래 이미지와 같이 uart를 사용하여 디버그 메시지 확인 및 사용자가 특정 기능을 확인하기 위한 커맨드를 입력할 수 있다. 이것은 개발할 때 반드시 필요한 기능이기 때문에 uart 관련 전용 파일로 만들어 다른 프로그램에서 쉽게 적용할 수 있도록 코딩하였다.
사용자 입력을 받으려면 uart data를 인터럽트로 받는 것보다 폴링 방식으로 data를 수신하는 것이 편하지만, 이 코드에서는 인터럽트 방식을 사용하였다. 따라서 아래 이미지와 같이 MCU를 설정하도록 한다.
STM32CubeIDE는 MCU 설정에 맞게 소스코드를 생성하는데, 이 글에서는 UART 관련 사항을 uart.h / uart.c 파일로 관리할 것이다. 우선 main.c 파일에서 MX_USART2_UART_Init() 함수를 제거하고 uart.h / uart.c 파일로 옮겨야 하며, 전체 코드는 아래 압축파일을 받아서 확인이 가능하다.
위 코드에서 표준출력을 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로 바로 바로 전송하도록 하였다.