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

【STM32】进阶(二):DMA+ADC实现模拟量检测

1、简述

DMA:Direct Memory Access,直接内存访问
ADC:Analog to Digital Converter,模数转换器,模拟信号转换成数字信号的电路(采样-量化-编码)

参考博客:
STM32DMA功能详解
STM32F4之ADC介绍

DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。这里的存储器可以是片内的SRAM(默认存放变量)或者是FLASH(默认存放常量,被const修饰的全局变量可以看成是常量类型),而外设指的其实是外设的数据寄存器。但它们本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。

我们知道CPU有转移数据、计算、控制程序转移等很多功能,系统运作的核心就是CPU,CPU无时不刻的在处理着大量的事务,但有些事情却没有那么重要,比方说数据的复制和存储数据,DMA的主要功能是用来搬数据,在传输数据的时候,CPU就可以不被占用用来干其他事情,对于实时性要求比较高的场合,我们可以利用DMA来减小CPU的负担。

因此:转移数据(尤其是转移大量数据)是可以不需要CPU参与。比如希望外设A的数据拷贝到外设B,只要给两种外设提供一条数据通路,直接让数据由A拷贝到B不经过CPU的处理,通过DMA解决大量数据转移过度消耗CPU资源的问题。

2、模拟量检测

2.1 初始化步骤

模拟量检测,需要将GPIO引脚设置为模拟输入模式、设置模数转换ADC、设置DMA等,完整初始化步骤如下

初始化时钟
初始化GPIO
初始化ADC
初始化DMA
使能ADC
使能DMA

2.2 初始化时钟

void RCC_Configuration(void)
{# a) DMA1 的时钟RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	# b) 使能GPIO 和 ADC1 的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE );# c) 因为ADC时钟不要超过14M,否则精度会下降,因此设置ADC分频因子=6,即72M/6=12MRCC_ADCCLKConfig(RCC_PCLK2_Div6); 
}

2.3 初始化GPIO

void GPIO_Configuration(void)
{GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; # a)将引脚设置为模拟输入引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);
}

2.4 初始化ADC

void ADC1_Configuration(void)
{ADC_InitTypeDef ADC_InitStructure;# a)将外设ADC1的全部寄存器重设为缺省值ADC_DeInit(ADC1); # b)设置为独立工作模式(ADC1和ADC2工作在独立模式)ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; # c)使能扫描模式ADC_InitStructure.ADC_ScanConvMode =ENABLE; # d)连续转换模式ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; # e)关闭外部触发转换ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; # f)ADC数据右对齐ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; # g)按顺序规划转换的 ADC 通道的数目ADC_InitStructure.ADC_NbrOfChannel = M; ADC_Init(ADC1, &ADC_InitStructure); # h)设置转换顺序和时间ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5 );# i)使能 ADC 的 DMA功能ADC_DMACmd(ADC1, ENABLE);# j)使能指定ADC,这里是ADC1ADC_Cmd(ADC1, ENABLE);# k)复位指定 ADC1 的校准寄存器ADC_ResetCalibration(ADC1); # l)等待完成复位校准寄存器while(ADC_GetResetCalibrationStatus(ADC1)); # m)开始指定 ADC1 的校准状态ADC_StartCalibration(ADC1); /# n)等待获取 ADC1 的校准状态while(ADC_GetCalibrationStatus(ADC1)); 
}

2.5 初始化DMA

void DMA_Configuration(void)
{DMA_InitTypeDef DMA_InitStructure;# a)将 DMA 的通道1寄存器重置为缺省值DMA_DeInit(DMA1_Channel1); # b)DMA 外设基地址指向 ADC1DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR; # c)设置内存基地址DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&AD_Value;# d)将内存作为数据传输的目的地 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; # e)DMA 的缓存大小DMA_InitStructure.DMA_BufferSize = N*M; # f)外设地址寄存器不变DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;# g)内存地址寄存器递增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;# h)外设数据宽度为16DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;# i)内存数据宽度为16DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; # j)循环缓存模式DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;# k)高优先级DMA_InitStructure.DMA_Priority = DMA_Priority_High;# l)这里是外设到内存直接传输,因此关闭内存到内存模式DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;DMA_Init(DMA1_Channel1, &DMA_InitStructure);
}

