STM32第十三课:DMA多通道采集光照烟雾

文章目录

  • 需求
  • 一、DMA(直接存储器存取)
  • 二、实现流程
    • 1.时钟使能
    • 2.设置外设寄存器地址
    • 3.设置存储器地址
    • 4.设置要传输的数据量
    • 5.设置通道优先级
    • 6.设置传输方向
    • 7.使通道和ADC转换
  • 三、数据处理
  • 四、需求实现
  • 总结


需求

通过DMA实现光照强度和烟雾浓度的多通道采集


一、DMA(直接存储器存取)

在这里插入图片描述
在这里插入图片描述
作用:
把外设的寄存器里面数据直接传输到存储器中
把存储器里面数据直接传输到外设的寄存器中
把存储器里面数据直接传输到存储器中
绕开了CPU。

二、实现流程

1、时钟使能
2、设置外设寄存器地址
3、设置存储器地址
4、设置要传输的数据量
5、设置通道优先级
6、设置传输方向:外设到存储器,还是存储器到存储器
循环模式
外设和存储器的增量模式:外设和存储器的地址是否向后偏移
外设和存储器的数据宽度:一个数据的大小(8位,16位,32位)
7、使通道和ADC转换

1.时钟使能

DMA有很多通道,本次配置的为DMA1通道
在这里插入图片描述
可见DMA1在AHB外设时钟使能寄存器的第0位。

RCC->AHBENR |= 0x1<<0;

2.设置外设寄存器地址

在这里插入图片描述
由于该ADC1的规则组通道数据寄存器在DR上,所以直接赋给就行。

DMA1_Channel1->CPAR = (uint32_t)&(ADC1->DR);//设置外设寄存器地址

3.设置存储器地址

存储器地址需要自己设置一个变量,我这里为了方便后续取中位数,定义了个结构体数组,存放烟雾和光照两种变量,每种最多能存放10个。
在这里插入图片描述

DMA1_Channel1->CMAR = (uint32_t)adcvalue;//设置存储器地址

结构体及定义

typedef struct{
	uint16_t light;
	uint16_t mq2;
}ADCARR;
ADCARR adcvalue[10]={0};

4.设置要传输的数据量

在这里插入图片描述
这个根据需求来设置,想要一次传多少数据就填多少。
我这里为了获取每组10个数据,共20个量。
所以这里我填写的是20。

DMA1_Channel1->CNDTR = 20;//3、设置要传输的数据量

5.设置通道优先级

在这里插入图片描述
在这里插入图片描述
由于此时就1个通道,所以填那个都无所谓。

DMA1_Channel1->CCR |= 0x3<<12;//4、设置通道优先级   最高

6.设置传输方向

外设到存储器,还是存储器到存储器
打开循环模式(循环读)
外设和存储器的增量模式:外设和存储器的地址是否向后偏移
外设和存储器的数据宽度:一个数据的大小(8位,16位,32位)
全在CCR寄存器上一位一位配就行了

		//5、设置传输方向:	//外设到存储器,还是存储器到存储器
	DMA1_Channel1->CCR &= ~(0x1<<14);//选择外设和存储器的之间的传输
	DMA1_Channel1->CCR &= ~(0x1<<4);//从外设读

	//外设和存储器的数据宽度:一个数据的大小(8位,16位,32位)
	DMA1_Channel1->CCR &= ~(0x3<<10);//存储器数据宽度
	DMA1_Channel1->CCR |= 0x1<<10;
	
	DMA1_Channel1->CCR &= ~(0x3<<8);//外设寄存器数据宽度
	DMA1_Channel1->CCR |= 0x1<<8;
	//外设和存储器的增量模式:外设和存储器的地址是否向后偏移
	DMA1_Channel1->CCR |= 0x1<<7;//存储器的指针增量打开
	DMA1_Channel1->CCR &= ~(0x1<<6);//外设的指针增量关闭
	DMA1_Channel1->CCR |= 0x1<<5;//循环模式打开

