上节学习了PWM的驱动与控制,并认识了直流电机和舵机,这节来学习ADC数模转换器的原理与应用,工业检测系统和日常生活中有许多物理量都是模拟量,比如温度、长度、压力、速度等等,这些物理模拟量可以通过传感器变成与之对应的电压、电流等电模拟量。由于单片机系统是一个典型的数字系统,为了实现数字系统对这些电模拟量的检测、运算和控制,就需要模数转换模块ADC。
1.ADC数模-模数转换器基本原理
ADC可以将引脚上连续变化的模拟电压转换为内存中储存的数字变量,建立模拟电路到数字电路的桥梁。(DAC数字模拟转换器)
输入电压范国0-3.3V,转换结果0-4095
18个输入通道,可测量16个外部(GPIO口)和2个内部信号源(内部温度传感器,内部参考电压)
规则组(常规使用)和注入组(实发事件)两个转换单元
模拟看门狗自动监测输入电压范围(高于阈值/低于时,看门狗会触发中断)
ADC转换步骤:采样,保持,量化,编码(后两个为ADC逐渐比较的过程)
stm32ADC总转换时间T=采样时间+12.5个ADC周期
2.ADC基本结构图
触发控制分为硬件触发(定时器),和软件触发
3.ADC通道与引脚的复用关系
由ADC的内部结构可知,STM32的ADC对应16个输入通道。这16个输入通道对应的GPIO端口如下表所示
4.规则组的四种转换模式
单次转换模式中:需要手动触发转换。 ADC就会对序列中的通道进行转换,转换完成后,将转换结果储存在数据寄存器中,同时将EOC标志位置1,转换过程结束。
在连续转换模式中:一次转换完成后不会停止,而是立刻开始下一轮的转换,并持续下去。这样就可以在最开始时触发一次,之后就可以一直转换了。这个模式的好处在于:开始转换后不需要等待,不需要手动开启转换,也不用判断转换是否结束。需要读取AD值时,直接在数据寄存器中读取即可。
5.硬件电路
6.ADC+单通道实例(代码已详细注释)
接线图如下
main.c
#include "stm32f10x.h"
#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);
OLED_ShowNum(2, 9, Voltage, 1);
OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);
Delay_ms(100);
}
}
AD.c
配置过程
①开RCC时钟,包括ADC和GPIO的时钟,ADCCLK
②配置GPIO成模拟输入的模式
③配置多路开关,把左边通道接到右边的规则组
④配置ADC转换器,用结构体来配置
⑤调用ADC_cmd()函数
#include "stm32f10x.h"
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//配置ADCCLK
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);//配置GPIO口
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//通道口+55.5个ADC采样周期
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);//复位ADC校准
while (ADC_GetResetCalibrationStatus(ADC1) == SET);//获取ADC重置校准器状态
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);//开始指定ADC的校准程序
}//以上为检验过程
//启动转换,获取结果
uint16_t AD_GetValue(void)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//设置ADC转换开始的触发条件为软件触发
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//规则组是否转换完成
return ADC_GetConversionValue(ADC1);//获取转换结果
}
AD.h
#ifndef __AD_H
#define __AD_H
void AD_Init(void);
uint16_t AD_GetValue(void);
#endif
OLED和Delay可从之前章节复制学习,内有详细配置过程及其代码解析
7.ADC+多通道实例
接线图如下
代码实例
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);
}
}
AD.c 和.h同单通道