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

news2024/10/6 12:22:01

文章目录

  • 需求
  • 一、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.coloradmin.cn/o/1879806.html

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

相关文章

JAVA期末速成库(12)第十三章

一、习题介绍 第十三章 Check Point&#xff1a;P501 13.3&#xff0c;13.17&#xff0c;13.28&#xff0c;13.29 Programming Exercise&#xff1a;13.1&#xff0c;13.6&#xff0c;13.11 二、习题及答案 Check Point&#xff1a; 13.3 True or false? a. An abst…

【做一道算一道】太平洋大西洋水流问题

太平洋大西洋水流问题 有一个 m n 的矩形岛屿&#xff0c;与 太平洋 和 大西洋 相邻。 “太平洋” 处于大陆的左边界和上边界&#xff0c;而 “大西洋” 处于大陆的右边界和下边界。 这个岛被分割成一个由若干方形单元格组成的网格。给定一个 m x n 的整数矩阵 heights &…

零基础开始学习鸿蒙开发-读书app简单的设计与开发

目录 1.首页设计 2.发现页面的设计 3.设置页面的设计 4.导航页设计 5.总结&#xff1a; 6.最终的效果 1.首页设计 Entry Component export struct home {State message: string 首页build() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.B…

Golang-map理解

golang-map语雀笔记整理 map的底层实现hmapbmap map是如何做到O(1)的复杂度的&#xff1f;map扩容策略 师兄问题回答 map的底层实现 hmap hmap的结构体核心字段有&#xff1a;buckets 桶数组地址&#xff0c; B 定位值&#xff0c;桶的数目是2^B个&#xff0c; count 当前map的…

Java基础(三)——类和对象、构造方法

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 ⚡开源项目&#xff1a; rich-vue3 &#xff08;基于 Vue3 TS Pinia Element Plus Spring全家桶 MySQL&#xff09; &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1…

Vue-cli项目及Element UI 环境搭建 保姆级教程

一、Vue-cli介绍及其作用 什么是Vue-cli手脚架 vue-cli 官方提供的一个脚手架&#xff0c;用于快速生成一个 vue 的项目模板&#xff1b;预先定义 好的目录结构及基础代码&#xff0c;就好比咱们在创建 Maven 项目时可以选择创建一个 骨架项目&#xff0c;这个骨架项目就是脚…

环回接口处理 IP 数据报的过程及 Loopback 接口的主要作用

环回接口处理 IP 数据报的过程 IPv4 中 传给环回地址&#xff08;127.0.0.1&#xff09;的任何数据均作为 IP 输入&#xff0c;直接送到环回接口&#xff08;环回&#xff1a;IP 输入队列&#xff09;。 传给广播地址或多播地址的数据报&#xff0c;会复制一份传给环回接口&…

MySQL高阶:事务和并发

事务和并发 1. 事务创建事务 2. 并发和锁定并发问题 3. 事务隔离等级3.1 读取未提交隔离级别3.2 读取已提交隔离级别3.3 重复读取隔离级别3.4 序列化隔离级别 4. 死锁 1. 事务 事务&#xff08;trasaction&#xff09;是完成一个完整事件的一系列SQL语句。这一组SQL语句是一条…

昇思25天学习打卡营第02天 | 快速入门

昇思25天学习打卡营第02天 | 快速入门 文章目录 昇思25天学习打卡营第02天 | 快速入门数据准备网络构建模型训练模型测试迭代数据集模型保存加载模型总结打卡 数据准备 MindSpore通过Dataset和Transforms实现高效的数据预处理 使用download下载数据&#xff0c;并创建数据集对…

Qt项目天气预报(8) - 绘制温度曲线 + 回车搜索(最终篇)

全部内容在专栏&#xff1a; Qt项目 天气预报_mx_jun的博客-CSDN博客 目录 绘制温度曲线 事件过滤器在子控件上绘图 子控件下载事件过滤器 事件过滤器进行绘图 - eventFilter 画初步高温曲线 画初步低温曲线 效果演示 画低温曲线 画高温曲线 效果演示 按下回车搜索: …

