STM32 串口 DMA 数据读取(详细代码)

这篇具有很好参考价值的文章主要介绍了STM32 串口 DMA 数据读取(详细代码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


最近重新开始学32,搞到串口 DMA 的时候, 数据读取卡了很长一段时间,最终,功夫不负有心人终于搞出来了。在此以记录一下,方便以后查询使用。
在调试的过程中也遇到了很多bug,有些简直就是低级问题,但是还是卡了很久,在此写出来给自己加深印象,同时已给后来者一个前车之鉴
1、在32的程序编写中,若使用到了中断部分,特别是中断函数一定要注意,查询清除中断标志位到底是什么。
获取中断标志位 是 USART_GetITStatus 函数,笔者就是没弄清 用成了 USART_ GetFlagStatus 函数,
USART_GetITStatus   检查指定的 USART 中断发生与否
USART_ GetFlagStatus 检查指定的 USART 标志位设置与否
就是这个问题卡了笔者很久,甚至笔者检查了很多次都没注意到

2、中断函数的使用, 这个在使用时若不注意就会用错,比如 中断串口2 ,却使用成了 中断串口1
关于中断函数的查询可以通过kile5 Startup文件夹 下的 startup_stm32f10x_md.s 进行查询

3、代码功能独立
这部分主要就是要将各函数功能进行,独立。方便理清逻辑,可能很多人觉得很麻烦,且很多人没有此习惯,但是在学习和工作中稍微花点时间,将其进行整理还是很有必要的。
笔者在调试这部分时,就出现了逻辑混乱的情况,将 GPIO, 串口,DMA 全部写在一起,导致最后混乱。最终每次只会搬运最后一个字符。具体原因笔者也没搞清楚。若有大佬能够了解,望指点一二。

————————————————————分割线————————————————————

以上废话,下为正文
DMA 串口数据读取主要就是 将串口的数据搬移到内存中
在实际代码中,笔者定义了一个char 数组进行存储。并通过重定义printf 的方式将 char 数组内的内容输出出来(后面看大家需不需要,也可以将DMA 数据发送的代码 分享出来 嘿嘿嘿)。
主要步骤如下
1、选择使用的串口(串口1)
2、查询该串口对应的引脚及DMA通道(A9,A10, DMA1chanel4, DMA1chanel5)Tip:可以通过STM32参考手册查询,后面会放的(感谢正点原子)
3、配置串口1 GPIO
4、配置串口1 USART 及 中断
5、配置串口1 对应 DMA

着重需要注意的是:这里使用的是 串口1 的 空闲中断。空闲中断在设备接收到数据后 一个字节内的时间还是为接收到数据就会产生,在清除之后,当下一次接收到数据之后的1个字节时间内才会再次产生。   说人话就是,正常情况下不会产生,只有在接收数据后,没有数据了,才会再次产生。
此处使用串口 的空闲中断可以实现 接收不定长数据,若使用的是 定长数据 就可以使用DMA 的接收完成中断(接收完成是当前数据接收完成,还需要判断接收次数是否为 0)

笔者设备 STM32F103RCT6 + window10,若其他设备可根据提供的资料进行查询对应的引脚和通道

————————————————————分割线————————————————————

开始放代码
配置引脚

void USART1_GPIO_Init(void){//初始化 串口 1 GPIO
                //GPIO引脚功能初始化结构体
                GPIO_InitTypeDef GPIO_InitStruct;
                //1、使能串口引脚,复用功能
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE);
               
                //2、初始化引脚功能 输入 悬空(输入高为高, 输入低为低), 输出 复用推挽
                GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//初始化引脚为 A9
                GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//初始化引脚功能为 复用推挽模式
                GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//设置引脚传输速率为 10MHz
                GPIO_Init(GPIOA, & GPIO_InitStruct);//初始化 A9引脚 (发送)
               
                GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//初始化引脚为 A10
                GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//初始化引脚功能为 悬空输入
                GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//设置引脚传输速率为 10MHz
                GPIO_Init(GPIOA, & GPIO_InitStruct);//初始化 A1引脚 (接收)
}

配置串口

