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

STM32 ADC

目录

ADC简介

逐次逼近型ADC 

STM32 ADC框图 

输入通道 

转换模式 

•单次转换,非扫描模式 

•连续转换,非扫描模式 

•单次转换,扫描模式 

•连续转换,扫描模式 

触发控制 

数据对齐 

转换时间 

校准

硬件电路 

AD单通道


 

ADC简介

逐次逼近型ADC 

左边IN0-7,八路输入通道,通过通道选择开关,选择一路输入到红点进行转换,下面这里是地址锁存和译码,就是你想选择哪一路就把通道号放在左边的三个脚上,然后给一个锁存信号,上面这里对应的通路开关就可以拨好了,这部分就相当于一个可以通过模拟信号的数据选择器,

输入信号选好了,到红点了,怎么才能知道这个电压对应的编码数据是多少呢,这就需要我们用逐次逼近的方法来一一比较了,右边的三角形是一个电压比较器,它可以判断两个输入信号电压的大小关系,两个输入端,一个上面是待测端,一个是DAC的电压输出端,DAC是数模转换器,给它一个数据就可以输出数据对应的电压

现在我们有了一个外部通道输入的未知编码的电压和一个DAC输出的已知编码的电压,它俩同时输入到电压比较器,进行大小判断,如果DAC输出的电压比较大,我就调小DAC数据,如果DAC输出的电压比较小,我就调大DAC数据,直到与外部通道输入的电压近似相等,这样DAC输入的数据就是外部电压的编码数据了,这个电压调节的过程就是逐次逼近寄存器SAR完成的,为了最快找到未知电压的编码,通常我们会使用二分法进行寻找

然后AD转换结束后,DAC的输入数据(蓝点),就是未知电压的编码,通过右边输出,8位就有8根线,12位就有12根线,上面EOC(转换结束信号)START是开始转换,给一个输入脉冲,开始转换,CLOCK是时钟,因为ADC是需要一步一步判断的需要时钟推动这个过程,

VREF+VREF-DAC的参考电压,比如给一个数据255,是对应5v还是3.3v呢就由这个参考电压决定,这个DAC的参考电压也决定了ADC的输入范围,所有它也是ADC参考电压,左边是整个芯片的供电VCCGND

STM32 ADC框图 

左上角是VREF+,VREF-,VDDA,VSSA,上面两个是ADC的参考电压决定ADC输入电压的范围,下面是ADC的供电引脚,一般情况下VREF+VDDAVREF-VSSA,这个芯片上VREF+VREF-引脚,在内部已经接在一起了,VDDAVSSA是内部模拟部分的电源,在这里VDDA3.3VVSSAGND,所以ADC的输入电压范围是0-3.3v 

左边是ADC的输入通道,包括16GPIO口,IN0-15和两个内部通道温度传感器和VREFINT(内部参考电压),然后到底模拟多路开关,可以知道我们想要选择的通道,右边是多路开关的输出,进入到模数转换器,这里模数转换器就是执行逐次比较的过程,转换结构会直接放进上面数据寄存器里,我们读取寄存器就能知道ADC转换的结果了,在左边对应普通的ADC多路开关只选择一个的,但这个不同,可以同时选择多个,而且在转换的时候,还分成了两个组,规则通道组和注入通道组,其中规则组可以一次性最多选16个通道,注入组最多可以选4个通道(就好比去餐厅点餐,普通ADC是,你指定一个菜(通道几的数据),老板给你做,然后做好了送给你,这里是你指定一个菜单,这个菜单最多可以填16个菜,然后直接递个菜单给老板,老板按照菜单的顺序依次做好,一次性给你端上来,这个菜单也分两种,一个是规则组,但是这个规则组只有一个数据寄存器,就是这桌子比较小,你如果上16个菜,那前15个菜就会被挤掉,你只能得到第16个菜,所有对于规则组来说,最后配合DMA来实现,DMA是个数据转移小能手,每上一个菜,它可以把这个菜挪到其他地方去,防止被覆盖,注入组相当于餐厅VIP,这个座位上一次性可以点4个菜,并且这个数据寄存器有4个,是可以同时上4个菜的)一般情况下使用规则组 

左下角是触发转换部分,也就是上图的START信号,开始转换,那对于STM32ADC,触发ADC开始转换的信号有两种,一种是软件触发,就是在程序中手动调用一条代码就可以启动了;另一种是硬件触发就是左边的触发源,上面是注入组触发源,下面是规则组的触发源,主要来自于定时器,比如给TIM3定个1ms的时间,并且把TIM3的更新事件选择位TRGO输出,然后在ADC这里选择开始触发信号为TIM3TRGO,这样TIM3的更新事件就能通过硬件自动触发ADC转换了,整个过程不需要进中断,节省了中断资源,当然这里还可以选择外部中断引脚来触发转换 

