PROJECT/USB to UART

STM32로 USB to UART 모듈 만들기 2편(FW-기본통신)

원원 2025. 6. 10. 18:36

안녕하세요.
NUCLEO-F103RB보드를 사용하고 USB to UART 펌웨어를 만들어보겠습니다.


구현해야 할 기능은 두 가지입니다.
1. USB CDC 통신
2. UART 통신
PC랑은 USB CDC통신을하고 MCU랑은 UART 통신을 한다면 USB to UART가 됩니다.


구체적으로 PC에서 데이터를 송신하는 경우와 MCU에서 데이터를 송신하는 경우 동작은 아래의 그림과 같습니다.
1. PC에서 데이터를 송신하는 경우 : PC 터미널프로그램에서 데이터를 입력하게되면 STM32 에서 CDC 리시브 함수가 호출되고 받은 데이터 그대로 UART로 데이터를 송신하면 됩니다.

2. MCU에서 데이터를 송신하는 경우 : MCU-A에서 데이터를 입력하게되면 STM32에서 UART 리시브함수(인터럽트방식)가 호출되고 CDC로 데이터를 송신하게 됩니다.



(코드구현)
※ USB CDC 구현
NUCLEO-F103RB으로 프로젝트를 안만들고 STM32F103RB로 프로젝트를 만들었습니다.

NUCLEO-F103RB보드는 클럭은 크리스탈에게 받는게 아닌 ST-LINK로부터 받으므로 HSE를 Bypass Clock Source로 설정합니다. LSE는 사용하지않으므로 Disable 했습니다.


ST-LINK 사용을위해 Seriral Wire로 설정합니다.


USB 사용을 위해 Device(FS)를 체크합니다.


USB 클래스중에 CDC를 선택합니다.


FS이므로 클럭의 속도를 48mhz로 바꿔야합니다.


뉴클레오보드는 DP 라인에 pull-up이 안걸려있으므로 pull-up을 따로 걸어줘야합니다. pull-up은 3.3V or GPIO로 주면되는데  3.3V로 걸면 mcu가 reset할때 pc에서 mcu가 reset했다는것을 감지하지 못합니다.
그래서 풀업을 GPIO로 걸고 초기상태를 HIGH으로하면 리셋할때 LOW가 되서 pc가 reset을 인식하고 reset이후에 high가 되므로 USB를 인식하게됩니다.


뉴클레오 보드는 USB통신을위한 커넥터가 따로 있지않으므로 별도의 커넥터로 연결해줍니다. 그리고 DP라인에 위에서 말한 풀업을 연결해줍니다. 저항값은 1.2kΩ~1.8kΩ사이의 값으로 하고 STM에서는 1.5kΩ을 권장하고있습니다.
(PA11 : DM)
(PA12 : DP)
(PC12 : DP Pull-up)
선을 연결하고 PC에 USB를 연결하면 아래와 같이 컴포트가 잡히게 됩니다.


※ UART 구현
STM32F103은 UART1~UART3번까지 사용이 가능한데 뉴클레오보드에서 UART2는 ST-LINK랑 연결되어있고 UART1은 설정하면 경고가뜨므로 UART3번을 사용하겠습니다. 그리고 수신인터럽트를 사용할것이므로 Enabled 를 체크합니다.



USB CDC, UART는 이제 사용하므로 구현해야하는 기능인 송신할때를 해보겠습니다.

1. PC에서 데이터를 송신하는경우
위에 설명한 내용에서 CDC_Receive_FS는 PC로부터 데이터를 수신했을때 호출되는 함수입니다.
해당함수는 USB_DEVICE -> APP -> usbd_cdc_if.c에 있습니다.

CDC에서 수신한대로 UART로 송신하게 됩니다.

/* USER CODE BEGIN INCLUDE */
extern UART_HandleTypeDef huart3; // ADD

/* USER CODE END INCLUDE */


static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  HAL_UART_Transmit(&huart3, Buf, (uint16_t)(*Len), 100); // ADD

  /* USER CODE BEGIN 6 */

  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  return (USBD_OK);
  /* USER CODE END 6 */
}

이제 테스트를 해보겠습니다. Baudrate와 기타 통신설정은 default인 115200으로 했습니다. MCU-A는 테스트로 다른 USB to UART를 사용했습니다.
왼쪽터미널로 송신을하면 MCU-A가 수신하게 됩니다.



2. MCU-A 에서 데이터를 송신하는경우

STM32에서 UART데이터를 수신하면 수신한대로 CDC로 데이터를 송신하면 됩니다.
main.c에 수신이터럽트를 추가하고 데이터를 수신한대로 CDC로 데이터를 송신합니다.
송신하는 함수는 CDC_Transmit_FS 입니다.
그리고 수신 인터럽트이므로 메인문에서 인터럽트 활성화를 해줍니다.

/* USER CODE BEGIN 0 */
#include "usbd_cdc_if.h"

uint8_t rx_data;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(huart->Instance == USART3) //ADD
  {
    CDC_Transmit_FS(&rx_data, 1);
    HAL_UART_Receive_IT(&huart3, &rx_data, 1);
  }
}
/* USER CODE END 0 */




  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart3, &rx_data, 1); //ADD
  /* USER CODE END 2 */


왼쪽터미널에서 송신하면 MCU-A에 데이터가 나오고 MCU-A에서 데이터를 송신하면 STM32에서 나오게 됩니다.
이제 STM32가 USB to UART 역할을하게 됩니다.


소스코드(Commits: USB to UART communication)
https://github.com/yhunterr/STM32_USBtoUART




현재 펌웨어에서는 Baudrate와 UART 설정을 자동으로 변경하지 않지만, PC에서 터미널 프로그램을 통해 연결하면 PC 측 설정(baudrate, stopbit, paritybit, databit)이 STM32로 전달됩니다.
STM32에서 이 설정값을 읽어 실제 UART 설정에 적용하면, 다양한 통신 설정에서도 USB to UART 기능을 지원할 수 있게 됩니다.

다음시간에 해당 기능을 구현해보겠습니다.