STM32-ADC(独立模式、双重模式)

ADC简介

18个通道:外部信号源就是16个GPIO回。在引脚上直接接模拟信号就行了,不需要侄何额外的电路。引脚就直接能测电压。2个内部信号源是内部温度传感器和内部参考电压。

逐次逼近型ADC:

它是一个独立的8位逐次逼近型ADC芯片,这个ADC0809是一款经典的ADC芯片。现在单片机的性能和集成都有很大的提升,很多单片机内部就有ADC芯片,这样就不用外挂芯片了,引脚可以直接测电压,使用还是非常方便的。

首先左边这里的IN0~IN7,是8路输入通道,通过通道选择开关,选中这一路,输入到这个点进行转换 。下面是地址锁存和译码,就是你想选中那个通道,就把通道号放在这三个脚上 ,然后给一个锁存信号,上面这里对应的通路开关就可以自动拨好了,这部分就相当于一个可必通过模拟信号的数据选择器。因为AD转换是一个很快的过程,你给个开始信号,过几个us就转换完成了,所以说如果你想转换多路信号,只需要一个AD转换器。然后加一个多路选择开,想转换哪路,就先拨一下,选中对应通道,然后再开始转换就行了。

STM32内部的ADC是有18个输入通道的,所以对应这里就是有18路输入的多路开关。

1个外部通道输入的未知编码的电压和一个DAC输出的已知编码的电压。它俩同时输入到电压比较器,进行大小判断。如果DAC输出的电压比较大,我就调小DAC数据,如果DAC输出的电压比较小,我就增大DAC数据,直至DAC输出的电压和外部通道输入的电压相等,这样DAC输入的数据就是外部电压的编码数据了。

ADC框图

左边是ADC的输入通道包括16个GPIO口,IN0~N15;和两个内部的通道,一个是内部温度传感器,另一个是参考电压。总共18个输入通道,然后到达这里,这是一个模拟多路开关,可以指定我们想要选择的通道。右边是多路开关的输出,进入模数转换器,转换结果会直接放在这个数据寄存器,我们读取寄存器就能知道ADC转换的结果了。

注入通道和规则通道

对于普通的ADC,多路开关一般都是只选中某一个通道、开始转换、等待转换完成、取出结果。

在这里它可以同时选中多个,在转换的时候,还分成了两个组,规则通道组和注入通道组。规则组:一次可以最多选中16个通道;注入组:最多可以选中4个通道。规则组虽然可以选中16个通道,但是数据寄存器只能存取一个结果,如果不想之前的结果被覆盖,那在转换完成之后,就要把结果拿走。注入组最多可以选4个通道,但他的数据寄存器有4个,所以他就不用担心数据被覆盖的问题了。

中断触发ADC转换

STM32的ADC,触发ADC开始转换的信号有两种:1、软件触发:就是在程序中手动调用一条代码,就可以启动转换了。2、硬件触发:左下角选中的触发源。这些触发源主要是定时器,有定时器的各个通道。

上面两个是ADC的参考电压,决定了ADC输入电压的范围;下面两个是ADC的供电引脚。一般情况VREF+要接VDDA,VREF-要接VSSA。

ADC的时钟:ADCCLK

ADCCLK来自预分频器,ADC预分频器是来源与RCC的。

ADCCLK最大14MHz,ADC预分频可以选择2、4、6、8分频,选择2分频,36MHz,超出ADCCLK的范围了,所以只能选择6分频或者8分频

ADC基本结构图

左边是输入通道,16个GPIO口,加两个内部的通道,然后进入AD转换器。

AD转换器里有两个组:

1、规则组:一次可以最多选中16个通道,虽然可以选中16个通道,但是数据寄存器只能存取一个结果,如果不想之前的结果被覆盖,那在转换完成之后,就要把结果拿走。

2、注入组:最多可以选中4个通道,但他的数据寄存器有4个,所以他就不用担心数据被覆盖的问题了。

触发控制:软件触发、硬件触发

RCC的时钟CLOCK:ADC逐次比较的过程就是有这个这个时钟推动的。

输入通道

转换模式

1、单次转换,非扫描模式

选择指定的转换通道,然后我们就可以触发转换,ADC对选定的通道进行模数转换,转换完成之后,转换结果放在数据寄存器里,同时EOC标志位置1,整个转换过程就结束了。
─ 转换数据被储存在16位ADC_DR寄存器中
─ EOC(转换结束)标志被设置
─ 如果设置了EOCIE,则产生中断。

