【STM32标准库】DMA双缓冲模式

news2024/11/16 3:40:23

1.双缓冲模式简介

设置DMA_SxCR寄存器的DBM位为1可启动双缓冲传输模式,并自动激活循环模式,所以设置普通模式或者循环模式都可以。

双缓冲不应用与存储器到存储器的传输。可以应用在从存储器到外设或者外设到存储器。

双缓冲模式下, 两个存储器地址指针都有效,即DMA_SxM1AR寄存器将被激活使用。开始传输使用DMA_SxM0AR寄存器的地址指针所对应的存储区, 当这个存储区数据传输完DMA控制器会自动切换至DMA_SxM1AR寄存器的地址指针所对应的另一块存储区, 如果这一块也传输完成就再切换至DMA_SxM0AR寄存器的地址指针所对应的存储区,这样循环调用。

所以我们需要配置传输完成中断,在中断服务函数中,我们可以获取正在使用哪一个buffer,然后可以去填充另一个buffer的数据。

2.示例

#ifndef __BSP_USART_H
#define __BSP_USART_H

#ifdef __cplusplus
extern "C"{

#endif

#include "stm32f4xx.h"
#include "stdio.h"

#define LOGGER_USART              USART1
#define LOGGER_USART_BAUDRATE     115200
#define LOGGER_USART_CLK          RCC_APB2Periph_USART1
#define LOGGER_USART_IRQHandler   USART1_IRQHandler
#define LOGGER_USART_IRQ          USART1_IRQn

#define USART1_TX_PIN             GPIO_Pin_9
#define USART1_TX_GPIO_Port       GPIOA
#define USART1_TX_GPIO_CLK        RCC_AHB1Periph_GPIOA
#define USART1_TX_AF              GPIO_AF_USART1
#define USART1_TX_SOURCE          GPIO_PinSource9

#define USART1_RX_PIN             GPIO_Pin_10
#define USART1_RX_GPIO_Port       GPIOA
#define USART1_RX_GPIO_CLK        RCC_AHB1Periph_GPIOA
#define USART1_RX_AF              GPIO_AF_USART1
#define USART1_RX_SOURCE          GPIO_PinSource10


//usart1_tx只能使用到DMA2_Stream7  Channel_4
#define USART1_TX_DMA_STREAM               DMA2_Stream7
#define USART1_TX_DMA_CHANNEL              DMA_Channel_4
#define USART1_TX_DMA_STREAM_CLK           RCC_AHB1Periph_DMA2
#define USART1_TX_DMA_IT_TCIF              DMA_IT_TCIF7
#define USART1_TX_DMA_IT_HTIF              DMA_IT_HTIF7
#define USART1_TX_DMA_STREAM_IRQn          DMA2_Stream7_IRQn
#define USART1_TX_DMA_STREAM_IRQHandler    DMA2_Stream7_IRQHandler

#define TX_BUFFER_SIZE                     5
#define USART1_TX_DR_BASE                  (&USART1->DR)  //(USART1_BASE+0x04)

void Init_USART(void);
void Init_USART_DMA(void);
void USART_DMA_SEND(uint8_t* data,uint32_t size);

#ifdef __cplusplus
}
#endif

#endif


#include "bsp_usart.h"
#include "string.h"

uint8_t USART_TX_BUFFER[TX_BUFFER_SIZE]={0x00,0x02,0x04,0x06,0x08};
uint8_t USART_TX_BUFFER1[TX_BUFFER_SIZE]={0x01,0x03,0x05,0x07,0x09};

void Init_USART(void)
{
	RCC_AHB1PeriphClockCmd(USART1_TX_GPIO_CLK|USART1_RX_GPIO_CLK,ENABLE);//使能GPIOA时钟
	RCC_APB2PeriphClockCmd(LOGGER_USART_CLK,ENABLE);//使能USART1时钟
	
    //USART1对应引脚复用映射
	GPIO_PinAFConfig(USART1_TX_GPIO_Port, USART1_TX_SOURCE,USART1_TX_AF);//PA9复用为USART1
	GPIO_PinAFConfig(USART1_RX_GPIO_Port, USART1_RX_SOURCE,USART1_RX_AF);//PA10复用为USART1
	
	//USART1端口配置
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin=USART1_TX_PIN;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;//复用功能
	GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;//推挽复用输出
	GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_UP;//上拉
	GPIO_InitStruct.GPIO_Speed=GPIO_Fast_Speed;//速度50MHz
	GPIO_Init(USART1_TX_GPIO_Port,&GPIO_InitStruct);//初始化PA9
	
	GPIO_InitStruct.GPIO_Pin=USART1_RX_PIN;
	GPIO_Init(USART1_RX_GPIO_Port,&GPIO_InitStruct);//初始化PA10
		
	//配置USART参数
	USART_InitTypeDef USART_Init_Struct;
	USART_Init_Struct.USART_BaudRate=LOGGER_USART_BAUDRATE;
	USART_Init_Struct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	USART_Init_Struct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
	USART_Init_Struct.USART_Parity=USART_Parity_No;
	USART_Init_Struct.USART_StopBits=USART_StopBits_1;
	USART_Init_Struct.USART_WordLength=USART_WordLength_8b;
	USART_Init(LOGGER_USART,&USART_Init_Struct);
	
	//配置中断控制器并使能USART接收中断
	NVIC_InitTypeDef NVIC_Init_Struct;
	NVIC_Init_Struct.NVIC_IRQChannel=LOGGER_USART_IRQ;
	NVIC_Init_Struct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init_Struct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_Init_Struct.NVIC_IRQChannelSubPriority=0;
	NVIC_Init(&NVIC_Init_Struct);
	USART_ITConfig(LOGGER_USART,USART_IT_IDLE,ENABLE);
	
	//使能USART
	USART_Cmd(LOGGER_USART,ENABLE);
	
	//使能USART_DMA
	USART_DMACmd(LOGGER_USART,USART_DMAReq_Tx|USART_DMAReq_Rx,ENABLE);
}

