在自然界中,大部分物理量都是模拟性质的,如速度、压力、温度、声音、重量等。通过传感器可以将这些模拟量转换成模拟电压或电流信号,这些模拟信号在数值上是连续的,而且具有无穷多的数值,其数学表达也非常复杂;数字信号则相对简单得多,它的幅度和取值不连续,只有表示“1”的高电平和表示“0”的低电平两种。在将模拟量转化为数字量的过程中,一般需要经过采样、量化和编码三个步骤。由于模拟信号在时间上是连续的,而A/D转换的过程是需要时间的,所以不可能把模拟信号的每一个瞬时值都转换为数字量,只能在连续变化的模拟量上按一定的时间规律取出与之对应的瞬时值,并对该瞬时值做一个量化标定,将标定的结果以数字的形式输出,从而实现从模拟量到数字量的转换。
11.1.1
逐次逼近型A/D转换器
A/D转换器有很多种类型,逐次逼近型A/D转换器是其中应用最广泛的一种。逐次逼近型A/D转换器的工作原理类似于用天平称量物品的重量,先将重物放在天平的一边,在天平的另一边放最大量程一半重量的砝码,比较天平两端的平衡情况,适当加减砝码,并多次地比较天平的平衡情况,直至平衡为止。逐次逼近型A/D转换器的内部结构如图11-1所示。
从图11-1中我们可以发现,逐次逼近型A/D转换器的内部实际上是由一个D/A转换器和一个比较器构成的。模拟信号从Vin端输入,参考电压从Vref端输入。转换过程初始时,逐次逼近寄存器SAR的值清零,转换开始后SAR将最高位置1,SAR中的数字量经过D/A转换后生成试探反馈电压Vf,这个电压随即送入比较器中与模拟输入电压Vin进行比较。如果Vin>Vf,则所置的1被保留,否则最高位的1会被舍掉,再将次高位置1,并重新经D/A转换后得到新的Vf再与Vin进行比较。此试探性比较重复进行,直至定出所有数据位的取舍,最后将得到的所有数据位作为转换结果输出出来。值得注意的是,逐次逼近型A/D转换器的转换时间是固定的,具体取决于A/D转换器的位数和转换时钟周期。
11.1.2
ADC模块的内部结构
ATmega32单片机片内配有10位精度的逐次逼近型ADC模块,该模块按需要可以设置成8路单端输入或7路差分输入,其中有2路为可选增益10×和200×的差分输入通道。ADC模块的结构如图11-2所示。
从图11-2中我们可以看出,与大多数逐次逼近型A/D转换器相同,ATmega32单片机片内的ADC模块核心部分也是由一个10位的D/A转换器和一个比较器构成。这两个单元电路受转换逻辑控制电路的控制,对输入的模拟电压进行多次的试探性比较,并将转换结果放置到ADC数据寄存器ADCH和ADCL中。ADC控制和状态寄存器ADCSRA用于对A/D转换器进行设置或指示A/D转换器的工作状态。当A/D转换完成后,会置位ADIF中断标志位,如果该中断得到允许,单片机会跳转到中断向量处并执行相应的中断服务程序。
ADC多工选择寄存器ADMUX用于对A/D转换器的多个模拟通道进行选择并设定其工作方式。在单端输入时,输入信号来自于PORTA端口的8个输入端(ADC7-ADC0),A/D转换电路可以对这8路输入电压分别进行采样;另外,ADC模块还支持差分电压输入,在此模式下,差分输入的负极选择是可编程的,可以由ADC2、ADC1或ADC0引脚担任,而正极则可以由余下的引脚担任。当差分输入的正负极由ADC1和ADC0,或者是ADC3与ADC2担任时,此差分输入具有可编程的增益控制级,可以在A/D转换前给差分输入电压提供0
dB(1x)、20
dB(10x)或46
dB(200x)的放大功能。
11.1.3
ADC模块的基准电压
ADC模块由AVCC引脚单独提供电源,为了更好地抑制噪声,通常在AVCC引脚上加一个LC网络对供电电源进行退耦。通过设置ADCSRA寄存器的ADEN位可以打开ADC模块的电源并且启动A/D转换器。ADEN位清零时ADC模块并不耗电,因此在单片机进入睡眠模式时可以将该位清零关闭ADC模块以降低功耗。
为了对外部模拟电压进行量化,A/D转换器必须要有一个确定的电压作为基准,这个电压称为参考电压。AREF引脚是ATmega32单片机的参考电压输入引脚,将一个固定的电压连接至AREF引脚可以作为ADC模块的参考电压。当AREF引脚没有连接任何外部参考电压源时,可以通过配置ADMUX寄存器的REFS1:REFS0位将AVCC或内部参考电压模块产生的2.56V电压接入AREF引脚,作为ADC模块的参考电压。为了抑制噪声,在AREF引脚上可以外接一个电容来进行退耦。另外,当使用片内参考电压时,参考电压可以从AREF引脚输出。
注意:只有当ADEN位置1时,参考电压及输入通道选择才会生效。另外,如果将一个固定电源接到了AREF引脚,那么就无须通过编程ADMUX寄存器的REFS1:REFS0位选择内部基准电压了,否则会导致片内基准源与外部参考电压源的短路。
11.1.4
ADC模块的转换结果
在单端输入状态下,10位的A/D转换器可以将V 在上面的公式中,ADC代表转换完成后的数字量,V 在上面的公式中,ADC代表转换完成后的数字量,V 当读取转换结果时,要先读取ADCL寄存器,再读取ADCH寄存器,这样可以保证读取到的数据内容是同一次转换的结果。当要求不是很高时,也可以使用8位的转换精度。在这种情况下,先将转换结果设定为左对齐方式,之后只需读取ADCH寄存器就可以了,ADCL寄存器的值可以忽略不计。
11.1.5
模拟通道的等效电路
单端模拟输入通道的等效电路如图11-4所示。从图中可以看出,ADC模块包含了一个采样保持电路,用以确保在转换过程中输入到ADC模块中的电压始终保持恒定。当ADC模块对来自于引脚ADCn的模拟信号进行采样时,开关K闭合,外部模拟电压经输入通道的组合电阻R后给采样保持电容C 如果外部模拟信号的输出阻抗不大于10
kΩ,采样保持电容C 11.1.6
A/D转换的时钟
在通常情况下,ATmega32单片机完成一次A/D转换需要13个ADC时钟周期。但在首次转换时,为了初始化模拟电路,需要25个ADC时钟周期才能完成一次A/D转换。A/D转换的时序如图11-5所示。
在图11-5中,单次的A/D转换启动后,多路选择开关和参考电压的准备需要1.5个ADC时钟周期。之后,采样保持电路开始对外部的模拟电压进行采样,并完成整个转换过程。转换结束后,ADC转换结果被送入ADC数据寄存器,ADIF标志位置位且ADSC位同时清零。
ADC模块的工作时钟由系统时钟通过专用的预分频器分频后,驱动ADC模块工作。ADC模块的预分频器结构如图11-6所示。
在图11-6中,系统时钟经7位的预分频器后,对时钟进行2至128级的预分频。ADCSRA寄存器的ADPS2:ADPS0位用于设定预分频器的分频比。为了保证A/D转换的精度,ADC模块的逐次逼近电路时钟频率需要控制在50
kHz至200
kHz之间。如果输入时钟的频率高于200
kHz,ADC模块的转换速度可以相应提高,但这会降低A/D转换的精度。ADC模块的时钟频率计算方法十分简单,例如,当系统时钟频率为16MHz,预分频比为128时,此时ADC模块的工作频率为125kHz,具体计算公式如下:
ADC模块时钟频率=(16×10
11.2
ADC模块的控制
11.2.1
ADC模块的工作方式
1.单次转换
将ADC启动转换控制位ADSC置1即可启动单次的A/D转换,在转换进行中此位始终保持为1,直到转换结束后ADSC位硬件清零,ADC转换结果被送入ADC数据寄存器中,ADIF标志位置位。如需再次进行A/D转换,可以软件将ADSC位再次置1来启动下一次转换。另外,如果在转换过程中选择了另外一个通道,那么ADC模块会在改变通道前完成这一次转换。
2.自动触发转换
ADC模块可以选择不同的触发源,用于自动触发A/D转换。将ADCSRA寄存器的ADATE位置1可以使能自动触发。这时特殊功能IO寄存器ADTS2:ADTS0位用于设定ADC模块的自动触发源,当所选的触发源产生上升沿触发信号时,ADC模块启动并开始A/D转换。使用自动触发转换需要注意以下几个方面:
1)当ADC模块被设置为连续转换模式时,A/D转换的二次启动将不依赖ADIF标志位的状态,而是转由系统自动控制,单次转换结束后,自动开始下一次转换。
2)转换过程中出现的触发信号会被忽略。当转换结束后,即使原触发信号仍然存在,也不会启动另一次新的转换。
3)无论A/D转换完成中断或全局中断是否被禁止,一旦A/D转换完成,中断标志位ADIF仍然会置1。这一特性可以在不产生中断的情况下触发一次转换,如果想再次触发A/D转换,需要软件清零此中断标志位。
11.2.2
ADC模块的控制寄存器
1.ADMUX寄存器
该寄存器是ADC模块的多工选择寄存器,包含了参考电压设定、转换结果存放方式以及输入通道选择等多个控制位。
ADMUX寄存器:ADC多工选择寄存器
bit 7:6REFS1:REFS0:ADC模块参考电压选择位,具体设置详见表11-1。
bit 5ADLAR:ADC转换结果存放方式控制位。该位置1时转换结果左对齐,清零时转换结果右对齐。
bit 4:0MUX4:MUX0:模拟通道与增益选择位,具体设置详见表11-2。
2.ADCSRA寄存器
该寄存器是ADC模块的控制和状态寄存器,包含了ADC模块的多个控制位及状态位。
ADCSRA寄存器:ADC控制和状态寄存器
bit 7ADEN:ADC模块使能位。该位置1时启动ADC模块,清零时关闭模块。
bit 6ADSC:ADC模块转换开始控制位。该位置1时将启动一次ADC转换,在转换进行过程中,ADSC位始终为1,直至转换完成后该位自动清零。
bit 5ADATE:ADC自动触发使能位。该位置1时将启动ADC模块的自动触发功能,触发信号源由SFIOR寄存器的ADC触发信号源选择位ADTS设置。
bit 4ADIF:ADC中断标志位。当ADC转换结束后,ADIF位自动置1,如果该中断得到允许,单片机将跳转到中断向量处并执行相应的中断服务程序,之后ADIF位自动清零。如果没有执行中断服务程序,A/D转换结束后需要向ADIF位软件写1来清零该位。
bit 3ADIE:ADC中断使能位。该位置1时ADC中断被使能,清零时ADC中断禁止。
bit 2:0ADPS2:ADPS0:ADC模块预分频选择位,具体设置详见表11-3。
3.ADCL及ADCH寄存器
该寄存器是ADC模块的数据寄存器,用于保存A/D的转换结果。
ADCL寄存器:ADC数据寄存器(低8位)
ADCH寄存器:ADC数据寄存器(高8位)
4.SFIOR寄存器
该寄存器是特殊功能IO寄存器,包含了多个A/D转换、模拟比较器等功能控制位。
SFIOR寄存器:特殊功能IO寄存器
bit 7-5:ADC自动触发源选择位,具体设置详见表11-4。
bit 4-0:暂不介绍。
11.3
ADC模块的编程应用
为了验证ADC模块的功能,我们设计了一个量程在0~5V的数字电压表,其精度可以达到0.0048V。在硬件连接上,需要对AVR系统板做一下驱动方式的变更,使用杜邦线将数码管的段驱动端由PORTA端口转移到PORTD端口,并且将ADC0引脚连接到系统板电位器输出端J2上。打开Atmel Studio 6.1软件,新建名为“ADC1”的项目,保存在chapter11文件夹下,软件自动添加ADC1.c源文件到新建的项目中。编辑ADC1.c源文件,具体代码详见代码清单11-1。
代码清单11-1
0~5V数字电压表
/*
*
ADC1.c *
0
~5V 数字电压表
*
Created: 2013/9/22
20:52:11
*
Author: GAO */
/*
PORTD 口驱动数码管段, PORTB 口高四位驱动位
ADC0
通道连接电位器输出端J2
*/
#include
//
包含AVR 头文件
#define F_CPU 16000000UL //
定义系统时钟
#include
//
包含延时函数头文件
unsigned char table0[]={0x3f,0x06,0x5b,0x4f,0x66, 0x6d,0x7d,0x07,0x7f,0x6f }; //
共阴无点
unsigned char table1[]={0xbf,0x86,0xdb,0xcf,0xe6, 0xed,0xfd,0x87,0xff,0xef }; //
共阴有点
unsigned int Z; //
定义显示变量
unsigned char AL,AH; //
定义转换结果变量
void display(unsigned int NUM); //
显示函数声明
void adc_init(void); //ADC 模块初始化函数声明
void adc_convert(void); //AD 转换函数声明
/**********
主函数**********/
int main(void)
{
DDRA=0x00; //PA 口为输入
DDRD=0xFF; //
设数码管段驱动端为输出
DDRB=0xF0; //
设数码管位驱动端为输出
adc_init(); //ADC 模块初始化
while(1)
{
adc_convert(); //
启动AD 转换
display(Z); //
显示转换结果
}
}
/**********
数码管显示函数**********/
void display(unsigned int NUM)
{
unsigned char NUM4,NUM3,NUM2,NUM1; NUM1=NUM%10; NUM2=NUM%100/10; NUM3=NUM%1000/100; NUM4=NUM/1000; PORTD=table0[NUM1]; PORTB=0x10; _delay_ms(2); //
延时2ms PORTD=0x00; PORTB=0x00; _delay_ms(1); //
延时1ms PORTD=table0[NUM2]; PORTB=0x20; _delay_ms(2); //
延时2ms PORTD=0x00; PORTB=0x00; _delay_ms(1); //
延时1ms PORTD=table0[NUM3]; PORTB=0x40; _delay_ms(2); //
延时2ms PORTD=0x00; PORTB=0x00; _delay_ms(1); //
延时1ms PORTD=table1[NUM4]; PORTB=0x80; _delay_ms(2); //
延时2ms PORTD=0x00; PORTB=0x00; _delay_ms(1); //
延时1ms }
/**********ACD 模块初始化函数**********/
void adc_init(void)
{
ADMUX=0x40; //
结果右对齐,AVCC 为基准电压,通道0
ADCSRA=0xA7; //128
预分频,禁止中断,使能自动转换
SFIOR=0x00; //
连续转换模式
_delay_ms(1); //
延时1ms ADCSRA=ADCSRA|0x40; //
开启一次AD 转换
}
/**********AD 转换函数**********/
void adc_convert(void)
{
float CVAL; //
定义浮点型变量
unsigned int TEMP; TEMP=0; AL=ADCL; //
读取转换结果
AH=ADCH; //
读取转换结果
TEMP=TEMP|AH; TEMP=TEMP<<8; TEMP=TEMP|AL; CVAL=TEMP; //
转换结果为0-1023
CVAL=CVAL*5/1023*1000; //CVAL 为1
时,表示的电压是5/1023V ,将值扩大1000
倍用于显示
Z=CVAL; //
浮点数赋给整型数则取整数部分
}
/**********
结束**********/
以上代码经编译后,下载到AVR系统板中,程序运行后效果如图11-7所示。图中数码管显示的是当前ADC0通道上的模拟电压值。使用改锥旋动电位器W1,数码管的值会有变化,该变化显示了模拟电压值的变化。如果我们将以上电路和程序稍做改动,就可以将其应用于对多种模拟量和传感器的检测中,相信这个例子一定会起到抛砖引玉的作用,帮助你开启模数转换的应用之门。