LDM论文解读

论文名称&#xff1a;High-Resolution Image Synthesis with Latent Diffusion Models 发表时间&#xff1a;CVPR2022 作者及组织&#xff1a;Robin Rombach, Andreas Blattmann, Dominik Lorenz,Patrick Esser和 Bjorn Ommer, 来自Ludwig Maximilian University of Munich &a…

AI奏响未来乐章:音乐界的革命性变革

AI在创造还是毁掉音乐 引言 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;正在逐渐渗透到我们生活的每一个角落&#xff0c;音乐领域也不例外。AI技术的引入&#xff0c;不仅为音乐创作、教育、体验带来了革命性的变革&#xff0c;更为整个音乐产业注入了…

顺序表应用——通讯录

在本篇之前的顺序表专题我们已经学习的顺序表的实现&#xff0c;了解了如何实现顺序表的插入和删除等功能&#xff0c;那么在本篇当中就要学习基于顺序表来实现通讯录&#xff0c;在通讯录当中能实现联系人的增、删、查改等功能&#xff0c;接下来就让我们一起来实现通讯录吧&a…

民用无人机企业招标投标需要资质证书详解

一、基础资质 在民用无人机企业的招标投标过程中&#xff0c;基础资质是首要考虑的因素。这些资质通常包括企业注册资质、税务登记证、组织机构代码证等。 1.1 企业注册资质 企业应具备合法的注册资质&#xff0c;即营业执照。该执照应包含企业名称、注册地址、法定代表人、…

Java [数据结构] Deque与Queue

&#x1f93a;深入理解 Java 中的 Deque 和 Queue&#x1f93a; 在现代软件开发中&#xff0c;数据结构是构建高效、可维护代码的基础。 Java 作为一门广泛应用的编程语言&#xff0c;其丰富的集合框架&#xff08;Collections Framework&#xff09;为开发者提供了多种强大的…

Zabbix 排坑版 Centos7

systemctl stop firewalld;systemctl disable firewalld;setenforce 0sed -i s/SELINUXenforcing/SELINUXdisabled/ /etc/selinux/configzabbix源地址,可以自己选版本&#xff0c;安装都大差不差 rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5…

【车载开发系列】S32 Design Studio工具安装步骤

【车载开发系列】S32 Design Studio工具安装步骤 S32 Design Studio工具安装步骤 【车载开发系列】S32 Design Studio工具安装步骤※关键字提炼※一. 准备工作二. 下载安装包三. 获取License许可四. 开始预安装五. 开始正式安装六. 启动软件七. 安装插件八. 卸载插件九. 确认安…

【操作系统】进程管理——进程控制和进程通信(个人笔记)

学习日期&#xff1a;2024.6.30 内容摘要&#xff1a;进程控制的概念&#xff0c;进程控制相关的“原语”&#xff0c;进程通信 进程控制 原语 进程控制用“原语”实现。原语是一种特殊的程序&#xff0c;它的执行具有原子性&#xff0c;也就是说&#xff0c;这段程序的执行…

vs code python开发笔记

目录 选择python 解析器 安装插件 不全&#xff1a; 调试启动目录问题&#xff1a; 2.选择python解释器 选择python 解析器 ctrl shift P select interpreter 安装插件 不全&#xff1a; remote ssh python debuger 左下角&#xff0c;点击左右左右箭头&#xff0c;远程…

后端之路第三站(Mybatis)——JDBC跟Mybatis、lombok

一、什么是JDBC JDBC就是sun公司研发的一套通过java来操控数据库的工具&#xff0c;对应不同的数据库系统有不同的JDBC&#xff0c;而他们统称【驱动】&#xff0c;这就是上一篇我们提到创建Mybatis项目时要引入的依赖、以及连接数据库四要素里的第一要素。 JDBC有自己一套原始…