STM32ADC

news2024/9/22 11:32:34

ADC简介:有打moba游戏的别搞混了,这不是射手adc。在32中,ADC的全称为:Analog-to-Digital Converter,指模拟/数字转换器 也就是模拟-数字电路的转换器。其实通俗的来讲,它就是一个电压表

目录

一.ADC原理

1.ADC框图

2.通道

3.规则组/注入组&转换顺序

4.转换触发

5.周期 

6.转换模式

7.校验 

二.配置

1.ADC基本配置流程(代码)

2.进阶用法:ADC与DMA结合使用

好了,祝你看完就会。


一.ADC原理

1.ADC框图

重点看红框内部的结构就行了,通过接触GPIO来将外接设备的模拟电压进行转换,变为数字逻辑电平,这就是它的主要功能。

2.通道

再经典不过的东西,TIM,DMA都有的东西。功能也和它们差不多,这里对应的自然就是GPIO。不同的通道固定只能监听固定的GPIO口。

这里唯一不同的点就是不同的ADC(比如ADC1和ADC2)同时监听PA0口并不会像其他外设一样出问题,完全可以同时监听(但本质上是间歇性监听,就是不断轮番监听)。另外一般在应用场景中ADC是要同时监听多个通道的

3.规则组/注入组&转换顺序

规则组:如果把ADC看作一个测量电压的机器,那么规则组就是他的流水线。其上面按Rank摆放着一组通道(GPIO口),ADC就依次测量并且把数据放在寄存器中

注入组:很简单,注入组看作中断执行的流水线即可。(假设正在执行规则组此时进来一组注入组则优先执行完注入组再回过头执行规则组。

转换顺序:由三个寄存器控制,看一眼图就行。

4.转换触发

大体来说其实就是硬件自动触发还是软件控制触发:

可以看到要么就是各种定时器或者外部中断触发要么就是软件触发。一般还是软件触发用的比较多也比较方便。

5.周期 

首先ADC的是中原是PCLK,他的时钟源不能超过14Mhz。再应用中,通过选择采集时间来控制对应的周期:

采样时间随箭头增长。

6.转换模式

主要分为三种:
扫描模式:只循环测量规则组的第一个(不论组里配置了几个通道)
循环模式:测量完后继续从头重复测量
间歇模式:不用。

另外,当哪一种模式都不配置的时候,它就变为普通模式,就是你触发它一次它测量一次:

7.校验 

这个看起来可有可无的东西其实再最终结果中产生的影响还是蛮大的:
1.一定要校验,否则电压测量会有约0.05的误差
2.必须再初始化后进行,不然数据特别不正常。(别问问就是作者吃过瘪了)

二.配置

1.ADC基本配置流程(代码)

ADC的流程和其他基本外设配置的流程也极度相似:

ADC基本初始化(数据对齐;搬运模式;转换触发)HAL_ADC_Init()        
                                |
通道配置(通道选择;测量时间;规则通道Rank选择)HAL_ADC_ConfigChannel()
                                |
               校验 HAL_ADCEx_Calibration_Start()
                                |
经典Msp配置(对应GPIO口配置;时钟使能;分频数选择;对应IO口时钟来源)HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
                                |
                  软件触发  HAL_ADC_Start(&adc_init);
                                |
     等待测量结束返回对应值  HAL_ADC_PollForConversion(&adc_init,10);

#include "adc.h"

ADC_HandleTypeDef adc_init = {0};
ADC_ChannelConfTypeDef adc_channel_init = {0};
void ADC_INIT(){
	//ADC基本模式配置
	adc_init.Instance = ADC1;
	adc_init.Init.ContinuousConvMode = DISABLE;				//连续转变模式配置
	adc_init.Init.DiscontinuousConvMode = DISABLE;			//间断转变模式配置
	adc_init.Init.DataAlign = ADC_DATAALIGN_RIGHT;			//测得数据对齐模式
	adc_init.Init.ExternalTrigConv = ADC_SOFTWARE_START;	//触发模式
	adc_init.Init.NbrOfConversion = 1;						//转变数量
	adc_init.Init.NbrOfDiscConversion = 0;					//间断模式转变量
	adc_init.Init.ScanConvMode = DISABLE;					//扫描模式
	//ADC通道配置
	adc_channel_init.Channel = ADC_CHANNEL_1;					//通道选择
	adc_channel_init.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;	//测量时间
	adc_channel_init.Rank = ADC_REGULAR_RANK_1;					//规则通道位置配置
						
	HAL_ADC_Init(&adc_init);
	HAL_ADC_ConfigChannel(&adc_init,&adc_channel_init);
	HAL_ADCEx_Calibration_Start(&adc_init);						//校验,否则电压测量会有约

}

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc){
	if(hadc->Instance == ADC1){
		__HAL_RCC_ADC1_CLK_ENABLE();
		__HAL_RCC_GPIOA_CLK_ENABLE();
		
		GPIO_InitTypeDef gpio_init;
		gpio_init.Mode = GPIO_MODE_ANALOG;
		gpio_init.Pin = GPIO_PIN_1;
		//ADC时钟配置
		RCC_PeriphCLKInitTypeDef PeriphClkInit;
		PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;		//ADC时钟分频
		PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;		//外设时钟选择
		
		HAL_GPIO_Init(GPIOA,&gpio_init);
		HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);	
	}
}