void USART1_Init(uint32_t BaudRate){               
                //串口初始化结构体
                USART_InitTypeDef USART_InitStruct;
                //串口优先级结构体
                NVIC_InitTypeDef NVIC_InitStruct;

                //1、使能串口引脚,复用功能
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

                //2、串口初始化
                //2.1 初始化波特率
                USART_InitStruct.USART_BaudRate = BaudRate;//初始化串口波特率(使用形参确定)
                //2.2 初始化数据位
                USART_InitStruct.USART_WordLength = USART_WordLength_8b;//初始化串口数据位为 8 位
                        //2.3 初始化停止位
                USART_InitStruct.USART_StopBits = USART_StopBits_1;//初始化串口数据位为 0 位
                //2.4 初始化奇偶校验位
                USART_InitStruct.USART_Parity = USART_Parity_No;//初始化串口奇偶校验位
                //2.5 初始化流控
                USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//初始化串口控制为无流控
                //2.6 初始化串口工作模式
                USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;

                USART_Init(USART1, & USART_InitStruct);               

                //3、设置串口 接收中断 (空闲中断-搬运数据完毕 或 未搬运数据 当开始搬运数据后 一段时间未搬运下一个数据,发出此中断)
                //3.1 配置串口中断优先级通道
                NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
                //3.2 使能命令
                NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
                //3.3 设置抢占优先级
                NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
                //3.4 设置优先级
                NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
                NVIC_Init( & NVIC_InitStruct);
               
                //5、配置串口接收中断
                USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//串口1 空闲中断               
                //5、中断处理
                USART_DMACmd(USART1,USART_DMAReq_Rx, ENABLE);//开启串口1 DMA 接收请求
                //6、使能串口
                USART_Cmd(USART1,ENABLE);//若重载 printf ,就无需使用 DMA 进行数据搬移。(故此处应该无需使用重载)
}

配置 DMA

void USART1_DMA_Init(void){
        //DMA初始化结构体
        DMA_InitTypeDef DMA_InitStruct;

        //1、使能DMA时钟
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
       
        //2、接收数据 DMA通道5初始化
        //2.1 初始化外设地址
        DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)& USART1->DR;//设置串口发送数据寄存器
        //2.2 初始化内存地址
        DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)Get_Date;//接收内存地址
        //2.3 初始化 DMA 方向
        DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//外设为源地址
        //2.4 初始化 发送数据大小 ,6.2内存大小
        DMA_InitStruct.DMA_BufferSize = BUFFSIZE;//此处的接收数据大小设置一个固定的,较大的
        //2.5 初始化外设地址增量(向发送端写入数据,无需使用增量)
        DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//无增量
        //2.6 初始化内存地址增量(读取内存地址,写入外设,有增量)
        DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;//有增量
        //2.7 初始化外设数据宽度
        DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//无增量的话应该不用设置
        //2.8 初始化内存数据宽度
        DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//增量为全字
        //2.9 初始化DMA工作模式
        DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;//正常循环模式
        //2.10 初始化DMA通道优先级
        DMA_InitStruct.DMA_Priority = DMA_Priority_High;//通道优先级 高
        //2.11 初始化DMA存储器to存储器(内存写入外设,无需使用)
        DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;//不使用 内存到内存               

        DMA_Init(DMA1_Channel5, & DMA_InitStruct);//通道5 串口1 读数据

        DMA_Cmd(DMA1_Channel5, ENABLE);//开启通道5 使能位
}

中断和调用函数部分文章来源地址https://www.toymoban.com/news/detail-602036.html

void Mov_DmaDate_To_Buffer(void){//搬移函数,将DMA寄存器中的数据搬移到另一个寄存器
        DMA_Cmd(DMA1_Channel5, DISABLE);//关闭 DMA 通道 5 (关闭串口1 读)
                       
        DMA1_Channel5->CNDTR = BUFFSIZE;//重新设置 通道 待传输数据大小
               
        DMA_Cmd(DMA1_Channel5, ENABLE);//开启 DMA 通道 5 (开启串口1 读)
       
        printf("%s\n",Get_Date);//输出接收的数据
}


void USART1_IRQHandler(void){//串口1 中断处理函数
        if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET){//若中断标志位为 空闲中断
                //调用搬移函数
                Mov_DmaDate_To_Buffer();//调用空闲中断处理函数

                USART_ReceiveData(USART1);//通过读取数据实现关闭 空闲中断
               
                //清除标志位
                Recevie_State = true;//设置接收数据完毕标志位
        }
}

