[STM32] printf, scanf 사용하기

pikamon·2021년 1월 5일
0

STM32

목록 보기
8/12

본 글은 STM32F769I MCU를 기준으로 작성되었습니다.
세부적인 내용은 제품군마다 조금씩 다를 수 있습니다.


stm32에서는 syscall.c 파일 안에 있는 _write, _read 등의 시스템 콜 함수를 재정의함으로써 GNU C의 printf, scanf 함수를 그대로 이용할 수 있다고 한다.

printf와 scanf를 UART1을 통해 사용하도록 설정해보자.

1. printf 사용하기

_write 함수를 재정의함으로써 사용할 수 있다.

int _write(int fd, char *ptr, int len)
{
	HAL_UART_Transmit(&huart1, (unsigned char*)ptr, len, HAL_MAX_DELAY);
	return len;
}

주의할 점은 표준 입출력 사용 시 버퍼링 정책을 사용하지 않도록 해주어야 한다.
setvbuf 함수를 이용하여 설정 가능하다.

setvbuf(stdout, NULL, _IONBF, 0);

1. 예제

printf 함수를 통해 UART1로 1초마다 Hello world!를 출력하는 코드를 작성하였다.

#include "main.h"

#include <stdio.h>

UART_HandleTypeDef huart1;

void SystemClock_Config(void)
{
	RCC_OscInitTypeDef RCC_OscInitStruct = { 0, };
	RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0, };

	__HAL_RCC_PWR_CLK_ENABLE();
	__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);

	RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
	RCC_OscInitStruct.HSIState = RCC_HSI_ON;
	RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
	RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
	RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
	RCC_OscInitStruct.PLL.PLLM = 8;
	RCC_OscInitStruct.PLL.PLLN = 192;
	RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
	RCC_OscInitStruct.PLL.PLLQ = 4;
	RCC_OscInitStruct.PLL.PLLR = 2;
	HAL_RCC_OscConfig(&RCC_OscInitStruct);

	RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK2;
	RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
	RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
	HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3);
}

static void MX_USART1_UART_Init(void)
{
	huart1.Instance = USART1;
	huart1.Init.BaudRate = 115200;
	huart1.Init.WordLength = UART_WORDLENGTH_8B;
	huart1.Init.StopBits = UART_STOPBITS_1;
	huart1.Init.Parity = UART_PARITY_NONE;
	huart1.Init.Mode = UART_MODE_TX_RX;
	huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart1.Init.OverSampling = UART_OVERSAMPLING_16;
	huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
	huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
	HAL_UART_Init(&huart1);
}

static void MX_GPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = { 0, };

	__HAL_RCC_GPIOA_CLK_ENABLE();

	GPIO_InitStruct.Pin =  GPIO_PIN_10 | GPIO_PIN_9;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

int _write(int fd, char *ptr, int len)
{
	HAL_UART_Transmit(&huart1, (unsigned char*)ptr, len, HAL_MAX_DELAY);
	return len;
}

int main(void)
{
	HAL_Init();
	MX_GPIO_Init();
	MX_USART1_UART_Init();

	char *str = "Hello world!";

	setvbuf(stdout, NULL, _IONBF, 0);

	while (1)
	{
		printf("%s\r\n", str);
		HAL_Delay(1000);
	}
}

void Error_Handler(void)
{

}

2. 빌드 및 실행 결과

빌드 후 실행하여 Baudrate 115200으로 터미널을 열면 아래와 같이 1초 주기로 Hello world!가 출력되는 것을 볼 수 있다.

2. scanf 사용하기

scanf도 마찬가지로 _read 함수를 재정의함으로써 사용할 수 있다.

int _read(int file, char *ptr, int len)
{
	HAL_UART_Receive(&huart1, (unsigned char*)ptr, len, HAL_MAX_DELAY);
	return len;
}

다만 printf와 달리 __io_putchar, __io_getchar 함수도 같이 재정의해주어야 한다.

int __io_putchar(int ch)
{
	HAL_UART_Transmit(&huart1, (unsigned char*)&ch, 1, HAL_MAX_DELAY);
	return ch;
}

int __io_getchar(void)
{
	uint8_t ch = 0;
	HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
	return ch;
}

1. 예제

scanf로 입력받은 문자열을 그대로 printf로 출력하는 코드를 작성하였다.
UART1 115200을 이용한다.

#include "main.h"

#include <stdio.h>

UART_HandleTypeDef huart1;

void SystemClock_Config(void)
{
	RCC_OscInitTypeDef RCC_OscInitStruct = { 0, };
	RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0, };

	__HAL_RCC_PWR_CLK_ENABLE();
	__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);

	RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
	RCC_OscInitStruct.HSIState = RCC_HSI_ON;
	RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
	RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
	RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
	RCC_OscInitStruct.PLL.PLLM = 8;
	RCC_OscInitStruct.PLL.PLLN = 192;
	RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
	RCC_OscInitStruct.PLL.PLLQ = 4;
	RCC_OscInitStruct.PLL.PLLR = 2;
	HAL_RCC_OscConfig(&RCC_OscInitStruct);

	RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK2;
	RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
	RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
	HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3);
}

static void MX_USART1_UART_Init(void)
{
	huart1.Instance = USART1;
	huart1.Init.BaudRate = 115200;
	huart1.Init.WordLength = UART_WORDLENGTH_8B;
	huart1.Init.StopBits = UART_STOPBITS_1;
	huart1.Init.Parity = UART_PARITY_NONE;
	huart1.Init.Mode = UART_MODE_TX_RX;
	huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart1.Init.OverSampling = UART_OVERSAMPLING_16;
	huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
	huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
	HAL_UART_Init(&huart1);
}

static void MX_GPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = { 0, };

	__HAL_RCC_GPIOA_CLK_ENABLE();

	// Initialize USART1
	GPIO_InitStruct.Pin =  GPIO_PIN_10 | GPIO_PIN_9;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

int _write(int fd, char *ptr, int len)
{
	HAL_UART_Transmit(&huart1, (unsigned char*)ptr, len, HAL_MAX_DELAY);
	return len;
}

int _read(int file, char *ptr, int len)
{
	HAL_UART_Receive(&huart1, (unsigned char*)ptr, len, HAL_MAX_DELAY);
	return len;
}

int __io_putchar(int ch)
{
	HAL_UART_Transmit(&huart1, (unsigned char*)&ch, 1, HAL_MAX_DELAY);
	return ch;
}

int __io_getchar(void)
{
	uint8_t ch = 0;
	HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
	return ch;
}

int main(void)
{
	HAL_Init();
	MX_GPIO_Init();
	MX_USART1_UART_Init();

	char str[256] = { 0, };

	setvbuf(stdin, NULL, _IONBF, 0);
	setvbuf(stdout, NULL, _IONBF, 0);

	while (1)
	{
		scanf("%s", str);
		printf("%s\r\n", str);
	}
}

void Error_Handler(void)
{

}

2. 빌드 및 실행 결과

빌드 후 실행하여 터미널에서 키보드를 입력하면 입력한 내용이 그대로 출력되는 것을 볼 수 있다.

사실 문제점이 몇 개 있는데, 하나는 입력한 문자가 바로바로 터미널에 출력되지 않는 것이며, 또 하나는 스페이스바를 누르면 입력한 문자열이 출력된다는 것이다.
버퍼링 정책 해제하면 되는 건줄 알았는데, 방법이 잘못되었거나 내가 잘못 알고 있는듯... 재확인 필요함.

profile
개발자입니당 *^^* 깃허브 https://github.com/pikamonvvs

0개의 댓글