uint32_t value_get(){
	HAL_ADC_Start(&adc_init);					//软件启动转换
	HAL_ADC_PollForConversion(&adc_init,10);	//等待转换结束
	return (uint16_t)HAL_ADC_GetValue(&adc_init);			//返回

}

2.进阶用法:ADC与DMA结合使用

 流程和上面基本一样,只是有以下区别:
1.转换模式调为循环模式                              adc_init.Init.ContinuousConvMode = ENABLE;
2.通道初始化需要同时初始化多个                adc_channel_init.Channel = ADC_CHANNEL_2;
3.ADC初始化转化数量需要更改                           adc_init.Init.NbrOfConversion = 2 
4.Msp引脚初始化需要增加                      gpio_init.Pin = GPIO_PIN_1|GPIO_PIN_2;
5.数据对齐方式需要更改               adc_init.Init.ExternalTrigConv = ADC_SOFTWARE_START;

ADC部分:

#include "adc.h"

ADC_HandleTypeDef adc_init = {0};
ADC_ChannelConfTypeDef adc_channel_init = {0};
void ADC_INIT(){
	//ADC基本模式配置
	adc_init.Instance = ADC1;
	adc_init.Init.ContinuousConvMode = ENABLE;				//连续转变模式配置
	adc_init.Init.DiscontinuousConvMode = DISABLE;			//间断转变模式配置
	adc_init.Init.ScanConvMode = DISABLE;					//扫描模式配置
	adc_init.Init.DataAlign = ADC_DATAALIGN_RIGHT;			//测得数据对齐模式
	adc_init.Init.ExternalTrigConv = ADC_SOFTWARE_START;	//触发模式
	adc_init.Init.NbrOfConversion = 2;					//转变数量
	adc_init.Init.NbrOfDiscConversion = 0;					//间断模式转变量
					//扫描模式
	//ADC通道配置
	adc_channel_init.Channel = ADC_CHANNEL_1;					//通道选择
	adc_channel_init.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;	//测量时间
	adc_channel_init.Rank = ADC_REGULAR_RANK_1;					//规则通道位置配置
							
	HAL_ADC_Init(&adc_init);	
	HAL_ADC_ConfigChannel(&adc_init,&adc_channel_init);
	
	adc_channel_init.Channel = ADC_CHANNEL_2;					//通道选择
	adc_channel_init.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;	//测量时间
	adc_channel_init.Rank = ADC_REGULAR_RANK_2;					//规则通道位置配置
	
	HAL_ADC_ConfigChannel(&adc_init,&adc_channel_init);
	
	HAL_ADCEx_Calibration_Start(&adc_init);					//校验,否则电压测量会有约0.05的误差
																//注意: 

}

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc){
	if(hadc->Instance == ADC1){
		__HAL_RCC_ADC1_CLK_ENABLE();
		__HAL_RCC_GPIOA_CLK_ENABLE();
		
		GPIO_InitTypeDef gpio_init;
		gpio_init.Mode = GPIO_MODE_ANALOG;
		gpio_init.Pin = GPIO_PIN_1|GPIO_PIN_2;
		
		//ADC时钟配置
		RCC_PeriphCLKInitTypeDef PeriphClkInit;
		PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;		//ADC时钟分频
		PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;		//外设时钟选择
		
		HAL_GPIO_Init(GPIOA,&gpio_init);
		HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
	
	}
}