2、连续转换,非扫描模式

首先,他还是非扫描模式,所以菜单列表就只用第一个,然后他与单次转换的不同的是,它在第一次转换之后不会停止,而是立刻开始下一轮的转换,然后一直持续。

3、单次转换,扫描模式

这个模式也是单次转换,所以每触发一次,转换结束后,就会停下来,下次转换就得再触发才能开始。然后它是扫描模式,这就会用到这个菜单列表了,这里每个位置是通道几可以任意指定,并且也是可以重复的。

16个通道位置用不完的情况,只用前几个,那就需要再给一个通道数目的参数(几个通道)。比如说这里指定的通道数目为7,那就只看前7个位置,然后每次触发之后,他就一次对这前7个位置进行AD转换,转换结果都放在数据寄存器里。为了防止数据被覆盖,就需要用DMA及时将数据挪走。7个通道转化完成之后,产生EOC信号,转换结束。

4、连续转换,扫描模式

就是在单次转换,扫描模式的基础上,一次转换完之后,立刻开始下一轮的转换。

数据对齐

STM32F103C8T6这个ADC是12位的,它的转换结果就是一个12位的数据,但是数据寄存器是16位的,所以就存在一个数据对齐的问题。

数据右对齐:12位的数据向右靠,高位多出来的几位就补0

数据左对齐:低位多出来的几位补0

们一般使用的是右对齐,这样读取的16位寄存器,直接就是转换结果。如果是左对齐的话,结果比实际结果大,左移4位,相当于把结果乘16了。

转换时间

校准

示例工程

1、AD单通道实现

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	
	/*设置ADC时钟*/
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA0引脚初始化为模拟输入
	
	/*规则组通道配置*/
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);		//规则组序列1的位置,配置为通道0
	
	/*ADC初始化*/
	ADC_InitTypeDef ADC_InitStructure;						//定义结构体变量
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//模式,选择独立模式,即单独使用ADC1
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//数据对齐,选择右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//外部触发,使用软件触发,不需要外部触发
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;		//连续转换,失能,每转换一次规则组序列后停止
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;			//扫描模式,失能,只转换规则组的序列1这一个位置
	ADC_InitStructure.ADC_NbrOfChannel = 1;					//通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
	ADC_Init(ADC1, &ADC_InitStructure);						//将结构体变量交给ADC_Init,配置ADC1
	
	/*ADC使能*/
	ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行
	
	/*ADC校准*/
	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
}

/**
  * 函    数:获取AD转换的值
  * 参    数:无
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(void)
{
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束
	return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}
#ifndef __AD_H
#define __AD_H

void AD_Init(void);
uint16_t AD_GetValue(void);

#endif
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t ADValue;			//定义AD值变量
float Voltage;				//定义电压变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();			//OLED初始化
	AD_Init();				//AD初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "ADValue:");
	OLED_ShowString(2, 1, "Voltage:0.00V");
	
	while (1)
	{
		ADValue = AD_GetValue();					//获取AD转换的值
		Voltage = (float)ADValue / 4095 * 3.3;		//将AD值线性变换到0~3.3的范围,表示电压
		
		OLED_ShowNum(1, 9, ADValue, 4);				//显示AD值
		OLED_ShowNum(2, 9, Voltage, 1);				//显示电压值的整数部分
		OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);	//显示电压值的小数部分
		
		Delay_ms(100);			//延时100ms,手动增加一些转换的间隔时间
	}
}

2、AD多通道实现

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	
	/*设置ADC时钟*/
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA0、PA1、PA2和PA3引脚初始化为模拟输入
	
	/*不在此处配置规则组序列,而是在每次AD转换前配置,这样可以灵活更改AD转换的通道*/
	
	/*ADC初始化*/
	ADC_InitTypeDef ADC_InitStructure;						//定义结构体变量
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//模式,选择独立模式,即单独使用ADC1
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//数据对齐,选择右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//外部触发,使用软件触发,不需要外部触发
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;		//连续转换,失能,每转换一次规则组序列后停止
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;			//扫描模式,失能,只转换规则组的序列1这一个位置
	ADC_InitStructure.ADC_NbrOfChannel = 1;					//通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
	ADC_Init(ADC1, &ADC_InitStructure);						//将结构体变量交给ADC_Init,配置ADC1
	
	/*ADC使能*/
	ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行
	
	/*ADC校准*/
	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
}

