当前位置: 首页 > article >正文

9.DMA

          

目录

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	0x4001244Cvoid 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:14RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);ADC_InitTypeDef ADC_InitStruct;/*启动1次,获取1次转换结果,再次启动,再获取*/ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;// 是否开启连续模式  ADC_CR2的位1ADC_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:16ADC_InitStruct.ADC_NbrOfChannel = 2;// 待转换的通道的数量  ADC_SQR1位23:20 参考手册 11.3.3ADC_InitStruct.ADC_ScanConvMode = ENABLE;是否开启扫描  多通道必须扫描,单通道无所谓  参考手册 11.3.8  ADC_CR1的位8ADC_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;//内存基地址是否递增	
#endifDMA_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的位8ADC_Cmd(ADC1, ENABLE);//使能ADCADC_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.lryc.cn/news/2378550.html

相关文章:

  • 大语言模型 10 - 从0开始训练GPT 0.25B参数量 补充知识之模型架构 MoE、ReLU、FFN、MixFFN
  • python基础语法(三-中)
  • 【gitee 初学者矿建仓库】
  • 思路收集文档
  • OpenCV 光流估计:从原理到实战
  • 使用HtmlAgilityPack采集墨迹天气中的天气数据
  • ZTE 7551N 中兴小鲜60 远航60 努比亚小牛 解锁BL 刷机包 刷root 展讯 T760 bl
  • SearxNG本地搜索引擎
  • MyBatis 核心组件源码分析
  • 信息系统项目管理师高级-软考高项案例分析备考指南(2023年案例分析)
  • stack和queue简单模拟实现
  • 如何安装双系统?即windows已经安装,如何安装ubuntu 22.04LTS
  • 产品经理入门(2)产品体验报告
  • C43-指针与数组
  • UDP--DDR--SFP,FPGA实现之ddr读写控制模块
  • 云计算与大数据进阶 | 26、解锁云架构核心:深度解析可扩展数据库的5大策略与挑战(上)
  • AI Agent | Coze 插件使用指南:从功能解析到实操步骤
  • 06、基础入门-SpringBoot-依赖管理特性
  • MK米客方德SD NAND:无人机存储的高效解决方案
  • 【vscode】解决vscode无法安装远程服务器插件问题,显示正在安装
  • 1688 数据接口调用秘籍:高效获取商品实时信息的开发指南
  • 【Spring】Spring的请求处理
  • 粒子群算法(PSO算法)
  • git提交库常用词
  • LLM智能体新纪元:深入解析MCP与A2A协议,赋能智能自动化协作
  • SAP学习笔记 - 开发豆知识01 - CDS SDK命令出乱码 (cds init CAP-Test03 --add java)
  • (C语言)超市管理系统 (正式版)(指针)(数据结构)(清屏操作)(文件读写)(网页版预告)(html)(js)(json)
  • 进阶-数据结构部分:​​​​​​​2、常用排序算法
  • 解决 Three.js Raycaster 点击位置与实际交点偏差问题
  • 25、DeepSeek-R1论文笔记