• 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
    ");
     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);
  • 相关阅读:
    第十九节,使用RNN实现一个退位减法器
    深度学习系列经典博客收藏
    第十八节,TensorFlow中使用批量归一化(BN)
    第十七节,深度学习模型的训练技巧-优化卷积核,多通道卷积
    第十六节,使用函数封装库tf.contrib.layers
    第十五节,利用反卷积技术复原卷积网络各层图像
    第十四节,TensorFlow中的反卷积,反池化操作以及gradients的使用
    第十三节,使用带有全局平均池化层的CNN对CIFAR10数据集分类
    第十二节,TensorFlow读取数据的几种方法以及队列的使用
    在hadoop集群添加了slave节点的方法
  • 原文地址:https://www.cnblogs.com/1024E/p/13251725.html
Copyright © 2020-2023  润新知