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 설정 절차
- 프로젝트 생성:
- STM32CubeMX를 열고 "New Project" 선택.
- MCU 선택 창에서 "STM32G474" 검색, 패키지(예: LQFP64) 선택.
- 프로젝트 이름 지정 및 저장 경로 설정.
- 시스템 클럭 설정 (최대 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로 설정.
- 클럭 트리에서 에러 없음 확인.
- 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.
- 디버깅 인터페이스 설정:
- "System Core" > "SYS"에서 Debug를 "Serial Wire" (SWD)로 설정.
- SWD 핀 (PA13: SWDIO, PA14: SWCLK) 자동 예약.
- 프로젝트 설정 및 코드 생성:
- Project Manager 탭:
- Project Name과 저장 경로 확인.
- Toolchain/IDE를 "STM32CubeIDE"로 설정.
- Code Generator 옵션에서 "Copy only the necessary library files" 선택.
- "Generate Code" 클릭.
- Project Manager 탭:
3.2. UART 동작 원리
- 시스템 초기화:
- 클럭을 170 MHz로 설정하고, UART 및 DMA 클럭 활성화 (__HAL_RCC_USART2_CLK_ENABLE(), __HAL_RCC_DMA1_CLK_ENABLE()).
- HAL 라이브러리와 시스템 타이머(Systick) 초기화 (HAL_Init()).
- UART 설정:
- UART_HandleTypeDef 구조체를 사용하여 보드레이트, 데이터 비트, 패리티, 스톱 비트 설정.
- HAL_UART_Init 함수로 설정 적용.
- DMA 설정 (예제 3):
- DMA_HandleTypeDef 구조체를 사용하여 DMA 채널 설정.
- HAL_UART_Transmit_DMA 및 HAL_UART_Receive_DMA로 데이터 전송/수신.
- 인터럽트 설정 (예제 2):
- NVIC 인터럽트를 활성화 (HAL_NVIC_SetPriority, HAL_NVIC_EnableIRQ).
- 사용자 콜백 함수 (HAL_UART_RxCpltCallback) 오버라이드.
- 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 클럭, 디버깅
'MCU > STM32' 카테고리의 다른 글
[STM32G474] ADC 사용법: HAL API로 단일 변환, 연속 변환, 스캔 모드 설정 및 코드 예제 (0) | 2025.08.19 |
---|---|
[STM32G474] 타이머 트리거 사용법: HAL API로 타이머 트리거 설정 및 코드 예제 (0) | 2025.08.19 |
[STM32G474] 타이머 사용법: HAL API로 타이머 설정 및 코드 예제 (0) | 2025.08.19 |
[STM32G474] I2C, SPI 사용법: HAL API로 설정 및 코드 예제 (0) | 2025.08.19 |
[STM32G474] GPIO 사용법: HAL API로 GPIO 설정 및 코드 예제 (0) | 2025.08.19 |
[STM32] 링커 스크립트: 상세 설명, 구조, 작성 방법 및 예제 (STM32 Linker Script: Detailed Explanation, Structure, Writing Methods, and Examples) (3) | 2025.08.18 |
[ STM32]STM32L432KC로 Modbus RTU Slave 코드 구현: DMA와 저전력 최적화 (0) | 2025.08.13 |
[STM32G474] 를 이용한 동일 어드레스를 갖는 64채널 I2C 디바이스 제어 (0) | 2025.08.12 |