STM32 串口DMA接收不定长数据

发布时间 2023-06-09 13:56:49作者: 妖岭

串口DMA接收数据,减少CPU占用

 

思路:DMA+空闲中断(无法确认数据帧结束)+循环缓存

DMA初始化

void DMA1_Stream_Config(DMA_Stream_TypeDef *DMA_Stream, uint32_t MemAddress, uint32_t ParAddress, uint32_t Size)
{
    DMA_Stream->CR &= ~(1UL);   //stream disable
    DMA_Stream->M0AR = MemAddress;
    DMA_Stream->PAR = ParAddress;
    DMA_Stream->NDTR = Size;


}
void DMA1_Stream_Enable(DMA_Stream_TypeDef *DMA_Stream)
{
    DMA1->LIFCR = 0xFFFFFFFF;   //clear all complete and half interrupt flag
    DMA_Stream->CR |= (1UL<<4);   //enable DMA TC interrupt
    DMA_Stream->CR |= (1UL);   //stream enable
}

void DMA1_Stream_Disable(DMA_Stream_TypeDef *DMA_Stream)
{
    DMA_Stream->CR &= ~(1UL);   //stream disable
    DMA1->LIFCR = 0xFFFFFFFF;   //clear complete and half interrupt flag
    DMA_Stream->CR &= ~((1UL<<3)| (1UL<<4));   //disable DMA TC interrupt and HalfTC interrupt
}


typedef struct
{
volatile uint8_t rx_buf[BUFF_SIZE]; // The rove receive buffer
volatile uint8_t rx_base_buf[BASE_BUFF_SIZE]; // The base receive buffer
volatile uint8_t tx_buf[BASE_BUFF_SIZE]; // The transmit buffer
volatile int16_t rx_front;
volatile int16_t rx_rear;
volatile int16_t rx_base_front;
volatile int16_t rx_base_rear;
volatile int16_t tx_front;
volatile int16_t tx_rear;
} UARTType;
extern UARTType uart7;



DMA1_Stream_Config(DMA1_Stream1, (uint32_t)uart7.rx_buf, (uint32_t)&(UART7->RDR), BUFF_SIZE);
DMA1_Stream_Enable(DMA1_Stream1);

 UART 初始化

/* UART7 init function */
void MX_UART7_Init(void)
{

  /* USER CODE BEGIN UART7_Init 0 */
    uint8_t temp;
  /* USER CODE END UART7_Init 0 */

  /* USER CODE BEGIN UART7_Init 1 */

  /* USER CODE END UART7_Init 1 */
  huart7.Instance = UART7;
  huart7.Init.BaudRate = 460800;
  huart7.Init.WordLength = UART_WORDLENGTH_8B;
  huart7.Init.StopBits = UART_STOPBITS_1;
  huart7.Init.Parity = UART_PARITY_NONE;
  huart7.Init.Mode = UART_MODE_TX_RX;
  huart7.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart7.Init.OverSampling = UART_OVERSAMPLING_16;
  huart7.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart7.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart7.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart7) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart7, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart7, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&huart7) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN UART7_Init 2 */
  UART7->CR3 |= (1U<<6);    //Enable UART DMA Receive

  __HAL_UART_CLEAR_FLAG(&huart7, UART_CLEAR_IDLEF);
  __HAL_UART_ENABLE_IT(&huart7, UART_IT_IDLE);
  /* USER CODE END UART7_Init 2 */

}

.....................

/* UART7 clock enable */
__HAL_RCC_UART7_CLK_ENABLE();


__HAL_RCC_GPIOB_CLK_ENABLE();
/**UART7 GPIO Configuration
PB4 ------> UART7_TX
PB3 ------> UART7_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_UART7;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);


GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_UART7;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);


/* UART7 DMA Init */
/* UART7_RX Init */
hdma_uart7_rx.Instance = DMA1_Stream1;
hdma_uart7_rx.Init.Request = DMA_REQUEST_UART7_RX;
hdma_uart7_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_uart7_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_uart7_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_uart7_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_uart7_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_uart7_rx.Init.Mode = DMA_CIRCULAR;
hdma_uart7_rx.Init.Priority = DMA_PRIORITY_LOW;
hdma_uart7_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_uart7_rx) != HAL_OK)
{
Error_Handler();
}


__HAL_LINKDMA(uartHandle,hdmarx,hdma_uart7_rx);


/* UART7 interrupt Init */
HAL_NVIC_SetPriority(UART7_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(UART7_IRQn);

....................

中断处理函数

void DMA1_Stream1_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream1_IRQn 0 */

    if(DMA1->LISR & (1UL<<11))   //TC
    {
        DMA1->LIFCR |= (1UL<<11);
    }
    else
    {
  /* USER CODE END DMA1_Stream1_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_uart7_rx);
  /* USER CODE BEGIN DMA1_Stream1_IRQn 1 */
    }
  /* USER CODE END DMA1_Stream1_IRQn 1 */
}

 

