STM32F072使用DMA+IDLE进行串口接收不定长数据有问题,改为DMA+RTO接收正常。

把调试STM32F072串口过程中,出现了一小点问题,记录下来,F0的串口寄存器有些增加的功能以前没用到,F0增加了modbus协议之类的接收超时处理,完美实现了不定长数据包的帧接收。
本次使用USART2进行通讯。
cubeMX中的设置
DMA设置:
STM32F072使用DMA+IDLE进行串口接收不定长数据有问题,改为DMA+RTO接收正常。_第1张图片
dma.c中生成

void MX_DMA_Init(void) 
{
  /* Init with LL driver */
  /* DMA controller clock enable */
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
  /* DMA interrupt init */
  /* DMA1_Channel4_5_6_7_IRQn interrupt configuration */
  NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 0);
  NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn);
}

USART2设置:
STM32F072使用DMA+IDLE进行串口接收不定长数据有问题,改为DMA+RTO接收正常。_第2张图片
usart.c中生成

void MX_USART2_UART_Init(void)
{
  LL_USART_InitTypeDef USART_InitStruct = {0};
  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2);
   
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
  /**USART2 GPIO Configuration  
  PA2   ------> USART2_TX
  PA3   ------> USART2_RX 
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_2;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  GPIO_InitStruct.Pin = LL_GPIO_PIN_3;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  /* USART2 DMA Init */
   
  /* USART2_RX Init */
  LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_5, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
  LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_5, LL_DMA_PRIORITY_MEDIUM);
  LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_5, LL_DMA_MODE_CIRCULAR);
  //LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_5, LL_DMA_MODE_NORMAL);
  LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_5, LL_DMA_PERIPH_NOINCREMENT);
  LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_5, LL_DMA_MEMORY_INCREMENT);
  LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_5, LL_DMA_PDATAALIGN_BYTE);
  LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_5, LL_DMA_MDATAALIGN_BYTE);
  /* USART2_TX Init */
//  LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_4, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
//  LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PRIORITY_MEDIUM);
//  LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MODE_CIRCULAR);
//  //LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MODE_NORMAL);
//  LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PERIPH_NOINCREMENT);
//  LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MEMORY_INCREMENT);
//  LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PDATAALIGN_BYTE);
//  LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MDATAALIGN_BYTE);
  USART_InitStruct.BaudRate = 2000000;
  USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
  USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
  USART_InitStruct.Parity = LL_USART_PARITY_NONE;
  USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
  USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
  USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
  LL_USART_Init(USART2, &USART_InitStruct);
  LL_USART_DisableIT_CTS(USART2);
  LL_USART_ConfigAsyncMode(USART2);
  LL_USART_Enable(USART2);
}

user.c中添加

void USART2_Interrupt_Enable(void)
{
  /* USART2 interrupt Init */
  NVIC_SetPriority(USART2_IRQn, 0);
  NVIC_EnableIRQ(USART2_IRQn);
}
void USART2_DMA_Recive(uint8_t *p_data, uint16_t length)
{
  LL_USART_Disable(USART2);
  USART2->CR3 |= USART_CR3_DMAR | USART_CR3_DMAT; 
  USART2->CR1 |= USART_CR1_IDLEIE;                /*enable usart2 idle interrupt*/
  DMA1_Channel5->CCR &= ~(DMA_CCR_EN);                      //disable the dma 
  DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);           //Peripheral address
  DMA1_Channel5->CMAR = (uint32_t)p_data;                   //memory address
  DMA1_Channel5->CNDTR = length;                            //Set the length
  DMA1_Channel5->CCR |= DMA_CCR_MINC | DMA_CCR_EN;          //enable the DMA 
  LL_USART_Enable(USART2);
}

