본문 바로가기
MCU/STM32

[STM32G474] UART 사용법: HAL API로 UART 설정 및 코드 예제

by linuxgo 2025. 8. 19.
반응형

1. STM32G474 UART 개요

STM32G474는 STMicroelectronics의 STM32G4 시리즈에 속하는 고성능 32비트 ARM Cortex-M4 마이크로컨트롤러로, 최대 170MHz로 동작하며 다양한 UART(Universal Asynchronous Receiver/Transmitter) 모듈을 제공합니다. UART는 시리얼 통신을 통해 데이터를 송수신하는 데 사용되며, 디버깅, 센서 데이터 수집, 외부 장치와의 통신 등에 활용됩니다. 이 문서에서는 STM32G474의 UART를 HAL API를 사용하여 설정하고 사용하는 방법을 상세히 다룹니다. 모든 예제 코드는 STM32CubeMX로 생성된 완전한 코드로 구성되며, STM32CubeIDE에서 실행 가능합니다. 각 코드에는 상세한 주석이 포함되어 있습니다.

UART 모듈의 주요 특징

  • 인스턴스: STM32G474는 USART1, USART2, USART3, UART4, UART5 등 여러 UART/USART 모듈을 제공.
  • 통신 모드: 비동기(Asynchronous), 동기(Synchronous, USART 전용), IrDA, LIN 등 지원.
  • 보드레이트: 최대 10 Mbps (클럭 설정에 따라 다름).
  • 데이터 형식: 7비트/8비트/9비트 데이터, 패리티(odd/even/none), 1/2 스톱 비트.
  • 하드웨어 플로우 컨트롤: CTS/RTS 지원 (일부 인스턴스).
  • 인터럽트: 송신 완료, 수신 완료, 오류 감지 등 다양한 인터럽트 지원.
  • DMA: 고속 데이터 전송을 위해 DMA 지원.
  • 클럭: APB1(USART2-5, UART4-5) 또는 APB2(USART1) 클럭 사용.

HAL API는 복잡한 레지스터 조작을 추상화하여 UART 설정 및 통신을 간소화합니다.

2. 주요 UART HAL API 함수

2.1. UART 초기화 및 설정 함수

  • *HAL_UART_Init(UART_HandleTypeDef huart)
    •   설명: UART 모듈을 초기화하며 보드레이트, 데이터 비트, 패리티, 스톱 비트 등을 설정.
    •   매개변수: huart (UART 핸들러 구조체).
    •   사용 예: USART2를 115200 보드레이트, 8비트, 패리티 없음, 1 스톱 비트로 설정.
  • *HAL_UART_DeInit(UART_HandleTypeDef huart)
    •   설명: UART 모듈을 기본 상태로 초기화.
  • **HAL_UART_Transmit(UART_HandleTypeDef huart, uint8_t pData, uint16_t Size, uint32_t Timeout)
    •   설명: 지정된 데이터를 UART로 전송 (폴링 모드).
    •   매개변수: pData (전송할 데이터 버퍼), Size (데이터 길이), Timeout (최대 대기 시간).
  • **HAL_UART_Receive(UART_HandleTypeDef huart, uint8_t pData, uint16_t Size, uint32_t Timeout)
    •   설명: UART로부터 데이터를 수신 (폴링 모드).
    •   반환값: HAL_OK (성공), HAL_TIMEOUT (시간 초과) 등.

