最近在项目中给串口的接收添加DMA,遇到的问题:
1、“配置好”DMA后,但是DMA不工作
初始化串口1为接收、DMA1的通道3,并使能相应的外设,外设的时钟也全部打开,但是通过调试发现,DMA就是不传输数据。
问题点:忽视了DMA的请求映像
每个DMA控制器都分管着不同的外设DMA请求,通过查表发现串口1的接收只能映射到DMA1的通道5.因此DMA的通道选择错误。现粘贴串口一的中断处理程序如下:(以下程序并没有采用循环队列的方式接受数据)
static unsigned char APP_ComBuf[100];
unsigned char Receive_Message[100] = {0};
static int APP_iConter = 0;
void USART1_IRQHandler(void)
{
u8 Res,i;
OS_ERR err;
#ifdef SYSTEM_SUPPORT_OS
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
{
DMA_Cmd(DMA1_Channel5,DISABLE);
APP_iConter=USART1->DR;//
APP_iConter=USART1->SR;
APP_iConter=100-DMA_GetCurrDataCounter(DMA1_Channel5);//
APP_ComBuf[APP_iConter]=0;//
DMA1_Channel5->CNDTR=100;//重新设置传输量,这一步是必须的。
if(APP_iConter>=6)
{
for(i=0;i<APP_ComBuf[2];i++)
{
Receive_Message[i] = APP_ComBuf[i];
}
memset(APP_ComBuf,0,sizeof(APP_ComBuf));
OSTaskQPost((OS_TCB* )&APPTCB,
(void* )Receive_Message,
(OS_MSG_SIZE)Receive_Message[2],
(OS_OPT )OS_OPT_POST_FIFO,
(OS_ERR* )&err);
}
DMA_Cmd(DMA1_Channel5,ENABLE);
}
NVIC_Clear(USART1);
#ifdef SYSTEM_SUPPORT_OS
OSIntExit();
#endif
}
在设置好DMA的通道后,每收到数据后,使能DMA后,DMA会自动将串口接收到的数据搬运到存储区App_ComBuff中,如果没有DMA1_Channel5->CNDTR=100;这一步,或者该步配置错误,那么每当串口接收到的数据都会向存储区App_ComBuff中存储,App_ComBuff的地址递增。于此同时APP_iConter也在累积,APP_ComBuf[2]也不能被解析到,因为数据随着APP_ComBuf地址的递增,数据都往后存储,帧长度并未存储到APP_ComBuf[2]中,因此自己认为的APP_ComBuf[2]存储的是帧长度,但是APP_ComBuf[2]始终是0。再次理解一下 DMA1_Channel5->CNDTR=100;这句话的必要性,串口采用的是空闲中断接收数据,并非采用的是接收中断,因此当串口处于空闲中断时,这时候已经传输完成了一帧数据。因此,将 DMA1_Channel5->CNDTR=100;便可以在下次空闲中断时,将串口接收数据寄存器中的数据搬运到存储器中的首地址。这样for(i=0;i<APP_ComBuf[2];i++)这个for循环也才可以得以执行。另:DMA通道x的传输数量寄存器DMA_CNDTRx,当这个寄存器的内容为0时,无论通道是否开启,都不会发生任何数据传输。以上是本人通过DMA接收数据的运用的肤浅理解。
关于采用队列的方式接收串口数据,后面继续更新,谢谢。