7.使通道和ADC转换

使能一下CRR第0位

	DMA1_Channel1->CCR |= 0x1<<0;

将ADC的扫描和循环开启,最后开启ADC1的转换

ADC_SoftwareStartConvCmd(ADC1, ENABLE);

此时ADC1就会不停转换,并将光照强度参数和烟雾浓度参数通过DMA从DR寄存器发送到我们定义的变量(存储器)中。

三、数据处理

为了使获取的数据更加精准,我定义了一个新的函数用来求光照和烟雾获取的每10个参数中间的平均数。
代码如下:

void Get_Smoke_Light_MidValue()
{
	uint16_t Mid_Light_Value[10]={0};
	uint16_t Mid_Smoke_Value[10]={0};
	uint16_t i=0,j=0,temp=0;
	for(i=0;i<10;i++){
		Mid_Light_Value[i] = adcvalue[i].light;
	}
  for(i=0;i<10;i++){
		Mid_Smoke_Value[i] = adcvalue[i].mq2;
	}
	for(i=0;i<10-1;i++)//光照
	{
		for(j=0;j<9-i;j++)
		{
			if(Mid_Light_Value[j]<Mid_Light_Value[j+1]){
				temp = Mid_Light_Value[j];
				Mid_Light_Value[j] = Mid_Light_Value[j+1];
				Mid_Light_Value[j+1] = temp;
				}
		}
	}
	temp=0;
		for(i=0;i<10-1;i++)//烟雾
	{
		for(j=0;j<9-i;j++)
		{
			if(Mid_Smoke_Value[j]<Mid_Smoke_Value[j+1]){
				temp = Mid_Smoke_Value[j];
				Mid_Smoke_Value[j] = Mid_Smoke_Value[j+1];
				Mid_Smoke_Value[j+1] = temp;
				}
		}
	}
	
	adcData.light = (Mid_Light_Value[4]+Mid_Light_Value[5])/2;
	adcData.mq2 = (Mid_Smoke_Value[4]+Mid_Smoke_Value[5])/2;
	printf("Light = %d\r\n",adcData.light);
	printf("Smoke = %d\r\n",adcData.mq2);
	return; 
}

四、需求实现

main.c

#include "stm32f10x.h"
#include "usart.h"
#include "stdio.h"
#include "delay.h"
#include "string.h"
#include "pwm.h"
#include "adc.h"


int main()
{
		NVIC_SetPriorityGrouping(5);//两位抢占两位次级
    Usart1_Config(); 
	  SysTick_Config(72000);
	  RGBpwm_Config();
	  uint8_t cai_count=0;
	  uint16_t cont=0;
	  Adc_Config();
    while(1)
    {	
			if(ledcnt[0]>=ledcnt[1]){//过去3s
			ledcnt[0]=0;
					Get_Smoke_Light_MidValue();
		}
    }
		
		return 0;
}

adc.c

#include "ADC.h"

ADCARR adcvalue[10]={0};
ADCARR adcData={0};