2.2. 인터럽트 및 DMA 관련 함수

  • **HAL_UART_Transmit_IT(UART_HandleTypeDef huart, uint8_t pData, uint16_t Size)
    •   설명: 인터럽트를 사용하여 데이터 전송.
  • **HAL_UART_Receive_IT(UART_HandleTypeDef huart, uint8_t pData, uint16_t Size)
    •   설명: 인터럽트를 사용하여 데이터 수신.
  • *HAL_UART_TxCpltCallback(UART_HandleTypeDef huart)
    •   설명: 전송 완료 시 호출되는 콜백 함수 (오버라이드 필요).
  • *HAL_UART_RxCpltCallback(UART_HandleTypeDef huart)
    •   설명: 수신 완료 시 호출되는 콜백 함수 (오버라이드 필요).
  • **HAL_UART_Transmit_DMA(UART_HandleTypeDef huart, uint8_t pData, uint16_t Size)
    •   설명: DMA를 사용하여 데이터 전송.
  • **HAL_UART_Receive_DMA(UART_HandleTypeDef huart, uint8_t pData, uint16_t Size)
    •   설명: DMA를 사용하여 데이터 수신.
  • *HAL_UART_DMACpltCallback(UART_HandleTypeDef huart)
    •   설명: DMA 전송/수신 완료 시 호출되는 콜백 함수.

3. UART 설정 및 동작 원리

STM32G474의 UART를 효과적으로 사용하려면 시스템 클럭, UART 설정, DMA 설정, 디버깅 및 프로그램 다운로드 절차를 정확히 구성해야 합니다. 아래는 STM32CubeMX와 STM32CubeIDE를 사용한 설정 절차와 동작 원리를 설명합니다.

3.1. STM32CubeMX 설정 절차

  1. 프로젝트 생성:
    •   STM32CubeMX를 열고 "New Project" 선택.
    •   MCU 선택 창에서 "STM32G474" 검색, 패키지(예: LQFP64) 선택.
    •   프로젝트 이름 지정 및 저장 경로 설정.
  2. 시스템 클럭 설정 (최대 170 MHz, HSE 사용):
    •   Pinout & Configuration 탭에서 "System Core" > "RCC" 선택.
    •   "HSE"를 "Crystal/Ceramic Resonator"로 설정 (외부 8 MHz 크리스털 가정).
    •   Clock Configuration 탭:
      •   HSE를 8 MHz로 입력.
      •   PLL Source Mux를 "HSE"로 설정.
      •   PLL 설정: PLLM = 1, PLLN = 85, PLLR = 2, PLLR Enable 활성화.
      •   System Clock Mux를 "PLLCLK"로 설정.
      •   결과: SYSCLK = (8 MHz / 1) * 85 / 2 = 170 MHz.
      •    HCLK (AHB), PCLK1 (APB1), PCLK2 (APB2)를 170 MHz로 설정 (Divider = 1).
      •   Flash Latency를 4 WS로 설정.
      •   클럭 트리에서 에러 없음 확인.
  3. UART 설정:
    •   Pinout & Configuration 탭에서 "Connectivity" > "USART2" 선택.
    •   Mode를 "Asynchronous"로 설정.
    •   Parameter Settings:
      •   Baud Rate: 115200.
      •   Word Length: 8 Bits.
      •   Parity: None.
      •   Stop Bits: 1.
    •   GPIO Settings:
      •   PA2 (USART2_TX), PA3 (USART2_RX) 자동 설정.
      •   속도: High.
    •   인터럽트 및 DMA 활성화 (예제 2 및 3):
      •   NVIC Settings에서 "USART2 global interrupt" 활성화 (예제 2).
      •   DMA Settings에서 "USART2_TX" 및 "USART2_RX"에 DMA 채널 추가 (예제 3).
        •   TX: DMA1_Channel6, Direction: Memory to Peripheral.
        •   RX: DMA1_Channel5, Direction: Peripheral to Memory.
        •   Mode: Normal, Priority: Low.
  4. 디버깅 인터페이스 설정:
    •   "System Core" > "SYS"에서 Debug를 "Serial Wire" (SWD)로 설정.
    •   SWD 핀 (PA13: SWDIO, PA14: SWCLK) 자동 예약.
  5. 프로젝트 설정 및 코드 생성:
    •   Project Manager 탭:
      •   Project Name과 저장 경로 확인.
      •   Toolchain/IDE를 "STM32CubeIDE"로 설정.
      •   Code Generator 옵션에서 "Copy only the necessary library files" 선택.
    •   "Generate Code" 클릭.

