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 로 인해 시스템 리부팅이 발생되므로,

수신부와 송신부를 별도로 설정하는게 좋다.

 

 

반응형