//库函数
void Adc_Config(void)
{
	//开时钟ADC1和PC,PA
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC|RCC_APB2Periph_ADC1,ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6); 
	//配置DMA
	RCC->AHBENR |= 0x1<<0;//开启DMA时钟
	DMA1_Channel1->CPAR = (uint32_t)&(ADC1->DR);//设置外设寄存器地址
	DMA1_Channel1->CMAR = (uint32_t)adcvalue;//设置存储器地址
	DMA1_Channel1->CNDTR = 20;//3、设置要传输的数据量
	DMA1_Channel1->CCR |= 0x3<<12;//4、设置通道优先级   最高
		//5、设置传输方向:	//外设到存储器,还是存储器到存储器
	DMA1_Channel1->CCR &= ~(0x1<<14);//选择外设和存储器的之间的传输
	DMA1_Channel1->CCR &= ~(0x1<<4);//从外设读

	//外设和存储器的数据宽度:一个数据的大小(8位,16位,32位)
	DMA1_Channel1->CCR &= ~(0x3<<10);//存储器数据宽度
	DMA1_Channel1->CCR |= 0x1<<10;
	
	DMA1_Channel1->CCR &= ~(0x3<<8);//外设寄存器数据宽度
	DMA1_Channel1->CCR |= 0x1<<8;
	//外设和存储器的增量模式:外设和存储器的地址是否向后偏移
	DMA1_Channel1->CCR |= 0x1<<7;//存储器的指针增量打开
	DMA1_Channel1->CCR &= ~(0x1<<6);//外设的指针增量关闭
	DMA1_Channel1->CCR |= 0x1<<5;//循环模式打开
	//6、使能所使用的通道
	DMA1_Channel1->CCR |= 0x1<<0;
	
	//配置GPIO口
	GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PA5,光敏
  GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//PC1,烟雾
  GPIO_Init(GPIOC, &GPIO_InitStructure);
	//配置ADC1
	ADC_InitTypeDef ADC_InitStruct={0};  
	ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;//ADC独立模式
	ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
	ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//选择软件SWSTART位触发
	ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;//连续模式
	ADC_InitStruct.ADC_ScanConvMode = ENABLE;//开启扫描
	ADC_InitStruct.ADC_NbrOfChannel = 2;
	ADC_Init(ADC1,&ADC_InitStruct);
	//配置通道
	ADC_RegularChannelConfig(ADC1, ADC_Channel_5,1, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_11,2, ADC_SampleTime_239Cycles5);	
	ADC_Cmd(ADC1, ENABLE);
	ADC1->CR2 |= 0x1<<8;//开启ADC的DMA请求
	//校准
	ADC_ResetCalibration(ADC1);
	  while(ADC_GetResetCalibrationStatus(ADC1));
  ADC_StartCalibration(ADC1);
		while(ADC_GetCalibrationStatus(ADC1));	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

void Get_Smoke_Light_MidValue()
{
	uint16_t Mid_Light_Value[10]={0};
	uint16_t Mid_Smoke_Value[10]={0};
	uint16_t i=0,j=0,temp=0;
	for(i=0;i<10;i++){
		Mid_Light_Value[i] = adcvalue[i].light;
	}
  for(i=0;i<10;i++){
		Mid_Smoke_Value[i] = adcvalue[i].mq2;
	}
	for(i=0;i<10-1;i++)//光照
	{
		for(j=0;j<9-i;j++)
		{
			if(Mid_Light_Value[j]<Mid_Light_Value[j+1]){
				temp = Mid_Light_Value[j];
				Mid_Light_Value[j] = Mid_Light_Value[j+1];
				Mid_Light_Value[j+1] = temp;
				}
		}
	}
	temp=0;
		for(i=0;i<10-1;i++)//烟雾
	{
		for(j=0;j<9-i;j++)
		{
			if(Mid_Smoke_Value[j]<Mid_Smoke_Value[j+1]){
				temp = Mid_Smoke_Value[j];
				Mid_Smoke_Value[j] = Mid_Smoke_Value[j+1];
				Mid_Smoke_Value[j+1] = temp;
				}
		}
	}
	
	adcData.light = (Mid_Light_Value[4]+Mid_Light_Value[5])/2;
	adcData.mq2 = (Mid_Smoke_Value[4]+Mid_Smoke_Value[5])/2;
	printf("Light = %d\r\n",adcData.light);
	printf("Smoke = %d\r\n",adcData.mq2);
	return; 
}

adc.h

#ifndef _ADC_H_
#define _ADC_H_
#include "stm32f10x.h"
#include "stdio.h"


typedef struct{
	uint16_t light;
	uint16_t mq2;
}ADCARR;
void Get_Smoke_Light_MidValue();
void Adc_Config(void);
#endif
		