3.2. UART 동작 원리

  1. 시스템 초기화:
    •   클럭을 170 MHz로 설정하고, UART 및 DMA 클럭 활성화 (__HAL_RCC_USART2_CLK_ENABLE(), __HAL_RCC_DMA1_CLK_ENABLE()).
    •   HAL 라이브러리와 시스템 타이머(Systick) 초기화 (HAL_Init()).
  2. UART 설정:
    •   UART_HandleTypeDef 구조체를 사용하여 보드레이트, 데이터 비트, 패리티, 스톱 비트 설정.
    •   HAL_UART_Init 함수로 설정 적용.
  3. DMA 설정 (예제 3):
    •   DMA_HandleTypeDef 구조체를 사용하여 DMA 채널 설정.
    •   HAL_UART_Transmit_DMA 및 HAL_UART_Receive_DMA로 데이터 전송/수신.
  4. 인터럽트 설정 (예제 2):
    •   NVIC 인터럽트를 활성화 (HAL_NVIC_SetPriority, HAL_NVIC_EnableIRQ).
    •   사용자 콜백 함수 (HAL_UART_RxCpltCallback) 오버라이드.
  5. UART 동작:
    •   송신: HAL_UART_Transmit, HAL_UART_Transmit_IT, 또는 HAL_UART_Transmit_DMA.
    •   수신: HAL_UART_Receive, HAL_UART_Receive_IT, 또는 HAL_UART_Receive_DMA.
    •   디버깅: 수신 데이터를 PC 터미널로 확인 (예: Tera Term).

3.3. 디버깅 및 프로그램 다운로드

  •   디버깅:
    •   ST-LINK 디버거 연결 (SWDIO: PA13, SWCLK: PA14).
    •   STM32CubeIDE에서 "Debug Configurations" 설정 (ST-LINK, SWD).
    •   브레이크포인트 설정 및 변수 (예: 수신 버퍼) 모니터링.
  •   다운로드:
    •   "Build Project"로 빌드 후, "Run Configurations"에서 .elf 파일 다운로드.
    •   프로그램 실행 후 UART 통신 확인 (PC 터미널 사용).

4. UART 예제 코드

아래는 STM32G474의 UART를 HAL API로 제어하는 STM32CubeMX 생성 예제 코드입니다. 시스템 클럭은 HSE 8 MHz를 사용하여 170 MHz로 설정됩니다.

4.1. 예제 1: 기본 UART 송신 (폴링 모드)

USART2를 사용하여 "Hello, STM32!" 메시지를 1초 간격으로 전송합니다.

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * STM32CubeMX-generated code for USART2 to transmit "Hello, STM32!" every 1 second.
  * System clock set to 170 MHz using HSE.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
UART_HandleTypeDef huart2; // USART2 핸들러 구조체
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define TX_BUFFER_SIZE 20 // 송신 버퍼 크기
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
volatile uint32_t uartStatus = 0; // UART 상태 변수: 0 (초기화 완료), 1 (동작 중), 0xFFFF (오류)
uint8_t txBuffer[TX_BUFFER_SIZE] = "Hello, STM32!\r\n"; // 송신할 메시지
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void); // 시스템 클럭 설정 함수 선언
static void MX_GPIO_Init(void); // GPIO 초기화 함수 선언
static void MX_USART2_UART_Init(void); // USART2 초기화 함수 선언

