问一个关于串口DMA的问题,感觉和串口接收缓冲区有关。 最近在做一个项目,要用串口接收固定40个字节的数据,每隔10ms40个字节的数据。 但是接收数据之前要用串口发命令给设备,请求这些数据,发送请求的时候,设备也会回复几个字节的应答。 当发送完请求的命令后,收到指定的回复(3个字节)后,每隔10ms就会有40个字节的需要的数据通过串口发回来。 但是前面发送的命令和回复的应答都是4、5个字节,所以我没用DMA,而是直接用串口接收中断。 当在串口接收终端中接收到正确的回复后,马上开启DMA模式,用DMA接收数据,DMA接收完成中断一次,就接收到40个字节。 比如发送命令 char command[ ] = { ‘s','e','n','d' } 4个字节, 接收到 char respond[] = { ' s' , 'e', 'n', 'd', 'o','k'} 6个字节, 然后设备就一直发送40字节的数据。 问题来了,我把DMA的计数设置成40,然后开启的时候,第一次的DMA中断的第一个字节收到的是之前应答的最后一个字节’k',然后才是40字节的数据, 所以接收到39字节数据时就DMA中断了,最后一个字节又到下一次DMA中断才收到。 后面的所有DMA中断都会因为这个原因而错开1字节。但是我在串口中断中已经接收了最后一个字节了(确实接收到了,usart_receive 了最后一个字节了),为什么开启DMA时还会接收到最后一个字节 (我也尝试过开dma前执行一遍 usart_receive(USART),也不行) 。 可能是最后一个字节还留在缓冲区里(我猜想)?,所以有什么办法可以清空串口接收缓冲区吗? 后面我的解决办法是第一次开DMA时,把DMA计数加1 ,即41, 然后在中断中重新设置DMA计数为40,这样是把问题解决了,就是第一组数据丢弃了。 不过我还是想知道是什么原因,是缓冲区问题吗,还是别的?所以来这里求助一下,看看有没有遇到相同问题的,或者有对stm32了解深入一点的大神解答下,谢谢!! |
RE:想问一个串口DMA的问题。
RE:想问一个串口DMA的问题。
回复:想问一个串口DMA的问题。
经楼主这么一分析,我也认为可以是缓冲区的问题,要不这样,用串口收完"K"后再开DMA接收,应该数据就全了吧
串口已经收完K了,我还用另一个串口发回来看过了。
RE:想问一个串口DMA的问题。
void DMA_Configuration()
{
DMA_InitTypeDef DMA_InitStructure;
/* USARTy_Tx_DMA_Channel (triggered by USARTy Tx event) Config */
DMA_DeInit(USARTy_Tx_DMA_Channel);
DMA_InitStructure.DMA_PeripheralBaseAddr = USARTy_DR_Base;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)tmp;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = 0x40;
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_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(USARTy_Tx_DMA_Channel, &DMA_InitStructure);
/* USARTy RX DMA1 Channel (triggered by USARTy Rx event) Config */
DMA_DeInit(USARTy_Rx_DMA_Channel);
DMA_InitStructure.DMA_PeripheralBaseAddr = USARTy_DR_Base;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer_rx;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = RxBufferSize;
DMA_Init(USARTy_Rx_DMA_Channel, &DMA_InitStructure);
}
RE:想问一个串口DMA的问题。
void My_Usart_Init()
{
USART_InitTypeDef USART_InitStructure;
RCC_Configuration();
DMA_Configuration();
GPIO_Configuration();
NVIC_Configuration();
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;
//³õʼ»¯´®¿Ú2
USART_Init(USARTy,&USART_InitStructure);
//ʹÄÜ´®¿Ú½ÓÊÕÖжÏ
USART_ITConfig(USARTy,USART_IT_RXNE ,ENABLE);
//ʹÄÜ´®¿Ú2
USART_Cmd(USARTy,ENABLE);
//ʹÄÜ´®¿ÚYµÄDMA
USART_DMACmd(USARTy,USART_DMAReq_Tx | USART_DMAReq_Rx,ENABLE);
// DMA_ITConfig(USARTy_Tx_DMA_Channel, DMA_IT_TC, ENABLE);
// Periph_NVIC_config(USARTy_Tx_DMA_IRQn,0,5,ENABLE);
/////* Enable the USARTz Interrupt */
DMA_ITConfig(USARTy_Rx_DMA_Channel, DMA_IT_TC, ENABLE);
Periph_NVIC_config(USARTy_Rx_DMA_IRQn,0,3,ENABLE);
回复:想问一个串口DMA的问题。
这个是串口的配置。
因为有时要改串口,所以代码里面用了宏,但是这些定义应该是没问题的,因为都能正常使用了(只是多一位)。我想没有必要贴上来吧。
void My_Usart_Init()
{
USART_InitTypeDef USART_InitStructure;
RCC_Configuration();
DMA_Configuration();
GPIO_Configuration();
NVIC_Configuration();
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(USARTy,&USART_InitStructure);
//开接收中断
USART_ITConfig(USARTy,USART_IT_RXNE ,ENABLE);
//使能串口
USART_Cmd(USARTy,ENABLE);
//使能串口DMA
USART_DMACmd(USARTy,USART_DMAReq_Tx | USART_DMAReq_Rx,ENABLE);
// DMA_ITConfig(USARTy_Tx_DMA_Channel, DMA_IT_TC, ENABLE);
// Periph_NVIC_config(USARTy_Tx_DMA_IRQn,0,5,ENABLE);
/////* Enable the USARTz Interrupt */
DMA_ITConfig(USARTy_Rx_DMA_Channel, DMA_IT_TC, ENABLE); 使能DMA接收中断
Periph_NVIC_config(USARTy_Rx_DMA_IRQn,0,3,ENABLE); //开NVIC中断,自己写的函数,这样代码好看点。
回复:想问一个串口DMA的问题。
volatile char buffer_rx[60];
volatile u8 num = 0;
volatile u8 commandflag = 0;
void USART3_IRQHandler(void)
{
USART_ClearITPendingBit(USART3,USART_IT_RXNE);
if( !commandflag ) { //第一次接收到串口回复后开定时器,后面的字节直接跳过。
commandflag = 1;
TIM_SetCounter( TIM3, 0 );
TIM_Cmd(TIM3,ENABLE);
}
buffer_rx[num++] = USART_ReceiveData(USART3); //保存数据。
//memset(RxBuffer1,0,RxBufferSize1);
}
volatile char commandWaitingFlag = 0;
/**
* @brief This function handles TIM2 global interrupt request by resuming the
* iNemoData task.
*/
void TIM3_IRQHandler( void ) {
if(TIM_GetITStatus(TIM3, TIM_IT_Update))
{
TIM_ClearITPendingBit( TIM3, TIM_IT_Update );
TIM_Cmd(TIM3,DISABLE); //关定时器。
commandflag = 0; //下次接收回复后开定时器。
commandWaitingFlag = 1; // 这个标志位用于在发送命令后,检查回复的有效性。
printf("%d: %x %x %x\r\n", num, buffer_rx[0], buffer_rx[1], buffer_rx[2]); //用USART1发送出回复,串口中断是USART3。这行可省略,调试用的。
num = 0;
GPIOA->ODR ^= 1 CNDTR = RxBufferSize + 1; //这里其实是41,理想值当然是40啦
USARTy_Rx_DMA_Channel->CMAR = (uint32_t)buffer_rx; //接收数据的buffer
DMA_Cmd(USARTy_Rx_DMA_Channel,ENABLE); //开启串口DMA
}
res = true;
} else {
res = false;
}
return res;
}
嗯上面就是所有的代码了。还有,很感谢你们看完这些东西。
</p>