void UART7_IRQHandler(void)
{
  /* USER CODE BEGIN UART7_IRQn 0 */
    BaseType_t status;
/*
    if(UART7->ISR&(1<<5)) //RXNEIE
    {
        resume=1;
        uint16_t temp = uart7.rx_rear;
        uart7.rx_buf[temp] = UART7->RDR;
        temp = ((temp)>=BUFF_SIZE-1) ? 0 : (temp)+1;
        uart7.rx_rear = temp;
        SET_BIT(UART7->CR1, USART_CR1_RXNEIE);
    }

    else */
    if(UART7->ISR & (1UL<<4))   //IDLE
    {
        uart7.rx_rear = (BUFF_SIZE - DMA1_Stream1->NDTR)>=BUFF_SIZE?0:BUFF_SIZE - DMA1_Stream1->NDTR;
        SCB_InvalidateDCache_by_Addr((uint32_t*)uart7.rx_buf, BUFF_SIZE);//DMA和D-Cache数据一致性处理

        //唤醒数据处理任务
        if(dataTaskHandle)
        {
            status = xTaskResumeFromISR(dataTaskHandle);//恢复任务
            if(status==pdTRUE)
            {
                portYIELD_FROM_ISR(status);
            }
        }
        //获取星历命令
        if(!(--IPSeph_Time))
        {
            Send_Cmd(&huart7, "$PSTMDUMPEPHEMS\r\n");
            IPSeph_Time = COUNT;
        }
        UART7->ICR |= (1UL<<4);
    }
    else if(UART7->ISR & (1UL<<3))   //ORE
    {
        UART7->ICR |= (1UL<<3);
    }
    else if(UART7->ISR & (1UL<<1))  //FE
    {
        UART7->ICR |= (1UL<<1);
    }
    else if(UART7->ISR & (1UL<<2))  //NE
    {
        UART7->ICR |= (1UL<<2);
    }
    else
    {
  /* USER CODE END UART7_IRQn 0 */
  HAL_UART_IRQHandler(&huart7);
  /* USER CODE BEGIN UART7_IRQn 1 */
    }
  /* USER CODE END UART7_IRQn 1 */
}

获取循环缓存数据

//写缓冲区
void WriteRxBuffer(UARTType * pUART, uint8_t info)
{
    uint16_t temp = pUART->rx_rear;
    pUART->rx_buf[temp] = info;
    temp = ((temp)>=BUFF_SIZE-1) ? 0 : (temp)+1;
    pUART->rx_rear = temp;
}

//读缓冲区
uint32_t ReadRxBuffer(UARTType * pUART, uint8_t *info, uint32_t size)
{
    uint32_t i;
    uint16_t temp = pUART->rx_front;
    for(i = 0; i<size; i++)
    {
        if(temp==pUART->rx_rear)
            break;
        info[i] = pUART->rx_buf[temp];
        temp = ((temp)>=BUFF_SIZE-1) ? 0 : (temp)+1;
    }
    pUART->rx_front = temp;
    return i;
}

void StartDataTask(void *argument)
{
    uint32_t nb[2] = {0};
    uint8_t byte[1] = {0};            //存储从uart7.rx_buf缓冲区获取的1byte Rover数据
    uint32_t get_size = 0;

    for(;;)
    {
        get_size = ReadRxBuffer(&uart7, byte, 1);
        if(get_size)
        {
            roveBuff[nb[0]++] = byte[0];
        }
        if((byte[0]=='\n')&&(nb[0]))
        {
            MyPrintf((char*)roveBuff, nb[0]);   //输出数据
            memset(roveBuff, 0, nb[0]);
            nb[0] = 0;
        }
    }
}

输出数据

void UART5_IRQHandler(void)
{
  /* USER CODE BEGIN UART5_IRQn 0 */
    uint8_t temp;
    if(UART5->ISR&(1<<5))
    {
        temp = UART5->RDR;
        if(interr_receive_bytes < BASE_BUFF_SIZE)
            uart7.rx_base_buf[interr_receive_bytes++] = temp;
        __HAL_TIM_SET_COUNTER(&htim2, 0);
        SET_BIT(UART5->CR1, USART_CR1_RXNEIE);
    }
    else if(UART5->ISR&(1U<<7)&& UART5->CR1&(1U<<7))//
    {
        uint16_t temp = uart7.tx_front;
        UART5->TDR=uart7.tx_buf[temp];
        temp = ((temp)>=BUFF_SIZE/2-1) ? 0 : (temp)+1;
        if(temp==uart7.tx_rear)
        {
            CLEAR_BIT(UART5->CR1,1U<<7);//数据发送完毕,禁止串口的发送缓冲区空中断
        }
        uart7.tx_front = temp;
    }
    else if(UART5->ISR & (1UL<<3))   //ORE
    {
        UART5->ICR |= (1UL<<3);
    }
    else if(UART5->ISR & (1UL<<1))  //FE
    {
        UART5->ICR |= (1UL<<1);
    }
    else if(UART5->ISR & (1UL<<2))  //NE
    {
        UART5->ICR |= (1UL<<2);
    }
    else
    {
  /* USER CODE END UART5_IRQn 0 */
  HAL_UART_IRQHandler(&huart5);
  /* USER CODE BEGIN UART5_IRQn 1 */
    }
  /* USER CODE END UART5_IRQn 1 */
}

//将字符串放入发送缓冲区并开启中断发送
void MyPrintf(char* str, uint32_t len)
{
    //挂起所有任务,防止数据错乱
    vTaskSuspendAll();
    uint16_t temp = uart7.tx_rear;
    for(uint32_t i = 0; i<len; i++)
    {
        uart7.tx_buf[temp] = str[i];
        temp = ((temp)>=BUFF_SIZE/2-1) ? 0 : (temp)+1;
    }
    uart7.tx_rear = temp;
    //恢复所有任务
    xTaskResumeAll();
    SET_BIT(UART5->CR1,1U<<7);//使能串口的发送缓冲区空中断
}