ADC(Analog-Digital Converter)模拟-数字转换器
ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。
12位逐次逼近型ADC,1us转换时间。
分辨率含义:0 ~ 2^12,即0 ~ 4095 ,位数越高量化结果越精细,分辨率越高
STM32的ADC输入是0~3.3V,所以ADC是将0~3.3V线性映射成0~4095。
VREF与VCC相连,输入电压模拟量范围由VREF决定。
可以单通道输入,也可以多通道接不通传感器输入。
输出是八位二进制数,因此是8位ADC。
STM32的ADC完整框图
VREF与VDDA内部电源连接。
1、单次转换,非扫描模式
2、连续转换,非扫描模式
3、单次转换,扫描模式
4、连续转换,扫描模式
单次转换:每次从开始触发(软件触发或者硬件触发)到数据写入规则组数据寄存器后置EOC标志位,转换结束。后面要再采集数据需要重新上述过程。
连续转换:每次从开始触发(软件触发或者硬件触发)到数据写入规则组数据寄存器后置EOC标志位,转换不结束,不需要再次触发,继续采集数据写入数据寄存器。
扫描模式:多通道采集数据,并在序列中指定好通道顺序,开始触发后,数据寄存器逐个写入指定序列中数据(会覆盖,因此采用这个模式需要配合DMA使用)序列中数据读完后置EOC标志位。
非扫描模式:单通道单序列就是非扫描模式,默认。
如图:
触发控制有硬件和软件两种触发:如图
数据对齐方式:
因为STM32中ADC是12位的,而ADC的数据寄存器是16位的,因此有两种对齐方式。其中左对齐会损失精度,右对齐不会。左对齐降低精度,只读高八位,舍去后四位。
AD转换的步骤:采样,保持,量化,编码
其中采样、保持放一起,量化、编码放一起。
功能实现:用电位计产生0~3.3V的连续变化的模拟电压,在LCD显示转换后的数据和处理后得到的电压值
流程:
参考上面的框图,具体步骤如下:
第一步开启RCC时钟,包括ADC和GPIO的时钟,ADCCLK的分频器也要配置(给ADC配置时钟信号的用来计算采样的时间。72MHZ/ADCCLK);
第二步配置GPIO,把需要用的GPIO配置成模拟输入的模式;
第三步配置多路开关(黄颜色部分),把左边的通道接入到右边的规则组列表里;
第四步配置ADC转换器,库函数中用结构体来配置,包括单次转换还是连续转换、扫描还是非扫描、通道数量、触发源、数据对齐方式(左对齐or右对齐)。若需要模拟看门狗,会有几个函数配置阈值和检测通道。若想开启中断,就在中断输出控制里用ITConfig函数开启对应的中断输出,然后再在NVIC里,配置一下优先级,就可以触发中断了;
第五步调用ADC_Cmd函数,开启ADC。
注:开启ADC后,根据手册建议,可以对ADC进行校准,减小误差。在ADC工作时,如果想要软件触发转换,那会有函数可以触发;如果想读取转换结果,那也会有函数可以读取结果。
#include "stm32f10x.h" // Device header
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //提供时钟,72/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);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
///选择规则组的输入通道.第三个参数为
//要配置的ADC通道在转换序列中的排位。取值为1~16,表示该通道在转换序列中的第几个进行转换。
//例如,若取值为2,则表示该通道在转换序列中排在第2位。
//ADC模块配置
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作在单ADC模式
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐方式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //采用软件触发,还是硬件触发
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //连续或单一模式。
ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 扫描(多通道)或单(单通道)模式。
ADC_InitStructure.ADC_NbrOfChannel = 1; //指定在哪个序列上
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
//校准模块
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); //软件触发,开始工作
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //获取转换结束EOC标志位
return ADC_GetConversionValue(ADC1);//转换完成会自动清除标志位
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t ADValue;
float Voltage;
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1, 1, "ADValue:");
OLED_ShowString(2, 1, "Volatge:0.00V");
while (1)
{
ADValue = AD_GetValue();
Voltage = (float)ADValue / 4095 * 3.3; //12位ADC
OLED_ShowNum(1, 9, ADValue, 4);
OLED_ShowNum(2, 9, Voltage, 1);
OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);
Delay_ms(100);
}
}