总结

1.先开时钟,配置DMA。
2.打开ADC1的两个通道(烟雾和光照),开启扫描和循环。
3.使能DMA并开启ADC转换。
4.数据处理及主函数调用。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VQVAE:Neural Discrete Representation Learning

论文名称&#xff1a;Neural Discrete Representation Learning 开源地址 发表时间&#xff1a;NIPS2017 作者及组织&#xff1a;Aaron van den Oord,Oriol Vinyals和Koray Kavukcuoglu, 来自DeepMind。 1、VAE 简单回顾下VAE的损失函数&#xff0c;ELBO的下界为&#xff1a; …

单晶层状氧化物制作方法技术资料 纳离子技术

网盘 https://pan.baidu.com/s/1hjHsXvTXG74-0fDo5TtXWQ?pwd10jk 单晶型高熵普鲁士蓝正极材料及其制备方法与应用.pdf 厘米级铬氧化物单晶及其制备方法和存储器件.pdf 多孔氧化物单晶材料及其制备方法和应用.pdf 大单晶层状氧化物正极材料及其制备方法和应用.pdf 富钠P2相层状…

3DMAX折纸插件FoldPoly使用方法详解

3DMAX折纸插件FoldPoly使用教程 3DMAX折纸插件FoldPoly&#xff0c;用于挤出可编辑多边形的边&#xff08;边界&#xff09;并可旋转&#xff08;折叠&#xff09;新生成的面&#xff0c;创建类似手工折纸以及纸箱包装盒的建模效果。 【版本要求】 3dMax2014 - 2025&#xff…

线性代数|机器学习-P20鞍点和极值

文章目录 1 . 瑞利商的思考1.1 瑞利商的定义1.2 投影向量 2. 拉格朗日乘子法3. 鞍点 1 . 瑞利商的思考 1.1 瑞利商的定义 假设A是n阶实对称矩阵&#xff0c;x是n维非零列向量&#xff0c;那么瑞利商表示如下&#xff1a; R ( A , x ) x T A x x T x \begin{equation} R(A,x…

嵌入式学习——硬件(ARM内核汇编指令)——day52

ARM汇编指令 学习arm汇编的主要目的是为了编写arm启动代码&#xff0c;启动代码启动以后&#xff0c;引导程序到c语言环境下运行。换句话说启动代码的目的是为了在处理器复位以后搭建c语言最基本的需求。因此启动代码的主要任务有&#xff1a; 初始化异常向量表&#xff1b;初…

前端学习 Vue 插槽如何实现组件内容分发?

目录 一、Vue.js框架介绍二、什么是Vue 插槽三、Vue 插槽的应用场景四、Vue 插槽如何实现组件内容分发 一、Vue.js框架介绍 Vue.js是一个用于构建用户界面的渐进式JavaScript框架。它设计得非常灵活&#xff0c;可以轻松地被集成到现有的项目中&#xff0c;也可以作为一个完整…

Redis-实战篇-缓存击穿问题及解决方案

文章目录 1、缓存击穿2、常见的解决方案有两种&#xff1a;2.1、互斥锁2.2、逻辑过期2.3、两种方案对比 3、利用互斥锁解决缓存击穿问题3.1、ShopServiceImpl.java3.2、使用 jmeter.bat 测试高并发 4、利用逻辑过期解决缓存击穿问题 1、缓存击穿 缓存击穿问题 也叫 热点key问题…

半个月从几十升粉到500(发红包喽)

目录 1. 背景2. 涨粉秘籍2.1 持续创作高质量内容2.1.1 保持频率2.1.2 技术文章为主2.1.3 图文并茂 2.2 积极参与社区活动2.2.1 社区分享2.2.2 发文活动 2.3 互动与建立信任2.3.1 与读者互动2.3.2 红包互动2.3.3 动态分享 2.4 标题与内容的优化2.4.1 标题吸引2.4.2 内容实用 2.5…