/**
  * 函    数:获取AD转换的值
  * 参    数:ADC_Channel 指定AD转换的通道,范围:ADC_Channel_x,其中x可以是0/1/2/3
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
	ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);	//在每次转换前,根据函数形参灵活更改规则组的通道1
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束
	return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}
#ifndef __AD_H
#define __AD_H

void AD_Init(void);
uint16_t AD_GetValue(uint8_t ADC_Channel);

#endif
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t AD0, AD1, AD2, AD3;	//定义AD值变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();				//OLED初始化
	AD_Init();					//AD初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "AD0:");
	OLED_ShowString(2, 1, "AD1:");
	OLED_ShowString(3, 1, "AD2:");
	OLED_ShowString(4, 1, "AD3:");
	
	while (1)
	{
		AD0 = AD_GetValue(ADC_Channel_0);		//单次启动ADC,转换通道0
		AD1 = AD_GetValue(ADC_Channel_1);		//单次启动ADC,转换通道1
		AD2 = AD_GetValue(ADC_Channel_2);		//单次启动ADC,转换通道2
		AD3 = AD_GetValue(ADC_Channel_3);		//单次启动ADC,转换通道3
		
		OLED_ShowNum(1, 5, AD0, 4);				//显示通道0的转换结果AD0
		OLED_ShowNum(2, 5, AD1, 4);				//显示通道1的转换结果AD1
		OLED_ShowNum(3, 5, AD2, 4);				//显示通道2的转换结果AD2
		OLED_ShowNum(4, 5, AD3, 4);				//显示通道3的转换结果AD3
		
		Delay_ms(100);			//延时100ms,手动增加一些转换的间隔时间
	}
}

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

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

相关文章

net core 程序运行报错,需要kb2533623补丁

报错大概如下: Failed to load the dll from xxxx 0x80070057 The library hostfxr.dll was found, but loading it from .xxxx\hostfxr.dll failed 目前微软官方已经停止这个补丁下载了,找个了多个网址不是带病毒就是带推广了,下面这个目前…

I2C通信的详细讲解

物理接口: SCL SDA (1)SCL(serial clock):时钟线,传输CLK信号,一般是I2C主设备向从设备提供时钟的通道。 (2)SDA(serial data):数据…

【从零开始手搓12306项目】第一阶段遇到的问题及解决方案

IDEA中datebase连接mysql失败 读取外包函数报错 注意区分private和public 找不到数据库? 一定要注意数据库的url链接,在datebase的url复制过来 xml和java对应不上? 最好复制一遍到xml文件 git忽略条件文件目录 定义Git全局的 .gitigno…

还有同学开题报告没写吗?

引言 作为一名在软件技术领域深耕多年的专业人士,我不仅在软件开发和项目部署方面积累了丰富的实践经验,更以卓越的技术实力获得了🏅30项软件著作权证书的殊荣。这些成就不仅是对我的技术专长的肯定,也是对我的创新精神和专业承诺…

Golang面试题四(GMP)

目录 1.Goroutine 定义 2.GMP 指的是什么 3.GMP模型的简介 全局队列(Global Queue) P的本地队列 P列表 M列表 4.有关P和M的个数问题 P的数量问题 M的数量问题 P和M何时会被创建 5.调度器P的设计策略 复⽤线程 work stealing机制 hand off…

Adobe将Sora、Runway、Pika,集成在PR中

4月15日晚,全球多媒体巨头Adobe在官网宣布,将OpenAI的Sora、Pika 、Runway等著名第三方文生视频模型,集成在视频剪辑软件Premiere Pro中(简称“PR”)。 同时,Adob也会将自身研发的Firefly系列模型包括视频…

Java工程师常见面试题:Java基础(一)

1、JDK 和 JRE 有什么区别? JDK是Java开发工具包,它包含了JRE和开发工具(如javac编译器和java程序运行工具等),主要用于Java程序的开发。而JRE是Java运行环境,它只包含了运行Java程序所必须的环境&#xf…

社交媒体数据恢复:钉钉

在数字化办公日益普及的今天,钉钉作为一款综合性的企业级通讯工具,已经深入到众多企业和个人的工作与生活中。然而,在日常使用过程中,我们难免会遇到一些意外情况导致数据丢失的问题。本文将针对钉钉数据恢复这一主题,…

Cisco ACI使用Postman配置交换机-未完待续

先看下不使用脚本的情况下是怎么配置交换机端口的? 例: 有10个交换机接口要开trunk,透传50个vlan, 使用GUI的操作方式为 1 进入EPG -->Static port 2 右键,绑定接口 3 选中node -->指定接口—>指定vlan —>…

意大利侍酒师Galvan Maurizia分享意大利葡萄酒与美食文化魅力

在酒水行业日益繁荣的今天,消费者对酒类产品的品质、文化和品味的追求不断提升。为了满足这一市场需求,云仓酒庄近日宣布开启首届《综合品酒师》培训,旨在培养更多具备专业素养和品鉴能力的品酒师,为酒水行业的专业化和形象提升注…

【行为型模式】观察者模式

一、观察者模式概述​ 软件系统其实有点类似观察者模式,目的:一个对象的状态或行为的变化将导致其他对象的状态或行为也发生改变,他们之间将产生联动。 观察者模式属于对象行为型: 1.定义了对象之间一种一对多的依赖关系&#xff…

翱途O2OA新手上路-服务器下载及私有云部署

本篇主要简要描述从官网下载服务器,进行部署,启动的过程,并且描述在部署过程中常见的问题与报错以及云服务器安全策略配置和O2OA服务器端口修改的方式。 O2OA部署的服务器要求不高,一般使用4C8G以上的服务器均可正常运行。 一、检…

锂电池充放电管理-单片机通用

锂电池充放电管理-单片机通用 一、锂电池充放电检测的原理二、power.c的实现三、power.h的实现四、锂电池检测和充电电路 一、锂电池充放电检测的原理 ①两节锂电池通过电阻分压检测ADC,再根据电压划分电量等级;②充电使用的是锂电池充电IC方案&#xf…

2024年4月最新版GPT

2024年4月最新版ChatGPT/GPT4, 附上最新的使用教程。 随着人工智能技术的不断发展,ChatGPT和GPT4已经成为了人们日常生活中不可或缺的助手。2024年4月,OpenAI公司推出了最新版本的GPT4,带来了更加强大的功能和更加友好的用户体验。本文将为大家带来最新版GPT4的实用…

每日一题(PTAL2-006):树的遍历--树的构建,队列

因为要层序遍历&#xff0c;所以我们可以考虑构建一颗二叉树。构建完只有利用队列就可以就行层序遍历。 #include <bits/stdc.h> using namespace std; int p1[35]; int p2[35]; typedef struct Tree {int val;struct Tree* left;struct Tree* right; }TT; typedef TT* …

鸿蒙入门02-首次安装和配置

注&#xff1a;还没有安装编辑器&#xff08; deveco studio &#xff09;的小伙伴请看鸿蒙入门01-下载和安装-CSDN博客 首次安装配置 编辑器&#xff08; deveco studio &#xff09;安装完毕以后需要进入配置界面进行相关配置配置完毕以后才可以正常使用 环境配置&#xf…

微信小程序全局配置

全局配置文件及常用的配置项 小程序根目录下的 app.json 文件是小程序的全局配置文件。常用的配置项如下&#xff1a; ① pages 记录当前小程序所有页面的存放路径 ② window 全局设置小程序窗口的外观 ③ tabBar 设置小程序底部的 tabBar 效果 ④ style 是否启用新版的组件样…

MongoDB的CURD(增删改查操作)

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f525; 欢迎来到我的博客 &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️寻至善的主页 ✈️如果喜欢这篇文章的话 &#x1f64f;大大们可以动动发财的小手&#x1f449;&#…

【IT运维入门(ITHW)系列】之「快速部署」第二期清单(持续更新)

ITHW是Information Technology Hello World的缩写简拼。意在提供IT领域的入门相关知识&#xff0c;近期给大家带来的是主流技术选型的快速部署系列&#xff0c;意在最大程度地简化部署过程&#xff0c;以便能快速体验或测试相关技术选型。 「快速部署」第一期清单 ITHW快捷部署…

Day 15 Linux网络管理

IP解析 IP地址组成&#xff1a;IP地址由4部分数字组成&#xff0c;每部分数字对应于8位二进制数字&#xff0c;各部分之间用小数点分开&#xff0c;这是点分2进制。如果换算为10进制我们称为点分10进制。 每个ip地址由两部分组成网络地址(NetID)和主机地址(HostID).网络地址表…