OpenEdv-开源电子网

 找回密码
 立即注册

扫一扫,访问微社区

正点原子新作:阿波罗STM32F767&F429&探索者STM32F4开发板&赶快来下载资料哦。

查看: 145|回复: 4

串口DMA接收数据为什么不全,求纠正接收中断

[复制链接]

  离线 

10

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
122
金钱
122
注册时间
2015-1-22
在线时间
25 小时
发表于 2017-12-7 20:05:58 | 显示全部楼层 |阅读模式
20金钱
本帖最后由 leozzd 于 2017-12-7 20:07 编辑

使用串口1的DMA,能正常发送,在接收上有好几个疑问:
1、中断上需要用DMA发送中断、DMA接收中断、串口接收中断吗?
2、还需开启串口接收空闲总中断吗?USART_ITConfig(USART1, USART_IT_IDLE , ENABLE);  
下面这段程序,在接收上哪里存在问题,请各路大神指点。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void usart1_init(u32 bound)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
        DMA_InitTypeDef DMA_InitStructure;

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA , ENABLE);        //使能USART1,GPIOA时钟
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1
        
        USART_DeInit(USART1);  //复位串口1
        //USART1_TX   PA.9
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //复用推挽输出
        GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9

        //USART1_RX          PA.10
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
        GPIO_Init(GPIOA, &GPIO_InitStructure);  //初始化PA10

        //485EN   PA.8
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;        //内部上拉输出
        GPIO_Init(GPIOA, &GPIO_InitStructure);
        //SEND_BTN = DISABLE;

        //USART 初始化设置
        USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
        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(USART1, &USART_InitStructure); //初始化串口
        USART_Cmd(USART1, ENABLE);                    //使能串口
        
        USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);                                                //使能USART1发送DMA请求
        USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);                                                //使能USART1接收DMA请求
        
        //USART_ITConfig(USART1, USART_IT_IDLE , ENABLE);           //空闲总中断
        
        USART_ClearFlag(USART1, USART_FLAG_TC); // 清发送外城标志
        
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

        //DMA1_4_NVIC        
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级3
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1        ;                //子优先级3
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
        //DMA1_5_NVIC        
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
        
        //DMA1_4_Config
        DMA_DeInit(DMA1_Channel4);
        DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(USART1->DR);                        //设置DMA源
        DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_TX_BUF;                         //DMA内存基地址
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                                        //外设作为DMA的目的端
        DMA_InitStructure.DMA_BufferSize = 2048;                                                        //传输大小
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                        //外设地址不增加
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                                //内存地址自增1
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;                //外设数据宽度8bit
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                //内存数据宽度8bit
        DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                                                //DMA_Mode_Normal(只传送一次), DMA_Mode_Circular (不停地传送)
        DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                                        //DMA传送优先级为高
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                                                //非内存到内存
        DMA_Init(DMA1_Channel4, &DMA_InitStructure);                                                //初始化DMA通道4        
        DMA_Cmd(DMA1_Channel4, DISABLE);                                                                //关闭DMA1通道4
        
        DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);                                        //使能DMA通道4传输完成中断
                        
        //DMA1_5_Config
        DMA_DeInit(DMA1_Channel5);
        DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(USART1->DR);                        //设置DMA源
        DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_RX_BUF;                        //DMA外设基地址                                
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                                        //内存作为DMA的目的端
        DMA_InitStructure.DMA_BufferSize = 10;                                                                //传输大小
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                        //外设地址不增加
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                                //内存地址自增1
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;                //外设数据宽度8bit
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                //内存数据宽度8bit
        DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                                                //DMA_Mode_Normal(只传送一次), DMA_Mode_Circular (不停地传送)
        DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                                        //DMA传送优先级为高
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                                                //非内存到内存
        DMA_Init(DMA1_Channel5, &DMA_InitStructure);                                                //初始化DMA通道5        
        DMA_Cmd(DMA1_Channel5, ENABLE);                                                                //使能DMA1通道5
        
        DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);                                     //使能DMA通道5传输完成中断
}


//DMA1_C4发送中断
void DMA1_Channel4_IRQHandler(void)
{
        if (DMA_GetFlagStatus(DMA1_FLAG_TC4) != RESET)//等待通道4传输完成
        {
                        DMA_ClearFlag(DMA1_FLAG_TC4);//清除通道4传输完成标志
                        DMA_SendState_Flag = FREE;
        }
}
//DMA1_C5接收中断
void DMA1_Channel5_IRQHandler(void)
{
        if(DMA_GetFlagStatus(DMA1_FLAG_TC5) != RESET)
        {
                //DMA_Cmd(DMA1_Channel5, DISABLE);                                        //关闭DMA1通道5
                //DMA_ClearFlag(DMA1_IT_TC5);     //清除通道4传输完成标志
                DMA_SendState_Flag = FREE;               
        }
        
        DMA_ClearITPendingBit(DMA1_IT_TC5);
        usart1_send(USART1_RX_BUF, 1000);
}


