STM32
STM32 UART DMA(Direct Memory Access) 송신/수신 설정
크게웃기
2025. 3. 14. 11:53
반응형
우선 아래 사진 처럼 UART에 DMA 를 사용 할 수 있도록 설정한다.
MODE :
- Normal (1회 성)
- Circular (계속)
Data Width 는 Byte로 설정한다.
이제 코드를 추가한다.
Main.c
#define DMA_RX_LEN 16 // 4배수의 값으로 넣어야함.
uint8_t recvBuff[DMA_RX_LEN]; // 반드시 전역으로 선언 할 것.
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_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;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart1, recvBuff, DMA_RX_LEN);
/* USER CODE END USART1_Init 2 */
}
stm32f1xx_it.c - RS232
extern uint8_t recvBuff[DMA_RX_LEN];
uint8_t tmpBuff[DMA_RX_LEN];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// 데이터 수신.
if(huart->Instance == USART1)
{
uint16_t writePtr = 0;
uint16_t readPtr = 0;
uint16_t recvLen = 0;
writePtr = ARRAY_LEN(recvBuff) - huart->hdmarx->Instance->CNDTR;
if(writePtr != readPtr)
{
memset(tmpBuff, 0x00, DMA_RX_LEN);
if(writePtr > readPtr)
{
recvLen = writePtr - readPtr;
memcpy(tmpBuff, recvBuff + readPtr, recvLen);
}
else
{
recvLen = ARRAY_LEN(recvBuff) - readPtr;
memcpy(tmpBuff, recvBuff + readPtr, recvLen);
if(writePtr > 0)
{
recvLen += writePtr;
memcpy(tmpBuff + recvLen, recvBuff, writePtr);
}
}
readPtr = writePtr;
}
// 데이터 송신 - RS232
// 수신 데이터 즉시 전송함.
HAL_UART_Transmit_DMA(&huart1, tmpBuff, sizeof(tmpBuff));
// 수신 대기
HAL_UART_Receive_DMA(&huart1, recvBuff, DMA_RX_LEN);
}
}
RS232의 경우 데이터 송신만 하면 됨.
extern uint8_t recvBuff[DMA_RX_LEN];
uint8_t tmpBuff[DMA_RX_LEN];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// 데이터 수신.
if(huart->Instance == USART3)
{
uint16_t writePtr = 0;
uint16_t readPtr = 0;
uint16_t recvLen = 0;
writePtr = ARRAY_LEN(recvBuff) - huart->hdmarx->Instance->CNDTR;
if(writePtr != readPtr)
{
memset(tmpBuff, 0x00, DMA_RX_LEN);
if(writePtr > readPtr)
{
recvLen = writePtr - readPtr;
memcpy(tmpBuff, recvBuff + readPtr, recvLen);
}
else
{
recvLen = ARRAY_LEN(recvBuff) - readPtr;
memcpy(tmpBuff, recvBuff + readPtr, recvLen);
if(writePtr > 0)
{
recvLen += writePtr;
memcpy(tmpBuff + recvLen, recvBuff, writePtr);
}
}
readPtr = writePtr;
}
// 데이터 송신 - RS485
// 수신 데이터 즉시 전송함.
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,1);
// Delay 추가 필요.
HAL_UART_Transmit_DMA(&huart3, tmpBuff, sizeof(tmpBuff));
// Delay 추가 필요.
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,0);
memset(tmpBuff, 0x00, sizeof(tmpBuff));
memset(recvBuff, 0x00, sizeof(recvBuff));
HAL_UART_Receive_DMA(&huart3, recvBuff, DMA_RX_LEN);
}
}
RS485의 경우 데이터 송/수신 시 GPIO pin 을 High/Log set 해줘야한다.
// Delay 추가 필요 부분에 HAL_Delay 를 추가 하면 되지만,
HAL_UART_RxCpltCallback 안에 추가 하게 되면 IWDG 로 인해 시스템 리부팅이 발생되므로,
수신부와 송신부를 별도로 설정하는게 좋다.
반응형