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

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

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设置:
stm32|STM32F072使用DMA+IDLE进行串口接收不定长数据有问题,改为DMA+RTO接收正常。
文章图片

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 } }

经运行后接收正常了,不会掉数据了。
【stm32|STM32F072使用DMA+IDLE进行串口接收不定长数据有问题,改为DMA+RTO接收正常。】据说是IDLE与DMA配合不太好,还得继续探究。

    推荐阅读