stm32串口接收/发送+DMA内存到外设

简要说一下实验目的:上位机给单片机发送数据,单片机使用串口接收中断接收。在接收中断中,串口向DMA控制器发送请求,把内存中的数据发送到串口的DR寄存器(发送到上位机)

1.串口的基本配置配置略过,需要注意的是打开串口的接收中断,编写接收中断函数

  串口接收中断的NVIC配置

 1   /* 配置USART为中断源 */
 2   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
 3   /* 抢断优先级*/
 4   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
 5   /* 子优先级 */
 6   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
 7   /* 使能中断 */
 8   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
 9   /* 初始化配置NVIC */
10   NVIC_Init(&NVIC_InitStructure);
11   USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);

  串口中断函数

 1 void USART1_IRQHandler(void)
 2 {
 3   if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)//串口接收中断
 4 { 
 5   USART_ClearITPendingBit(DEBUG_USARTx,USART_IT_RXNE);
 6   printf("usart\n");
 7  
 8   DMA_Cmd(DMA1_Channel4,ENABLE);//串口向dma发送请求
 9   DMA_ClearFlag(DMA1_FLAG_GL4);
10  
11   //DMA1_Channel4->CNDTR = SENDBUFF_SIZE;
12  
13 USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE);
14 }
15 }

2.DMA 配置

 1 void USARTx_DMA_Config(void)
 2 {
 3         DMA_InitTypeDef DMA_InitStructure;
 4         NVIC_InitTypeDef NVIC_InitStructure;     /* Configure one bit for preemption priority */
 5     
 6         // 开启DMA时钟
 7         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
 8         // 设置DMA源地址:串口数据寄存器地址*/
 9     DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;
10         // 内存地址(要传输的变量的指针)
11         DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
12         // 方向:从内存到外设    
13         DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
14         // 传输大小    
15         DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
16         // 外设地址不增        
17         DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
18         // 内存地址自增
19         DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
20         // 外设数据单位    
21         DMA_InitStructure.DMA_PeripheralDataSize = 
22       DMA_PeripheralDataSize_Byte;
23         // 内存数据单位
24         DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;     
25         // DMA模式,一次或者循环模式
26         DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
27         //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;    
28         // 优先级:中    
29         DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; 
30         // 禁止内存到内存的传输
31         DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
32         // 配置DMA通道           
33         DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);        
34         
35         
36         //DMA中断的NVIC
37     //    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);     
38         NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;     
39         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
40         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
41         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;          
42         NVIC_Init(&NVIC_InitStructure);
43         DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);  //配置DMA发送完成后产生中断
44         DMA_ITConfig(DMA1_Channel4,DMA_IT_HT,ENABLE);  //配置DMA发送完成后产生中断
45 //        USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
46             //DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
47         // 使能DMA
48         DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);
49 }

    DMA中断函数

 1 void DMA1_Channel4_IRQHandler(void)
 2 {
 3     if(DMA_GetITStatus(DMA1_IT_TC4)==SET)
 4     {
 5         LED1(1);
 6             printf("d");
 7         DMA_ClearITPendingBit(DMA1_IT_TC4); //清除全部中断标志
 8             DMA_ClearFlag(DMA1_FLAG_GL4);
 9             DMA_Cmd(DMA1_Channel4,DISABLE); 
10 //            DMA_Cmd(DMA1_Channel4,DISABLE);
11     }
12         if(DMA_GetITStatus(DMA1_IT_HT4)==SET)
13     {
14         LED1(1);
15             printf("h");
16         DMA_ClearITPendingBit(DMA1_IT_HT4); //清除全部中断标志
17     }
18 }

3.结果

串口接收中断中 向上位机发送“usart”,然后发送DMA请求,把内存中的数据“p”发送到串口(外设,由上位机接收)。DMA半传输完成中断中向上位机发送“h”,DMA全部完成传输中断中向上位机发送“d”。

下图为实验结果

经过多次调试,发现了几个问题

问题1:如果DMA发送了N个数据,上位机接收过程中总会少2个数据。缺少的位置是h和d的位置。

问题2:在串口接收中断中 使用USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE); 串口向DMA发送请求 发送数据时,只会在第一次进入接收中断里发生DMA传输。第二次往后都只会进串口接收中断,不会有DMA传输。DMA设置为normal模式,按照网上的说法,清中断DMA_ClearITPendingBit(DMA1_IT_TC4),关DMA,以及在第二次传输前设置数据大小//DMA_SetCurrDataCounter(DMA1_Channel4,SENDBUFF_SIZE); (或者DMA1_Channel4->CNDTR = SENDBUFF_SIZE;)以及修改串口中断和DMA中断的NVIC优先级都不会有第二次传输。尝试了这些方法都不能正常启动DMA第二次传输。暂时先记录一下,解决了再来写。###:解决了,只用修改一下DMA_Mode即可。DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; 之前是DMA_InitStructure.DMA_Mode = DMA_Mode_Normal。感谢一位热心网友。

 其他问题和总结(自己总结的,理解的不到位的请大佬指正):