/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init(); // HAL 라이브러리 및 시스템 타이머(Systick) 초기화

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config(); // 시스템 클럭을 170 MHz로 설정 (HSE 8 MHz 사용)

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init(); // GPIO 초기화
  MX_USART2_UART_Init(); // USART2 초기화
  /* USER CODE BEGIN 2 */
  if (uartStatus != 1) // UART 초기화 성공 여부 확인
  {
    uartStatus = 0xFFFF; // 초기화 실패 시 오류 상태 설정
    while (1); // 오류 발생 시 무한 루프
  }
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_UART_Transmit(&huart2, txBuffer, strlen((char*)txBuffer), 100); // 메시지 전송
    HAL_Delay(1000); // 1초 대기
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0}; // 오실레이터 설정 구조체 초기화
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; // 클럭 설정 구조체 초기화

  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); // 전압 레귤레이터를 Scale 1로 설정

  /** Initializes the RCC Oscillators
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 외부 HSE 오실레이터 사용
  RCC_OscInitStruct.HSEState = RCC_HSE_ON; // HSE 활성화
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // PLL 활성화
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // PLL 소스를 HSE로 설정
  RCC_OscInitStruct.PLL.PLLM = 1; // PLL 분주기 M = 1
  RCC_OscInitStruct.PLL.PLLN = 85; // PLL 곱셈기 N = 85
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // PLL P 출력 분주기 = 2
  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // PLL Q 출력 분주기 = 2
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // PLL R 출력 분주기 = 2
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler(); // 설정 실패 시 오류 처리
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{
  huart2.Instance = USART2; // USART2 선택
  huart2.Init.BaudRate = 115200; // 보드레이트 115200
  huart2.Init.WordLength = UART_WORDLENGTH_8B; // 8비트 데이터
  huart2.Init.StopBits = UART_STOPBITS_1; // 1 스톱 비트
  huart2.Init.Parity = UART_PARITY_NONE; // 패리티 없음
  huart2.Init.Mode = UART_MODE_TX_RX; // 송수신 모드
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 하드웨어 플로우 컨트롤 비활성화
  huart2.Init.OverSampling = UART_OVERSAMPLING_16; // 16배 오버샘플링
  if (HAL_UART_Init(&huart2) != HAL_OK) // UART 초기화
  {
    Error_Handler();
  }
  uartStatus = 1; // UART 초기화 성공 표시
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE(); // GPIOA 포트 클럭 활성화
}

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  uartStatus = 0xFFFF; // 오류 상태 설정
  while (1)
  {
  }
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  while (1)
  {
  }
}
#endif /* USE_FULL_ASSERT */

설명:

  • 기능: USART2로 "Hello, STM32!" 메시지를 1초 간격으로 전송.
  • 설정: 115200 보드레이트, 8비트, 패리티 없음, 1 스톱 비트.
  • GPIO: PA2 (TX), PA3 (RX).
  • 출력: PC 터미널에서 메시지 확인.
  • 클럭: HSE 8 MHz, PLL로 170 MHz SYSCLK.

4.2. 예제 2: UART 수신 및 송신 (인터럽트 모드)

USART2를 사용하여 수신된 데이터를 에코백(echo back)합니다.

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * STM32CubeMX-generated code for USART2 to receive data and echo back using interrupts.
  * System clock set to 170 MHz using HSE.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
UART_HandleTypeDef huart2; // USART2 핸들러 구조체
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define RX_BUFFER_SIZE 1 // 수신 버퍼 크기 (1바이트씩 수신)
#define TX_BUFFER_SIZE 1 // 송신 버퍼 크기
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
volatile uint32_t uartStatus = 0; // UART 상태 변수: 0 (초기화 완료), 1 (동작 중), 0xFFFF (오류)
uint8_t rxBuffer[RX_BUFFER_SIZE]; // 수신 버퍼
uint8_t txBuffer[TX_BUFFER_SIZE]; // 송신 버퍼
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void); // 시스템 클럭 설정 함수 선언
static void MX_GPIO_Init(void); // GPIO 초기화 함수 선언
static void MX_USART2_UART_Init(void); // USART2 초기화 함수 선언

