当前位置:网站首页>STM32之ADC
STM32之ADC
2022-07-02 22:09:00 【叫什么呀】
文章目录
一、ADC简介
ADC (Analog-Digital Converter) 模拟-数字转换器
ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。12位逐次逼近型ADC**,1us转换时间
输入电压范围∶0-3.3V,转换结果范围∶0~4095。18个输入通道,可测量16个外部和2个内部信号源规则组和注入组两个转换单元
模拟看门狗自动监测输入电压范围**
12位ADC的值,量化的范围就是0 - 2^12-1 ,即0-4095。位数越高。量化结果就越精细。对应分辨率就越高。
关于这个模拟看门狗,我们可以设定阈值。当AD值高于它设定的上阈值或者低于下阈值时,它就会申请中断,你就可以在中断函数里执行相应的操作。
1、逐次逼近型ADC
STM32的ADC的原理和这个一样,这个是ADC0809的内部结构
首先进行通道开关的选择。在左方有个通道选择开关。选择的通道的数字是存在下方的ADDA,ADDB,ADDC中。你想选择IN0-IN7中的哪一个通道,就把那个通道号放在这三个里面。ALE是锁存信号,给ALE置位,上面这里对应的通路开关就可以自动拨好了。
然后来到了比较器的上面一根线。
在比较器中用逐次逼近的方法来一一比较
比较器的作用就是判断两个输入信号电压的大小关系。
比较器的上面一根线是一个外部通道输入的,未知编码的电压。
比较器的下面一根线是一个DAC输出的,已知编码的电压。
如果DAC输出的电压比较小。我就增大DAC数据。直到DAC输出的电压和外部通道输入的电压近视相等。这样DAC输入的数据就是外部电压的编码数据了。(这里就是输入到8位三态锁存缓冲器)
而这个增大或减小去调整DAC的数据的过程,是那个逐次逼近寄存器SAR在起作用。
它通常会使用二分法进行查找,比如在这里是8位的ADC,就是0-255。第一次比较的时候,我们就给DAC输入255的一半进行比较。第二次比较的时候。再就给128的一半64。就这么继续往下比较。而且128,64,32正好是二进制每一位的位权。也就是说,这个比较的过程,就是对二进制从高位到低位依次判断是1是0的过程。那对于8位的ADC,从高位到低依次判断8次就能找到未知电压的编码。12位的就是判断12次。
EOC是End Of Gonvert转换结束信号
START是开始转换。给一个输入脉冲,开始转换。
CLOCK是ADC时钟。
下面。VRVF+和VREF-是DAC的参考电压。
比如你给一个数据255。是对应5V还是3.3V呢,就由这个参考电压决定。
这个DAC的参考电压也决定了ADC的输入范围,所以它也是ADC参考电压。
通常参考电压的正极和VCC是一样的,会接一起。参考电的负极和GND也是一样的。也接在一起。
2、ADC模块框图
3、ADC基本结构
左边是输入通道,16个GPO口,外加两个内部的通道。
然后进入AD转换器
AD转换器里有两个组,一个是规则组,一个是注入组
规则组最多可以选中16个通道。注入组最多可以选择4个通道。
然后转换的结果可以存放在AD数据寄存器里,规则组有1个数据寄存器,注入组有4个数据寄存器。
然后下面这里有触发控制。提供了开始转换的START信号。
触发控制可以选择硬件触发和软件触发,硬件触发主要来自于定时器和外部中断触发。
右边这里是来自RCC的ADC时钟CLOCK,ADC逐次比较的过程就是由这个时钟推动的
有三个方式可以申请中断,首先规则组和注入组转换完成会有EOC信号,会置一个标志位。其次是看门狗,如果超过阈值,就通过中断输出控制,像NVIC申请中断。
4、转换模式
规则组
触发—进入第一个序列位置(选中1个)---- 然后就转换完毕了
此时生成一个EOC的信号
如果要转换就要再次触发
触发—进入第一个序列位置(选中1个)---- 此次转换完毕 -----生成EOC ----- 不需要触发继续重复上述过程
这个模式也是触发一次用一次
但是一次有好几个序列
所以要给一个通道数目,就是有几个序列能用,有几个就从序列1开始往下排几个
每次触发后,就依次对这前7个(通道数目)进行AD转换,且为了防止数据覆盖,要及时用DMA挪走数据
那7个通道转换完成之后,产生EOC信号,转换结束。
就是上述过程,触发了就一直一直转
5、触发控制
6、数据对齐
一般情况下都是右对齐,前面加0。
也有的情况是左对齐
7、通道采样时间
8、校准
1、ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差。
2、 建议在每次上电后执行一次校准。
3、 启动校准前,ADC必须处于关电状态超过至少两个ADC时钟周期。
二、代码
我使用的板子是正点原子的迷你板
STM32的型号是 STM32F103RCT
有三个ADC
我们在这里使用ADC1的通道1
1、一些函数
ADC规则组通道配置
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
是否允许外部触发转换
void ADC_ExternalTrigInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
获取转换值
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
双ADC模式读取转换结果
uint32_t ADC_GetDualModeConversionValue(void);
2、ADC初始化
#include "stm32f10x.h"
void Adc_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
//1、开启时钟ADC1的时钟和GPIOA的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE );
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE );
//2、配置分频,因为最大不能超过14MHz 72/6=12
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
//3、配置GPIOA和ADC结构体 PA1 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1); //复位ADC1
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
//4、使能ADC1
ADC_Cmd(ADC1, ENABLE);
//5、校准并等待校准结束
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
}
关于分频
3、实验获取PA1的电压并显示
首先PA1是ADC通道1
所以这个函数中的ADC_Channel要选择如图所示
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime)
adc.c
#include "stm32f10x.h"
void Adc_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
//1、开启时钟ADC1的时钟和GPIOA的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE );
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE );
//2、配置分频,因为最大不能超过14MHz 72/6=12
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
//3、配置GPIOA和ADC结构体 PA1 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1); //复位ADC1
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
//4、使能ADC1
ADC_Cmd(ADC1, ENABLE);
//5、校准并等待校准结束
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
}
uint16_t AD_GetValue(u8 ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
}
//多获取几次数据求平均值更稳定
uint16_t Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=AD_GetValue(ch);
delay_ms(5);
}
return temp_val/times;
}
main.c
#include "stm32f10x.h"
#include "delay.h"
#include "ADC.h"
#include "lcd.h"
int main(void)
{
float temp;
u16 adcx;
delay_init(); //延时函数初始化
LCD_Init();
Adc_Init();
LCD_ShowString(10,70,200,16,16,"AD_GetValue");
LCD_ShowString(10,100,200,16,16,"AD_Average");
LCD_ShowString(10,150,200,16,16,"ADC_CH1_VOL:0.000V");
while(1)
{
LCD_ShowxNum(110,70,AD_GetValue(ADC_Channel_1),4,16,0);
LCD_ShowxNum(110,100,Get_Adc_Average(ADC_Channel_1,10),4,16,0);
adcx=Get_Adc_Average(ADC_Channel_1,10);
temp=(float)adcx*(3.3/4096);
adcx=temp;
LCD_ShowxNum(106,150,adcx,1,16,0);//显示整数
temp-=adcx;
temp*=1000;
LCD_ShowxNum(122,150,temp,3,16,0X80); //显示小数
}
}
接3.3v和接GND以及不接是浮空,电平不稳定一直变
边栏推荐
- Xiaopeng P7 had an accident and the airbag did not pop up. Is this normal?
- The motivation of AES Advanced Encryption Protocol
- Chow-Liu Tree
- Jatpack------LiveData
- stop slave卡住--事务的事件没有复制完整
- 杰理之内置关机电流 1.2uA,之后不能长按开机【篇】
- DTM distributed transaction manager PHP collaboration client V0.1 beta release!!!
- 【板栗糖GIS】global mapper 如何通过dsm批量制作贴地等高线
- 海思3559万能平台搭建:在截获的YUV图像上画框
- [NPUCTF2020]ezlogin xPATH注入
猜你喜欢
分享 10 个 JS 闭包面试题(图解),进来看看你能答对多少
[LeetCode] 反转字符串中的单词 III【557】
MySQL查询附近的数据.并按距离进行排序.
Mask R-CNN
`${}`的用法
Gas station [problem analysis - > problem conversion - > greed]
情感对话识别与生成简述
Qt QScrollArea
[LeetCode] 多数元素【169】
[chestnut sugar GIS] ArcMap - why should the tick of classic capture be removed when using custom capture?
随机推荐
[羊城杯2020]easyphp
[chestnut sugar GIS] how does global mapper batch produce ground contour lines through DSM
首批 | 腾讯云完成国内首个云原生安全成熟度评估
Niuke: Dragon and dungeon games
uniapp微信登录返显用户名和头像
Kubernetes uses the host name to allocate the pod on the specified node
Splunk audit 的设定
Jerry's prototype has no touch, and the reinstallation becomes normal after dismantling [chapter]
Golang面试整理 三 简历如何书写
LC173. 二叉搜索树迭代器
Local dealers play the community group purchase mode and share millions of operations
Data analysis learning records -- complete a simple one-way ANOVA with Excel
[leetcode] most elements [169]
数据标注典型案例,景联文科技如何助力企业搭建数据方案
數據分析學習記錄--用EXCEL完成簡單的單因素方差分析
DTM distributed transaction manager PHP collaboration client V0.1 beta release!!!
Storage unit conversion
杰理之样机在多次触摸后会触发关机【篇】
Jerry's prototype will trigger shutdown after multiple touches [chapter]
数据分析学习记录(二)---响应曲面法及Design-Expert的简单使用