void usart1_send(u8 *buffer, u16 len)
{
        if(len == 0)
                return ;
        while (DMA_SendState_Flag == USEING);//判断是否占用
                memcpy(USART1_TX_BUF,buffer,len);                                             //复制数组
        DMA_Cmd(DMA1_Channel4, DISABLE);                                                     //关闭DMA1通道4
        DMA_SetCurrDataCounter(DMA1_Channel4,len);                                     //DMA通道的DMA缓存的大小
        DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);                            //使能DMA通道4传输完成中断
        DMA_Cmd(DMA1_Channel4, ENABLE);                                                     //使能USART1 TX DMA1 所指示的通道
        DMA_SendState_Flag = USEING;
}

void usart1_printf(char* fmt, ...)
{
        va_list ap;
        va_start(ap, fmt);
        vsprintf((char*)USART1_TX_BUF, fmt, ap);
        va_end(ap);
        usart1_send(USART1_TX_BUF, strlen((const char*)USART1_TX_BUF));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
在接收数据时,DMA_InitStructure.DMA_BufferSize = 10;        DMA接收缓存大小定为10,接收10个数据,打印也是10个,但是打印出来的结果错位了。

打印情况

打印情况

总感觉这里的接收中断出现很大的问题。

回复

使用道具 举报

  离线 

0

主题

8

帖子

0

精华

初级会员

Rank: 2

积分
87
金钱
87
注册时间
2014-6-26
在线时间
12 小时
发表于 7 天前 | 显示全部楼层
上电后在你发1234567890之前是不是发过其它数据并且不是10字节的。导致MDA存储数据的那10字节的存储区一直是错位的。
DMA每次存数据都是从上次结束的位置开始存的。
回复

使用道具 举报

  离线 

5

主题

95

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
293
金钱
293
注册时间
2017-1-3
在线时间
35 小时
发表于 7 天前 | 显示全部楼层
建议每次发送完成后将原来的接收buffer清空,使用 memset(buffer,0,len)
回复

使用道具 举报

  离线 

7

主题

56

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1356
金钱
1356
注册时间
2015-11-5
在线时间
231 小时
发表于 7 天前 | 显示全部楼层
串口空闲中断就行了,给你个例子吧,串口DMA发送+串口空闲中断DMA接收。
[AppleScript] 纯文本查看 复制代码
void uart_init(u32 bound){
   //GPIO端口设置
	
	DMA_InitTypeDef  DMA_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能 

		
	//串口1对应引脚复用映射
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1
	
	//USART1端口配置
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10

   //USART1 初始化设置
	USART_InitStructure.USART_BaudRate = bound;//波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	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(USART1, &USART_InitStructure); //初始化串口1
	
	
	//Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;		//抢占优先级0
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;				//子优先级0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;					//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);									//根据指定的参数初始化VIC寄存?
  
  	DMA_DeInit(DMA2_Stream5);
	while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE){}//等待DMA可配置 
    DMA_InitStructure.DMA_Channel = DMA_Channel_4;  //通道选择
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (u32)RX_Buff;//DMA 存储器0地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;//存储器到外设模式
	DMA_InitStructure.DMA_BufferSize = sizeof(RX_Buff);//数据传输量 
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器数据长度:8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;// 使用普通模式 
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输
	DMA_Init(DMA2_Stream5, &DMA_InitStructure);//初始化DMA Stream
	DMA_Cmd(DMA2_Stream5, ENABLE); 								//使能DMA1的通道6 
		
	//USART 中断设置	
    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);					//开启空闲中断
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);  					//使能串口1 的DMA接收
    USART_Cmd(USART1, ENABLE);                    					//使能串口	
}

void U1_RX_DMA_Enable(u16 length)
{
	DMA_Cmd(DMA2_Stream5, DISABLE);                      //关闭DMA传输 
	while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE){}	//确保DMA可以被设置  
	DMA_SetCurrDataCounter(DMA2_Stream5,length); //数据传输量  
	DMA_Cmd(DMA2_Stream5, ENABLE);                      //开启DMA传输 
}

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
//	u8 i;
	u16 length;
	if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)  //UART1接收空闲中断
	{
		length= USART1->SR;  	//读取USART1-SR和DR这两步是必须的
        length = USART1->DR;
		length = sizeof(RX_Buff)-DMA_GetCurrDataCounter(DMA2_Stream5);;	//算出接本帧数据长度
		
//		printf("本次数据长度:%d\r\n",length);
//		for(i=0;i<length;i++)
	//			printf("RX_Buff[i] = %c\r\n",RX_Buff[i]);
			U1_RX_DMA_Enable(sizeof(RX_Buff));     	//恢复DMA,等待下一次接收
			USART_ClearITPendingBit(USART1, USART_IT_IDLE); 		//清除中断标志,其实不用请,读取数据会自动清除
	 } 	
}

回复

使用道具 举报

  离线 

7

主题

56

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1356
金钱
1356
注册时间
2015-11-5
在线时间
231 小时
发表于 7 天前 | 显示全部楼层
我都是串口空闲中断+DMA接收,然后在中断里把数据拷贝到环型队列(相当于生产者),在别处读取队列里的数据(相当于消费者)。灵活一点的话,串口空闲中断后可以改变DMA的内存基地址和DMA接收长度(修改一下U1_RX_DMA_Enable这个函数),建议直接操作寄存器这样更快。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则




QQ|联系我们|手机版|官方淘宝店|新浪微博|微信公众平台|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2017-12-15 20:21

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表
/* */