9.DMA

news2025/7/19 4:47:54

          

目录

DMA —为 CPU 减负 

DMA 的简介和使用场景

DMA 的例子讲解

STM32 的 DMA 框图和主要特性

​编辑 DMA 的通道的对应通道外设 – DMA 和哪些外设使用

​编辑​编辑ADC_DR 寄存器地址的计算

常见的数据滤波方法

ADC+DMA 的编程


DMA —为 CPU 减负 

DMA 的简介和使用场景

        外设--UART SPI ADC 

        存储器--RAM ROM(FLASH) 

        使用场景:外设和存储器之间或者存储器和存储器之间 

DMA 的例子讲解

        无 DMA:任何指令都需要 CPU 去处理 

        搬砖:需要自己亲手去搬运 

        有 DMA:安排一个人,告诉他,把砖搬走

STM32 的 DMA 框图和主要特性

 

 DMA 的通道的对应通道外设 – DMA 和哪些外设使用

ADC_DR 寄存器地址的计算

        光照硬件电路-->PA5-->ADC1_IN5-->DMA1_CH1 

       1. 找外设基地址 

        通过查看数据手册存储器图

        2.找寄存器的偏移地址 

        通过参考手册 11.12.14 确定 ADC1_DR 寄存器的偏移 

        3.计算寄存器的地址 

                ADC1_DR=0x40012400+0x4C=0x4001244C 

常见的数据滤波方法

        1. 均值滤波 

        2. 限幅滤波 -- 10%的变化幅度 

                目前检测的温度,都是 10℃,突然检测到 30℃,限制 1 个变化的幅度,比如说 10%,那么再 10℃ ±10%都认为是正常的,超过认为不正常。 

        3. 中值滤波、高斯滤波、算术平均滤波 

ADC+DMA 的编程

#include "ADC.h"
#include "stdio.h"
#include "DMA.h"

#if(USE_ADC_DMA_BUFF==0)
uint16_t ADC_DMA_LIGHT_Value = 0;
uint16_t ADC_DMA_SMOKE_Value = 0;
#elif(USE_ADC_DMA_BUFF==1)
uint16_t ADC_DMA_LIGHT_Value = 0;
uint16_t ADC_DMA_SMOKE_Value = 0;
uint16_t  ADC_DMA_Value[2];
#endif

#define SAMPLE_COUNT 10 // 滤波所用样本数量
uint16_t light_sample[SAMPLE_COUNT];//存储光照采样值
uint16_t smoke_sample[SAMPLE_COUNT];//存储烟雾采样值
uint8_t light;//光照循环变量
uint8_t smoke;//烟雾循环变量

#define ADC1_DR	0x4001244C

void DMA_Config(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);
	GPIO_InitTypeDef GPIO_InitStruct = {0};//给结构体赋值
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;//代配置引脚
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;//引脚速率
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;//代配置引脚
	GPIO_Init(GPIOC, &GPIO_InitStruct);
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);  //使用ADC1时钟
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//分频,RCC_CFGR寄存器位15:14
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	ADC_InitTypeDef ADC_InitStruct;
	/*启动1次,获取1次转换结果,再次启动,再获取*/
	ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;// 是否开启连续模式  ADC_CR2的位1
	ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;//数据对齐方式 ADC_CR2的位11 16的寄存器存放12位转换结果 右对齐方便取数据
	ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;// 是否使用外部触发,ADC_CR2的位19:17 通过SWSTART位软件启动
	ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;// 独立模式   ADC1  ADC2独立工作 参考手册 11.9  ADC_CR1的位19:16
	ADC_InitStruct.ADC_NbrOfChannel = 2;// 待转换的通道的数量  ADC_SQR1位23:20 参考手册 11.3.3
	ADC_InitStruct.ADC_ScanConvMode = ENABLE;是否开启扫描  多通道必须扫描,单通道无所谓  参考手册 11.3.8  ADC_CR1的位8
	ADC_Init(ADC1, &ADC_InitStruct);

	DMA_InitTypeDef DMA_InitStruct = {0};