void LOGGER_USART_IRQHandler(void)
{
	
}

//重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
    /* 发送一个字节数据到串口 */
    USART_SendData(LOGGER_USART, (uint8_t) ch);

    /* 等待发送完毕 */
    while (USART_GetFlagStatus(LOGGER_USART, USART_FLAG_TXE) == RESET);

    return (ch);
}


void USART_DMA_SEND(uint8_t* data,uint32_t size)
{
	while (DMA_GetCmdStatus(USART1_TX_DMA_STREAM) != DISABLE) {
    }

	memcpy(USART_TX_BUFFER,data,size);
  	
	DMA_Cmd(USART1_TX_DMA_STREAM,DISABLE);
	DMA_SetCurrDataCounter(USART1_TX_DMA_STREAM,size);
	DMA_Cmd(USART1_TX_DMA_STREAM,ENABLE);
}


void Init_USART_DMA(void)
{
	/* 使能DMA时钟 */
    RCC_AHB1PeriphClockCmd(USART1_TX_DMA_STREAM_CLK, ENABLE);
	
	 /* 复位初始化DMA数据流 */
    DMA_DeInit(USART1_TX_DMA_STREAM);

    /* 确保DMA数据流复位完成 */
    while (DMA_GetCmdStatus(USART1_TX_DMA_STREAM) != DISABLE) {
    }
	
	DMA_InitTypeDef  DMA_InitStructure;
	DMA_InitStructure.DMA_BufferSize=TX_BUFFER_SIZE;//一次DMA事务传输的数据个数
	DMA_InitStructure.DMA_Channel=USART1_TX_DMA_CHANNEL;
	DMA_InitStructure.DMA_DIR=DMA_DIR_MemoryToPeripheral;
	DMA_InitStructure.DMA_FIFOMode=DMA_FIFOMode_Disable;
	DMA_InitStructure.DMA_FIFOThreshold=DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_Memory0BaseAddr= (uint32_t)USART_TX_BUFFER;
	DMA_InitStructure.DMA_MemoryBurst=DMA_MemoryBurst_Single;
	DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;
	DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)USART1_TX_DR_BASE;
	DMA_InitStructure.DMA_PeripheralBurst=DMA_PeripheralBurst_Single;
	DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_Priority=DMA_Priority_Low;
	DMA_Init(USART1_TX_DMA_STREAM,&DMA_InitStructure);
	
	//配置双缓冲
	DMA_DoubleBufferModeConfig(USART1_TX_DMA_STREAM,(uint32_t)USART_TX_BUFFER1,DMA_Memory_0);
	DMA_DoubleBufferModeCmd(USART1_TX_DMA_STREAM,ENABLE);
	
	DMA_ITConfig(USART1_TX_DMA_STREAM,DMA_IT_TC|DMA_IT_HT,ENABLE);
	
	DMA_ClearITPendingBit(USART1_TX_DMA_STREAM,USART1_TX_DMA_IT_TCIF|USART1_TX_DMA_IT_HTIF);
	
	//配置中断控制器并使能中断
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=USART1_TX_DMA_STREAM_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;
	NVIC_Init(&NVIC_InitStruct);
	
	DMA_Cmd(USART1_TX_DMA_STREAM,ENABLE);
}

