통신 방법
단방향 / 반이중-양방향&동시X /전이중 - 양방향&동시
동기식(synchronous)
비동기식(Asynchronous)
UART는 비동기식, USART는 동기식
Uart 종류
Polling :
데이터량이 적은 경우, 폴링 Blocking 시간 지연이 다른 기능에 영향이 적을 경우 사용 권장
폴링방식 - 정해진 시간, 순서에서 상태를 확인하여 상태의 변화가 있는지 없는 지를 확인
interrupt :
데이터 송수신을 대기하지 않음. 따라서 다른 기능의 정지가 없음. 하지만 데이터 송수신량이 많을 경우 동작의 정지가 있을 수 있음.
인터럽트 - MCU가 작업을 중단하고 인터럽트 서비스 루틴을 실행하는 것
DMA
MCU에서 수행하지 않고 UART가 직접 RAM에 데이터를 저장함. 가장 효율이 좋음.
UART 송신 함수
HAL_UART_Transmit(huart, pData, size, Timeout);
Uart 핸들러, 송신 데이터 포인터, 데이터 길이, ms단위 타임아웃(시간내 작업 끝나지 않으면 오류 간주)
초기설정
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART3_UART_Init();
// GPIO 핀 상태 설정
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET); // GPIOC 포트의 6번 핀을 LOW 상태로 설정 (출력 핀 OFF)
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // GPIOB 포트의 0번 핀을 LOW 상태로 설정 (출력 핀 OFF)
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET); // GPIOB 포트의 5번 핀을 LOW 상태로 설정 (출력 핀 OFF)
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET); // GPIOD 포트의 12번 핀을 HIGH 상태로 설정 (출력 핀 ON)
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET); // GPIOD 포트의 13번 핀을 HIGH 상태로 설정 (출력 핀 ON)
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET); // GPIOD 포트의 14번 핀을 HIGH 상태로 설정 (출력 핀 ON)
데이터 들어오면 1byte씩 'a'라는 변수에 넣음
a라는 변수값을 1byte씩 읽어서 전송
uint8_t a = 'a';
while (1)
{
if(HAL_UART_Receive(&huart3, &a, 1, 10) == HAL_OK)
//데이터 수신후 저장시
{
HAL_UART_Transmit(&huart3, &a, 1, 10);
}
}
}
UART를 통해 "Hello, World!" 메시지를 주기적으로 송신
main() 함수: 기본적인 HAL 및 시스템 클럭 초기화 후 GPIO 및 UART 초기화를 수행합니다. 이후 무한 루프에서 Hello, World! 메시지를 1초 간격으로 UART를 통해 전송합니다.
MX_USART1_UART_Init() 함수: USART1의 기본 설정을 수행합니다. 보드레이트는 9600bps로 설정되며, 8비트 데이터, 1비트 스톱비트, 패리티 없음으로 구성됩니다.
HAL_UART_Transmit() 함수: UART를 통해 데이터를 송신하는 함수입니다. 메시지의 길이와 최대 대기 시간을 설정하여 데이터를 보냅니다.
#include "main.h"
#include "stm32f0xx_hal.h"
// 전역 변수 선언
UART_HandleTypeDef huart1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
int main(void)
{
// HAL 라이브러리 초기화
HAL_Init();
// 시스템 클럭 구성
SystemClock_Config();
// GPIO 및 UART 초기화
MX_GPIO_Init();
MX_USART1_UART_Init();
// "Hello, World!" 메시지
char *message = "Hello, World!\r\n";
while (1)
{
// UART로 메시지 송신
HAL_UART_Transmit(&huart1, (uint8_t*)message, strlen(message), HAL_MAX_DELAY);
// 1초 대기
HAL_Delay(1000);
}
}
// USART1 초기화 함수
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1; // USART1 사용
huart1.Init.BaudRate = 9600; // 보드레이트 9600bps
huart1.Init.WordLength = UART_WORDLENGTH_8B; // 데이터 길이 8비트
huart1.Init.StopBits = UART_STOPBITS_1; // 스탑비트 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; // 오버샘플링 16
if (HAL_UART_Init(&huart1) != HAL_OK)
{
// 초기화 오류 처리
Error_Handler();
}
}
버퍼 초기화 및 메시지 전송:
uint8_t buffer[256];: 256 바이트 크기의 버퍼를 선언합니다. UART로 전송할 데이터를 임시로 저장하기 위해 사용됩니다.
sprintf((char *)buffer, "Hello, World!\n");: sprintf 함수를 사용하여 buffer에 "Hello, World!\n" 문자열을 저장합니다.
HAL_UART_Transmit(&huart2, buffer, strlen((char *)buffer), 100);: UART2를 통해 buffer에 저장된 데이터를 전송합니다. 전송할 데이터의 길이는 strlen 함수를 통해 계산되며, 타임아웃은 100ms로 설정됩니다.
/* USER CODE BEGIN Includes */
#include <string.h> // 문자열 처리 함수 사용을 위한 헤더파일 포함
#include <stdio.h> // 표준 입출력 함수 사용을 위한 헤더파일 포함
/* USER CODE END Includes */
/* USER CODE BEGIN 2 */
uint8_t buffer[256]; // 전송할 데이터를 저장할 버퍼 선언 (최대 256 바이트)
// buffer에 "Hello, World!\n" 문자열을 저장
sprintf((char *)buffer, "Hello, World!\n");
// UART2를 통해 buffer에 저장된 문자열 전송
// strlen을 사용해 전송할 데이터의 길이를 계산하고, 타임아웃은 100ms로 설정
HAL_UART_Transmit(&huart2, buffer, strlen((char *)buffer), 100);
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1) // 무한 루프 시작
{
// buffer에 "Loop! count\n" 형태로 현재 루프 카운트를 저장
// count는 루프의 반복 횟수를 나타내는 변수로, 1씩 증가
sprintf((char *)buffer, "Loop! %d\n", ++count);
// UART2를 통해 buffer에 저장된 문자열 전송
HAL_UART_Transmit(&huart2, buffer, strlen((char *)buffer), 100);
// 1초(1000ms) 동안 대기
HAL_Delay(1000);
/* USER CODE END WHILE */
write 함수 printf실행시 호출, printf함수 출력을 UART3으로 리디렉션
#include <stdio.h> //표준출력정의
static void MX_GPIO_Init(void);
static void MX_USART3_UART_Init(void);
/* USER CODE BEGIN PFP */
int _write(int file, char *p, int len)
{
HAL_UART_Transmit(&huart3, p, len, 10);
return len;
}
float f = 1.234;
while (1)
{
printf("Hello %f\n", f);
HAL_Delay(1000);
}
※ 표준입출력의 printf는 많이 무겁기 때문에 stm 라이브러리 Tiny printf를 사용함. 디버깅할때 printf 사용하지만 Release버전에는 printf를 사용하지 않음
float 변수 출력
프로젝트 속성 / C/C++ 빌드 / 세팅 / 툴세팅
[ Other flags ] or [Miscellaneous]
-u _printf_float
글로벌 인터럽트를 enable 함.
NVIC 코드를 생성하도록 함
인터럽트 코드를 따라가면 다음과 같이 몸체가 나타남
UART3_IRQHandler -> HAL_UART_IRQHandler -> UART_Receive_IT -> HAL_UART_RxCpltCallback 호출
함수 앞에 "__weak"는 사용자가 재정의 하여 사용하라는 의미임. 실제 인터럽트의 몸체로 사용자가 다시 정의하면 됨
Uart 송신 인터럽트 함수: Uart Tx로 데이터를 Size만큼 전송하면 인터럽트가 발생한다.
// huart: uart 인스턴스
// pData: 송신 데이터 버퍼
// Size: 송신 데이터 개수
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
Uart 수신 인터럽트 함수: Uart Rx로 데이터가 Size만큼 들어오면 인터럽트가 발생한다.
// huart: uart 인스턴스
// pData: 수신 데이터 버퍼
// Size: 수신 데이터 개수
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
예제0
uint8_t rx_data;
// 인터럽트 콜백 함수: 인터럽트가 발생되면 이 함수가 호출된다.
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
// 데이터 1개를 수신하면 인터럽트를 발생시킨다.
HAL_UART_Receive_IT(&huart1, &rx_data, 1);
// 받은 데이터를 전송한다.
HAL_UART_Transmit(&huart1, &rx_data, 1, 10);
}
}
MX_USART1_UART_Init();
/* Initialize interrupts */
MX_NVIC_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1, &rx_data, 1);
UART_HandleTypeDef huart3;
uint8_t rx3_data;
// 수신한 데이터 1byte 저장
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART3)
{
HAL_UART_Receive_IT(&huart3, &rx3_data, 1);
//첫번째 인터럽트 후 다시 인터럽트 발생
HAL_UART_Transmit(&huart3, &rx3_data, 1, 10);
}
}
// UART3를 통해 rx3_data에 1바이트 데이터 수신시 인터럽트 생성
// 무한루프 이전에 한번 실행, vodi UART4_IRQHandler(void) 함수 실행 위함
HAL_UART_Receive_IT(&huart3, &rx3_data, 1);
배열 인터럽트
uint8_t rx3_data[10];
HAL_UART_Receive_IT(&huart3, &rx3_data, 10);
에러 발생 종류
PE: 하드웨어적으로 패리티 체크 시 불일치 할 때 발생
NE: 하드웨어적으로 Noise detection 시 발생
FE: 하드웨어적으로 수신한 Frame 에 오류가 있는 경우 발생
ORE: 하드웨어적을 RDR 레지스터에 오버런이 발생한 경우 발생
DMA: DMA 전송 시 오류가 발생한 경우
Ref
https://louie0724.tistory.com/357
https://huroint.tistory.com/entry/STM32-Serial-%ED%86%B5%EC%8B%A0-UART
https://jeonhj.tistory.com/37
https://www.youtube.com/watch?v=6uq9v4nfuag&list=PLUaCOzp6U-RqMo-QEJQOkVOl1Us8BNgXk&index=5