#if(USE_ADC_DMA_BUFF==0)
	DMA_InitStruct.DMA_BufferSize = 1; //目标地址可以存放几次搬运的数据	
	DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)&ADC_DMA_LIGHT_Value;//内存基地址		
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Disable;//内存基地址是否递增
#elif(USE_ADC_DMA_BUFF==1)
	DMA_InitStruct.DMA_BufferSize = sizeof(ADC_DMA_Value)/sizeof(ADC_DMA_Value[0]); //目标地址可以存放几次搬运的数据	
	DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)ADC_DMA_Value;//内存基地址		
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存基地址是否递增	
#endif
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//设置DMA数据传输方向为从外设(ADC)到内存。外设是源地址
	DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;//不使用内存到内存,使用外设-->寄存器
	DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;//设置DMA模式为循环模式,在数据传输完成后会重新从内存开始,适用于需要持续获取数据的场景。
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//内存数据宽度,半字,ADC转换结果是12位的
	DMA_InitStruct.DMA_PeripheralBaseAddr = ADC1_DR;//ADC1的数据寄存器的地址。DMA将从该地址读取数据。
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设数据宽度,半字,ADC转换结果是12位的
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设基地址不递增  始终是ADC_DR寄存器
	DMA_InitStruct.DMA_Priority = DMA_Priority_High;//设置DMA的优先级为高,表示DMA请求优先级较高。
	
	DMA_Init(DMA1_Channel1, &DMA_InitStruct);
	
	ADC_DMACmd(ADC1, ENABLE);//使能ADC的DMA功能  ADC_CR2的位8
	
    ADC_Cmd(ADC1, ENABLE);//使能ADC
	
	ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_55Cycles5);//配置转换的ADC通道
	ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_55Cycles5);//配置转换的ADC通道
	
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
	//开机至少初始化1次
	ADC_ResetCalibration(ADC1);//将ADC_CR2的位3 RSTCAL位置1,初始化校准寄存器
	while(ADC_GetResetCalibrationStatus(ADC1));//等待校准寄存器初始化完成  ADC_CR2的位3是0 初始化完成  1未完成等待
	ADC_StartCalibration(ADC1);//将ADC_CR2的位2 CAL位置1,开始校准
	while(ADC_GetCalibrationStatus(ADC1));//等待A/D校准完成  ADC_CR2的位2是0 校准完成完成  1正在校准,未完成等待
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//启动ADC转换  ADC_CR2的位22

}


void ADC_DMA_Handle(void)
{
#if(USE_ADC_DMA_BUFF==0)
	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)//未转换完成死等
	ADC_DMA_LIGHT_Value = ADC_GetConversionValue(ADC1);
	printf("光照ADC采样值=%d\r\n",ADC_DMA_LIGHT_Value);
	printf("光照ADC采样口=%.2f\r\n",(3.3/4096)*ADC_DMA_LIGHT_Value);
	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)
	ADC_SMK_Value = ADC_GetConversionValue(ADC1);
	printf("烟雾采样值=%d\r\n",ADC_SMK_Value);
	printf("烟雾浓度采样口=%.2f\r\n",(3.3/4096)*ADC_SMK_Value);
	printf("\r\n");
#elif(USE_ADC_DMA_BUFF==1)
		ADC_DMA_LIGHT_Value = ADC_DMA_Value[0];//光照采样值
		light_sample[light++] = ADC_DMA_LIGHT_Value;
		if(light >= SAMPLE_COUNT)
		{//保存关照,10个一组
			light = 0;
		}
		uint32_t light_sum = 0;//光照10个数据总和
		for (int i = 0; i < SAMPLE_COUNT; i++)
		{
			light_sum += light_sample[i];
		}
		//算平均值
		uint16_t light_avg = light_sum / SAMPLE_COUNT;
		printf("光照采样平均值=%d\r\n",light_avg);
		printf("光照ADC采样口平均值=%.2f\r\n",(3.3/4096)*light_avg);
		printf("\r\n");
		
		ADC_DMA_SMOKE_Value = ADC_DMA_Value[1];//烟雾采样值
		smoke_sample[smoke++] = ADC_DMA_SMOKE_Value;
		if(smoke >= SAMPLE_COUNT)
		{//保存烟雾,10个一组
			smoke = 0;
		}
		uint32_t smoke_sum = 0;//烟雾10个数据总和
		for (int i = 0; i < SAMPLE_COUNT; i++)
		{
			smoke_sum += smoke_sample[i];
		}
	
		float smoke_avg = (float)smoke_sum / SAMPLE_COUNT;
		printf("烟雾采样平均值=%.1f\r\n",smoke_avg);
		printf("烟雾浓度采样口平均值=%.2f\r\n",(3.3/4096)*smoke_avg);
		printf("\r\n");
	