void DMA1_Channel4_5_6_7_IRQHandler(void) //此中断始终未进入过
{
  /* USER CODE BEGIN DMA1_Channel4_5_6_7_IRQn 0 */
  /* USER CODE END DMA1_Channel4_5_6_7_IRQn 0 */
  /* USER CODE BEGIN DMA1_Channel4_5_6_7_IRQn 1 */
  /* USER CODE END DMA1_Channel4_5_6_7_IRQn 1 */
}
void USART2_IRQHandler(void)
{
  if((USART2->ISR & USART_ISR_ORE) == USART_ISR_ORE) 
  {  
    USART2->ICR |= USART_ICR_ORECF;
  }
   
  if((USART2->ISR & USART_ISR_IDLE) == USART_ISR_IDLE)  //The new frame data receive 
  {
    USART2->ICR |= USART_ICR_IDLECF;
     
#ifdef USE_APP_VCOM
    cdc_rx1_in_bytes(vcom_Rxbuf1, U2_DMA_LEN-DMA1_Channel5->CNDTR); /*读取解析数据*/
    //memset(vcom_Rxbuf1, 0, VCOM_RX_EP_SIZE);  // debug
#endif
    DMA1_Channel5->CCR &= ~(DMA_CCR_EN);      //disable the dma 
    LL_DMA_ClearFlag_GI5(DMA1);
    LL_DMA_ClearFlag_TC5(DMA1);
    LL_DMA_ClearFlag_HT5(DMA1);
    LL_DMA_ClearFlag_TE5(DMA1);
    DMA1_Channel5->CNDTR = U2_DMA_LEN;        //Set the length
    DMA1_Channel5->CCR |= DMA_CCR_EN;         //enable the dma
  }
   
  if((USART2->ISR & USART_ISR_RTOF) == USART_ISR_RTOF)
  {
    USART2->ICR |= USART_ICR_RTOCF;
  }
}

初始化时调用:

  USART2_DMA_Recive(vcom_Rxbuf1, U2_DMA_LEN);
  USART2_Interrupt_Enable();

运行后,发送16个字符以内时,进入cdc_rx1_in_bytes()接收都是正常的。
超过16个字符时,进入cdc_rx1_in_bytes()当时的长度U2_DMA_LEN-DMA1_Channel5->CNDTR都是16,
若设断点在此处时,接收长度是正常的。加延时也无效,但是在cdc_rx1_in_bytes()里,

MEMCPY(at_t.buf, buf, len);
at_t.len = len;
at_t.buf[len] = '\0';
at_t.pr = 0;

结尾要是加两行printf()输出字符到别的UART显示时,读取DMA1_Channel5->CNDTR再显示,长度是对的。
由此说明IDLE中断与DMA1_Channel5->CNDTR变化不同步,DMA1_Channel5->CNDTR变化总是落后。

后查看手册,发现STM32F072的USART支持帧超时接收中断RTO功能。
将初始化部分改为使能RTO中断

void USART2_DMA_Recive(uint8_t *p_data, uint16_t length)
{
  LL_USART_Disable(USART2);
  USART2->CR3 |= USART_CR3_DMAR | USART_CR3_DMAT;         //USART_CR3_OVRDIS;
  USART2->CR1 |= USART_CR1_RTOIE;                         //enable usart2 RTO interrupt
  USART2->CR2 |= USART_CR2_RTOEN;
  USART2->RTOR = 220UL;                                   // when bps>19200, 220bit timeout
  DMA1_Channel5->CCR &= ~(DMA_CCR_EN);                    //disable the dma 
  DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);         //Peripheral address
  DMA1_Channel5->CMAR = (uint32_t)p_data;                 //memory address
  DMA1_Channel5->CNDTR = length;                          //Set the length
  DMA1_Channel5->CCR |= DMA_CCR_MINC | DMA_CCR_EN;        //enable the DMA 
  LL_USART_Enable(USART2);
}
void USART2_IRQHandler(void)
{
  if((USART2->ISR & USART_ISR_ORE) == USART_ISR_ORE) 
  {  
    USART2->ICR |= USART_ICR_ORECF;
  }
   
  if((USART2->ISR & USART_ISR_IDLE) == USART_ISR_IDLE)  //The new frame data receive 
  {
    USART2->ICR |= USART_ICR_IDLECF;
  }
   
  if((USART2->ISR & USART_ISR_RTOF) == USART_ISR_RTOF)
  {
    USART2->ICR |= USART_ICR_RTOCF;
     
#ifdef USE_APP_VCOM
    cdc_rx1_in_bytes(vcom_Rxbuf1, U2_DMA_LEN-DMA1_Channel5->CNDTR); /*读取解析数据*/
    //memset(vcom_Rxbuf1, 0, VCOM_RX_EP_SIZE);  // debug
#endif
    DMA1_Channel5->CCR &= ~(DMA_CCR_EN);      //disable the dma 
    LL_DMA_ClearFlag_GI5(DMA1);
    LL_DMA_ClearFlag_TC5(DMA1);
    LL_DMA_ClearFlag_HT5(DMA1);
    LL_DMA_ClearFlag_TE5(DMA1);
    DMA1_Channel5->CNDTR = U2_DMA_LEN;        //Set the length
    DMA1_Channel5->CCR |= DMA_CCR_EN;         //enable the dma
  }
}

经运行后接收正常了,不会掉数据了。

据说是IDLE与DMA配合不太好,还得继续探究。

你可能感兴趣的:(stm32,单片机,uart,串口通信)