STM32 实现ADC 库函数版
ADC 顾名思义 将模拟信号转换为数字信号
ADC转换分为2个通道组:规则通道组和注入通道组。
规则通道相当于正常运行的程序,而注入通道,就是中断。
程序正常运行(执行规则通道),外部中断产生,执行中断代码(执行注入通道)
STM32的ADC的规则通道组包含16个转换,而注入通道包含4个通道内
数据存储方式:左对齐 右对齐(存储在16位数据寄存器中 左高 右低)
不要将高于3.3V的接到ADC上面
通道16 只在ADC1 (内部温度传感器)
通道17 只在ADC1 (内部参考电压VREF)
外设挂接在APB2
ADC时钟 最大14M 通过分频因子改变时钟 一般分频设置为6 (72 / 6 = 12)
ADC总转换时间公式
Tconv = 采样时间 + 12.5个周期
ADC 转换模式 : 单次转换 和 连续转换
单次转换 ADC执行一次 转换,可以通过外部触发执行转换
连续转换 ADC执行的一次 后准备下一次转换
配置步骤
(1)使能端口时钟和ADC时钟,设置引脚模式为模拟输入
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AN; //模拟输入模式
(2)设置ADC的分频因子
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
(3)初始化ADC参数,包括ADC工作模式、规则序列等
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
//
typedef struct
{
uint32_t ADC_Mode; // ADC 工作模式选择
FunctionalState ADC_ScanConvMode; / ADC 扫描(多通道)或者单次(单通道)模式选择 */
FunctionalState ADC_ContinuousConvMode; // ADC 单次转换或者连续转换选择
uint32_t ADC_ExternalTrigConv; // ADC 转换触发信号选择
uint32_t ADC_DataAlign; // ADC 数据寄存器对齐格式
uint8_t ADC_NbrOfChannel; // ADC 采集通道数
} ADC_InitTypeDef;
/***/ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
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_InitStructure.ADC_NbrOfChannel = 1;//1个转换在规则序列中 也就是只转换规则序列1
ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化
/********************************************************************************/
(4)使能ADC并校准
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
ADC_Cmd(ADC1, ENABLE);//开启AD转换器
执行复位校准的方法是:
ADC_ResetCalibration(ADC1);
执行 ADC 校准的方法是:
ADC_StartCalibration(ADC1); //开始指定 ADC1 的校准状态
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
(5)读取ADC转换值
设置规则序列通道以及采样周期的库函数是:
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t
ADC_Channel,uint8_t Rank, uint8_t ADC_SampleTime);ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5 );
设置好规则序列通道及采样周期,接下来就要开启转换,由于我们采
用的是软件触发,库函数
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
开启转换之后,就可以获取ADC 转换结果数据,调用的库函数是:
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
获取 AD 转换的状态信息的库函数是:
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t
ADC_FLAG);
例如我们要判断 ADC1 的转换是否结束,方法是:
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
实现代码如下
#include "Adc.h"
void ADCx_Init(void)
{
GPIO_InitTypeDef gpioA;
ADC_InitTypeDef adc;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);
//设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
gpioA.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
gpioA.GPIO_Pin = GPIO_Pin_1;
gpioA.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&gpioA);
adc.ADC_Mode = ADC_Mode_Independent; //独立
adc.ADC_ScanConvMode = DISABLE; //非扫描式
adc.ADC_ContinuousConvMode = DISABLE; //关闭连续转换
adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //禁止触发检测,使用软件触发
adc.ADC_DataAlign = ADC_DataAlign_Right; //右对齐
adc.ADC_NbrOfChannel = 1; //1个转换在规则序列中 也就是只转换规则序列1
ADC_Init(ADC1,&adc);
ADC_Cmd(ADC1,ENABLE); //开启AD转换
ADC_ResetCalibration(ADC1); //重置指定的ADC的校准寄存器
//获取ADC重置校准寄存器的状态
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1); //开始指定ADC的校准状态
while(ADC_GetCalibrationStatus(ADC1)); //获取指定ADC的校准程序
ADC_SoftwareStartConvCmd(ADC1,ENABLE); //使能
}
u16 Get_ADC_Value(u8 ch,u8 times)
{
u32 temp_val = 0;
u8 i;
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC1,ch,1,ADC_SampleTime_239Cycles5);
for(i = 0; i < times; i++)
{
ADC_SoftwareStartConvCmd(ADC1,ENABLE); //使能指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)); //等待转换结束
temp_val += ADC_GetConversionValue(ADC1);
delay_ms(5);
}
return temp_val / times;
}
主函数实现如下
#include "stm32f10x.h"
#include "Uart.h"
#include "Adc.h"
#include "led.h"
int main()
{
u16 indata = 0;
u8 i;
float vol;
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
UART1_Init(9600);
printf ("捕获开始\r\n");
ADCx_Init();
LED_Init();
led1 = 0;
while(1)
{
i++;
if(i % 20 == 0)
{
led1 = !led1;
}
if(i % 50 == 0)
{
indata = Get_ADC_Value(ADC_Channel_1,20);
printf("检测AD值为: %d\r\n",indata);
vol = (float) indata * (3.3 / 4096);
printf("检测电压值为: %.2fV\r\n",vol);
}
delay_ms(500);
}
}