STM32 UART Setup for printf & scanf

표준 입출력으로 uart로 설정하는 것은 인터럽트없이 가능하다. 아래 이미지와 같이 UART 및 LED Pin만 설정하여 printf와 scanf 동작을 확인할 예정이다.

UART를 USART2으로 설정하여 PA2, PA3를 사용하고 있으며, STM32F4xx 데모보드에서 해당 Pin에 와이어를 연결하도록 한다. UART로 표준 입출력을 사용하기 위해 syscall.c 파일에 weak 함수로 설정된 _read() / _write() 함수를 재정의하여 사용하면 간단하게 구현이 가능하다.

시스템콜인 _read()와 _write() 함수를 아래와 같이 재정하여 간단하게 UART로 표준 입출력으로 설정할 수 있다

int _read(int file, char *ptr, int len)
{
	if(HAL_UART_Receive(&huart2, (uint8_t *)ptr, len, 10) == HAL_OK)
		return len;
	return -1;
}

int _write(int file, char *ptr, int len)
{
	if(HAL_UART_Transmit(&huart2, (uint8_t *)ptr, len, 10) == HAL_OK)
		return len;
	return -1;
}

위 코드에서 _write() 함수로 printf() 함수를 사용하여 표준 출력을 UART로 보내는 것은 문제가 없으나, _read() 함수로 사용하여 scanf(), getchar()와 같은 표준 입력을 받는 것은 문제가 발생한다.

 

그래서, 포인터 단위로 처리하는 _read(), _write() 함수 대신, 표준 입력을 받아서 처리하기 위해 1바이트 단위로 처리하는 __io_getchar()와 __io_puchar() 함수를 재정하여 사용해야 한다.

위와 같이 두 함수는 syscall.c 파일에서 weak symbol로 설정하여 다른 파일에서 strong symbol이 존재하면 strong symbol을 사용하게 설정되어 있다.

 

__io_putchar(), __io_getchar() 함수는 main.c 파일에서 아래와 같이 재정의하여 UART로 표준 입출력을 할 수 있도록 하였다. 여기서 __io_getchar() 함수는 사용자 입력을 가독성 좋게 하기 위해 몇가지 트릭을 추가하였다.

int __io_putchar(int ch)
{
	if(HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 10) != HAL_OK)
		return -1;
	return ch;
}

int __io_getchar(void)
{
	char data[4];
	uint8_t ch, len = 1;

	while(HAL_UART_Receive(&huart2, &ch, 1, 10) != HAL_OK)
	{
	}

	memset(data, 0x00, 4);
	switch(ch)
	{
		case '\r':
		case '\n':
			len = 2;
			sprintf(data, "\r\n");
			break;

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

		default:
			data[0] = ch;
			break;
	}
	HAL_UART_Transmit(&huart2, (uint8_t *)data, len, 10);
	return ch;
}

위 코드로 UART로 표준 입출력을 설정하는 것은 완료하였고, 아래 코드로 간단하게 표준 입출력을 확인할 수 있다.

int main(void)
{
	HAL_Init();
	SystemClock_Config();

	MX_GPIO_Init();
	MX_USART2_UART_Init();
	setbuf(stdin, NULL);

	while (1)
	{
		int num;

		printf("input number :");
		scanf("%d", &num);
		printf("number = %d\r\n", num);
	}
}

 위 코드에서 가장 중요한 부분은 setbuf(stdin, NULL)을 설정하여 표준 입력시 버퍼를 사용하지 않게 설정해야 한다는 것이다. 만약 setbuf(stdin, NULL)을 설정하지 않으면 사용자 입력상태에서 블럭상태가 유지되지 않는다. 이것을 때문에 하루동안 많은 꽃삽질을 하였다. 주의하도록 하자.

 

그리고, C언어의 표준 입력 함수인 scanf(), getchar(), gets() 등을 사용하기 위해서는 추가적인 작업을 해야하기 때문에 이것을 사용하기보다는 표준출력만 UART로 설정하고 사용자 입력은 표준 입력 함수를 사용하는 것보다 따로 만들어 사용하는 것이 정신건가에 좋을 것 같다는 결론에 도달하였다.

 

다음 포스팅에서는 인터럽트 방식으로 사용자 입력을 받아, 나만의 콘솔 입력함수를 만들어 콘솔 명령어로 간단하게 STM32F4xx 데모보드를 테스트를 할 수 있는 펌웨어를 만들어 볼 생각이다.