【C++】C++ 超市会员卡管理系统(面向对象)(源码+数据)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

【从零开始学架构 架构基础】五 架构设计的复杂度来源:低成本、安全、规模

架构设计的复杂度来源其实就是架构设计要解决的问题&#xff0c;主要有如下几个&#xff1a;高性能、高可用、可扩展、低成本、安全、规模。复杂度的关键&#xff0c;就是新旧技术之间不是完全的替代关系&#xff0c;有交叉&#xff0c;有各自的特点&#xff0c;所以才需要具体…

解决idea中git无法管理项目中所有需要管理的文件

点击文件->设置 选择版本控制—>目录映射 点击加号 设置整个项目被Git管理

springboot助农电商系统-计算机毕业设计源码08655

摘要 近年来&#xff0c;电子商务的快速发展引起了行业和学术界的高度关注。基于移动端的助农电商系统旨在为用户提供一个简单、高效、便捷的农产品购物体验&#xff0c;它不仅要求用户清晰地查看所需信息&#xff0c;而且还要求界面设计精美&#xff0c;使得功能与页面完美融…

【GPU虚拟化到池化技术深度分析 2024】

文末有福利&#xff01; 随着大模型的兴起&#xff0c;对GPU算力的需求越来越多&#xff0c;而当前现实情况使企业往往受限于有限的GPU卡资源&#xff0c;即便进行了虚拟化&#xff0c;往往也难以充分使用GPU卡资源或持续使用资源。为解决GPU算力资源不均衡等问题&#xff0c;…

国标GB/T 28181详解:国标GBT28181-2022第三方呼叫控制的视音频文件下载流程

目录 一、定义 1、国标GB/T 28181 2、第三方呼叫控制的视音频文件下载流程 二、作用 1、提供有效的数据回顾机制 2、增强监控系统的功能性 3、保障数据传输与存储的可靠性 4、实现精细化的操作与控制 5、促进监控系统的集成与发展 6、提供清晰的信令流程和操作规范 三…

驱动开发:配置Visual Studio驱动开发环境

100编程书屋_孔夫子旧书网 配置驱动开发环境配置驱动开发模板配置驱动双机调试 在正式开始驱动开发之前&#xff0c;需要自行搭建驱动开发的必要环境&#xff0c;首先我们需要安装Visual Studio 2013这款功能强大的程序开发工具&#xff0c;在课件内请双击ISO文件并运行内部的…

基于web的产品管理系统

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于web的产品管理系统,java项目。 ecli…

5G RAN

两个entity&#xff1a;NodeB、UE entity之间传输数据的东东 entity内部的流水线岗位&#xff1a;L3/L2/L1 岗位之间是消息交互/信令交互

阿里云centos7.9 挂载数据盘到 www目录

一、让系统显示中文 参考&#xff1a;centos7 怎么让命令行显示中文&#xff08;英文-&#xff1e;中文&#xff09;_如何在命令行中显示中文-CSDN博客 1、输入命令&#xff1a;locale -a |grep "zh_CN" 可以看到已经存在了中文包 2、输入命令&#xff1a;sudo vi…

在Clion使用CubeMX Stm32的步骤

Step1 准备软件&#xff0c;安装环境&#xff1a; 1. cubemx v6.5.0&#xff08;可以兼容以前版本的project&#xff09; https://www.st.com.cn/zh/development-tools/stm32cubemx.html STM32CubeMX 默认安装目录, 6.5版本可以兼容老版本 C:\Program Files\STMicroelectroni…

Golang | Leetcode Golang题解之第199题二叉树的右视图

题目&#xff1a; 题解&#xff1a; /** 102. 二叉树的递归遍历*/ func levelOrder(root *TreeNode) [][]int {arr : [][]int{}depth : 0var order func(root *TreeNode, depth int)order func(root *TreeNode, depth int) {if root nil {return}if len(arr) depth {arr a…