把调试STM32F072串口过程中,出现了一小点问题,记录下来,F0的串口寄存器有些增加的功能以前没用到,F0增加了modbus协议之类的接收超时处理,完美实现了不定长数据包的帧接收。
本次使用USART2进行通讯。
cubeMX中的设置
DMA设置:
文章图片
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设置:
文章图片
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配合不太好,还得继续探究。
推荐阅读
- stm32|基于STM32和freeRTOS智能门锁设计方案
- 单片机|单片机初学者做项目为什么这么难(单片机初学者心得有哪些)
- 单片机|自学单片机好找工作吗(会单片机能找什么工作?)
- 单片机|keil把源代码生成lib的方法
- c语言|一文搞懂栈(stack)、堆(heap)、单片机裸机内存管理malloc
- 单片机|Arduino、arm、树莓派、单片机四者有什么不同()
- 日常分享|共享充电宝方案原理,具体部件组成以及主控MUC参数
- #|ARM裸机开发(汇编LED灯实验(I.MX6UL芯片))
- 灵动微电子全新超值型MM32F基本特性及目标应用
- AD中PCB布局与布线的原则