void USART1_TX_DMA_STREAM_IRQHandler(void)
{
	if(SET==DMA_GetITStatus(USART1_TX_DMA_STREAM,USART1_TX_DMA_IT_HTIF))
	{
		//half transfer complete
		//printf("half transfer\r\n");
		DMA_ClearITPendingBit(USART1_TX_DMA_STREAM,USART1_TX_DMA_IT_HTIF);
	}
	else if(SET==DMA_GetITStatus(USART1_TX_DMA_STREAM,USART1_TX_DMA_IT_TCIF))
	{
		//transfer complete
		//printf("transfer complete\r\n");
		if(0==DMA_GetCurrentMemoryTarget(USART1_TX_DMA_STREAM))
		{
			//Current memory buffer used is Memory 0
			for(int i=0;i<TX_BUFFER_SIZE;i++)
			{
				USART_TX_BUFFER1[i]+=2;
			}
		}else
		{
			//Current memory buffer used is Memory 1
			for(int i=0;i<TX_BUFFER_SIZE;i++)
			{
				USART_TX_BUFFER[i]+=2;
			}
		}
		
		DMA_ClearITPendingBit(USART1_TX_DMA_STREAM,USART1_TX_DMA_IT_TCIF);
	}
}

在这里插入图片描述

有一个函数需要注意

	//配置双缓冲
	DMA_DoubleBufferModeConfig(USART1_TX_DMA_STREAM,(uint32_t)USART_TX_BUFFER1,DMA_Memory_0);
	DMA_DoubleBufferModeCmd(USART1_TX_DMA_STREAM,ENABLE);

配置双缓冲的主要函数。

DMA_GetCurrentMemoryTarget(USART1_TX_DMA_STREAM)

该函数可以获取DMA正在使用的buffer,然后我们就可以去填充另外一个buffer了。

3.疑问

双缓冲模式应用在对DMA连续传输要求比较高的地方,不需要一次DMA buffer传输结束后有过多的操作然后进行下一次传输。但是,我们使用half transfer和transfer complete两个中断好像也可以做到,当有half transfer中断时,我们去更新buffer的前一半数据,当有transfer complete中断时,我们去更新buffer的后一半数据,然后配置循环模式。
这种方式与使用双buffer有什么区别吗?可能单buffer在传输中去修改其中的值不是一个稳妥的方式吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1909573.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pbootCMS 数据库sqlite转mysql数据库

前言 pbootCMS默认使用 sqlite数据库 &#xff0c;那么什么是sqlite数据库呢&#xff1f; SQLite&#xff0c;是一款轻型的数据库&#xff0c;是遵守ACID的关系型数据库管理系统&#xff0c;它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌…

叉车指纹锁有规定要装吗

叉车作为工业运输的重要工具&#xff0c;其安全性能一直备受关注。在这个信息化、智能化的时代&#xff0c;对于叉车这类高风险的设备&#xff0c;安全性措施显得尤为重要。而叉车指纹锁作为一种高科技安全设备&#xff0c;其在叉车管理中的应用逐渐受到重视。 那么&#xff0c…

探展2024世界人工智能大会之合合信息扫描黑科技~

文章目录 ⭐️ 前言⭐️ AIGC古籍修复文化遗产焕新⭐️ 高效的文档图像处理解决方案⭐️ AIGC扫描黑科技一键全搞定⭐️ 行业级的大模型加速器⭐️ 结语 ⭐️ 前言 大家好&#xff0c;我是 哈哥&#xff08;哈哥撩编程&#xff09; &#xff0c;这次非常荣幸受邀作为专业观众参…

IP-GUARD如何禁止电脑自带摄像头

IP-GUARD可以通过设备管理模块禁止USB接口&#xff0c;所以USB外置摄像头很容易就可以禁止了。 但是笔记本自带摄像头无法禁止&#xff0c;配置客户端策略如下&#xff1a; device_control_unknown_mode1 device_control_unphysical_mode3

PMP–知识卡片--Scrum角色

Scrum 角色 Scrum 团队由 5 到 9 个&#xff08;72&#xff09;团队成员组成。有三种类型角色&#xff1a; 产品负责人&#xff08;PO&#xff09;&#xff1a;产品负责人定义项目愿景、需求和优先级&#xff0c;对产品成功负责。Scrum Master&#xff1a;负责团队&#xff0c…

Unity海面效果——5、水沫和海平线

Unity引擎制作海面效果 大家好&#xff0c;我是阿赵。 继续做海面效果&#xff0c;上次做完了漫反射颜色和水波动画&#xff0c;还有法线和高光效果。 原则上来说&#xff0c;这个海面已经基本能看了&#xff0c;从性能的考虑&#xff0c;到这里差不多可以停止了。不过有些细节…

SpringCloud跨微服务的远程调用,如何发起网络请求,RestTemplate

在我们的业务流程之中不一定都会是自己模块查询自己模块的信息&#xff0c;有些时候就需要去结合其他模块的信息来进行一些查询完成相应的业务流程&#xff0c;但是在SpringCloud每个模块都相对独立&#xff0c;数据库也有数据隔离。所以当我们需要其他微服务模块的信息的时候&…

HackTheBox--IClean