到了这里,关于STM32 串口 DMA 数据读取(详细代码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • stm32和python实现DMA+串口数据收发

    1-0 串口配置 1-1 DMA发送模式配置 1-2 通过DMA传输数据到USART1的发送寄存器 1-3 串口数据发送 将usart1_dma_tx_data()函数放在main函数中或者中断处理函数中即可,如下所示: 2-1 DMA接收模式配置 2-2 串口结束中断 2-3 对串口接收的数据进行处理 3 完整程序

    2024年02月14日
    浏览(43)
  • STM32 F4串口空闲中断 + DMA实现数据发送

    最近在做 STM32 + ROS车的项目,STM32与ROS之间通信由于数据量大,所以在 STM32端 使用 空闲中断 + DMA 的方案来减轻 CPU 的压力。 一、空闲中断 空闲中断 顾名思义为空了,闲了,没事了进的中断,在 没有数据流 的时候会进入进行读取。 在我们串口进行发送时实则为连续发送,两

    2024年02月16日
    浏览(64)
  • HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)

    最近想做一个控制电机的项目,其中会用到Pytho与单片机STM32之间的互同,最近也在看一些关于数据通信和拆包的相关知识,所以记录一下这段时间里对两者之间的互通所做的事情和发现的问题,以供自己和大家参考。 单片机的串口是我们常用的与电脑通信的外设,本次与P

    2024年01月22日
    浏览(57)
  • 【STM32 HAL库实战】串口DMA + 空闲中断 实现不定长数据接收

    STM32CubeMX最新版: 打开STM32CubeMX软件,点击ACCESS TO MCU SELECTOR,在Commercial Part Number 中输入MCU型号,例如我在这里输入了STM32L431RCT6。选中正确型号然后双击进入下一步的配置界面。 1.1 SYS配置如图 1.2 RCC配置如图 开启了外部晶振,若无则都选择Disable 1.3 USART1配置 NVIC Settings 注意

    2024年02月03日
    浏览(80)
  • STM32:串口轮询模式、中断模式、DMA模式和接收不定长数据

           在STM32每个串口的内部都有两个寄存器:发送数据寄存器(TDR)/发送移位寄存器,当我们调用HAL_UART_Transmit 把数据发送出去时,CPU会将数据依次将数据发送到数据寄存器中,移位寄存器中的数据会根据我们设置的比特率传化成高低电平从TX引脚输出。待发送移位寄存器中发

    2024年02月07日
    浏览(57)
  • STM32使用三种方式(阻塞、中断、DMA)实现串口发送和接收数据

    记录下学习STM32开发板的心得的和遇见的问题。 板卡型号:STM32F405RGT6 软件:STM32CubeMX、IAR STM32串口外设提供了3种接收和发送方式:阻塞、中断、DMA,主要给大家分享中断方式接收不定长数据和DMA使用空闲中断接收不定长数据。 阻塞发送: 阻塞接收: 两个函数需要注意的就

    2024年02月03日
    浏览(52)
  • STM32使用串口空闲中断(IDLE)和 DMA接收一串数据流

    方法一、使用宏定义判断IDLE标志位 空闲的定义是总线上在一个字节的时间内没有再接收到数据,USART_IT_IDLE空闲中断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收到数据的时候发生的。 串口空闲中断(UART_IT_IDLE):STM32的IDLE的中断在串口无数据接收的情况

    2024年01月23日
    浏览(58)
  • 嵌入式开发--STM32用DMA+IDLE中断方式串口接收不定长数据

    之前讲过用 利用IDLE空闲中断来接收不定长数据 ,但是没有用到DMA,其实用DMA会更加的高效,MCU也可以腾出更多的性能去处理应该做的事情。 IDLE顾名思义,就是空闲的意思,即当监测到串口空闲超过1个串口的数据帧时,会使状态寄存器(SR或ISR)的IDLE位置位,如果此时控制

    2024年04月17日
    浏览(65)
  • stm32f103c8r6 串口2数据DMA的接收

    #define USART_REC_LEN              16      //定义最大接收字节数  16 extern u8 USART2_RX_BUF[USART_REC_LEN]; u8 USART2_RX_BUF[USART_REC_LEN];  void uart2_init(u32 bound) {     //GPIO端口设置     GPIO_InitTypeDef GPIO_InitStructure;     USART_InitTypeDef USART_InitStructure;     //NVIC_InitTypeDef NVIC_InitStructure;   

    2024年01月24日
    浏览(45)
  • stm32串口空闲中断+DMA传输接受不定长数据+letter shell 实现命令行

    空闲中断(IDLE),俗称帧中断,即第一帧数据接收完毕到第二帧数据开始接收期间存在一个空闲状态(每接收一帧数据后空闲标志位置1),检测到此空闲状态后即执行中断程序。 空闲中断的优点在于省去了帧头帧尾的检测 ,进入中断程序即意味着已经接收到一组完整数据,仅需

    2024年02月03日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包