目录
1. ADC模数转换的常见应用场景
ADC模数转换在嵌入式系统中有着广泛的应用场景,下面是一些常见的应用场景:
温度监测: 在温度监测系统中,我们可以使用温度传感器将环境温度转换为模拟信号,然后通过ADC模块将模拟信号转换为数字信号,以便微控制器进行处理和显示。
光照检测: 光照传感器通常将光强度转换为模拟信号输出,通过ADC模块可以将这些模拟信号转换为数字信号,用于判断光照强度,并根据需要调整亮度或执行其他控制操作。
电压测量: 在电源管理系统中,我们可能需要测量电池电压或供电电压。通过连接电压传感器将电压转换为模拟信号,然后利用ADC模块将模拟信号转换为数字信号,以便进行电压监测和管理。
音频处理: 在音频系统中,我们可以使用麦克风或音频输入设备采集声音信号,然后通过ADC模块将模拟声音信号转换为数字信号,以便进行声音处理和分析。
2. ADC模数转换的配置
在应用中配置ADC模块是至关重要的,只有正确地配置了ADC模块,才能实现预期的功能。配置ADC模块涉及到多个方面,包括选择ADC通道、设置采样速率、选择转换模式等。下面我们将逐步介绍如何配置ADC模块。
选择ADC通道: 首先需要确定要转换的模拟信号的输入通道。根据实际连接的传感器或信号源,选择相应的ADC通道。
设置采样速率: 采样速率决定了模拟信号被转换为数字信号的频率。根据应用需求和信号特性,选择适当的采样速率。
选择转换模式: ADC模块通常支持单通道转换模式和多通道转换模式。根据应用需求选择合适的转换模式。
配置触发方式: ADC转换可以通过软件触发或外部触发方式启动。根据应用场景选择合适的触发方式。
设置参考电压: 参考电压是ADC模块进行模拟信号转换的参考基准。根据系统电源和信号范围选择合适的参考电压。
通过合理配置ADC模块,我们才可以实现对不同模拟信号的精确转换和采集。
3. 实例演示代码
通过以下函数,很方便的就能配置出所需要的ADC初始化配置
void ADC1_Ind_Init(uint8_t PIN,uint8_t Conti_flag,uint8_t Scan_flag,uint8_t count)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1的时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
if(PIN == 0) gpio_init(RCC_APB2Periph_GPIOA,GPIO_Mode_AIN,GPIOA,GPIO_Pin_0); //通道0初始化
if(PIN == 1) gpio_init(RCC_APB2Periph_GPIOA,GPIO_Mode_AIN,GPIOA,GPIO_Pin_1); //通道1初始化
if(PIN == 2) gpio_init(RCC_APB2Periph_GPIOA,GPIO_Mode_AIN,GPIOA,GPIO_Pin_2); //通道2初始化
if(PIN == 3) gpio_init(RCC_APB2Periph_GPIOA,GPIO_Mode_AIN,GPIOA,GPIO_Pin_3); //通道3初始化
if(PIN == 4) gpio_init(RCC_APB2Periph_GPIOA,GPIO_Mode_AIN,GPIOA,GPIO_Pin_4); //通道4初始化
if(PIN == 5) gpio_init(RCC_APB2Periph_GPIOA,GPIO_Mode_AIN,GPIOA,GPIO_Pin_5); //通道5初始化
if(PIN == 6) gpio_init(RCC_APB2Periph_GPIOA,GPIO_Mode_AIN,GPIOA,GPIO_Pin_6); //通道6初始化
if(PIN == 7) gpio_init(RCC_APB2Periph_GPIOA,GPIO_Mode_AIN,GPIOA,GPIO_Pin_7); //通道7初始化
if(PIN == 8) gpio_init(RCC_APB2Periph_GPIOA,GPIO_Mode_AIN,GPIOA,GPIO_Pin_8); //通道8初始化
if(PIN == 9) gpio_init(RCC_APB2Periph_GPIOA,GPIO_Mode_AIN,GPIOA,GPIO_Pin_9); //通道9初始化
ADC_InitTypeDef ADC_InitStructure; //定义结构体变量
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式,即单独使用ADC1
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //软件触发
if(Conti_flag == 0) ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //连续转换,失能,每转换一次规则组序列后停止
if(Conti_flag == 1) ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换,使能,每转换一次规则组序列后继续转换
if(Scan_flag == 0) ADC_InitStructure.ADC_ScanConvMode = DISABLE; //扫描模式失能
if(Scan_flag == 1) ADC_InitStructure.ADC_ScanConvMode = ENABLE; //扫描模式使能,转换规则组的序列x多个位置
ADC_InitStructure.ADC_NbrOfChannel = count; //通道数为count,仅在扫描模式下有效
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE); //使能ADC1
/*ADC校准*/
ADC_ResetCalibration(ADC1); //内部电路自动执行校准
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);
if(Conti_flag == 1) ADC_SoftwareStartConvCmd(ADC1, ENABLE); //ADC持续工作
}
注意:参数Conti_flag和Scan_flag分别是连续模式与扫描模式的标志位
主函数调用函数
// ADC1_Ind_Init(0|1|2|3|4|5|6|7|8|9,0,0,1); //ADC1通道0-9初始化(单次)(非扫描) (通道数1)
ADC1_Ind_Init(0,1,0,1); //ADC1通道0初始化(连续)(非扫描) (通道数1)
通过该函数即可开启ADC1的功能,参数Conti_flag和Scan_flag后续可以搭配DMA使用
ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。在校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差。建议在每次上电后执行一次校准。
ADC_ResetCalibration(ADC1); //内部电路自动执行校准
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);
接着是单次开启/获取adc值的函数
//单次转换获取ADC值函数
uint16_t ADC1_Once_GetValue(uint8_t ADC_Channel,uint8_t count)
{
uint16_t value = 0;
long long value_sum = 0;
uint8_t i = 0;
if(count == 0) count = 1;
for(i = 0; i < count;i++ )
{
ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_239Cycles5); //在转换前更改规则组的通道
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待AD转换结束
value = ADC_GetConversionValue(ADC1);
value_sum += value;
}
return (uint16_t)(value_sum/count);
}
//连续转换获取ADC值函数
uint16_t ADC1_Conti_GetValue(uint8_t count)
{
uint16_t value = 0;
long long value_sum = 0;
uint8_t i = 0;
if(count == 0) count = 1;
for(i = 0; i < count;i++ )
{
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待AD转换结束
value = ADC_GetConversionValue(ADC1);
value_sum += value;
}
return (uint16_t)(value_sum/count);
}
在连续转换获取ADC值函数中,有count的函数参数,该参数是作为均值滤波的参数的,如果count为10,即进行10次均值滤波,该函数的参数类型为uint8_t,在进行滤波时,要注意滤波次数是否超过该类型的最大值
最后我利用手上的热敏电阻与落灰的空电路板焊接了一个简单的温度传感器电路测试,电路的原理图如下
主函数关键程序如下:
ADC1_Ind_Init(0,1,0,1); //ADC1通道0初始化(连续)(非扫描) (通道数1)
AD_ntc = ADC1_Conti_GetValue(100); //连续启动ADC
//测试用(热敏电阻ntc)
temp = -0.0418 * AD_ntc + 110.7;
OLED_ShowNum(1, 8, temp, 4); //显示通道0的转换结果
delay_ms(1000); //延时1s,方便查看
实验现象
4. 总结
ADC模数转换是嵌入式系统中常用的功能模块,它可以将模拟信号转换为数字信号,以便微控制器进行处理和分析。由于我常用的adc需求比较简单通用,故将基本的配置参数固定到了函数内部,无法通过函数参数去方便的调整细节,在使用时要注意参数是否符合项目要求并进行对应的更改。通过合理配置和使用ADC模块,我们可以实现对不同模拟信号的精确采集和处理,从而满足各种应用需求。