模拟看门狗,它里面可以存一个阈值高限和阈值低限,如果启动了模拟看门狗,并且指定了看门的通道,那这个看门狗就会关注它看门的通道,一旦超过这个阈值范围了,它就会乱叫,就会在上面,申请一个模拟看门狗的中断,最后通向NVIC,对于规则组和注入组而言,他们转换完成后,也会有一个EOC转换完成的信号,在这里EOC的规则组完成信号,JEOC是注入组完成的信号,这两个信号会在状态寄存器里置一个标志位,我们读取这个标志位,就能知道是不是转换结束了,同时这两个标志位也可以去到NVIC申请中断,如果开启了NVIC对于的通道,它们就会触发中断 

右边是ADC的时钟,相当于上图的CLOOK是用于驱动内部逐次比较的时钟,这个时钟是来自ADC的预分频器,这个ADC的预分频器来源于RCC的,只能选择6分频,结果是12M8分频,结果是9M这两个值

输入通道 

转换模式 

单次转换,非扫描模式 

 这个表就是规则组的菜单,你可以在这里点菜,就是写入你要转换的通道,在非扫描模式下,这个菜单只有第一个序列1的位置有效,这时,菜单同时选中一组的方式就退化为简单地选中一个的方式了,这序列1里指定我们想转换的通道,比如通道2,然后我们触发转换,ADC会对这个通道2进行模数转换,过一段时间,转换完成,转换结果放进数据寄存器里,同时给EOC标志位置1

如果想换一个通道转换,那在转换之前,把第一个位置通道2改为其他通道,然后在启动转换 

连续转换,非扫描模式 

 还是非扫描模式,所以菜单列表就只用第一个,然后与单次转换不同的是,它在一次转换结束后不会停止,而是立刻开始下一轮的转换,然后一直持续下去,这样就只需要最开始触发一次,之后就可以一直转换了,这个模式的好处是,开始转换之后不需要等待一段时间的

单次转换,扫描模式 

扫描模式就用到列表了,可以在菜单里点菜,比如第一个菜是通道2,第二个菜是通道5等等,这里每个位置是通道几可以任意指定,并且也是可以重复的

然后初始化结构体里还会有个参数,就是通道数目,因为这16个位置你可以不用完,只用前几个,那你就需要再给一个通道数目的参数,告诉它,我有几个通道,比如这里指定通道数目为7,那就只看前7个位置,然后每次触发之后,就依次对前七个位置进行AD转换,转换结果都放在寄存器里,这个防止数据被覆盖,就需要用DMA及时将数据挪走

连续转换,扫描模式 

次转换完成后立刻开始下一次的转换 

触发控制 

 

数据对齐 

ADC12位的,它的转换结果就是一个12位的数据

但是这个数据寄存器是16位的,所以就存在一个数据对齐的问题

第一种是数据右对齐,12位数据向右靠,高位多出来的就补0

第二种是数据左对齐,12位数据向左靠,低位多出来的就补0

一般使用右对齐

转换时间 

 校准

硬件电路 

 第一个是电位器产生一个可调的电压,这里可以接ADC的输入通道,比如PA0口,当滑动端往上滑时,电压增大,往下滑时,电压减小

中间是传感器输出电压的电路,一般来说,像光敏电阻,热敏电阻,红外接收管,麦克风等都可以等效为一个可变电阻,电阻阻值没办法进行测量,所以这里就可以通过和一个固定电阻串联分压,来得到一个反应电阻值电压的电路,传感器阻值变小时,下拉作用变强,输出端口电压就下降。传感器阻值变大时,下拉作用变弱,输出端受上拉电阻的作用,电压就升高,这个固定电阻一般可以选择和传感器阻值相近的电阻 

AD单通道

    1.开启ADC和GPIO的时钟//ADC的CLOCK分频器也要配置一下
    2.配置GPIO,把需要用的GPIO配置成模拟输入的模式
    3.配置多路开关,把左边的通道接到右边的规则组列表里(点菜)
    4.配置ADC转换器

ADC的库函数

void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);控制某个中断能不能进入NVIC

void ADC_ResetCalibration(ADC_TypeDef* ADCx);复位校准
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);获取复位校准状态
void ADC_StartCalibration(ADC_TypeDef* ADCx);开始校准
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);获取开始校准状态

void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);ADC软件开始转换控制,这个就是用于软件触发的函数,调用一下就能软件触发转换了(触发控制)

FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);ADC获取软件开始转换状态(跟转换是否结束毫无关系)一般不用

这两个函数是用来配置间断模式的                                                                                                void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);每隔几个通道简短一次
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);是不是启用间断模式

void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);ADC规则组通道模式(在转换模式的地方,给序列的每个位置填写指定的通道)

void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);ADC外部触发转换控制,就是是否允许外部触发转换

uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);ADC获取转换值,就是获取AD转换的数据寄存器,读取转换结果就要用这个函数