#endif
	

}
//void ADC_DMA_Handle(void)
//{
//	uint64_t temp1=0;
//	uint64_t temp2=0;	
//	for(uint8_t i=0;i<ADC_DMA_Count;i+=2)
//	{
//		temp1+=ADC_DMA_Value[i];
//		temp2+=ADC_DMA_Value[i+1];	
//	}
//	ADC_DMA_LIGHT_Value=temp1/5;
//	ADC_DMA_SMOKE_Value=temp2/5;	
//	printf("光照ADC采样值=%d\r\n",ADC_DMA_LIGHT_Value);
//	printf("光照ADC采样口电压=%.2f\r\n",(3.3/4096)*ADC_DMA_LIGHT_Value);	
//	printf("烟雾ADC采样值=%d\r\n",ADC_DMA_SMOKE_Value);
//	printf("烟雾ADC采样口电压=%.2f\r\n",(3.3/4096)*ADC_DMA_SMOKE_Value);		
//}

             

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

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

相关文章

大语言模型 10 - 从0开始训练GPT 0.25B参数量 补充知识之模型架构 MoE、ReLU、FFN、MixFFN

写在前面 GPT&#xff08;Generative Pre-trained Transformer&#xff09;是目前最广泛应用的大语言模型架构之一&#xff0c;其强大的自然语言理解与生成能力背后&#xff0c;是一个庞大而精细的训练流程。本文将从宏观到微观&#xff0c;系统讲解GPT的训练过程&#xff0c;…

python基础语法(三-中)

基础语法3&#xff1a; 2.列表与元组&#xff1a; <1>.列表、元组是什么&#xff1f; 都用来存储数据&#xff0c;但是两者有区别&#xff0c;列表可变&#xff0c;元组不可变。 <2>.创建列表&#xff1a; 创建列表有两种方式&#xff1a; [1].a 【】&#x…

ZTE 7551N 中兴小鲜60 远航60 努比亚小牛 解锁BL 刷机包 刷root 展讯 T760 bl

ZTE 7551N 中兴小鲜60 远航60 努比亚小牛 解锁BL 刷机包 刷root 3款机型是一个型号&#xff0c;包通用&#xff0c; ro.product.system.modelZTE 7551N ro.product.system.nameCN_P720S15 #################################### # from generate-common-build-props # Th…

信息系统项目管理师高级-软考高项案例分析备考指南(2023年案例分析)

个人笔记整理---仅供参考 计算题 案例分析里的计算题就是进度、挣值分析、预测技术。主要考査的知识点有:找关键路径、求总工期、自由时差、总时差、进度压缩资源平滑、挣值计算、预测计算。计算题是一定要拿下的&#xff0c;做计算题要保持头脑清晰&#xff0c;认真读题把PV、…

产品经理入门(2)产品体验报告

产品体验报告大纲&#xff1a;重点在产品体验——优点。 1.产品概括 可以从各大平台搜产品介绍。 2.市场分析 按照产品方向分析各个指标——包括有效使用时间,市场规模等。 3. 用户分析——对用户通过各项指标画像。 4.产品体验——对各项功能与设计的体验。 5.报告总结

C43-指针与数组

一 定义一个指针变量指向数组 1.途径一:指向数组首元素的地址 代码示例: #include <stdio.h> int main() {int arr[3]{2,4,5};int *p;p&arr[0];printf("该数组的首元素是:%d",*p);return 0; }成果展示: 报错与总结: 给指针变量赋值时,未在数组首元素前输…

UDP--DDR--SFP,FPGA实现之ddr读写控制模块

DDR读写控制模块实现介绍 由于该模块接口数量较多&#xff0c;为了详细说明模块实现&#xff0c;采用文字流程进行介绍 上级模块传输数据到来捕捉数据有效上升沿传输写指令&#xff0c;写有效&#xff0c;写指令成功被下一级模块缓存&#xff0c;进行写地址一次读写长度&…

云计算与大数据进阶 | 26、解锁云架构核心:深度解析可扩展数据库的5大策略与挑战(上)

在云应用/服务的 5 层架构里&#xff0c;数据库服务层稳坐第 4 把交椅&#xff0c;堪称其中的 “硬核担当”。它的复杂程度常常让人望而生畏&#xff0c;不少人都将它视为整个架构中的 “终极挑战”。 不过&#xff0c;也有人觉得可扩展存储系统才是最难啃的 “硬骨头”&#…

AI Agent | Coze 插件使用指南:从功能解析到实操步骤

一、前言 在人工智能技术飞速发展的今天&#xff0c;低代码开发模式正成为构建智能应用的主流趋势。对于希望快速搭建 AI Bot 的开发者和业务人员而言&#xff0c;coze作为一款强大的低代码 AI 开发平台&#xff0c;凭借其高度模块化的插件体系脱颖而出。这些插件就像搭建智能…