2.6 使能ADC 和 DMA

	ADC_SoftwareStartConvCmd(ADC1, ENABLE);DMA_Cmd(DMA1_Channel1, ENABLE); 

2.7 使用数据

使能后,从ADC1采集的模数转换后的值将循环存储在数组AD_Value中,对AD_Value中数据求平均值即可

#define N 50
#define M 1 
vu16 AD_Value[N][M];
vu16 After_filter[M];
int i;void filter(void)
{int sum = 0;u8 count;for ( count=0;count<N;count++){sum += AD_Value[count][0];}After_filter[0]=sum/N;sum=0;
}

3、时钟设置小结

3.1 GPIO作为输出时

如:点亮LED灯实验时,开启GPIOB的时钟:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO,ENABLE);

3.2 GPIO作为输入时(轮询方式)

如:按键实验,将PA0作为按键

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

3.3 GPIO作为输入时(中断方式)

如:按键实验,将PC13作为按键,以中断方式打开,需要打开复用时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO,ENABLE);

3.4 配置串口UASRT

需要先设置引脚的时钟,然后设置串口的时钟。
如:配置UASRT1时,用到了PA9和PA10,所有要开启GPIOA的时钟,另外还有开启USART1的时钟。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);  
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);    

3.5 配置DMA

DMA挂载在AHB总线上

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

3.6 配置基本定时器

基本定时器挂载在APB1总线上

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);

3.7 配置通用定时器

通用定时器挂载在APB1总线上。

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

3.8 其它注意事项

1)配置按键中断时,只需要开启相应的GPIO的时钟。初始化EXTI结构体时,不需要开启EXTI时钟。
2)配置NVIC中断向量控制器时,不需要开启时钟。
3)使用SysTick系统定时器时,不需要开启时钟。

http://www.lryc.cn/news/33034.html

相关文章:

  • Lab2_Simple Shell_2020
  • 2023最全电商API接口 高并发请求 实时数据 支持定制 电商数据 买家卖家数据
  • MySQL 的索引类型
  • < Linux > 进程信号
  • Pyspark基础入门7_RDD的内核调度
  • C/C++每日一练(20230307)
  • 一条SQL查询语句是如何执行的?
  • tcsh常用配置
  • YOLOv5源码逐行超详细注释与解读(2)——推理部分detect.py
  • 什么叫个非对称加密?中间人攻击?数字签名?
  • 2023.03.07 小记与展望
  • MyBatis源码分析(七)MyBatis与Spring的整合原理与源码分析
  • 基于声网 Flutter SDK 实现多人视频通话
  • IT服务管理(ITSM) 中的大数据
  • Validator校验之ValidatorUtils
  • C++---背包模型---采药(每日一道算法2023.3.7)
  • Java各种锁
  • TryHackMe-Tardigrade(应急响应)
  • 导出GIS | 将EXCEL表格中坐标导出成GIS格式文件
  • new set数组对象去重失败
  • Acwing: 一道关于线段树的好题(有助于全面理解线段树)
  • DD-1/40 10-40mA型【接地继电器】
  • 【女神节】简单使用C/C++和Python嵌套for循环生成一个小爱心
  • Biome-BGC生态系统模型与Python融合技术实践应用
  • ESP32 GPIO使用
  • JavaScript 高级4 :正则表达式
  • 如何让AI帮你干活-娱乐(3)
  • webview的工作、内存泄漏、漏洞以及缓存机制原理原理+方案解决
  • BFD协议原理
  • 你把骑行当什么? 它就是你需要的