/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART2) // USART2에서 수신 완료 확인
  {
    txBuffer[0] = rxBuffer[0]; // 수신된 데이터 복사
    HAL_UART_Transmit_IT(&huart2, txBuffer, TX_BUFFER_SIZE); // 데이터 에코백
    HAL_UART_Receive_IT(&huart2, rxBuffer, RX_BUFFER_SIZE); // 다음 수신 대기
    uartStatus = 1; // 동작 상태 업데이트
  }
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init(); // HAL 라이브러리 및 시스템 타이머(Systick) 초기화

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config(); // 시스템 클럭을 170 MHz로 설정

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init(); // GPIO 초기화
  MX_USART2_UART_Init(); // USART2 초기화
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart2, rxBuffer, RX_BUFFER_SIZE); // 첫 번째 수신 시작
  if (uartStatus != 1) // UART 초기화 성공 여부 확인
  {
    uartStatus = 0xFFFF; // 초기화 실패 시 오류 상태 설정
    while (1);
  }
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if (uartStatus == 0xFFFF) // 오류 상태 확인
    {
      while (1); // 오류 시 무한 루프
    }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 1;
  RCC_OscInitStruct.PLL.PLLN = 85;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  HAL_NVIC_SetPriority(USART2_IRQn, 0, 0); // USART2 인터럽트 우선순위 설정
  HAL_NVIC_EnableIRQ(USART2_IRQn); // USART2 인터럽트 활성화
  uartStatus = 1; // UART 초기화 성공 표시
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  __HAL_RCC_GPIOA_CLK_ENABLE(); // GPIOA 포트 클럭 활성화
}

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  uartStatus = 0xFFFF;
  while (1)
  {
  }
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  while (1)
  {
  }
}
#endif /* USE_FULL_ASSERT */

설명:

  • 기능: USART2로 수신된 데이터를 인터럽트를 통해 에코백.
  • 설정: 115200 보드레이트, 8비트, 패리티 없음, 1 스톱 비트.
  • GPIO: PA2 (TX), PA3 (RX).
  • 출력: PC 터미널에서 입력한 문자 즉시 수신 및 재전송.
  • 클럭: HSE 8 MHz, PLL로 170 MHz SYSCLK.

4.3. 예제 3: UART 수신 및 송신 (DMA 모드)

USART2를 사용하여 DMA를 통해 데이터를 수신하고 에코백합니다.

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * STM32CubeMX-generated code for USART2 to receive and transmit data using DMA.
  * System clock set to 170 MHz using HSE.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
UART_HandleTypeDef huart2; // USART2 핸들러 구조체
DMA_HandleTypeDef hdma_usart2_rx; // USART2 RX DMA 핸들러
DMA_HandleTypeDef hdma_usart2_tx; // USART2 TX DMA 핸들러
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define BUFFER_SIZE 32 // 송수신 버퍼 크기
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
volatile uint32_t uartStatus = 0; // UART 상태 변수: 0 (초기화 완료), 1 (동작 중), 0xFFFF (오류)
uint8_t rxBuffer[BUFFER_SIZE]; // 수신 버퍼
uint8_t txBuffer[BUFFER_SIZE]; // 송신 버퍼
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void); // 시스템 클럭 설정 함수 선언
static void MX_GPIO_Init(void); // GPIO 초기화 함수 선언
static void MX_DMA_Init(void); // DMA 초기화 함수 선언
static void MX_USART2_UART_Init(void); // USART2 초기화 함수 선언