MK米客方德SD NAND:无人机存储的高效解决方案

在无人机技术迅猛发展的当下&#xff0c;飞控系统的数据记录对于飞行性能剖析、故障排查以及飞行安全保障极为关键。以往&#xff0c;SD 卡是飞控 LOG 记录常见的存储介质&#xff0c;但随着技术的革新&#xff0c;新的存储方案不断涌现。本文聚焦于以 ESP32 芯片为主控制器的无…

【vscode】解决vscode无法安装远程服务器插件问题,显示正在安装

文章目录 现状分析采用VSIX离线安装第一步&#xff1a;离线下载插件包第二步&#xff1a;把下载好的插件文件上传到远程服务器上第三步&#xff1a;在windows下打开vscode&#xff0c;并链接远端&#xff0c;进行安装 现状分析 vscode无法远程安装扩展插件&#xff0c;显示正在…

【Spring】Spring的请求处理

欢迎来到啾啾的博客&#x1f431;。 记录学习点滴。分享工作思考和实用技巧&#xff0c;偶尔也分享一些杂谈&#x1f4ac;。 欢迎评论交流&#xff0c;感谢您的阅读&#x1f604;。 目录 引言HTTP/HTTPS协议Spring Web与Spring Web MVCSpring WebFlux 自定义的TPC/IP协议FTP、S…

粒子群算法(PSO算法)

粒子群算法概述 1.粒子群优化算法&#xff08;Particle Swarm Optimization&#xff0c;简称PSO&#xff09;。粒子群优化算法是在1995年由Kennedy博士和Eberhart博士一起提出的&#xff0c;它源于对鸟群捕食行为的研究。 2.基本核心是利用群体中的个体对信息的共享从而使得整…

LLM智能体新纪元:深入解析MCP与A2A协议,赋能智能自动化协作

LLM智能体&#xff08;LLM agents&#xff09;是能够自主行动以实现特定目标的AI系统。在实际应用中&#xff0c;智能体能够将用户请求拆解为多个步骤&#xff0c;利用知识库或API获取数据&#xff0c;最终整合出答案。这让智能体相比于传统独立聊天机器人拥有更强大的能力——…

SAP学习笔记 - 开发豆知识01 - CDS SDK命令出乱码 (cds init CAP-Test03 --add java)

1&#xff0c;现象 安装完VSCode以及各种需要的插件&#xff08;比如SAP CDS Language Support&#xff09;&#xff0c;就可以做CAP开发。 用这个命令创建Project&#xff1a;cds init CAP-Test03 --add java 然后出来一个乱码错误 adding java The derived package name c…

(C语言)超市管理系统 (正式版)(指针)(数据结构)(清屏操作)(文件读写)(网页版预告)(html)(js)(json)

目录 前言&#xff1a; 源代码&#xff1a; product.h product.c fileio.h fileio.c main.c json_export.h json_export.c tasks.json idex.html script.js 相关步骤&#xff1a; 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a; 第五步…

进阶-数据结构部分:​​​​​​​2、常用排序算法

飞书文档https://x509p6c8to.feishu.cn/wiki/FfpIwIPtviMMb4kAn3Sc40ABnUh 常用排序算法 这几种算法都是常见的排序算法&#xff0c;它们的优劣和适用场景如下&#xff1a; 冒泡排序&#xff08;Bubble Sort&#xff09;&#xff1a;简单易懂&#xff0c;时间复杂度较高&…

25、DeepSeek-R1论文笔记

DeepSeek-R1论文笔记 1、研究背景与核心目标2、核心模型与技术路线3、蒸馏技术与小模型优化4、训练过程简介5、COT思维链&#xff08;Chain of Thought&#xff09;6、强化学习算法&#xff08;GRPO&#xff09;7、冷启动**1. 冷启动的目的****2. 冷启动的实现步骤****3. 冷启动…

LeetCode --- 156双周赛

题目列表 3541. 找到频率最高的元音和辅音 3542. 将所有元素变为 0 的最少操作次数 3543. K 条边路径的最大边权和 3544. 子树反转和 一、找到频率最高的元音和辅音 分别统计元音和辅音的出现次数最大值&#xff0c;然后相加即可&#xff0c;代码如下 // C class Solution {…

npm 报错 gyp verb `which` failed Error: not found: python2 解决方案

一、背景 npm 安装依赖报如下错&#xff1a; gyp verb check python checking for Python executable "python2" in the PATH gyp verb which failed Error: not found: python2 一眼看过去都觉得是Python环境问题&#xff0c;其实并不是你python环境问题&#xf…