본문 바로가기

임베디드 & 로보틱스/STM32

[STM32-기초] UART 통신으로 Hello World 찍어보기 (Polling)

이전 시간에는 GPIO를 이용해 LED를 제어해봤습니다. 이번에는 임베디드 통신의 기초인 UART[Universal Asynchronous Receiver/Transmitter]를 사용하여 PC와 데이터를 주고 받는 실습을 해보겠습니다.

 

단순히 글자를 보내는 것 뿐만 아니라, PC에서 보낸 글자를 STM32가 받아서 다시 돌려주는 실습까지 진행합니다.


준비물

  • 개발 보드 : Nucleo-F746ZG
  • IDE : CubeIDE
  • 마이크로 5 핀 케이
  • 터미널 프로그램 [Tera Term, Putty 등]
    • 해당 포스트에선 Tera Term으로 진행

 

목표

  1. UART 통신의 개념과 하드웨어 연결법 이해하기
  2. Virtual COM Port(VCP) 이해하기
  3. Poliing 방식으로 PC에 "Hello World" 전송하기
  4. Interrupt 방식으로 PC에서 입력한 키를 다시 되돌려주기 (Echo)

1. UART 이론 및 하드웨어 연결

UART는 비동기 직렬 통신 방식으로, 클럭(Clock) 선 없이 Tx(송신)와 Rx(수신) 두 개의 선으로 데이터를 주고 받습니다. 가장 중요한 것은 속도(Baud Rate) 약속과 통신선의 교차 연결 입니다.

  • Baud Rate(보 레이트): 통신 속도. (예: 115200bps) 서로 속도가 다르면 글자(통신)이 깨집니다.
  • 연결 방법(교차 연결)
    • STM32 Tx 핀 ↔ USB 모듈 Rx
    • STM32 Rx 핀 ↔ USB 모듈 Tx
    • GNDGND(필수!)

왜 Baud Rate를 정해야 하는가?

더보기
더보기

UART는 클럭 신호선이 없습니다. 그래서 서로 "우리 1초에 비트 몇 개 보낼지 미리 정하자"라고 약속해야 합니다. 이를 Baud Rate라고 하며, 보통 9600bps 또는 115200bps를 가장 많이 사용합니다.


2. Virtual COM Port(VCP) 이해하기

"제 컴퓨터에는 Tx, Rx 선을 연결할 곳이 없는데요?"

STM32의 Virtual COM Port(VCP) 기능을 사용하면 Nucleo 보드 윗부분에 연결되어있는 ST-LINK 디버거에 연결된 USB 케이블을 통해 PC와 통신할 수 있습니다.

 

ST-LINK가 '중계기' 역할을 해줍니다.

STM32 칩이 특정 USART 핀으로 데이터를 보내면

ST-LINK가 이걸 받아서 USB 신호로 바꿔 PC로 보내줍니다.

PC에서는 마치 가상의 시리얼 포드(COM Port)가 연결된 것처럼 인식합니다.

 

[중요] Nucleo-F746ZG의 VCP 연결 핀은 매뉴얼(UM1974)에 따르면 USART3PD8, PD9 핀에 연결되어 있습니다.


3. CubeIDE 프로젝트 설정

1. Pinout & Configuration 확인/수정

  1. Pinout & Configuration 탭에서 ConnectivityUSART3 을 선택합니다.
  2. ModeAsynchronous(비동기)로 설정합니다.
  3. 오른쪽 칩 화면에서 PD8, PD9 핀이 초록색으로 활성화되었는지 확인합니다.(PD8, PD9 핀이 아니라 다른 핀으로 설정되어있다면 변경합니다.)
  4.  아래와 같이 설정 되었는지 확인

2. 파라미터(Parameter) 설정

  1. Buad Rate: 115200 BPS
  2. Word Length: 8 bits
  3. Parity: None
  4. Stop Bits: 1

3. 인터럽트 설정(NVIC Settings)

  1. USART3 global interrupt 항목의 Enabled 체크박스를 체크 합니다.

4. Polling 방식으로 PC에 "Hello World" 전송하기

main.c 에 아래와 같이 코드를 작성합니다.