DMA的配置则并不难,不多赘述,上代码:

#include "dma.h"

DMA_HandleTypeDef dma_init = {0};
extern ADC_HandleTypeDef adc_init;
void DMA_INIT(){
	__HAL_RCC_DMA1_CLK_ENABLE();	
	dma_init.Instance = DMA1_Channel1;	
	dma_init.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;		//千万千万千万注意,M和P千万千万不能对应错啊	
	dma_init.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;	
	dma_init.Init.MemInc = DMA_MINC_ENABLE;			//内存递增使能
	dma_init.Init.PeriphInc = DMA_PINC_DISABLE;
	dma_init.Init.Direction = DMA_PERIPH_TO_MEMORY;	
	dma_init.Init.Priority = DMA_PRIORITY_MEDIUM;
	dma_init.Init.Mode = DMA_CIRCULAR;		//循环模式
	HAL_DMA_Init(&dma_init);
	__HAL_LINKDMA(&adc_init,DMA_Handle,dma_init);
}

最后主函数:

这里唯一要讲的地方就是红框内的部分,你很有可能会问:为什么不干脆定义成uint32还非要等返回接收的时候进行强转呢?
原因很简单,但不知道就很难。 首先主要是由于区别之一的字节对齐问题,ADC的精度是12位,按照字节对齐则正好占两个字节也就是uint16。但ADC的DMA地址是连续的,也就是如果该数据超过这一字节那么就把下个字节也拿来用了。所以如果一开始你就用uint32,那么一个缓存中就会存入两个通道的数据。像这样:

好了,祝你看完就会。

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

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

相关文章

六、5 TIM输入捕获介绍

1、基本知识介绍 (1) 注意: ①4个输入捕获和输出比较通道,共用4个CCR寄存器 ②CH1到CH4,四个通道的引脚也是共用的 ③同一个定时器输入捕获和输出比较,不能同时使用 (2)输入捕获…

4G/5G无线视频采集设备如何通过国标28181接入到视频监控接入平台(视频统一接入平台)

目录 一、国标GB/T 28181介绍 1、国标GB/T28181 2、内容和特点 二、4G/5G无线视频采集设备 1、定义 2、主要功能: 3、技术特点 4、应用场景 二、接入准备工作 1、确定网络环境 (1)公网接入 (2)专网传输 2、…

使用 Rough.js 创建动态水平条形图

本文由ScriptEcho平台提供技术支持 项目地址:传送门 使用 Rough.js 创建动态可视化网络图 应用场景 Rough.js 是一个 JavaScript 库,它允许开发人员使用毛边风格创建可视化效果。该库适用于各种应用程序,例如: 数据可视化地图…

【C++11】解锁C++11新纪元:深入探索Lambda表达式的奥秘

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C “ 登神长阶 ” 🤡往期回顾🤡:C11右值引用 🌹🌹期待您的关注 🌹🌹 ❀C11 📒1. 可变参数模板…

.net # 检查 带有pdf xss

