电压表
,将引脚输入的高低电平间的任意电压进行量化,放在数据寄存器里面
12位
【分辨率】逐次逼近型ADC,1us转换时间【转化频率1MHZ】自动监测输入电压范围
【应用于中断】STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道
二分法选取的电压值恰好是二进制的位权
,所以8位只要分8次就可以找到对应编码电压,8位的二级制最大值是255只能选择6分频【12MHZ】或8分频【9MHZ】
关键函数
ADC_RegularChannelConfig()//模拟多路开关函数
ADC_SoftwareStartConvCmd()//软件触发函数,转换开启的函数
ADC_Cmd()//开关控制
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK函数在rcc.h文件里面
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待转换结果的完成,通过判断EOC标志位
因为数据寄存器16位,而ADC寄存器是12位,所以涉及数据对齐
数据右对齐:
AD转换的步骤:采样,保持,量化,编码
STM32 ADC的总转换时间为:
TCONV = 采样(保持)时间 + 12.5个ADC周期【ADCCLK】
例如:当ADCCLK=14MHz,采样时间为1.5个ADC周期
//采样保持时间在这个函数的最后一个参数里面设置
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//配置通道选择器
//ADCCLK设置
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK函数在rcc.h文件里面
//参数
@arg RCC_PCLK2_Div6: ADC clock = PCLK2/6//6分频为12MHZ
@arg RCC_PCLK2_Div8: ADC clock = PCLK2/8//8分频为9MHZ
固定代码
ADC_ResetCalibration(ADC1);//校准寄存器
while (ADC_GetResetCalibrationStatus(ADC1) == SET);//获取所选ADC重置校准寄存器状态。当校准完成会变为RESET,此时跳出循环
ADC_StartCalibration(ADC1);//启动选定的ADC校准过程。
while (ADC_GetCalibrationStatus(ADC1) == SET);//获取所选ADC校准状态。
当设定了阈值与电压值进行比较时会遇到抖动导致功能无法正常实现,可以采用如下方法:
AD.c
#include "stm32f10x.h" // Device header
void AD_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK函数在rcc.h文件里面
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//ADC输入引脚必须配置为模拟输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//ADC12_IN0
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//配置规则组通道选择器,第三个参数是通道里面的序列
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立通道or双通道
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//指定ADC数据对齐是左对齐还是右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//触发方式,外部触发None表示采用软件触发
//设置成单次转换非扫描
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//连续转换or单次转换
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//扫描模式or非扫描模式
ADC_InitStructure.ADC_NbrOfChannel = 1;//指定ADC通道数量,在扫描模式下才有
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
//校准固定代码
ADC_ResetCalibration(ADC1);//校准寄存器
while (ADC_GetResetCalibrationStatus(ADC1) == SET);//获取所选ADC重置校准寄存器状态。当校准完成会变为RESET,此时跳出循环
ADC_StartCalibration(ADC1);//启动选定的ADC校准过程。
while (ADC_GetCalibrationStatus(ADC1) == SET);//获取所选ADC校准状态。
//ADC_SoftwareStartConvCmd(ADC1, ENABLE);//当选择连续单通道时可以将触发函数写在初始化函数内部,在while循环里面就不会频繁触发。此时EOC标志位也可以不用判断是否置1,直接返回数据寄存器里面的ADC转换结果值即可
}
uint16_t AD_GetValue(void)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//启用或禁用所选ADC的软件触发
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待转换结果的完成,
return ADC_GetConversionValue(ADC1);//返回常规通道的最后ADCx转换结果数据。读取DR寄存器会自动清除EOC标志位,不用手动清除
}
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;
OLED_ShowNum(1, 9, ADValue, 4);
//showNum显示的是整数,通过计算获取小数部分
OLED_ShowNum(2, 9, Voltage, 1);
OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);
Delay_ms(100);
}
}
AD多通道采集一般采用扫描模式,在本案例中可以用单次转换非扫描模式,在每次触发转换之前,更改列表第一个序列的通道
AD.c
#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);
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);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
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(uint8_t ADC_Channel)
{
//在规则组通道选择器配置函数中,将采样通道设置为形参
ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);//第三个参数表示序列1
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//切换序列上的通道后在开启转换
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t AD0, AD1, AD2, AD3;
int main(void)
{
OLED_Init();
AD_Init();
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);
AD1 = AD_GetValue(ADC_Channel_1);
AD2 = AD_GetValue(ADC_Channel_2);
AD3 = AD_GetValue(ADC_Channel_3);
OLED_ShowNum(1, 5, AD0, 4);
OLED_ShowNum(2, 5, AD1, 4);
OLED_ShowNum(3, 5, AD2, 4);
OLED_ShowNum(4, 5, AD3, 4);
Delay_ms(100);
}
}
参考视频:江科大自化协