/* USER CODE BEGIN PV */
// 1. 보낼 메시지 준비 (\r\n은 줄바꿈 문자)
char msg[] = "Hello World!\r\n";
/* USER CODE END PV */
/* USER CODE BEGIN WHILE */
while (1)
{
	// 2. 데이터 전송
    // &huart3 : VCP와 연결된 USART3 채널 주소
    // (uint8_t*)msg : 보낼 데이터
    // sizeof(msg)-1 : 데이터 크기 (널 문자 제외)
    // 100 : 타임아웃 (100ms 동안 전송 못 하면 취소)
	HAL_UART_Transmit(&huart3, (uint8_t*)msg, sizeof(msg)-1, 100);

	// 3. 1초 대기
	HAL_Delay(1000);
/* USER CODE END WHILE */

 

[결과 확인] 

더보기
더보기

코드를 실행(Run)하고 PC의 터미널 프로그램(Tera Term 등)을 켜면, 1초마다 인사가 출력됩니다.

출력이 안 되요! [Truble shoting]

더보기
더보기

Tera Term의 시리얼 포트가 STLink로 잘 설정 되어 있는지 확인 합니다.

STLink로 설정 되어있지 않다면, STLink로 설정된 COM Port를 찾아 설정해줍니다.

제 경우에는 COM7이네요.

출력 값이 이상해요! [Truble shoting]

더보기
더보기

 

위와 같이 글자가 깨져서 보인다면 Serial port의 설정과 STM32의 UART 설정을 동일하게 맞춰주세요.

하나라도 다르다면 정상적인 통신이 불가능 합니다.


5. Interrupt 방식으로 PC에서 입력 받은 값을 다시 되돌려 주기 (Echo)

Polling 방식은 데이터를 받을 때 CPU가 하염없이 기다려야 한다는 단점이 있습니다. Interrupt(인터럽트)를 사용하면 CPU는 자기 일을 하다가, 데이터가 도착하는 순간에만 "똑똑! 데이터 왔어요!" 하고 알려주기 때문에 훨씬 효율적입니다.

 

우리가 키보드를 누르면 -> STM32가 받아서 -> 다시 화면에 띄워주는 Echo 기능을 구현해봅시다.

 

Step 1. 수신 준비 (main 함수, while 루프 시작 전) CPU에게 "데이터 들어오면 나한테 알려줘"라고 지시하는 과정입니다.

/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart3, &rx_data, 1);	// 인터럽트 방식으로 1글자 수신 대기 시작 
/* USER CODE END 2 */

 

Step 2. 콜백 함수 작성 (main.c 맨 아래, USER CODE 4 영역) 데이터가 도착하면(수신 완료), 하드웨어가 자동으로 이 함수를 호출합니다.

/* USER CODE BEGIN PV */
char msg[] = "Hello World!\r\n";
char msg2[] = " Recieved!\r\n";
uint8_t rx_data;
/* USER CODE END PV */
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART3)
	{ // 1. 받은 데이터(rx_data)를 다시 PC로 전송 (Echo)
		HAL_UART_Transmit(&huart3, &rx_data, 1, 10);
		HAL_UART_Transmit(&huart3, (uint8_t*)msg2, sizeof(msg2)-1, 100);
		// 2. [중요] 다음 글자를 받기 위해 인터럽트 재가동
		// 이걸 빼먹으면 딱 한 글자만 받고 끝납니다.
		HAL_UART_Receive_IT(&huart3, &rx_data, 1);
	}
}
/* USER CODE END 4 */

 

[결과 확인] 터미널 창을 켜고 키보드로 아무 글자나 쳐보세요. 내가 입력한 글자가 화면에 바로바로 나타난다면 성공입니다!


마무리

오늘은 UART 통신의 기본 개념부터 Nucleo 보드의 VCP 기능을 이용한 실습까지 진행해 보았습니다.

  • 하드웨어 연결: TX-RX 교차 연결이 기본이지만, Nucleo는 VCP(USART3) 덕분에 USB 선 하나로 해결된다.
  • 소프트웨어: 내가 보낼 땐 Polling, 남의 것을 받을 땐 Interrupt가 효율적이다.