IClean测试过程 1 信息收集 NMAP端口扫描 80端口测试 echo "10.10.11.12 capiclean.htb" | sudo tee -a /etc/hosts检查页面功能&#xff0c;除了 login 页面无其他可能利用点&#xff0c;可以尝试进行目录爆破和子域名扫描 目录扫描 ./gobuster dir -u http://c…

使用paddleOCR训练自己的数据集到ONNX推理

一、环境安装 1、安装paddlepaddle&#xff1b; https://www.paddlepaddle.org.cn/ 这里安装2.6.1的话使用onnx会出现swish算子报错的问题 python -m pip install paddlepaddle-gpu2.5.2 -i https://pypi.tuna.tsinghua.edu.cn/simple验证是否成功安装 python import paddl…

C++11——新特性超详细总结

目录 一、C11介绍 二、为什么要用C11 三、新特性总结 1、类型推导&#xff08;auto/decltype&#xff09; 2、序列for循环语句 3、 lamdba表达式 4、构造函数&#xff1a;委托构造和继承构造 5、容器&#xff08;array/forward_list&#xff09; 6、垃圾回收机制 7、…

MySQL Buffer Pool

总结自&#xff1a;小林coding&#xff0c;bojiangzhou 虽然说 MySQL 的数据是存储在磁盘里的&#xff0c;但是也不能每次都从磁盘里面读取数据&#xff0c;这样性能是极差的。 要想提升查询性能&#xff0c;加个缓存就行了嘛。所以&#xff0c;当数据从磁盘中取出后&#xff…

大话C语言:第29篇 指针

1 指针概念 指针&#xff1a;地址的变量化形式&#xff0c;其存储的是内存中某个存储单元的地址。它是地址的数值表示。 指针变量&#xff1a;一种特殊的变量&#xff0c;它专门用于存放变量的地址&#xff08;即指针&#xff09;。 注意&#xff0c;指针和指针变量的区别&am…

uniapp+uview实现手机端上传照片带水印(保姆级全过程)

目录 前言&#xff1a;实现思路 步骤一、在界面使用uview的u-upload组件、放置canvas标签 步骤二、在afterRead方法中获取照片url&#xff0c;并创建画布生成水印&#xff0c;再将生成水印的照片上传到服务器 1、afterRead方法 2、照片加水印的方法 3、上传照片至服务器 …

项目管理:这样制定计划,让你事半功倍

在项目管理中&#xff0c;制定计划是成功的关键&#xff0c;一个完善的计划不仅能够明确项目的方向和目标&#xff0c;还能为团队成员提供清晰的行动指南。 制定计划的四大要素——做什么、怎么做、何时做、谁来做&#xff0c;为这一过程提供了坚实的框架。而甘特图作为一种直观…

OS Copilot:新手测评体验

文章目录 前言一、OS Copilot&#xff08;阿里云操作系统智能助手&#xff09;简介二、测评体验总结OS Copilot 产品体验评测OS Copilot 产品功能反馈 前言 本文简单分享一下自己使用OS Copilot测评体验。 一、OS Copilot&#xff08;阿里云操作系统智能助手&#xff09;简介 …

普通人必看!AI绘画商业变现全攻略(附教程)

大部分的设计师除了主业以外&#xff0c;都会利用空余时间去接单做副业。 单子包括但不限于产品/品牌LOGO、电商产品图设计、海报、室内设计图等等&#xff0c;单价在几十到上千不等 引起了我的思考&#xff0c;我们普通人有没有机会&#xff0c;也能像他们一样去接单赚钱吗&a…

挖K脚本检测指南

免责声明:本文仅做分享... 目录 挖K样本-Win&Linux-危害&定性 Linux-Web 安全漏洞导致挖K事件 Windows-系统口令爆破导致挖K事件 --怎么被挖K了??? 已经取得了权限. 挖K样本-Win&Linux-危害&定性 危害&#xff1a;CPU 拉满&#xff0c;网络阻塞&…

Spring最早的源码

地址&#xff1a;Spring最早的源码

Uniapp组件使用的详细步骤

官方文档&#xff1a;uni-app官网 (dcloud.net.cn) 一般用到的组件都在这里&#xff1a; 看介绍里面的注意事项&#xff0c;有注意的地方认真查看&#xff0c;使用的时候例如&#xff0c;以下示例日历&#xff0c;把代码粘贴到你的文件里&#xff0c;然后下载组件 1&#xff…

商家转账到零钱分销返佣场景驳回处理办法

在处理商家转账到零钱分销返佣场景被驳回的问题时&#xff0c;商家需要了解驳回的原因&#xff0c;并采取相应的措施来解决这些问题。下面将详细介绍几种常见的驳回原因以及应对策略&#xff1a; 1. 多级分销模式问题 • 原因&#xff1a;如果业务模式涉及多级分销&#xff0c;…