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]){//过去3sledcnt[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,PARCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC|RCC_APB2Periph_ADC1,ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6); //配置DMARCC->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);//配置ADC1ADC_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.mzph.cn/web/37329.shtml

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

相关文章

【SkiaSharp绘图13】SKCanvas方法详解(二)填充颜色、封装对象、高性能绘制、点(集)(多段)线、圆角矩形、Surface、沿路径绘制文字

文章目录 SKCanvas方法DrawColor 填充颜色DrawDrawable 绘制封装对象DrawImage 高性能绘制图像SKBitmap与SKImage对比DrawPicture 绘制图像SKPicture DrawPoint / DrawPoints 绘制点DrawRoundRect/DrawRoundRectDifference绘制圆角矩形DrawSurface 绘制SurfaceDrawTextOnPath沿…

List接口, ArrayList Vector LinkedList

Collection接口的子接口 子类Vector&#xff0c;ArrayList&#xff0c;LinkedList 1.元素的添加顺序和取出顺序一致&#xff0c;且可重复 2.每个元素都有其对应的顺序索引 方法 在index 1 的位置插入一个对象&#xff0c;list.add(1,list2)获取指定index位置的元素&#…

sheng的学习笔记-AI-聚类(Clustering)

ai目录 sheng的学习笔记-AI目录-CSDN博客 基础知识 什么是聚类 在“无监督学习”(unsupervised learning)中&#xff0c;训练样本的标记信息是未知的&#xff0c;目标是通过对无标记训练样本的学习来揭示数据的内在性质及规律&#xff0c;为进一步的数据分析提供基础。此类学…

Android跨进程通信,binder传输数据过大导致客户端APP,Crash,异常捕获,监听异常的数值临界值,提前Hook拦截。

文章目录 Android跨进程通信&#xff0c;binder传输数据过大导致Crash&#xff0c;异常捕获&#xff0c;监听异常的数值临界值&#xff0c;提前Hook拦截。1.binder在做跨进程传输时&#xff0c;最大可以携带多少数据1.1有时候这个1m的崩溃系统捕获不到异常&#xff0c; 2.监测异…

志愿填报指南:为什么我强烈建议你报考计算机专业

首先恭喜2024届高考的同学们&#xff0c;你们已经通过了高考的考验&#xff0c;即将进入人生的新阶段——大学。 现在正是高考完填报志愿的时刻&#xff0c;Left听到身边朋友提到报考志愿的诸多问题&#xff1a; 志愿填报怎么填&#xff1f;我要报考什么专业&#xff1f;这个…

[Cloud Networking] OSPF

OSPF 开放式最短路径优先&#xff08;Open Shortest Path First&#xff09;是一种动态路由协议&#xff0c;它属于链路状态路由协议&#xff0c;具有路由变化收敛速度快、无路由环路、支持变长子网掩码和汇总、层次区域划分等优点。 1 OSPF Area 为了适应大型网络&#xff0…

可编程定时计数器8253/8254 - 8253入门

时钟-给设备打拍子 概述 在计算机系统中&#xff0c;为了使所有设备之间的通信井然有序&#xff0c;各通信设备间必须有统一的节奏&#xff0c;不能各干各的&#xff0c;这个节奏就被称为定时或时钟 时钟并不是计算机处理速度的衡量&#xff0c;而是一种使设备间相互配合而避…

Linux 磁盘挂载与分区

Linux 磁盘挂载与分区 vda1: 其中vd表示虚拟磁盘&#xff0c;a表示第一块磁盘&#xff0c;b表示第二块磁盘&#xff0c;1表示第一块磁盘的第一分区&#xff08;显然两块磁盘都只有一个分区&#xff09;图中可以看到&#xff0c;vda1磁盘只有一个分区&#xff0c;且全部挂载到根…

vue3使用vant4的列表vant-list点击进入详情自动滚动到对应位置,踩坑日记(一天半的踩坑经历)

1.路由添加keepAlive <!-- Vue3缓存组件&#xff0c;写法和Vue2不一样--><router-view v-slot"{ Component }"><keep-alive><component :is"Component" v-if"$route.meta.keepAlive"/></keep-alive><component…

如何在MySQL中按字符串中的数字排序

在管理数据库时&#xff0c;我们经常遇到需要按嵌入在字符串中的数字进行排序的情况。这在实际应用中尤为常见&#xff0c;比如文件名、代码版本号等字段中通常包含数字&#xff0c;而这些数字往往是排序的关键。本文将详细介绍如何在MySQL中利用正则表达式提取字符串中的数字并…

Java家教系统小程序APP公众号h5源码

让学习更高效&#xff0c;更便捷 &#x1f31f; 引言&#xff1a;家教新选择&#xff0c;小程序来助力 在快节奏的现代生活中&#xff0c;家长们越来越注重孩子的教育问题。然而&#xff0c;如何为孩子找到一位合适的家教老师&#xff0c;成为了许多家长头疼的问题。现在&…

谷歌个人号,20人连续封测14天所需设备该怎么解决?

现在&#xff0c;在Google Play上架应用&#xff0c;对于大部分开发者来说&#xff0c;真的是不小的挑战&#xff0c;因为目前谷歌上架政策越来越严格了。特别是从2023年11月13日起&#xff0c;新政策要求个人开发者账号的应用必须经过20个独立用户连续14天的封闭测试&#xff…

【C语言】--分支和循环(1)

&#x1f37f;个人主页: 起名字真南 &#x1f9c7;个人专栏:【数据结构初阶】 【C语言】 目录 前言1 if 语句1.1 if1.2 else1.3 嵌套if1.4 悬空else 前言 C语言是结构化的程序设计语言&#xff0c;这里的结构指的是顺序结构、选择结构、循环结构。 我们可以用if、switch实现分支…

vue2实例实现一个初步的vuex

vue2实例实现一个初步的vuex 实现源码&#xff1a;vue2-review 1.App.vue 2.store目录下的index.js 3.效果 微信公众号&#xff1a;刺头拾年

扩展阅读:什么是中断

如果用一句话概括操作系统的原理,那就是:整个操作系统就是一个中断驱动的死循环,用最简单的代码解释如下: while(true){doNothing(); } 其他所有事情都是由操作系统提前注册的中断机制和其对应的中断处理函数完成的。我们点击一下鼠标,敲击一下键盘,执行一个程序,…

Spring MVC中的DispatcherServlet、HandlerMapping和ViewResolver的作用

在Spring MVC框架中&#xff0c;DispatcherServlet、HandlerMapping和ViewResolver是核心组件&#xff0c;它们各自承担着不同的角色和任务&#xff1a; 1.DispatcherServlet&#xff1a;它是Spring MVC生命周期中的前端控制器&#xff0c;负责接收HTTP请求并将它们分发给相应的…

5.x86游戏实战-CE定位基地址

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;4.x86游戏实战-人物状态标志位 上一个内容通过CE未知的初始值、未变动的数值、…

JVM相关总结

JVM的些许问题 1.JVM内存区域划分 2.JVM类加载过程 3.JVM的垃圾回收机制 1.JVM的内存区域划分 一个运行起来的Java进程就是一个JVM虚拟机,需要从操作系统申请一大片内存,就会把内存划分成几个区域,每个区域都有不同的作用 常见的面试题 2.JVM类加载过程 熟练背诵 ! ! !…

WordPress中文网址导航栏主题风格模版HaoWa

模板介绍 WordPress响应式网站中文网址导航栏主题风格模版HaoWa1.3.1源码 HaoWA主题风格除行为主体导航栏目录外&#xff0c;对主题风格需要的小控制模块都开展了敞开式的HTML在线编辑器方式的作用配备&#xff0c;另外预埋出默认设置的编码构造&#xff0c;便捷大伙儿在目前…

入门JavaWeb之 JDBC 连接数据库

JDBC&#xff1a;Java Database Connectivity&#xff0c;Java 数据库连接 需要 jar 包支持&#xff1a; java.sql javax.sql mysql-connector-java&#xff08;连接驱动&#xff0c;必须导入&#xff09; 在 MySQL 先建个 jdbc 数据库后 USE jdbc; 执行后再 CREATE TABLE…