uint32_t ADC_GetDualModeConversionValue(void);ADC获取双模式转换值,这个是ADC模式读取转换结果的函数
 

下面带有inject的都是对注入组的配置

void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog);是否启动模拟看门狗
void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t LowThreshold);配置高低阈值
void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel);配置看门的通道

void ADC_TempSensorVrefintCmd(FunctionalState NewState);ADC温度传感器内部电压控制,这个是用来开启内部的两个通道的


 

include "stm32f10x.h"                  // Device headervoid AD_Init(void)
{//1.开启ADC和GPIO的时钟//ADC的CLOCK分频器也要配置一下/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz//2.配置GPIO,把需要用的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);						//3.配置多路开关,把左边的通道接到右边的规则组列表里(点菜)ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//4.配置ADC转换器ADC_InitTypeDef ADC_InitStructure;						//定义结构体变量ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//模式,选择独立模式,即单独使用ADC1ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//数据对齐,选择右对齐ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//外部触发,使用软件触发,不需要外部触发//对应机构框图左下角ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;		//连续转换(ENABLE),失能,每转换一次规则组序列后停止ADC_InitStructure.ADC_ScanConvMode = DISABLE;			//扫描模式(ENABLE),失能,只转换规则组的序列1这一个位置ADC_InitStructure.ADC_NbrOfChannel = 1;					//通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1ADC_Init(ADC1, &ADC_InitStructure);						//将结构体变量交给ADC_Init,配置ADC1//在规则组菜单列表的第一个位置写入通道0这个通道/*ADC使能*/ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行//根据手册要求还需要校准/*ADC校准*/ADC_ResetCalibration(ADC1);		//复位校准						//固定流程,内部有电路会自动执行校准while (ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1) == SET);}
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转换的结果
}
#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,手动增加一些转换的间隔时间}
}

AD多通道

#include "stm32f10x.h"                  // Device headervoid 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;//PA1接震动传感器GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA0、PA1引脚初始化为模拟输入/*不在此处配置规则组序列,而是在每次AD转换前配置,这样可以灵活更改AD转换的通道*//*ADC初始化*/ADC_InitTypeDef ADC_InitStructure;						//定义结构体变量ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//模式,选择独立模式,即单独使用ADC1ADC_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的数,在非扫描模式下,只能是1ADC_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);	//在每次转换前,根据函数形参灵活更改规则组的通道1ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"uint16_t AD0, AD1;	//定义AD值变量int main(void)
{/*模块初始化*/OLED_Init();				//OLED初始化AD_Init();					//AD初始化/*显示静态字符串*/OLED_ShowString(1, 1, "AD0:");OLED_ShowString(2, 1, "AD1:");while (1){AD0 = AD_GetValue(ADC_Channel_0);		//单次启动ADC,转换通道0AD1 = AD_GetValue(ADC_Channel_1);		//单次启动ADC,转换通道1OLED_ShowNum(1, 5, AD0, 4);				//显示通道0的转换结果AD0OLED_ShowNum(2, 5, AD1, 4);				//显示通道1的转换结果AD1Delay_ms(100);			//延时100ms,手动增加一些转换的间隔时间}
}


 

    

相关文章:

  • 数据结构 - 8( AVL 树和红黑树 10000 字详解 )
  • Android学习总结之kotlin协程面试篇
  • PowerShell 复制源文件夹中的所有文件和子文件夹到目
  • 机器学习 数据集
  • OpenCV 基于生物视觉模型的工具------模拟人眼视网膜的生物视觉机制类cv::bioinspired::Retina
  • 表达式求值(算法题)
  • Linux 常用命令 - tftp【简单文件传输协议】
  • 穿越“协议迷雾”:Modbus转Profinet与60LB伺服的传奇相遇
  • Hadoop MapReduce 图文代码讲解
  • 功能安全的关键——MCU锁步核技术全解析(含真实应用方案)
  • 什么是多模态大模型?为什么需要多模态大模型?
  • JAVA:Spring Boot 集成 Lua 的技术博客
  • IDEA 2024 版本配置热部署
  • SSM 框架是指什么,其优缺点,怎样用到在你的程序里
  • 图形渲染+事件处理最终版
  • KRaft (Kafka 4.0) 集群配置指南(超简单,脱离 ZooKeeper 集群)还包含了简化测试指令的脚本!!!
  • 线性回归算法介绍和代码例程
  • uniapp 微信小程序使用图表
  • uniapp中score-view中的文字无法换行问题。
  • MySQL的索引和事务
  • 中国海警依法驱离日非法进入我钓鱼岛领海船只
  • 北约年度报告渲染所谓“中国核威胁”,国防部回应
  • AI聊天机器人涉多起骚扰行为,专家呼吁加强伦理设计与监管
  • 汪海涛评《线索与痕迹》丨就虚而近实
  • 吴清:推动公募基金高质量发展的行动方案今天将会发布
  • 上海飞银川客机触地复飞后备降西安,亲历者:不少乘客都吐了