1.打开了串口接收和发送完成中断。在串口接收中断中 使用串口发送数据,有时候会出现接收到一个字符,多次进入接收中断,查找资料,可能存在中断嵌套,后来只在串口接收中断中接收,添加了一个按键,用按键去输出接收到的数据。这个没有问题。(在使用printf时,一定要勾选microlib(MDK))

2.关于串口接收中断和发送中断的问题。经过查找启动文件,发现在NVIC设置中断时只能设置串口中断(不区分时接收还是发送完成)NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;区分接收还是发送是在void USART1_IRQHandler(void) 中字节去判断是哪个标志位。那可能串口的接收中断和发送完成中断的优先级是一样的。

3.关于DMA的中断问题。DMA不是不需要cpu参与吗?为什么还要设置中断。DMA的传输数据过程是不需要cpu的,但是DMA有3个中断:半传输完成DMA1_IT_HT4、全部传输完成DMA1_IT_TC4、传输错误DMA1_IT_TE4。当DMA在传输数据完成一半、全部、出错时,需要向cpu发送中断。一样的,这三个中断也需要设置NVIC。和其他中断是一样的(比如:定时器中断、串口中断)。

 打开DMA中的函数:

DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);  //配置DMA发送完成后产生中断
DMA_ITConfig(DMA1_Channel4,DMA_IT_HT,ENABLE);  //配置DMA发送完成后产生中断

4.DMA1有7个通道,DMA2有5个,每个通道都是固定的和某个外设连接,比如要使用存储器到外设,外设到存储器模式,像上面的串口。要使用串口外设,该怎么选DMA通道,要查中文参考手册。存储器到存储器之间通道不固定,自选任选。

 

 5. 关于设置顺序,每个外设都有对应的通道,那到底是先设置外设呢,还是先设置通道。先设置通道,当需要开启DMA时,用对应外设去发请求。设置通道的函数在DMA库中,发送请求的函数在各个外设的库中。也就是说当外设发送DMA请求时,该外设对应的DMA通道一定是配置好的。

__STM32F10x_DMA_H头文件中:413行的函数 参数是 哪个通道的初始化和 通道初始化结构体

IIC头文件中:537行的函数 参数是 哪个外设的DMA请求和ENABLE

 

串口头文件中:372行的函数 参数是 哪个外设的DMA请求和ENABLE

 

6.每个通道都包括多个中断比如:半传输完成DMA1_IT_HT4、全部传输完成DMA1_IT_TC4、传输错误DMA1_IT_TE4。那这3个中断的优先级也不能单独配置。因为每个通道的中函数名是void DMA1_Channel4_IRQHandler(void)这种类型的。从名字上能看出通道上的中断是针对通道的,不针对具体中断。就和串口中断一样,是接收中断还是发送上完成中断,自己在中断函数中判断去。

 7.第6条是同一通道中的不同中断,那DMA1有7个通道,不同通道中的中断能设置不同优先级吗?DMA也是一个外设,如果DMA1的7个通道(7个外设)同时要请求DMA,那么DMA先处理哪个通道呢?通过观看通道Init函数,发现DMA_InitStructure结构体中有个变量DMA_InitStructure.DMA_Priority是设置优先级的,优先级可选项有4种:DMA_Priority_VeryHigh、DMA_Priority_High、DMA_Priority_Medium、DMA_Priority_Low。那么就是DMA中的不同通道是可以设置不同的优先级的。如果两个通道优先级一样。那通道号越小优先级越高(通道0>通道1)

8.在使用DMA时只要初始化DMA_InitStructure结构体中的所有变量和NVIC即可

 1 DMA_InitTypeDef DMA_InitStructure;
 2         NVIC_InitTypeDef NVIC_InitStructure;     /* Configure one bit for preemption priority */
 3     
 4         // 开启DMA时钟
 5         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
 6         // 设置DMA源地址:串口数据寄存器地址*/
 7     DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;
 8         // 内存地址(要传输的变量的指针)
 9         DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
10         // 方向:从内存到外设    
11         DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
12         // 传输大小    
13         DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
14         // 外设地址不增        
15         DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
16         // 内存地址自增
17         DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
18         // 外设数据单位    
19         DMA_InitStructure.DMA_PeripheralDataSize = 
20       DMA_PeripheralDataSize_Byte;
21         // 内存数据单位
22         DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;     
23         // DMA模式,一次或者循环模式
24         DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
25         //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;    
26         // 优先级:中    
27         DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; 
28         // 禁止内存到内存的传输
29         DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
30         // 配置DMA通道           
31         DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);        
32         
33         
34     
35     //    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);     
36         NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;     
37         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
38         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
39         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;          
40         NVIC_Init(&NVIC_InitStructure);
41         DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);  //配置DMA发送完成后产生中断
42         DMA_ITConfig(DMA1_Channel4,DMA_IT_HT,ENABLE);  //配置DMA发送完成后产生中断
43 //        USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
44             //DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
45         // 使能DMA
46         DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值