1.解决pdf含javasprct脚本动作,这里是验证pdf内部事件。相关pdf文件下载: 测试pdf文件 相关包 iTextSharp 5.5.13.4 iTextSharp using iTextSharp.text.pdf; using iTextSharp.text.pdf.parser;private Boolean IsPdfSafe(Stream stream){// PdfReader…

PyTorch+PyG实现图神经网络经典模型目录

前言 大家好,我是阿光。 本专栏整理了《图神经网络代码实战》,内包含了不同图神经网络的相关代码实现(PyG以及自实现),理论与实践相结合,如GCN、GAT、GraphSAGE等经典图网络,每一个代码实例都…

洛谷 P1739 表达式括号匹配 题解

题目描述 假设一个表达式有英文字母(小写)、运算符(、-、*、/)和左右小(圆)括号构成,以 作为表达式的结束符。请编写一个程序检查表达式中的左右圆括号是否匹配,若匹配&#xff0c…

springboot农产品报价系统-计算机毕业设计源码37300

摘 要 本研究基于鸿蒙系统,设计开发了一款农产品报价系统小程序,旨在帮助商家与买家更便捷、高效地进行交易。该系统利用鸿蒙系统的优势,实现了跨平台应用程序的开发,同时利用定位技术和数据采集技术,为用户提供了个性…

RoboCom 2021 编程技能赛决赛 7-4 猛犸不上 Ban

7-4 猛犸不上 Ban 赛题 分数 30 作者 DAI, Longao 单位 杭州百腾教育科技有限公司 在一个名叫刀塔的国家里,有一只猛犸正在到处跑着,希望能够用它的长角抛物技能来撞飞别人。已知刀塔国有 N 座城市,城市之间由 M 条道路互相连接&#xff…

【C语言】【数据结构】冒泡排序及优化

一、算法思想 冒泡排序是一种简单的排序算法。一次从前往后地走访待排序的元素序列被称为一趟,每一趟都会把相邻的两个元素的错误顺序交换,将当前趟次中最大或者最小的元素像“冒泡泡”一样冒到最后面,反复地走访元素序列,直到所有…

Maven 安装-从下载、安装、配置以及检查是否安装成功,最详细安装教程

以下内容参考:https://juejin.cn/post/6844903543711907848 原文标题:Maven入门,读完这篇就够了 作者:嘟嘟MD 链接:https://juejin.cn/post/6844903543711907848 来源:稀土掘金 ----- 注:所有流…

计算机组成原理——运算器ALU,移位操作

一、组合逻辑电路和时序逻辑电路 组合逻辑电路:其输出仅取决于当前输入组合,不依赖先前输出,不具备存储状态的能力 时序逻辑电路:其输出不仅取决于当前输入,还取决于先前的输出,具备存储状态的能力。 AL…

多 NodeJS 环境管理

前言 对于某个项目依赖特定版本的 NodeJS,或几个项目的 NodeJS 版本冲突时,需要在系统中安装多个版本的 NodeJS,这时可以使用一些工具来进行多个 NodeJS 的管理。 有很多类似的 NodeJS 管理工具,如 nvm, nvs, n 等,接…

【Unity】 HTFramework框架(五十四)【进阶篇】Deployment 轻量级资源部署管线

更新日期:2024年7月31日。 Github源码:[点我获取源码] 索引 Deployment 轻量级资源部署管线使用 Deployment一、创建部署配置二、编辑部署配置三、正式开始资源部署步骤一:资源打包步骤二:资源版本构建步骤三:资源版本…

学习C语言第19天(练习题)

编程题 第一题 改数字 //改数字 int gaishuzi(int * input) {int sum 0;int i 0;while (*input){int bit* input% 10;if (bit % 2 1){sum 1 * pow(10, i);i;}else{sum 0* pow(10, i);i;}*input / 10;}return sum; } int main() {int input 0;scanf("%d&quo…

域气象-大气化学在线耦合模式(WRF/Chem)在大气环境中的应用

随着我国经济快速发展,我国面临着日益严重的大气污染问题。近年来,严重的大气污染问题已经明显影响国计民生,引起政府、学界和人们越来越多的关注。大气污染是工农业生产、生活、交通、城市化等方面人为活动的综合结果,同时气象因…

嵌入式day17

尾插: 头删: 尾删: 双向链表: 循环链表: 内存泄漏(malloc 调用的节点需要手动清除) 清除: 头删效率更高,算法复杂度更低 共用体 共用体的成员,会共同占用相…

CX32L003F8P6T芯片解密程序破解

CX32L003F8P6T可替代N76E003 CX32L003是一款内嵌32位ARM Cortex-M0内核的超低功耗、Low Pin Count和宽电压工作范围(2.5V~5.5V)的微控制器,最高可运行在24MHz,内置32K/64K字节的嵌入式Flash,4K字节的SRAM,集成了12位1Msps高精度SA…

结构型设计模式:适配器/代理

结构型设计模式:适配器/代理 (qq.com)

软件测试——用例篇(下)

基于需求的设计⽅法 基于需求的设计⽅法也是总的设计测试⽤例的⽅法,在⼯作中, 我们需要参考需求⽂档/产品规格说明书来设计测试⽤例。测试⼈员接到需求之后, 要对需求进⾏分析和验证,从合理的需求中进⼀步分析细化需求&#x…