背景:兩片STM32通過串口通信,為了減小CPU負擔,采用DMA進行通信,發送端為STM32F103C8T6,接收端為STM32F407VET6。在調試的過程中發現,一直出現數據錯位的問題,接收端嘗試了串口空閑中斷和串口DMA傳輸完成中斷,錯位問題依舊,其實我之前遇到過這個問題,那次發送端沒有使用DMA,而是直接用串口發送,接收端采用DMA接收完成中斷,檢測到錯位后,延時重置DMA,直到DMA接收同步后,不再重置,此后DMA便會保持同步,不會錯位。但是這次不知道為什么采用上次的方法沒有解決,因此決定直接用最簡單粗暴的方法——查找,但是弊端是會在中斷中運行一段比較占空時間的代碼。
說明:主要部分在接收中斷(本文最后的代碼段),發送端發送的DMA數據長度為a,接收端DMA配置的BufferSize為2a,這樣即使錯位,在2a的數據長度中也一定會存在一段完整的有效數據。接收中斷中,在接收buffer的前半段查找幀頭,找到之后,判斷幀頭+a-1的位置是否是幀尾,如果是,則基本可以認為中間即為有效數據,將該段數據拷貝到一個新的數組中,等待解析。
配置部分:發送端 STM32F103C8T6
/* uart3 for communicate with the master */
void vUart3Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* USART3_RX GPIOB.11 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* USART3_TX GPIOB.10 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART3, &USART_InitStructure);
USART_Cmd(USART3, ENABLE);
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
{ /* send dma */
USART_DMACmd(USART3,USART_DMAReq_Tx,ENABLE);
DMA_DeInit(DMA1_Channel2);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(USART3->DR));
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendToMaster_Buff;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = USART3_DMA_send_buffersize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel2,&DMA_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_ITConfig(DMA1_Channel2,DMA_IT_TC,ENABLE);
DMA_Cmd(DMA1_Channel2,ENABLE);
}
}
配置部分:接收端 STM32F407VET6
void vUTConfig(void)
{
USART_InitTypeDef usart;
GPIO_InitTypeDef gpio;
NVIC_InitTypeDef nvic;
DMA_InitTypeDef dma;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9 ,GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
/* USART1_RX GPIOA.10 */
gpio.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
gpio.GPIO_Mode = GPIO_Mode_AF;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_Speed = GPIO_Speed_100MHz;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA,&gpio);
usart.USART_BaudRate = 115200;
usart.USART_WordLength = USART_WordLength_8b;
usart.USART_StopBits = USART_StopBits_1;
usart.USART_Parity = USART_Parity_No;
usart.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
usart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1,&usart);
USART_Cmd(USART1,ENABLE);
{ /* receive dma */
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
DMA_DeInit(DMA2_Stream2);
dma.DMA_Channel= DMA_Channel_4;
dma.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);
dma.DMA_Memory0BaseAddr = (uint32_t)ReceiveFromUT_Buffer;
dma.DMA_DIR = DMA_DIR_PeripheralToMemory;
dma.DMA_BufferSize = USART1_UT_DMA_receive_buffersize;
dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
dma.DMA_Mode = DMA_Mode_Circular;
dma.DMA_Priority = DMA_Priority_VeryHigh;
dma.DMA_FIFOMode = DMA_FIFOMode_Disable;
dma.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
dma.DMA_MemoryBurst = DMA_MemoryBurst_Single;
dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream2,&dma);
nvic.NVIC_IRQChannel = DMA2_Stream2_IRQn;
nvic.NVIC_IRQChannelPreemptionPriority = 1;
nvic.NVIC_IRQChannelSubPriority = 1;
nvic.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic);
DMA_ITConfig(DMA2_Stream2,DMA_IT_TC,ENABLE);
DMA_Cmd(DMA2_Stream2,ENABLE);
}
}
中斷部分:發送中斷
/* USART3 DMA send interrupt */
void DMA1_Channel2_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC2))
{
DMA_ClearFlag(DMA1_IT_TC2);
DMA_ClearITPendingBit(DMA1_IT_TC2);
// DMA_Cmd(DMA1_Channel2,DISABLE);
// DMA_SetCurrDataCounter(DMA1_Channel2,USART3_DMA_send_buffersize);
// DMA_Cmd(DMA1_Channel2, ENABLE);
}
}
中斷部分:接收中斷
/* USART1 dma receive for ut */
void DMA2_Stream2_IRQHandler(void)
{
uint8_t i = 0;
if(DMA_GetFlagStatus(DMA2_Stream2,DMA_IT_TCIF2) == SET)
{
/* 在前半部分查找幀頭並校驗對應位置是否為幀尾 */
for(i=0;i<(USART1_UT_DMA_receive_buffersize/2);i++)
{
if((ReceiveFromUT_Buffer[i] == 0x05)&&(ReceiveFromUT_Buffer[i+USART1_UT_DMA_receive_buffersize-1] == 0x06))
{
/* 拷貝有效數據段到待解析數組 */
memcpy(ReceiveFromUT_Data,&ReceiveFromUT_Buffer[i],USART1_UT_DMA_receive_buffersize/2);
/* 數據解析 */
UTReceive();
}
}
/* 沒有有效數據 */
if(i >= USART1_UT_DMA_receive_buffersize/2)
{
/* 重置DMA */
DMA_Cmd(DMA2_Stream2,DISABLE);
DMA_SetCurrDataCounter(DMA2_Stream2,USART1_UT_DMA_receive_buffersize);
DMA_Cmd(DMA2_Stream2,ENABLE);
}
DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);
DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);
}
}
——cloudos
——2020/4/17