/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART2) // USART2에서 DMA 수신 완료 확인
  {
    memcpy(txBuffer, rxBuffer, BUFFER_SIZE); // 수신 데이터를 송신 버퍼로 복사
    HAL_UART_Transmit_DMA(&huart2, txBuffer, BUFFER_SIZE); // DMA로 데이터 에코백
    HAL_UART_Receive_DMA(&huart2, rxBuffer, BUFFER_SIZE); // 다음 수신 대기
    uartStatus = 1; // 동작 상태 업데이트
  }
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init(); // HAL 라이브러리 및 시스템 타이머(Systick) 초기화

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config(); // 시스템 클럭을 170 MHz로 설정

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init(); // GPIO 초기화
  MX_DMA_Init(); // DMA 초기화 (UART 초기화 전에 호출)
  MX_USART2_UART_Init(); // USART2 초기화
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_DMA(&huart2, rxBuffer, BUFFER_SIZE); // DMA 수신 시작
  if (uartStatus != 1) // UART 및 DMA 초기화 성공 여부 확인
  {
    uartStatus = 0xFFFF; // 초기화 실패 시 오류 상태 설정
    while (1);
  }
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if (uartStatus == 0xFFFF) // 오류 상태 확인
    {
      while (1); // 오류 시 무한 루프
    }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 1;
  RCC_OscInitStruct.PLL.PLLN = 85;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief DMA Initialization Function
  * @param None
  * @retval None
  */
static void MX_DMA_Init(void)
{
  __HAL_RCC_DMA1_CLK_ENABLE(); // DMA1 클럭 활성화

  HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0); // USART2 RX DMA 인터럽트 우선순위
  HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn); // RX DMA 인터럽트 활성화
  HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0); // USART2 TX DMA 인터럽트 우선순위
  HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn); // TX DMA 인터럽트 활성화
}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* DMA 설정 */
  hdma_usart2_rx.Instance = DMA1_Channel5;
  hdma_usart2_rx.Init.Request = DMA_REQUEST_USART2_RX;
  hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
  hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
  hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma_usart2_rx.Init.Mode = DMA_NORMAL;
  hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;
  if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
  {
    Error_Handler();
  }
  __HAL_LINKDMA(&huart2, hdmarx, hdma_usart2_rx);

  hdma_usart2_tx.Instance = DMA1_Channel6;
  hdma_usart2_tx.Init.Request = DMA_REQUEST_USART2_TX;
  hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
  hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma_usart2_tx.Init.Mode = DMA_NORMAL;
  hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW;
  if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK)
  {
    Error_Handler();
  }
  __HAL_LINKDMA(&huart2, hdmatx, hdma_usart2_tx);

  uartStatus = 1; // UART 및 DMA 초기화 성공 표시
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  __HAL_RCC_GPIOA_CLK_ENABLE(); // GPIOA 포트 클럭 활성화
}

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  uartStatus = 0xFFFF;
  while (1)
  {
  }
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  while (1)
  {
  }
}
#endif /* USE_FULL_ASSERT */

설명:

  • 기능: USART2로 DMA를 통해 최대 32바이트 데이터를 수신하고 에코백.
  • 설정: 115200 보드레이트, 8비트, 패리티 없음, 1 스톱 비트, DMA1 채널 5(RX) 및 6(TX) 사용.
  • GPIO: PA2 (TX), PA3 (RX).
  • 출력: PC 터미널에서 입력한 데이터 즉시 수신 및 재전송.
  • 클럭: HSE 8 MHz, PLL로 170 MHz SYSCLK.

5. 추가 고려 사항

  • 클럭 설정: HSE 주파수(예: 8 MHz)에 따라 PLL 설정 조정 필요.
  • 보드레이트: 높은 보드레이트 사용 시 클럭 정확도 확인.
  • DMA: DMA 모드는 대량 데이터 전송 시 CPU 부하 감소, Normal 모드 사용 시 전송 완료 후 재설정 필요.
  • 인터럽트: DMA 및 UART 인터럽트 우선순위 충돌 주의.
  • 디버깅: SWD와 ST-LINK로 실시간 데이터 모니터링.
  • 저전력: UART 및 DMA 설정 시 불필요한 기능 비활성화로 전력 최적화.

키워드: STM32G474, UART, HAL API, STM32G4, 마이크로컨트롤러, STM32CubeIDE, STM32CubeMX, 시리얼 통신, 인터럽트, DMA, HSE 클럭, 디버깅

반응형