AVR单片机模数转换的ADC实验 ATmega16

6.2 模数转换的ADC实验 
6.2.1、实例功能 

AVR的模数转换器ADC具有下列特点:

  1. 10位精度;
  2. 0.5LSB积分非线形误差
  3. ±2LSB的绝对精度;
  4. 13µs~260µs的转换时间;
  5. 在最大精度下可达到每秒15kSPS的采样速率;
  6. 8路可选的单端输入通道;
  7. 7路差分输入通道;
  8. 2路差分输入通道带有可选的10×和200×增益;
  9. ADC转换结果的读取可设置为左端对齐(LEFT ADJUSTMENT);
  10. ADC的电压输入范围0~Vcc;
  11. 可选择的内部2.56V的ADC参考电压源;
  12. 自由连续转换模式和单次转换模式;
  13. ADC自动转换触发模式选择;
  14. ADC转换完成中断;
  15. 休眠模式下的噪声抑制器(NOISE CANCELER)。

在本实例中,我们将编写程序实现将模数转换后获得的电压值通过单片机的串口发送到计算机,然后通过计算机上的串口助手显示测量的电压值。 
本实例共有3个功能模块,分别描述如下: 
● 单片机系统:使用单片机的串口实现将模数转换后获得的电压值通过串口发送到计算机。 
● 外围电路:RS232电平转换电路,DB9串行接口插座,模拟电压输入采集电路。
● 软件程序:进一步熟悉单片机的串行通信,并掌握单片机的模数转换的方法。
6.2.2、器件和原理 
关于串行接口的原理已接单片机与计算机的串口的连接在上一实例中进行了描述,在本实例中不再重复。
本实例只介绍ATmega16单片机如何通过内置的模数转换模块采集外界输入的模拟电压。
1、ATmega16单片机的模数转换器ADC介绍 
由于单片机只能处理数字信号,所以外部的模拟信号量需要转变成数字量才能进一步的由单片机进行处理。ATmega16内部集成有一个10位逐次比较(successive approximation)ADC电路。因此使用AVR可以非常方便的处理输入的模拟信号量。 
ATmega16的ADC与一个8通道的模拟多路选择器连接,能够对以PORTA作为ADC输入引脚的8路单端模拟输入电压进行采样,单端电压输入以0V(GND)为参考。另外还支持16种差分电压输入组合,其中2种差分输入方式(ADC1,ADC0和ACD3,ADC2)带有可编程增益放大器,能在A/D转换前对差分输入电压进行0dB(1×),20dB(10×)或46dB(200×)的放大。还有七种差分输入方式的模拟输入通道共用一个负极(ADC1),此时其它任意一个ADC引脚都可作为相应的正极。若增益为1×或10×,则可获得8位的精度。如果增益为200×,那么转换精度为7位。 
AVR的ADC功能单元由独立的专用模拟电源引脚AVcc供电。AVcc和Vcc的电压差别不能大于±0.3V。ADC转换的参考电源可采用芯片内部的2.56V参考电源,或采用AVcc,也可使用外部参考电源。使用外部参考电源时,外部参考电源由引脚ARFE接入。使用内部电压参考源时,可以通过在AREF引脚外部并接一个电容来提高ADC的抗噪性能。
ADC功能单元包括采样保持电路,以确保输入电压在ADC转换过程中保持恒定。ADC通过逐次比较(successive approximation)方式,将输入端的模拟电压转换成10位的数字量。最小值代表地,最大值为AREF引脚上的电压值减1个LSB。可以通过ADMUX寄存器中REFSn位的设置,选择将芯片内部参考电源(2.56V)或AVcc连接到AREF,作为A/D转换的参考电压。这时,内部电压参考源可以通过外接于AREF引脚的电容来稳定,以改进抗噪特性。 
模拟输入通道和差分增益的选择是通过ADMUX寄存器中的MUX位设定的。任何一个ADC的输入引脚,包括地(GND)以及内部的恒定能隙(fixed bandgap)电压参考源,都可以被选择用来作为ADC的单端输入信号。而ADC的某些输入引脚则可选择作为差分增益放大器的正、负极输入端。当选定了差分输入通道后,差分增益放大器将两输入通道上的电压差按选定增益系数放大,然后输入到ADC中。若选定使用单端输入通道,则增益放大器无效。 
通过设置ADCSRA寄存器中的ADC使能位ADEN来使能ADC。在ADEN没有置“1”前,参考电压源和输入通道的选定将不起作用。当ADEN位清“0”后,ADC将不消耗能量,因此建议在进入节电休眠模式前将ADC关掉。 
ADC将10位的转换结果放在ADC数据寄存器中(ADCH和ADCL)。默认情况下,转换结果为右端对齐(RIGHT ADJUSTED)的。但可以通过设置ADMUX寄存器中ADLAR位,调整为左端对齐(LEFT ADJUSTED)。如果转换结果是左端对齐,并且只需要8位的精度,那么只需读取ADCH寄存器的数据作为转换结果就达到要求了。否则,必须先读取ADCL寄存器,然后再读取ADCH寄存器,以保证数据寄存器中的内容是同一次转换的结果。因为一旦ADCL寄存器被读取,就阻断了ADC对ADC数据寄存器的操作。这就意味着,一旦指令读取了ADCL,那么必须紧接着读取一次ADCH;如果在读取ADCL和读取ADCH的过程中正好有一次ADC转换完成,ADC的2个数据寄存器的内容是不会被更新的,该次转换的结果将丢失。只有当ADCH寄存器被读取后,ADC才可以继续对ADCL和ADCH寄存器操作更新。 
ADC有自己的中断,当转换完成时中断将被触发。尽管在顺序读取ADCL和ADCH寄存器过程中,ADC对ADC数据寄存器的更新被禁止,转换的结果丢失,但仍会触发ADC中断。 
2、ATmwga16单片机的模数转换器ADC相关的I/O寄存器 
1.ADC多路复用器选择寄存器—ADMUX

  1. 位7,6—REFS[1:0]:ADC参考电源选择

REFS1、REFS2用于选择ADC的参考电压源,见表6.2.1。如果这些位在ADC转换过程中被改变,新的选择将在该次ADC转换完成后(ADCSRA中的ADIF被置位)才生效。一旦选择内部参考源(AVcc、2.56V)为ADC的参考电压后,AREF引脚上不得施加外部的参考电源,只能与GND之间并接抗干扰电容。


表6.2.1  ADC参考电源选择

REFS1

REFS0

ADC参考电源

0

0

外部引脚AREF,断开内部参考源连接

0

1

AVcc,AREF外部并接电容

1

0

保留

1

1

内部2.56V,AREF外部并接电容

  1. 位5—ADLAR:ADC结果左对齐选择

ADLAR位决定转换结果在ADC数据寄存器中的存放形式。写“1”到ADLAR位,将使转换结果左对齐(LEFT ADJUST);否则,转换结果为右对齐(RIGHT ADJUST)。无论ADC是否正在进行转换,改变ADLAR位都将会立即影响ADC数据寄存器。

  1. 位4..0—MUX4:0:模拟通道和增益选择

这5个位用于对连接到ADC的输入通道和差分通道的增益进行选择设置,详见表6.2.2。注意,只有转换结束后(ADCSRA的ADIF是“1”),改变这些位才会有效。


表6.2.2   ADC输入通道和增益选择

MUX[4:0]

单端输入

差分正极输入

差分负极输入

增益

00000

ADC0

N/A

00001

ADC1

00010

ADC2

00011

ADC3

00100

ADC4

00101

ADC5

00110

ADC6

00111

ADC7

01000

N/A

ADC0

ADC0

10×

01001

ADC1

ADC0

10×

01010

ADC0

ADC0

200×

01011

ADC1

ADC0

200×

01100

ADC2

ADC2

10×

01101

ADC3

ADC2

10×

01110

ADC2

ADC2

200×

01111

ADC3

ADC2

200×

10000

ADC0

ADC1

10001

ADC1

ADC1

10010

ADC2

ADC1

10011

ADC3

ADC1

10100

ADC4

ADC1

10101

ADC5

ADC1

10110

ADC6

ADC1

10111

ADC7

ADC1

11000

ADC0

ADC2

11001

ADC1

ADC2

11010

ADC2

ADC2

11011

ADC3

ADC2

11100

ADC4

ADC2

11101

ADC5

ADC2

11110

1.22V(VBG)

N/A

11111

0V(GND)

   本实例中我们需要设置ADC的参考电压源为AVcc,即REFS0设置为1,ADC默认转换结果为右对齐,我们不需要改变,模拟通道选择ADC0通道单端输入,即MUX4:0。 
2.ADC控制和状态寄存器A—ADCSRA

  1. 位7—ADEN:ADC使能

该位写入“1”时使能ADC,写入“0”关闭ADC。如在ADC转换过程中将ADC关闭,该次转换随即停止。

  1. 位6—ADSC:ADC转换开始

在单次转换模式下,置该位为“1”,将启动一次转换。在自由连续转换模式下,该位写入“1”将启动第一次转换。先置位ADEN位使能ADC,再置位ADSC;或置位ADSC的同时使能ADC,都会使能ADC开始进行第一次转换。第一次ADC转换将需要25个ADC时钟周期,而不是常规转换的13个ADC时钟周期,这是因为第一次转换需要完成对ADC的初始化。 
在ADC转换的过程中,ADSC将始终读出为“1”。当转换完成时,它将转变为“0”。强制写入“0”是无效的。

  1. 位5—ADATE:ADC自动转换触发允许

当该位被置为“1”时,允许ADC工作在自动转换触发工作模式下。在该模式下,在触发信号的上升沿时ADC将自动开始一次ADC转换过程。ADC的自动转换触发信号源由SFIOR寄存器中的ADTS位选择确定。

  1. 位4—ADIF:ADC中断标志位

当ADC转换完成并且ADC数据寄存器被更新后该位被置位。如果ADIE位(ADC转换结束中断允许)和SREG寄存器中的I位被置“1”,ADC中断服务程序将被执行。ADIF在执行相应的中断处理向量时被硬件自动清零。此外,ADIF位可以通过写入逻辑“1”来清零。

  1. 位3—ADIE:ADC中断允许

当该位和SREG寄存器中的I位同时被置位时,允许ADC转换完成中断。

  1. 位2,0—ADPS[2:0]:ADC预分频选择

这些位决定了XTAL时钟与输入到ADC的ADC时钟之间分频数,见表6.2.3。

表6.2.3   ADC时钟分频

ADPS[2:0]

分  频  系  数

000

2

001

2

010

4

011

8

100

16

101

32

110

64

111

128

本实例中我们需要使能ADC,即ADEN设置为1,我们不用自动转换,也不需要中断,所以,ADTE、ADIE位不需要设置。在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。本例中使用的时钟是12M的,所以要将时钟64分频,分频后ADC频率为188KHz,即时钟分频选择ADPS[2:0]=6。

3.ADC数据寄存器—ADCL和ADCH

  1. ADLAR = 0,ADC转换结果右对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第1张图片

  1. ADLAR = 1,ADC转换结果左对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第2张图片
当ADC转换完成后,可以读取ADC寄存器的ADC0-ADC9得到ADC的转换的结果。如果是差分输入,转换值为二进制的补码形式。一旦开始读取ADCL后,ADC数据寄存器就不能被ADC更新,直到ADCH寄存器被读取为止。因此,如果结果是左对齐(ADLAR=1),且不需要大于8位的精度的话,仅仅读取ADCH寄存器就足够了。否则,必须先读取ADCL寄存器,再读取ADCH寄存器。ADMUX寄存器中的ADLAR位决定了从ADC数据寄存器中读取结果的格式。如果ADLAR位为“1”,结果将是左对齐;如果ADLAR位为“0”(默认情况),结果将是右对齐。 
4.特殊功能I/O寄存器—SFIOR

  1. 位7..5—ADTS[2:0]:ADC自动转换触发源选择

当ADCSRA寄存器中的ADATE为“1”,允许ADC工作在自动转换触发工作模式时,这3位的设置用于选择ADC的自动转换触发源。如果禁止了ADC的自动转换触发(ADATE为“0”),这3个位的设置值将不起任何作用。

表10-6   ADC自动转换触发源的选择

ADTS[2:0]

触  发  源

000

连续自由转换

001

模拟比较器

010

外部中断0

011

T/C0比较匹配

100

T/C0溢出

101

T/C1比较匹配B

110

T/C1溢出

111

T/C1输入捕捉

本例中我们不使用自动转换功能,所以该寄存器可以不必设置。
6.2.3、电路 
本实例的电路包括232电平转换电路和电阻分压电路,这两种电路在前面的实例中均做过介绍,这里不再重复。
1、电路原理
在本实例中利用MAX3232芯片使单片机输出的TTL电平转换为标准的RS232电平,从而使计算机能够识别。同时将计算机输出的RS232电平转换为单片机可以识别的TTL电平。
利用电位器产生电阻分压电路,从而产生变化的模拟电压加到单片机的模拟信号采集端口,供单片机采集。
2、电路连接 
电路中MAX3232芯片的9、10引脚分别连接单片机的PD0、PD1端口,MAX3232的13、14引脚分别连接计算机串口线的3、2脚。
电位器RP2的动片引脚连接单片机的模拟信号采集通道PA0(ADC0)。
3、特别说明
本学习板采用的是串口插座是公头的,所以与计算机相连的串口连接线应该是交叉串口线,而不是串口延长线。
6.2.4、程序设计 
1、程序功能 

程序的功能是通过单片机的串行接口,将单片机采集到的模拟电压值发送到计算机中,通过计算机上的串口助手显示采集的电压值。    
● 单片机串行接收中断的编程
在本例中,我们用到了单片机的串行接收中断,需要编写串行接收中断服务程序,通过查询WINAVR(GCC)的中断库函数手册,可以查找到ATmega16单片机串行接收中断的中断向量为USART_RXC_vect。据此我们可以编写串行接收中断服务程序,如下:
//接收中断函数 
ISR(USART_RXC_vect )
{
unsigned char Rev;
Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 
Usart_PutChar(Rev);    //将接收到的数据发送 
}
在中断服务程序中,我们首先把单片机串口接收到的数据放入变量Rev中,然后调用上一实例中编写的串行接口字节发送函数将变量Rev中的数据发送到计算机。 
2、 单片机与计算机串行通信结果的观察 
在观察本例运行结果时,我们同样要用到串口助手,本例中,单片机发送串口数据采用的波特率是9600bps,数据格式是8位数据位,1位停止位,无奇偶校验。在串口助手里面,我们也要将波特率和数据格式设置成一样的。 
3、函数说明 
本实例用到了6个函数,分别是: 
void Port_Init(void);   //端口初始化配置 
void Usart_Init(void);  //USART寄存器设置 
void AD_Init(void);    //AD初始化 
void Usart_PutChar(unsigned char cTXData);  //字节发送函数 
void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 
unsigned int AD_GetData(void);    //AD转换函数   
4、使用WINAVR开发环境,在本例中我们使用的是外部12M的晶振,所以需要将MAKEFILE文件中的时钟频率修改为12M。另外在程序烧录到单片机的时候,熔丝位也要选择为外部12M晶振(注意是晶振,不是外部振荡器,一定不要选择错了,否则会导致单片机不能再烧写程序)。
5、程序代码

#include         #include #include   //中断函数头文件

//常量声明 #define BAUD 9600         //波特率设置值

//全局变量声明 unsigned int ADData;          //AD转换获得的数据

//函数声明 void Port_Init(void);   //端口初始化配置 void Usart_Init(void);  //USART寄存器设置 void AD_Init(void);    //AD初始化 void Usart_PutChar(unsigned char cTXData);  //字节发送函数 void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 unsigned int AD_GetData(void);    //AD转换函数

int main(void)             { unsigned char Delay3s;

      Port_Init(); Usart_Init(); AD_Init(); Usart_PutString("AD转换测试程序"); Usart_PutString("测得ADC0通道的电压值为:"); sei();          //使能全局中断   while(1) { ADData = (int)((long)AD_GetData() * 5010 / 1024);         //将获得的AD值转换为电压值 //单位为mv。 Usart_PutChar(ADData / 1000 + 0x30);      //得到电压值的千位并发送 Usart_PutChar('.');                          //发送小数点 Usart_PutChar(ADData % 1000 / 100 + 0x30);   //得到电压值的百位并发送 Usart_PutChar(ADData % 100 / 10 + 0x30);    //得到电压值的十位并发送 Usart_PutChar(ADData % 10 + 0x30);     //得到电压值的个位并发送 Usart_PutChar('V');                  //发送电压符号“V” Usart_PutChar(0x0d);     // Usart_PutChar(0x0a);     //  AD值发送结束,回车换行 for(Delay3s = 0;Delay3s < 30;Delay3s++)     //延时3S { _delay_ms(90); } } }

//端口状态初始化设置函数 void Port_Init() { PORTA = 0X00;         DDRA = 0x00;   //ADC通道设置为输入口,高阻态     }

//USART寄存器配置函数 void Usart_Init() { UCSRA = 0X00;     UCSRC |= (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0);  //异步,数据格式8,N,1 //UCSRC寄存器与UBRRH寄存器共用相同的I/O地址,写 UCSRC 时, URSEL 应设置为 1。 UBRRL = (F_CPU / BAUD / 16 - 1) % 256;    //波特率设置 UBRRH = (F_CPU / BAUD / 16 - 1) / 256;         UCSRB |= (1 << RXCIE) | (1 << RXEN) | (1 << TXEN);    //发送使能 }

//字节发送函数 void Usart_PutChar(unsigned char cTXData) { while( !(UCSRA & (1 << UDRE)) );  //只有数据寄存器为空时才能发送数据 UDR = cTXData;                   //发送数据送USART I/O数据寄存器-UDR }

//接收中断函数 ISR(USART_RXC_vect ) { unsigned char Rev; Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 Usart_PutChar(Rev);    //将接收到的数据发送 }

void Usart_PutString(unsigned char *pcString) { while (*pcString) { Usart_PutChar(*pcString++);   } Usart_PutChar(0x0D); Usart_PutChar(0x0A);  //结尾发送回车换行 }

//AD转换初始化函数 void AD_Init() { ADMUX |= (1 << REFS0);     //ADC参考电压为AVcc,ADC结果右对齐,选择通道ADC0 ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); //使能AD转换,ADC时钟64分频 }

//AD转换函数 unsigned int AD_GetData() { ADCSRA |= (1 << ADSC);     //开始AD转换 while(!(ADCSRA & (1 << ADIF)));    //等待转换完成 ADCSRA |= (1 << ADIF);              //清零ADC中断标志位 return ADC;        //返回ADC值 }

附录:ADC应用设计要点

1.预分频与转换时间 
在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。在要求转换精度低于10位的情况下,ADC的采样时钟可以高于200kHz,以获得更高的采样率。 
ADC模块中包含一个预分频器的ADC时钟源,它可以对大于100KHz的系统时钟进行分频,以获得合适的ADC时钟提供ADC使用。预分频器的分频系数由ADCSRA寄存器中的ADPS位设置的。一旦寄存器ADCSRA中的ADEN位置“1”(ADC开始工作),预分频器就启动开始计数。ADEN位为“1”时,预分频器将一直工作;ADEN位为“0”时,预分频器一直处在复位状态。 
AVR的ADC完成一次转换的时间见表6.2.5。从表中可以看出,完成一次ADC转换通常需要13-14个ADC时钟。而启动ADC开始第一次转换到完成的时间需要25个ADC时钟,这是因为要对ADC单元的模拟电路部分进行初始化。


表6.2.5  ADC转换和采样保持时间

转 换 形 式

采样保持时间

完 成 转 换 总 时 间

启动ADC后的第一次转换

13.5个ADC时钟

25个ADC时钟

正常转换,单端输入

1.5个ADC时钟

13个ADC时钟

自动触发方式

2个ADC时钟

13.5个ADC时钟

正常转换,差分输入

1.5/2.5个ADC时钟

13/14个ADC时钟

当ADCSRA寄存器中的ADSC位置位,启动ADC转换时,A/D转换将在随后ADC时钟的上升沿开始。一次正常的A/D转换开始时,需要1.5个ADC时钟周期的采样保持时间(ADC首次启动后需要13.5个ADC时钟周期的采样保持时间)。当一次A/D转换完成后,转换结果写入ADC数据寄存器,ADIF(ADC中断标志位)将被置位。在单次转换模式下,ADSC也同时被清零。用户程序可以再次置位ADSC位,新的一次转换将在下一个ADC时钟的上升沿开始。 
当ADC设置为自动触发方式时,触发信号的上升沿将启动一次ADC转换。转换完成的结果将一直保持到下一次触发信号的上升沿出现,然后开始新的一次ADC转换。这就保证了使ADC每隔一定的时间间隔进行一次转换。在这种方式下,ADC需要2个ADC时钟周期的采样保持时间。 
在自由连续转换模式下,一次转换完毕后马上开始一次新的转换,此时,ADSC位一直保持为“1”。

2.ADC输入通道和参考电源的选择 
寄存器ADMUX中的MUXn和REFS1、REFS0位实际上是一个缓冲器,该缓冲器与一个MCU可以随机读取的临时寄存器相连通。采用这种结构,保证了ADC输入通道和参考电源只能在ADC转换过程中的安全点被改变。在ADC转换开始前,通道和参考电源可以不断被更新,一旦转换开始,通道和参考电源将被锁定,并保持足够时间,以确保ADC转换的正常进行。在转换完成前的最后一个ADC时钟周期(ADCSRA的ADIF位置“1”时),通道和参考电源又开始重新更新。注意,由于A/D转换开始于置位ADSC后的第一个ADC时钟的上升沿,因此,在置位ADSC后的一个ADC时钟周期内不要将一个新的通道或参考电源写入到ADMUX寄存器中。 
改变差分输入通道时需特别当心。一旦确定了差分输入通道,增益放大器需要125µs的稳定时间。所以在选择了新的差分输入通道后的125µs内不要启动A/D转换,或将这段时间内转换结果丢弃。通过改变ADMUX中的REFS1、REFS0来更改参考电源后,第一次差分转换同样要遵循以上的时间处理过程。

  1. 当要改变ADC输入通道时,应该遵守以下方式,以保证能够选择到正确的通道:

在单次转换模式下,总是在开始转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到转换完成后,再改变通道选择。 
在连续转换模式下,总是在启动ADC开始第一次转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到第一次转换完成后再改变通道的设置。然而由于此时新一次的转换已经自动开始,所以,当前这次的转换结果仍反映前一通道的转换值,而下一次的转换结果将为新设置通道的值。

  1. ADC电压参考源

ADC的参考电压(VREF)决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果接近于0x3FF。ADC的参考电压VREF可以选择为AVCC或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。 
AVCC通过一个无源开关连接到ADC。内部2.56V参考源是由内部能隙参考源(VBC)通过内部的放大器产生的。注意,无论选用什么内部参考电源,外部AREF引脚都是直接与ADC相连的,因此,可以通过外部在AREF引脚和地之间并接一个电容,使内部参考电源更加稳定和抗噪。可以通过使用高阻电压表测量AREF引脚,来获得参考电源VREF的电压值。由于VREF是一个高阻源,因此,只有容性负载可以连接到该引脚。 
如果将一个外部固定的电压源连接到AREF引脚,那就不能使用任何的内部参考电源,否则就会使外部电压源短路。外部参考电源的范围应在2.0V到AVCC-0.2V之间。参考电源改变后的第一次ADC转换结果可能不太准确,建议抛弃该次转换结果。 
3.ADC转换结果 
A/D转换结束后(ADIF = 1),在ADC数据寄存器(ADCL和ADCH)中可以取得转换的结果。对于单端输入的A/D转换,其转换结果为: 
ADC =(VIN×1024)/ VREF 
其中VIN表示选定的输入引脚上的电压,VREF表示选定的参考电源的电压。0x000表示输入引脚的电压为模拟地,0x3FF表示输入引脚的电压为参考电压值减去一个LSB。 
对于差分转换,其结果为: 
ADC=(VPOS-VNEG) ×GAIN×512/VREF 
例:若差分输入通道选择为ADC3-ADC2,10倍增益,参考电压2.56V,左端对齐(ADMUX=0xED),ADC3引脚上电压300mV,ADC2引脚上电压500mV。 
则ADCR =(300-500)×10×512 / 2560 = -400 = 0x270
ADCL=0x00,ADCH=0x9C。 
若结果为右端对齐时(ADLAR=“0”),则ADCL=0x70,ADCH=0x02。

附录2、ADC应用设计的深入讨论 
尽管AVR内部集成了10位的ADC,但是在实际应用中,要想真正实现10位精度,比较稳定的ADC的话,并不象上一节中的例子那么简单。需要进一步从硬件、软件等方面进行综合的、细致的考虑。下面介绍一些在ADC设计应用中应该考虑的几个要点。 
1.AVcc的稳定性。 
AVcc是提供给ADC工作的电源,如果AVcc不稳定,就会影响ADC的转换精度。在图10-5中,系统电源通过一个LC滤波后接入AVcc,这样就能很好的抑制掉系统电源中的高频躁声,提高了AVcc的稳定性。另外在要求比较高的场合使用ADC时,PA口上的那些没被用做ADC输入的端口尽量不要作为数字I/O口使用。因为PA口的工作电源是由AVcc提供的,如果PA口上有比较大的电流波动,也会影响AVcc的稳定。 
2.参考电压VREF的选择确定 
在实际应用中,要根据输入测量电压的范围选择正确的参考电压VREF,以求得到比较好的转换精度。ADC的参考电压VREF还决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果全部接近于0x3FF,因此ADC的参考电压应稍大于模拟输入电压的最高值。 
ADC的参考电压VREF可以选择为AVCC,或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。外接参考电压应该稳定,并大于2.0V(芯片的工作电压为1.8V时,外接参考电压应大于1.0V)。要求比较高的场合,建议在AREF引脚外接标准参考电压源来作为ADC的参考电源。 
3. ADC通道带宽和输入阻抗 
不管使用单端输入转换还是差分输入转换方式,所有模拟输入口的输入电压应在AVcc-GNG之间。 
在单端ADC转换方式时,ADC通道的输入频率带宽取决于ADC转换时钟频率。一次常规的ADC转换需要13个ADC时钟,当ADC转换时钟为1MHz时,一秒种内ADC采样转换的次数约77K。根据采样定理,此时ADC通道的带宽为38.5KHz。 
差分方式ADC转换的带宽是由芯片内部的差分放大器的带宽决定,为4KHz。 
AVR的ADC输入阻抗典型值为100MΩ,为保证测量的准确,被测信号源的输出阻抗要尽可能的低,应在10K以下。 
4. ADC采样时钟的选择 
通常条件下,AVR的ADC逐次比较电路要达到转换的最大精度,需要一个50K~200KHz的采样时钟。一次正常的ADC转换过程需要13个采样时钟,假定ADC采样时钟为200KHz,那么最高的采样速率为200K/13=15.384K。因此根据采样定理,理论上被测模拟信号的最高频率为7.7K! 
尽管可以设置ADC的采样时钟为1M,但并不能提高ADC转换精度,反而会降低转换精度(受逐次比较硬件电路的限制),因此AVR的ADC不能完成高速ADC的任务。如果所需的转换精度低于10位,那么采样时钟可以高于200KHz,以达到更高的采样频率。 
ADC采样时钟的选择方式为:给出或估计被测模拟信号的最高频率fs,取采样频率为fs的4-10倍,再乘上13为ADC采样时钟频率,该频率应在50K~200KHz之间。 如果该频率大于200KHz,则ADC的10位精度不能保证。如果该频率小于50Khz,则可选择50K~200KHz之间的数值。 
5.模拟噪声的抑制 
器件外部和内部的数字电路会产生电磁干扰,并会影响模拟测量的精度。如果ADC转换精度要求很高,可以采用以下的技术来降低噪声的影响: 
(1)使模拟信号的通路尽可能的短。模拟信号连线应从模拟地的布线盘上通过,并使它们尽可能远离高速开关数字信号线。 
(2)AVR的AVcc引脚应该通过LC网络与数字端电源Vcc相连。
(3)采用ADC噪声抑制器功能来降低来自MCU内部的噪声。 
(4)如果某些ADC引脚是作为通用数字输出口使用,那么在ADC转换过程中,不要改变这些引脚的状态。
6.ADC的校正 
由于AVD内部ADC部分的放大器非线性等客观原因,ADC的转换结果会有误差的。如果要获得高精度的ADC转换,还需要对ADC结果进行校正。具体的方法请参考AVR应用笔记AVR120(avr_app_120.pdf),在这篇应用设计参考中详细介绍了误差的种类,以及校正方案。 
7.ADC精度的提高 
在有了上述几点的保证后,通过软件的手段也能适当的提高ADC的精度。如采用多次测量取平均,软件滤波算法等。在AVR应用笔记AVR121(avr_app_121.pdf)中介绍了一种使用过采样算法的软件实现,可以将ADC的精度提高到11位或更高,当然这是在花费更多的时间基础上实现的。

6.2 模数转换的ADC实验 
6.2.1、实例功能 

AVR的模数转换器ADC具有下列特点:

  1. 10位精度;
  2. 0.5LSB积分非线形误差
  3. ±2LSB的绝对精度;
  4. 13µs~260µs的转换时间;
  5. 在最大精度下可达到每秒15kSPS的采样速率;
  6. 8路可选的单端输入通道;
  7. 7路差分输入通道;
  8. 2路差分输入通道带有可选的10×和200×增益;
  9. ADC转换结果的读取可设置为左端对齐(LEFT ADJUSTMENT);
  10. ADC的电压输入范围0~Vcc;
  11. 可选择的内部2.56V的ADC参考电压源;
  12. 自由连续转换模式和单次转换模式;
  13. ADC自动转换触发模式选择;
  14. ADC转换完成中断;
  15. 休眠模式下的噪声抑制器(NOISE CANCELER)。

在本实例中,我们将编写程序实现将模数转换后获得的电压值通过单片机的串口发送到计算机,然后通过计算机上的串口助手显示测量的电压值。 
本实例共有3个功能模块,分别描述如下: 
● 单片机系统:使用单片机的串口实现将模数转换后获得的电压值通过串口发送到计算机。 
● 外围电路:RS232电平转换电路,DB9串行接口插座,模拟电压输入采集电路。
● 软件程序:进一步熟悉单片机的串行通信,并掌握单片机的模数转换的方法。
6.2.2、器件和原理 
关于串行接口的原理已接单片机与计算机的串口的连接在上一实例中进行了描述,在本实例中不再重复。
本实例只介绍ATmega16单片机如何通过内置的模数转换模块采集外界输入的模拟电压。
1、ATmega16单片机的模数转换器ADC介绍 
由于单片机只能处理数字信号,所以外部的模拟信号量需要转变成数字量才能进一步的由单片机进行处理。ATmega16内部集成有一个10位逐次比较(successive approximation)ADC电路。因此使用AVR可以非常方便的处理输入的模拟信号量。 
ATmega16的ADC与一个8通道的模拟多路选择器连接,能够对以PORTA作为ADC输入引脚的8路单端模拟输入电压进行采样,单端电压输入以0V(GND)为参考。另外还支持16种差分电压输入组合,其中2种差分输入方式(ADC1,ADC0和ACD3,ADC2)带有可编程增益放大器,能在A/D转换前对差分输入电压进行0dB(1×),20dB(10×)或46dB(200×)的放大。还有七种差分输入方式的模拟输入通道共用一个负极(ADC1),此时其它任意一个ADC引脚都可作为相应的正极。若增益为1×或10×,则可获得8位的精度。如果增益为200×,那么转换精度为7位。 
AVR的ADC功能单元由独立的专用模拟电源引脚AVcc供电。AVcc和Vcc的电压差别不能大于±0.3V。ADC转换的参考电源可采用芯片内部的2.56V参考电源,或采用AVcc,也可使用外部参考电源。使用外部参考电源时,外部参考电源由引脚ARFE接入。使用内部电压参考源时,可以通过在AREF引脚外部并接一个电容来提高ADC的抗噪性能。
ADC功能单元包括采样保持电路,以确保输入电压在ADC转换过程中保持恒定。ADC通过逐次比较(successive approximation)方式,将输入端的模拟电压转换成10位的数字量。最小值代表地,最大值为AREF引脚上的电压值减1个LSB。可以通过ADMUX寄存器中REFSn位的设置,选择将芯片内部参考电源(2.56V)或AVcc连接到AREF,作为A/D转换的参考电压。这时,内部电压参考源可以通过外接于AREF引脚的电容来稳定,以改进抗噪特性。 
模拟输入通道和差分增益的选择是通过ADMUX寄存器中的MUX位设定的。任何一个ADC的输入引脚,包括地(GND)以及内部的恒定能隙(fixed bandgap)电压参考源,都可以被选择用来作为ADC的单端输入信号。而ADC的某些输入引脚则可选择作为差分增益放大器的正、负极输入端。当选定了差分输入通道后,差分增益放大器将两输入通道上的电压差按选定增益系数放大,然后输入到ADC中。若选定使用单端输入通道,则增益放大器无效。 
通过设置ADCSRA寄存器中的ADC使能位ADEN来使能ADC。在ADEN没有置“1”前,参考电压源和输入通道的选定将不起作用。当ADEN位清“0”后,ADC将不消耗能量,因此建议在进入节电休眠模式前将ADC关掉。 
ADC将10位的转换结果放在ADC数据寄存器中(ADCH和ADCL)。默认情况下,转换结果为右端对齐(RIGHT ADJUSTED)的。但可以通过设置ADMUX寄存器中ADLAR位,调整为左端对齐(LEFT ADJUSTED)。如果转换结果是左端对齐,并且只需要8位的精度,那么只需读取ADCH寄存器的数据作为转换结果就达到要求了。否则,必须先读取ADCL寄存器,然后再读取ADCH寄存器,以保证数据寄存器中的内容是同一次转换的结果。因为一旦ADCL寄存器被读取,就阻断了ADC对ADC数据寄存器的操作。这就意味着,一旦指令读取了ADCL,那么必须紧接着读取一次ADCH;如果在读取ADCL和读取ADCH的过程中正好有一次ADC转换完成,ADC的2个数据寄存器的内容是不会被更新的,该次转换的结果将丢失。只有当ADCH寄存器被读取后,ADC才可以继续对ADCL和ADCH寄存器操作更新。 
ADC有自己的中断,当转换完成时中断将被触发。尽管在顺序读取ADCL和ADCH寄存器过程中,ADC对ADC数据寄存器的更新被禁止,转换的结果丢失,但仍会触发ADC中断。 
2、ATmwga16单片机的模数转换器ADC相关的I/O寄存器 
1.ADC多路复用器选择寄存器—ADMUX

  1. 位7,6—REFS[1:0]:ADC参考电源选择

REFS1、REFS2用于选择ADC的参考电压源,见表6.2.1。如果这些位在ADC转换过程中被改变,新的选择将在该次ADC转换完成后(ADCSRA中的ADIF被置位)才生效。一旦选择内部参考源(AVcc、2.56V)为ADC的参考电压后,AREF引脚上不得施加外部的参考电源,只能与GND之间并接抗干扰电容。


表6.2.1  ADC参考电源选择

REFS1

REFS0

ADC参考电源

0

0

外部引脚AREF,断开内部参考源连接

0

1

AVcc,AREF外部并接电容

1

0

保留

1

1

内部2.56V,AREF外部并接电容

  1. 位5—ADLAR:ADC结果左对齐选择

ADLAR位决定转换结果在ADC数据寄存器中的存放形式。写“1”到ADLAR位,将使转换结果左对齐(LEFT ADJUST);否则,转换结果为右对齐(RIGHT ADJUST)。无论ADC是否正在进行转换,改变ADLAR位都将会立即影响ADC数据寄存器。

  1. 位4..0—MUX4:0:模拟通道和增益选择

这5个位用于对连接到ADC的输入通道和差分通道的增益进行选择设置,详见表6.2.2。注意,只有转换结束后(ADCSRA的ADIF是“1”),改变这些位才会有效。


表6.2.2   ADC输入通道和增益选择

MUX[4:0]

单端输入

差分正极输入

差分负极输入

增益

00000

ADC0

N/A

00001

ADC1

00010

ADC2

00011

ADC3

00100

ADC4

00101

ADC5

00110

ADC6

00111

ADC7

01000

N/A

ADC0

ADC0

10×

01001

ADC1

ADC0

10×

01010

ADC0

ADC0

200×

01011

ADC1

ADC0

200×

01100

ADC2

ADC2

10×

01101

ADC3

ADC2

10×

01110

ADC2

ADC2

200×

01111

ADC3

ADC2

200×

10000

ADC0

ADC1

10001

ADC1

ADC1

10010

ADC2

ADC1

10011

ADC3

ADC1

10100

ADC4

ADC1

10101

ADC5

ADC1

10110

ADC6

ADC1

10111

ADC7

ADC1

11000

ADC0

ADC2

11001

ADC1

ADC2

11010

ADC2

ADC2

11011

ADC3

ADC2

11100

ADC4

ADC2

11101

ADC5

ADC2

11110

1.22V(VBG)

N/A

11111

0V(GND)

   本实例中我们需要设置ADC的参考电压源为AVcc,即REFS0设置为1,ADC默认转换结果为右对齐,我们不需要改变,模拟通道选择ADC0通道单端输入,即MUX4:0。 
2.ADC控制和状态寄存器A—ADCSRA

  1. 位7—ADEN:ADC使能

该位写入“1”时使能ADC,写入“0”关闭ADC。如在ADC转换过程中将ADC关闭,该次转换随即停止。

  1. 位6—ADSC:ADC转换开始

在单次转换模式下,置该位为“1”,将启动一次转换。在自由连续转换模式下,该位写入“1”将启动第一次转换。先置位ADEN位使能ADC,再置位ADSC;或置位ADSC的同时使能ADC,都会使能ADC开始进行第一次转换。第一次ADC转换将需要25个ADC时钟周期,而不是常规转换的13个ADC时钟周期,这是因为第一次转换需要完成对ADC的初始化。 
在ADC转换的过程中,ADSC将始终读出为“1”。当转换完成时,它将转变为“0”。强制写入“0”是无效的。

  1. 位5—ADATE:ADC自动转换触发允许

当该位被置为“1”时,允许ADC工作在自动转换触发工作模式下。在该模式下,在触发信号的上升沿时ADC将自动开始一次ADC转换过程。ADC的自动转换触发信号源由SFIOR寄存器中的ADTS位选择确定。

  1. 位4—ADIF:ADC中断标志位

当ADC转换完成并且ADC数据寄存器被更新后该位被置位。如果ADIE位(ADC转换结束中断允许)和SREG寄存器中的I位被置“1”,ADC中断服务程序将被执行。ADIF在执行相应的中断处理向量时被硬件自动清零。此外,ADIF位可以通过写入逻辑“1”来清零。

  1. 位3—ADIE:ADC中断允许

当该位和SREG寄存器中的I位同时被置位时,允许ADC转换完成中断。

  1. 位2,0—ADPS[2:0]:ADC预分频选择

这些位决定了XTAL时钟与输入到ADC的ADC时钟之间分频数,见表6.2.3。

表6.2.3   ADC时钟分频

ADPS[2:0]

分  频  系  数

000

2

001

2

010

4

011

8

100

16

101

32

110

64

111

128

本实例中我们需要使能ADC,即ADEN设置为1,我们不用自动转换,也不需要中断,所以,ADTE、ADIE位不需要设置。在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。本例中使用的时钟是12M的,所以要将时钟64分频,分频后ADC频率为188KHz,即时钟分频选择ADPS[2:0]=6。

3.ADC数据寄存器—ADCL和ADCH

  1. ADLAR = 0,ADC转换结果右对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第3张图片

  1. ADLAR = 1,ADC转换结果左对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第4张图片
当ADC转换完成后,可以读取ADC寄存器的ADC0-ADC9得到ADC的转换的结果。如果是差分输入,转换值为二进制的补码形式。一旦开始读取ADCL后,ADC数据寄存器就不能被ADC更新,直到ADCH寄存器被读取为止。因此,如果结果是左对齐(ADLAR=1),且不需要大于8位的精度的话,仅仅读取ADCH寄存器就足够了。否则,必须先读取ADCL寄存器,再读取ADCH寄存器。ADMUX寄存器中的ADLAR位决定了从ADC数据寄存器中读取结果的格式。如果ADLAR位为“1”,结果将是左对齐;如果ADLAR位为“0”(默认情况),结果将是右对齐。 
4.特殊功能I/O寄存器—SFIOR

  1. 位7..5—ADTS[2:0]:ADC自动转换触发源选择

当ADCSRA寄存器中的ADATE为“1”,允许ADC工作在自动转换触发工作模式时,这3位的设置用于选择ADC的自动转换触发源。如果禁止了ADC的自动转换触发(ADATE为“0”),这3个位的设置值将不起任何作用。

表10-6   ADC自动转换触发源的选择

ADTS[2:0]

触  发  源

000

连续自由转换

001

模拟比较器

010

外部中断0

011

T/C0比较匹配

100

T/C0溢出

101

T/C1比较匹配B

110

T/C1溢出

111

T/C1输入捕捉

本例中我们不使用自动转换功能,所以该寄存器可以不必设置。
6.2.3、电路 
本实例的电路包括232电平转换电路和电阻分压电路,这两种电路在前面的实例中均做过介绍,这里不再重复。
1、电路原理
在本实例中利用MAX3232芯片使单片机输出的TTL电平转换为标准的RS232电平,从而使计算机能够识别。同时将计算机输出的RS232电平转换为单片机可以识别的TTL电平。
利用电位器产生电阻分压电路,从而产生变化的模拟电压加到单片机的模拟信号采集端口,供单片机采集。
2、电路连接 
电路中MAX3232芯片的9、10引脚分别连接单片机的PD0、PD1端口,MAX3232的13、14引脚分别连接计算机串口线的3、2脚。
电位器RP2的动片引脚连接单片机的模拟信号采集通道PA0(ADC0)。
3、特别说明
本学习板采用的是串口插座是公头的,所以与计算机相连的串口连接线应该是交叉串口线,而不是串口延长线。
6.2.4、程序设计 
1、程序功能 

程序的功能是通过单片机的串行接口,将单片机采集到的模拟电压值发送到计算机中,通过计算机上的串口助手显示采集的电压值。    
● 单片机串行接收中断的编程
在本例中,我们用到了单片机的串行接收中断,需要编写串行接收中断服务程序,通过查询WINAVR(GCC)的中断库函数手册,可以查找到ATmega16单片机串行接收中断的中断向量为USART_RXC_vect。据此我们可以编写串行接收中断服务程序,如下:
//接收中断函数 
ISR(USART_RXC_vect )
{
unsigned char Rev;
Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 
Usart_PutChar(Rev);    //将接收到的数据发送 
}
在中断服务程序中,我们首先把单片机串口接收到的数据放入变量Rev中,然后调用上一实例中编写的串行接口字节发送函数将变量Rev中的数据发送到计算机。 
2、 单片机与计算机串行通信结果的观察 
在观察本例运行结果时,我们同样要用到串口助手,本例中,单片机发送串口数据采用的波特率是9600bps,数据格式是8位数据位,1位停止位,无奇偶校验。在串口助手里面,我们也要将波特率和数据格式设置成一样的。 
3、函数说明 
本实例用到了6个函数,分别是: 
void Port_Init(void);   //端口初始化配置 
void Usart_Init(void);  //USART寄存器设置 
void AD_Init(void);    //AD初始化 
void Usart_PutChar(unsigned char cTXData);  //字节发送函数 
void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 
unsigned int AD_GetData(void);    //AD转换函数   
4、使用WINAVR开发环境,在本例中我们使用的是外部12M的晶振,所以需要将MAKEFILE文件中的时钟频率修改为12M。另外在程序烧录到单片机的时候,熔丝位也要选择为外部12M晶振(注意是晶振,不是外部振荡器,一定不要选择错了,否则会导致单片机不能再烧写程序)。
5、程序代码

#include         #include #include   //中断函数头文件

//常量声明 #define BAUD 9600         //波特率设置值

//全局变量声明 unsigned int ADData;          //AD转换获得的数据

//函数声明 void Port_Init(void);   //端口初始化配置 void Usart_Init(void);  //USART寄存器设置 void AD_Init(void);    //AD初始化 void Usart_PutChar(unsigned char cTXData);  //字节发送函数 void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 unsigned int AD_GetData(void);    //AD转换函数

int main(void)             { unsigned char Delay3s;

      Port_Init(); Usart_Init(); AD_Init(); Usart_PutString("AD转换测试程序"); Usart_PutString("测得ADC0通道的电压值为:"); sei();          //使能全局中断   while(1) { ADData = (int)((long)AD_GetData() * 5010 / 1024);         //将获得的AD值转换为电压值 //单位为mv。 Usart_PutChar(ADData / 1000 + 0x30);      //得到电压值的千位并发送 Usart_PutChar('.');                          //发送小数点 Usart_PutChar(ADData % 1000 / 100 + 0x30);   //得到电压值的百位并发送 Usart_PutChar(ADData % 100 / 10 + 0x30);    //得到电压值的十位并发送 Usart_PutChar(ADData % 10 + 0x30);     //得到电压值的个位并发送 Usart_PutChar('V');                  //发送电压符号“V” Usart_PutChar(0x0d);     // Usart_PutChar(0x0a);     //  AD值发送结束,回车换行 for(Delay3s = 0;Delay3s < 30;Delay3s++)     //延时3S { _delay_ms(90); } } }

//端口状态初始化设置函数 void Port_Init() { PORTA = 0X00;         DDRA = 0x00;   //ADC通道设置为输入口,高阻态     }

//USART寄存器配置函数 void Usart_Init() { UCSRA = 0X00;     UCSRC |= (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0);  //异步,数据格式8,N,1 //UCSRC寄存器与UBRRH寄存器共用相同的I/O地址,写 UCSRC 时, URSEL 应设置为 1。 UBRRL = (F_CPU / BAUD / 16 - 1) % 256;    //波特率设置 UBRRH = (F_CPU / BAUD / 16 - 1) / 256;         UCSRB |= (1 << RXCIE) | (1 << RXEN) | (1 << TXEN);    //发送使能 }

//字节发送函数 void Usart_PutChar(unsigned char cTXData) { while( !(UCSRA & (1 << UDRE)) );  //只有数据寄存器为空时才能发送数据 UDR = cTXData;                   //发送数据送USART I/O数据寄存器-UDR }

//接收中断函数 ISR(USART_RXC_vect ) { unsigned char Rev; Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 Usart_PutChar(Rev);    //将接收到的数据发送 }

void Usart_PutString(unsigned char *pcString) { while (*pcString) { Usart_PutChar(*pcString++);   } Usart_PutChar(0x0D); Usart_PutChar(0x0A);  //结尾发送回车换行 }

//AD转换初始化函数 void AD_Init() { ADMUX |= (1 << REFS0);     //ADC参考电压为AVcc,ADC结果右对齐,选择通道ADC0 ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); //使能AD转换,ADC时钟64分频 }

//AD转换函数 unsigned int AD_GetData() { ADCSRA |= (1 << ADSC);     //开始AD转换 while(!(ADCSRA & (1 << ADIF)));    //等待转换完成 ADCSRA |= (1 << ADIF);              //清零ADC中断标志位 return ADC;        //返回ADC值 }

附录:ADC应用设计要点

1.预分频与转换时间 
在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。在要求转换精度低于10位的情况下,ADC的采样时钟可以高于200kHz,以获得更高的采样率。 
ADC模块中包含一个预分频器的ADC时钟源,它可以对大于100KHz的系统时钟进行分频,以获得合适的ADC时钟提供ADC使用。预分频器的分频系数由ADCSRA寄存器中的ADPS位设置的。一旦寄存器ADCSRA中的ADEN位置“1”(ADC开始工作),预分频器就启动开始计数。ADEN位为“1”时,预分频器将一直工作;ADEN位为“0”时,预分频器一直处在复位状态。 
AVR的ADC完成一次转换的时间见表6.2.5。从表中可以看出,完成一次ADC转换通常需要13-14个ADC时钟。而启动ADC开始第一次转换到完成的时间需要25个ADC时钟,这是因为要对ADC单元的模拟电路部分进行初始化。


表6.2.5  ADC转换和采样保持时间

转 换 形 式

采样保持时间

完 成 转 换 总 时 间

启动ADC后的第一次转换

13.5个ADC时钟

25个ADC时钟

正常转换,单端输入

1.5个ADC时钟

13个ADC时钟

自动触发方式

2个ADC时钟

13.5个ADC时钟

正常转换,差分输入

1.5/2.5个ADC时钟

13/14个ADC时钟

当ADCSRA寄存器中的ADSC位置位,启动ADC转换时,A/D转换将在随后ADC时钟的上升沿开始。一次正常的A/D转换开始时,需要1.5个ADC时钟周期的采样保持时间(ADC首次启动后需要13.5个ADC时钟周期的采样保持时间)。当一次A/D转换完成后,转换结果写入ADC数据寄存器,ADIF(ADC中断标志位)将被置位。在单次转换模式下,ADSC也同时被清零。用户程序可以再次置位ADSC位,新的一次转换将在下一个ADC时钟的上升沿开始。 
当ADC设置为自动触发方式时,触发信号的上升沿将启动一次ADC转换。转换完成的结果将一直保持到下一次触发信号的上升沿出现,然后开始新的一次ADC转换。这就保证了使ADC每隔一定的时间间隔进行一次转换。在这种方式下,ADC需要2个ADC时钟周期的采样保持时间。 
在自由连续转换模式下,一次转换完毕后马上开始一次新的转换,此时,ADSC位一直保持为“1”。

2.ADC输入通道和参考电源的选择 
寄存器ADMUX中的MUXn和REFS1、REFS0位实际上是一个缓冲器,该缓冲器与一个MCU可以随机读取的临时寄存器相连通。采用这种结构,保证了ADC输入通道和参考电源只能在ADC转换过程中的安全点被改变。在ADC转换开始前,通道和参考电源可以不断被更新,一旦转换开始,通道和参考电源将被锁定,并保持足够时间,以确保ADC转换的正常进行。在转换完成前的最后一个ADC时钟周期(ADCSRA的ADIF位置“1”时),通道和参考电源又开始重新更新。注意,由于A/D转换开始于置位ADSC后的第一个ADC时钟的上升沿,因此,在置位ADSC后的一个ADC时钟周期内不要将一个新的通道或参考电源写入到ADMUX寄存器中。 
改变差分输入通道时需特别当心。一旦确定了差分输入通道,增益放大器需要125µs的稳定时间。所以在选择了新的差分输入通道后的125µs内不要启动A/D转换,或将这段时间内转换结果丢弃。通过改变ADMUX中的REFS1、REFS0来更改参考电源后,第一次差分转换同样要遵循以上的时间处理过程。

  1. 当要改变ADC输入通道时,应该遵守以下方式,以保证能够选择到正确的通道:

在单次转换模式下,总是在开始转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到转换完成后,再改变通道选择。 
在连续转换模式下,总是在启动ADC开始第一次转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到第一次转换完成后再改变通道的设置。然而由于此时新一次的转换已经自动开始,所以,当前这次的转换结果仍反映前一通道的转换值,而下一次的转换结果将为新设置通道的值。

  1. ADC电压参考源

ADC的参考电压(VREF)决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果接近于0x3FF。ADC的参考电压VREF可以选择为AVCC或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。 
AVCC通过一个无源开关连接到ADC。内部2.56V参考源是由内部能隙参考源(VBC)通过内部的放大器产生的。注意,无论选用什么内部参考电源,外部AREF引脚都是直接与ADC相连的,因此,可以通过外部在AREF引脚和地之间并接一个电容,使内部参考电源更加稳定和抗噪。可以通过使用高阻电压表测量AREF引脚,来获得参考电源VREF的电压值。由于VREF是一个高阻源,因此,只有容性负载可以连接到该引脚。 
如果将一个外部固定的电压源连接到AREF引脚,那就不能使用任何的内部参考电源,否则就会使外部电压源短路。外部参考电源的范围应在2.0V到AVCC-0.2V之间。参考电源改变后的第一次ADC转换结果可能不太准确,建议抛弃该次转换结果。 
3.ADC转换结果 
A/D转换结束后(ADIF = 1),在ADC数据寄存器(ADCL和ADCH)中可以取得转换的结果。对于单端输入的A/D转换,其转换结果为: 
ADC =(VIN×1024)/ VREF 
其中VIN表示选定的输入引脚上的电压,VREF表示选定的参考电源的电压。0x000表示输入引脚的电压为模拟地,0x3FF表示输入引脚的电压为参考电压值减去一个LSB。 
对于差分转换,其结果为: 
ADC=(VPOS-VNEG) ×GAIN×512/VREF 
例:若差分输入通道选择为ADC3-ADC2,10倍增益,参考电压2.56V,左端对齐(ADMUX=0xED),ADC3引脚上电压300mV,ADC2引脚上电压500mV。 
则ADCR =(300-500)×10×512 / 2560 = -400 = 0x270
ADCL=0x00,ADCH=0x9C。 
若结果为右端对齐时(ADLAR=“0”),则ADCL=0x70,ADCH=0x02。

附录2、ADC应用设计的深入讨论 
尽管AVR内部集成了10位的ADC,但是在实际应用中,要想真正实现10位精度,比较稳定的ADC的话,并不象上一节中的例子那么简单。需要进一步从硬件、软件等方面进行综合的、细致的考虑。下面介绍一些在ADC设计应用中应该考虑的几个要点。 
1.AVcc的稳定性。 
AVcc是提供给ADC工作的电源,如果AVcc不稳定,就会影响ADC的转换精度。在图10-5中,系统电源通过一个LC滤波后接入AVcc,这样就能很好的抑制掉系统电源中的高频躁声,提高了AVcc的稳定性。另外在要求比较高的场合使用ADC时,PA口上的那些没被用做ADC输入的端口尽量不要作为数字I/O口使用。因为PA口的工作电源是由AVcc提供的,如果PA口上有比较大的电流波动,也会影响AVcc的稳定。 
2.参考电压VREF的选择确定 
在实际应用中,要根据输入测量电压的范围选择正确的参考电压VREF,以求得到比较好的转换精度。ADC的参考电压VREF还决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果全部接近于0x3FF,因此ADC的参考电压应稍大于模拟输入电压的最高值。 
ADC的参考电压VREF可以选择为AVCC,或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。外接参考电压应该稳定,并大于2.0V(芯片的工作电压为1.8V时,外接参考电压应大于1.0V)。要求比较高的场合,建议在AREF引脚外接标准参考电压源来作为ADC的参考电源。 
3. ADC通道带宽和输入阻抗 
不管使用单端输入转换还是差分输入转换方式,所有模拟输入口的输入电压应在AVcc-GNG之间。 
在单端ADC转换方式时,ADC通道的输入频率带宽取决于ADC转换时钟频率。一次常规的ADC转换需要13个ADC时钟,当ADC转换时钟为1MHz时,一秒种内ADC采样转换的次数约77K。根据采样定理,此时ADC通道的带宽为38.5KHz。 
差分方式ADC转换的带宽是由芯片内部的差分放大器的带宽决定,为4KHz。 
AVR的ADC输入阻抗典型值为100MΩ,为保证测量的准确,被测信号源的输出阻抗要尽可能的低,应在10K以下。 
4. ADC采样时钟的选择 
通常条件下,AVR的ADC逐次比较电路要达到转换的最大精度,需要一个50K~200KHz的采样时钟。一次正常的ADC转换过程需要13个采样时钟,假定ADC采样时钟为200KHz,那么最高的采样速率为200K/13=15.384K。因此根据采样定理,理论上被测模拟信号的最高频率为7.7K! 
尽管可以设置ADC的采样时钟为1M,但并不能提高ADC转换精度,反而会降低转换精度(受逐次比较硬件电路的限制),因此AVR的ADC不能完成高速ADC的任务。如果所需的转换精度低于10位,那么采样时钟可以高于200KHz,以达到更高的采样频率。 
ADC采样时钟的选择方式为:给出或估计被测模拟信号的最高频率fs,取采样频率为fs的4-10倍,再乘上13为ADC采样时钟频率,该频率应在50K~200KHz之间。 如果该频率大于200KHz,则ADC的10位精度不能保证。如果该频率小于50Khz,则可选择50K~200KHz之间的数值。 
5.模拟噪声的抑制 
器件外部和内部的数字电路会产生电磁干扰,并会影响模拟测量的精度。如果ADC转换精度要求很高,可以采用以下的技术来降低噪声的影响: 
(1)使模拟信号的通路尽可能的短。模拟信号连线应从模拟地的布线盘上通过,并使它们尽可能远离高速开关数字信号线。 
(2)AVR的AVcc引脚应该通过LC网络与数字端电源Vcc相连。
(3)采用ADC噪声抑制器功能来降低来自MCU内部的噪声。 
(4)如果某些ADC引脚是作为通用数字输出口使用,那么在ADC转换过程中,不要改变这些引脚的状态。
6.ADC的校正 
由于AVD内部ADC部分的放大器非线性等客观原因,ADC的转换结果会有误差的。如果要获得高精度的ADC转换,还需要对ADC结果进行校正。具体的方法请参考AVR应用笔记AVR120(avr_app_120.pdf),在这篇应用设计参考中详细介绍了误差的种类,以及校正方案。 
7.ADC精度的提高 
在有了上述几点的保证后,通过软件的手段也能适当的提高ADC的精度。如采用多次测量取平均,软件滤波算法等。在AVR应用笔记AVR121(avr_app_121.pdf)中介绍了一种使用过采样算法的软件实现,可以将ADC的精度提高到11位或更高,当然这是在花费更多的时间基础上实现的。6.2 模数转换的ADC实验 

6.2.1、实例功能 
AVR的模数转换器ADC具有下列特点:

  1. 10位精度;
  2. 0.5LSB积分非线形误差
  3. ±2LSB的绝对精度;
  4. 13µs~260µs的转换时间;
  5. 在最大精度下可达到每秒15kSPS的采样速率;
  6. 8路可选的单端输入通道;
  7. 7路差分输入通道;
  8. 2路差分输入通道带有可选的10×和200×增益;
  9. ADC转换结果的读取可设置为左端对齐(LEFT ADJUSTMENT);
  10. ADC的电压输入范围0~Vcc;
  11. 可选择的内部2.56V的ADC参考电压源;
  12. 自由连续转换模式和单次转换模式;
  13. ADC自动转换触发模式选择;
  14. ADC转换完成中断;
  15. 休眠模式下的噪声抑制器(NOISE CANCELER)。

在本实例中,我们将编写程序实现将模数转换后获得的电压值通过单片机的串口发送到计算机,然后通过计算机上的串口助手显示测量的电压值。 
本实例共有3个功能模块,分别描述如下: 
● 单片机系统:使用单片机的串口实现将模数转换后获得的电压值通过串口发送到计算机。 
● 外围电路:RS232电平转换电路,DB9串行接口插座,模拟电压输入采集电路。
● 软件程序:进一步熟悉单片机的串行通信,并掌握单片机的模数转换的方法。
6.2.2、器件和原理 
关于串行接口的原理已接单片机与计算机的串口的连接在上一实例中进行了描述,在本实例中不再重复。
本实例只介绍ATmega16单片机如何通过内置的模数转换模块采集外界输入的模拟电压。
1、ATmega16单片机的模数转换器ADC介绍 
由于单片机只能处理数字信号,所以外部的模拟信号量需要转变成数字量才能进一步的由单片机进行处理。ATmega16内部集成有一个10位逐次比较(successive approximation)ADC电路。因此使用AVR可以非常方便的处理输入的模拟信号量。 
ATmega16的ADC与一个8通道的模拟多路选择器连接,能够对以PORTA作为ADC输入引脚的8路单端模拟输入电压进行采样,单端电压输入以0V(GND)为参考。另外还支持16种差分电压输入组合,其中2种差分输入方式(ADC1,ADC0和ACD3,ADC2)带有可编程增益放大器,能在A/D转换前对差分输入电压进行0dB(1×),20dB(10×)或46dB(200×)的放大。还有七种差分输入方式的模拟输入通道共用一个负极(ADC1),此时其它任意一个ADC引脚都可作为相应的正极。若增益为1×或10×,则可获得8位的精度。如果增益为200×,那么转换精度为7位。 
AVR的ADC功能单元由独立的专用模拟电源引脚AVcc供电。AVcc和Vcc的电压差别不能大于±0.3V。ADC转换的参考电源可采用芯片内部的2.56V参考电源,或采用AVcc,也可使用外部参考电源。使用外部参考电源时,外部参考电源由引脚ARFE接入。使用内部电压参考源时,可以通过在AREF引脚外部并接一个电容来提高ADC的抗噪性能。
ADC功能单元包括采样保持电路,以确保输入电压在ADC转换过程中保持恒定。ADC通过逐次比较(successive approximation)方式,将输入端的模拟电压转换成10位的数字量。最小值代表地,最大值为AREF引脚上的电压值减1个LSB。可以通过ADMUX寄存器中REFSn位的设置,选择将芯片内部参考电源(2.56V)或AVcc连接到AREF,作为A/D转换的参考电压。这时,内部电压参考源可以通过外接于AREF引脚的电容来稳定,以改进抗噪特性。 
模拟输入通道和差分增益的选择是通过ADMUX寄存器中的MUX位设定的。任何一个ADC的输入引脚,包括地(GND)以及内部的恒定能隙(fixed bandgap)电压参考源,都可以被选择用来作为ADC的单端输入信号。而ADC的某些输入引脚则可选择作为差分增益放大器的正、负极输入端。当选定了差分输入通道后,差分增益放大器将两输入通道上的电压差按选定增益系数放大,然后输入到ADC中。若选定使用单端输入通道,则增益放大器无效。 
通过设置ADCSRA寄存器中的ADC使能位ADEN来使能ADC。在ADEN没有置“1”前,参考电压源和输入通道的选定将不起作用。当ADEN位清“0”后,ADC将不消耗能量,因此建议在进入节电休眠模式前将ADC关掉。 
ADC将10位的转换结果放在ADC数据寄存器中(ADCH和ADCL)。默认情况下,转换结果为右端对齐(RIGHT ADJUSTED)的。但可以通过设置ADMUX寄存器中ADLAR位,调整为左端对齐(LEFT ADJUSTED)。如果转换结果是左端对齐,并且只需要8位的精度,那么只需读取ADCH寄存器的数据作为转换结果就达到要求了。否则,必须先读取ADCL寄存器,然后再读取ADCH寄存器,以保证数据寄存器中的内容是同一次转换的结果。因为一旦ADCL寄存器被读取,就阻断了ADC对ADC数据寄存器的操作。这就意味着,一旦指令读取了ADCL,那么必须紧接着读取一次ADCH;如果在读取ADCL和读取ADCH的过程中正好有一次ADC转换完成,ADC的2个数据寄存器的内容是不会被更新的,该次转换的结果将丢失。只有当ADCH寄存器被读取后,ADC才可以继续对ADCL和ADCH寄存器操作更新。 
ADC有自己的中断,当转换完成时中断将被触发。尽管在顺序读取ADCL和ADCH寄存器过程中,ADC对ADC数据寄存器的更新被禁止,转换的结果丢失,但仍会触发ADC中断。 
2、ATmwga16单片机的模数转换器ADC相关的I/O寄存器 
1.ADC多路复用器选择寄存器—ADMUX

  1. 位7,6—REFS[1:0]:ADC参考电源选择

REFS1、REFS2用于选择ADC的参考电压源,见表6.2.1。如果这些位在ADC转换过程中被改变,新的选择将在该次ADC转换完成后(ADCSRA中的ADIF被置位)才生效。一旦选择内部参考源(AVcc、2.56V)为ADC的参考电压后,AREF引脚上不得施加外部的参考电源,只能与GND之间并接抗干扰电容。


表6.2.1  ADC参考电源选择

REFS1

REFS0

ADC参考电源

0

0

外部引脚AREF,断开内部参考源连接

0

1

AVcc,AREF外部并接电容

1

0

保留

1

1

内部2.56V,AREF外部并接电容

  1. 位5—ADLAR:ADC结果左对齐选择

ADLAR位决定转换结果在ADC数据寄存器中的存放形式。写“1”到ADLAR位,将使转换结果左对齐(LEFT ADJUST);否则,转换结果为右对齐(RIGHT ADJUST)。无论ADC是否正在进行转换,改变ADLAR位都将会立即影响ADC数据寄存器。

  1. 位4..0—MUX4:0:模拟通道和增益选择

这5个位用于对连接到ADC的输入通道和差分通道的增益进行选择设置,详见表6.2.2。注意,只有转换结束后(ADCSRA的ADIF是“1”),改变这些位才会有效。


表6.2.2   ADC输入通道和增益选择

MUX[4:0]

单端输入

差分正极输入

差分负极输入

增益

00000

ADC0

N/A

00001

ADC1

00010

ADC2

00011

ADC3

00100

ADC4

00101

ADC5

00110

ADC6

00111

ADC7

01000

N/A

ADC0

ADC0

10×

01001

ADC1

ADC0

10×

01010

ADC0

ADC0

200×

01011

ADC1

ADC0

200×

01100

ADC2

ADC2

10×

01101

ADC3

ADC2

10×

01110

ADC2

ADC2

200×

01111

ADC3

ADC2

200×

10000

ADC0

ADC1

10001

ADC1

ADC1

10010

ADC2

ADC1

10011

ADC3

ADC1

10100

ADC4

ADC1

10101

ADC5

ADC1

10110

ADC6

ADC1

10111

ADC7

ADC1

11000

ADC0

ADC2

11001

ADC1

ADC2

11010

ADC2

ADC2

11011

ADC3

ADC2

11100

ADC4

ADC2

11101

ADC5

ADC2

11110

1.22V(VBG)

N/A

11111

0V(GND)

   本实例中我们需要设置ADC的参考电压源为AVcc,即REFS0设置为1,ADC默认转换结果为右对齐,我们不需要改变,模拟通道选择ADC0通道单端输入,即MUX4:0。 
2.ADC控制和状态寄存器A—ADCSRA

  1. 位7—ADEN:ADC使能

该位写入“1”时使能ADC,写入“0”关闭ADC。如在ADC转换过程中将ADC关闭,该次转换随即停止。

  1. 位6—ADSC:ADC转换开始

在单次转换模式下,置该位为“1”,将启动一次转换。在自由连续转换模式下,该位写入“1”将启动第一次转换。先置位ADEN位使能ADC,再置位ADSC;或置位ADSC的同时使能ADC,都会使能ADC开始进行第一次转换。第一次ADC转换将需要25个ADC时钟周期,而不是常规转换的13个ADC时钟周期,这是因为第一次转换需要完成对ADC的初始化。 
在ADC转换的过程中,ADSC将始终读出为“1”。当转换完成时,它将转变为“0”。强制写入“0”是无效的。

  1. 位5—ADATE:ADC自动转换触发允许

当该位被置为“1”时,允许ADC工作在自动转换触发工作模式下。在该模式下,在触发信号的上升沿时ADC将自动开始一次ADC转换过程。ADC的自动转换触发信号源由SFIOR寄存器中的ADTS位选择确定。

  1. 位4—ADIF:ADC中断标志位

当ADC转换完成并且ADC数据寄存器被更新后该位被置位。如果ADIE位(ADC转换结束中断允许)和SREG寄存器中的I位被置“1”,ADC中断服务程序将被执行。ADIF在执行相应的中断处理向量时被硬件自动清零。此外,ADIF位可以通过写入逻辑“1”来清零。

  1. 位3—ADIE:ADC中断允许

当该位和SREG寄存器中的I位同时被置位时,允许ADC转换完成中断。

  1. 位2,0—ADPS[2:0]:ADC预分频选择

这些位决定了XTAL时钟与输入到ADC的ADC时钟之间分频数,见表6.2.3。

表6.2.3   ADC时钟分频

ADPS[2:0]

分  频  系  数

000

2

001

2

010

4

011

8

100

16

101

32

110

64

111

128

本实例中我们需要使能ADC,即ADEN设置为1,我们不用自动转换,也不需要中断,所以,ADTE、ADIE位不需要设置。在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。本例中使用的时钟是12M的,所以要将时钟64分频,分频后ADC频率为188KHz,即时钟分频选择ADPS[2:0]=6。

3.ADC数据寄存器—ADCL和ADCH

  1. ADLAR = 0,ADC转换结果右对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第5张图片

  1. ADLAR = 1,ADC转换结果左对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第6张图片
当ADC转换完成后,可以读取ADC寄存器的ADC0-ADC9得到ADC的转换的结果。如果是差分输入,转换值为二进制的补码形式。一旦开始读取ADCL后,ADC数据寄存器就不能被ADC更新,直到ADCH寄存器被读取为止。因此,如果结果是左对齐(ADLAR=1),且不需要大于8位的精度的话,仅仅读取ADCH寄存器就足够了。否则,必须先读取ADCL寄存器,再读取ADCH寄存器。ADMUX寄存器中的ADLAR位决定了从ADC数据寄存器中读取结果的格式。如果ADLAR位为“1”,结果将是左对齐;如果ADLAR位为“0”(默认情况),结果将是右对齐。 
4.特殊功能I/O寄存器—SFIOR

  1. 位7..5—ADTS[2:0]:ADC自动转换触发源选择

当ADCSRA寄存器中的ADATE为“1”,允许ADC工作在自动转换触发工作模式时,这3位的设置用于选择ADC的自动转换触发源。如果禁止了ADC的自动转换触发(ADATE为“0”),这3个位的设置值将不起任何作用。

表10-6   ADC自动转换触发源的选择

ADTS[2:0]

触  发  源

000

连续自由转换

001

模拟比较器

010

外部中断0

011

T/C0比较匹配

100

T/C0溢出

101

T/C1比较匹配B

110

T/C1溢出

111

T/C1输入捕捉

本例中我们不使用自动转换功能,所以该寄存器可以不必设置。
6.2.3、电路 
本实例的电路包括232电平转换电路和电阻分压电路,这两种电路在前面的实例中均做过介绍,这里不再重复。
1、电路原理
在本实例中利用MAX3232芯片使单片机输出的TTL电平转换为标准的RS232电平,从而使计算机能够识别。同时将计算机输出的RS232电平转换为单片机可以识别的TTL电平。
利用电位器产生电阻分压电路,从而产生变化的模拟电压加到单片机的模拟信号采集端口,供单片机采集。
2、电路连接 
电路中MAX3232芯片的9、10引脚分别连接单片机的PD0、PD1端口,MAX3232的13、14引脚分别连接计算机串口线的3、2脚。
电位器RP2的动片引脚连接单片机的模拟信号采集通道PA0(ADC0)。
3、特别说明
本学习板采用的是串口插座是公头的,所以与计算机相连的串口连接线应该是交叉串口线,而不是串口延长线。
6.2.4、程序设计 
1、程序功能 

程序的功能是通过单片机的串行接口,将单片机采集到的模拟电压值发送到计算机中,通过计算机上的串口助手显示采集的电压值。    
● 单片机串行接收中断的编程
在本例中,我们用到了单片机的串行接收中断,需要编写串行接收中断服务程序,通过查询WINAVR(GCC)的中断库函数手册,可以查找到ATmega16单片机串行接收中断的中断向量为USART_RXC_vect。据此我们可以编写串行接收中断服务程序,如下:
//接收中断函数 
ISR(USART_RXC_vect )
{
unsigned char Rev;
Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 
Usart_PutChar(Rev);    //将接收到的数据发送 
}
在中断服务程序中,我们首先把单片机串口接收到的数据放入变量Rev中,然后调用上一实例中编写的串行接口字节发送函数将变量Rev中的数据发送到计算机。 
2、 单片机与计算机串行通信结果的观察 
在观察本例运行结果时,我们同样要用到串口助手,本例中,单片机发送串口数据采用的波特率是9600bps,数据格式是8位数据位,1位停止位,无奇偶校验。在串口助手里面,我们也要将波特率和数据格式设置成一样的。 
3、函数说明 
本实例用到了6个函数,分别是: 
void Port_Init(void);   //端口初始化配置 
void Usart_Init(void);  //USART寄存器设置 
void AD_Init(void);    //AD初始化 
void Usart_PutChar(unsigned char cTXData);  //字节发送函数 
void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 
unsigned int AD_GetData(void);    //AD转换函数   
4、使用WINAVR开发环境,在本例中我们使用的是外部12M的晶振,所以需要将MAKEFILE文件中的时钟频率修改为12M。另外在程序烧录到单片机的时候,熔丝位也要选择为外部12M晶振(注意是晶振,不是外部振荡器,一定不要选择错了,否则会导致单片机不能再烧写程序)。
5、程序代码

#include         #include #include   //中断函数头文件

//常量声明 #define BAUD 9600         //波特率设置值

//全局变量声明 unsigned int ADData;          //AD转换获得的数据

//函数声明 void Port_Init(void);   //端口初始化配置 void Usart_Init(void);  //USART寄存器设置 void AD_Init(void);    //AD初始化 void Usart_PutChar(unsigned char cTXData);  //字节发送函数 void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 unsigned int AD_GetData(void);    //AD转换函数

int main(void)             { unsigned char Delay3s;

      Port_Init(); Usart_Init(); AD_Init(); Usart_PutString("AD转换测试程序"); Usart_PutString("测得ADC0通道的电压值为:"); sei();          //使能全局中断   while(1) { ADData = (int)((long)AD_GetData() * 5010 / 1024);         //将获得的AD值转换为电压值 //单位为mv。 Usart_PutChar(ADData / 1000 + 0x30);      //得到电压值的千位并发送 Usart_PutChar('.');                          //发送小数点 Usart_PutChar(ADData % 1000 / 100 + 0x30);   //得到电压值的百位并发送 Usart_PutChar(ADData % 100 / 10 + 0x30);    //得到电压值的十位并发送 Usart_PutChar(ADData % 10 + 0x30);     //得到电压值的个位并发送 Usart_PutChar('V');                  //发送电压符号“V” Usart_PutChar(0x0d);     // Usart_PutChar(0x0a);     //  AD值发送结束,回车换行 for(Delay3s = 0;Delay3s < 30;Delay3s++)     //延时3S { _delay_ms(90); } } }

//端口状态初始化设置函数 void Port_Init() { PORTA = 0X00;         DDRA = 0x00;   //ADC通道设置为输入口,高阻态     }

//USART寄存器配置函数 void Usart_Init() { UCSRA = 0X00;     UCSRC |= (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0);  //异步,数据格式8,N,1 //UCSRC寄存器与UBRRH寄存器共用相同的I/O地址,写 UCSRC 时, URSEL 应设置为 1。 UBRRL = (F_CPU / BAUD / 16 - 1) % 256;    //波特率设置 UBRRH = (F_CPU / BAUD / 16 - 1) / 256;         UCSRB |= (1 << RXCIE) | (1 << RXEN) | (1 << TXEN);    //发送使能 }

//字节发送函数 void Usart_PutChar(unsigned char cTXData) { while( !(UCSRA & (1 << UDRE)) );  //只有数据寄存器为空时才能发送数据 UDR = cTXData;                   //发送数据送USART I/O数据寄存器-UDR }

//接收中断函数 ISR(USART_RXC_vect ) { unsigned char Rev; Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 Usart_PutChar(Rev);    //将接收到的数据发送 }

void Usart_PutString(unsigned char *pcString) { while (*pcString) { Usart_PutChar(*pcString++);   } Usart_PutChar(0x0D); Usart_PutChar(0x0A);  //结尾发送回车换行 }

//AD转换初始化函数 void AD_Init() { ADMUX |= (1 << REFS0);     //ADC参考电压为AVcc,ADC结果右对齐,选择通道ADC0 ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); //使能AD转换,ADC时钟64分频 }

//AD转换函数 unsigned int AD_GetData() { ADCSRA |= (1 << ADSC);     //开始AD转换 while(!(ADCSRA & (1 << ADIF)));    //等待转换完成 ADCSRA |= (1 << ADIF);              //清零ADC中断标志位 return ADC;        //返回ADC值 }

附录:ADC应用设计要点

1.预分频与转换时间 
在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。在要求转换精度低于10位的情况下,ADC的采样时钟可以高于200kHz,以获得更高的采样率。 
ADC模块中包含一个预分频器的ADC时钟源,它可以对大于100KHz的系统时钟进行分频,以获得合适的ADC时钟提供ADC使用。预分频器的分频系数由ADCSRA寄存器中的ADPS位设置的。一旦寄存器ADCSRA中的ADEN位置“1”(ADC开始工作),预分频器就启动开始计数。ADEN位为“1”时,预分频器将一直工作;ADEN位为“0”时,预分频器一直处在复位状态。 
AVR的ADC完成一次转换的时间见表6.2.5。从表中可以看出,完成一次ADC转换通常需要13-14个ADC时钟。而启动ADC开始第一次转换到完成的时间需要25个ADC时钟,这是因为要对ADC单元的模拟电路部分进行初始化。


表6.2.5  ADC转换和采样保持时间

转 换 形 式

采样保持时间

完 成 转 换 总 时 间

启动ADC后的第一次转换

13.5个ADC时钟

25个ADC时钟

正常转换,单端输入

1.5个ADC时钟

13个ADC时钟

自动触发方式

2个ADC时钟

13.5个ADC时钟

正常转换,差分输入

1.5/2.5个ADC时钟

13/14个ADC时钟

当ADCSRA寄存器中的ADSC位置位,启动ADC转换时,A/D转换将在随后ADC时钟的上升沿开始。一次正常的A/D转换开始时,需要1.5个ADC时钟周期的采样保持时间(ADC首次启动后需要13.5个ADC时钟周期的采样保持时间)。当一次A/D转换完成后,转换结果写入ADC数据寄存器,ADIF(ADC中断标志位)将被置位。在单次转换模式下,ADSC也同时被清零。用户程序可以再次置位ADSC位,新的一次转换将在下一个ADC时钟的上升沿开始。 
当ADC设置为自动触发方式时,触发信号的上升沿将启动一次ADC转换。转换完成的结果将一直保持到下一次触发信号的上升沿出现,然后开始新的一次ADC转换。这就保证了使ADC每隔一定的时间间隔进行一次转换。在这种方式下,ADC需要2个ADC时钟周期的采样保持时间。 
在自由连续转换模式下,一次转换完毕后马上开始一次新的转换,此时,ADSC位一直保持为“1”。

2.ADC输入通道和参考电源的选择 
寄存器ADMUX中的MUXn和REFS1、REFS0位实际上是一个缓冲器,该缓冲器与一个MCU可以随机读取的临时寄存器相连通。采用这种结构,保证了ADC输入通道和参考电源只能在ADC转换过程中的安全点被改变。在ADC转换开始前,通道和参考电源可以不断被更新,一旦转换开始,通道和参考电源将被锁定,并保持足够时间,以确保ADC转换的正常进行。在转换完成前的最后一个ADC时钟周期(ADCSRA的ADIF位置“1”时),通道和参考电源又开始重新更新。注意,由于A/D转换开始于置位ADSC后的第一个ADC时钟的上升沿,因此,在置位ADSC后的一个ADC时钟周期内不要将一个新的通道或参考电源写入到ADMUX寄存器中。 
改变差分输入通道时需特别当心。一旦确定了差分输入通道,增益放大器需要125µs的稳定时间。所以在选择了新的差分输入通道后的125µs内不要启动A/D转换,或将这段时间内转换结果丢弃。通过改变ADMUX中的REFS1、REFS0来更改参考电源后,第一次差分转换同样要遵循以上的时间处理过程。

  1. 当要改变ADC输入通道时,应该遵守以下方式,以保证能够选择到正确的通道:

在单次转换模式下,总是在开始转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到转换完成后,再改变通道选择。 
在连续转换模式下,总是在启动ADC开始第一次转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到第一次转换完成后再改变通道的设置。然而由于此时新一次的转换已经自动开始,所以,当前这次的转换结果仍反映前一通道的转换值,而下一次的转换结果将为新设置通道的值。

  1. ADC电压参考源

ADC的参考电压(VREF)决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果接近于0x3FF。ADC的参考电压VREF可以选择为AVCC或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。 
AVCC通过一个无源开关连接到ADC。内部2.56V参考源是由内部能隙参考源(VBC)通过内部的放大器产生的。注意,无论选用什么内部参考电源,外部AREF引脚都是直接与ADC相连的,因此,可以通过外部在AREF引脚和地之间并接一个电容,使内部参考电源更加稳定和抗噪。可以通过使用高阻电压表测量AREF引脚,来获得参考电源VREF的电压值。由于VREF是一个高阻源,因此,只有容性负载可以连接到该引脚。 
如果将一个外部固定的电压源连接到AREF引脚,那就不能使用任何的内部参考电源,否则就会使外部电压源短路。外部参考电源的范围应在2.0V到AVCC-0.2V之间。参考电源改变后的第一次ADC转换结果可能不太准确,建议抛弃该次转换结果。 
3.ADC转换结果 
A/D转换结束后(ADIF = 1),在ADC数据寄存器(ADCL和ADCH)中可以取得转换的结果。对于单端输入的A/D转换,其转换结果为: 
ADC =(VIN×1024)/ VREF 
其中VIN表示选定的输入引脚上的电压,VREF表示选定的参考电源的电压。0x000表示输入引脚的电压为模拟地,0x3FF表示输入引脚的电压为参考电压值减去一个LSB。 
对于差分转换,其结果为: 
ADC=(VPOS-VNEG) ×GAIN×512/VREF 
例:若差分输入通道选择为ADC3-ADC2,10倍增益,参考电压2.56V,左端对齐(ADMUX=0xED),ADC3引脚上电压300mV,ADC2引脚上电压500mV。 
则ADCR =(300-500)×10×512 / 2560 = -400 = 0x270
ADCL=0x00,ADCH=0x9C。 
若结果为右端对齐时(ADLAR=“0”),则ADCL=0x70,ADCH=0x02。

附录2、ADC应用设计的深入讨论 
尽管AVR内部集成了10位的ADC,但是在实际应用中,要想真正实现10位精度,比较稳定的ADC的话,并不象上一节中的例子那么简单。需要进一步从硬件、软件等方面进行综合的、细致的考虑。下面介绍一些在ADC设计应用中应该考虑的几个要点。 
1.AVcc的稳定性。 
AVcc是提供给ADC工作的电源,如果AVcc不稳定,就会影响ADC的转换精度。在图10-5中,系统电源通过一个LC滤波后接入AVcc,这样就能很好的抑制掉系统电源中的高频躁声,提高了AVcc的稳定性。另外在要求比较高的场合使用ADC时,PA口上的那些没被用做ADC输入的端口尽量不要作为数字I/O口使用。因为PA口的工作电源是由AVcc提供的,如果PA口上有比较大的电流波动,也会影响AVcc的稳定。 
2.参考电压VREF的选择确定 
在实际应用中,要根据输入测量电压的范围选择正确的参考电压VREF,以求得到比较好的转换精度。ADC的参考电压VREF还决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果全部接近于0x3FF,因此ADC的参考电压应稍大于模拟输入电压的最高值。 
ADC的参考电压VREF可以选择为AVCC,或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。外接参考电压应该稳定,并大于2.0V(芯片的工作电压为1.8V时,外接参考电压应大于1.0V)。要求比较高的场合,建议在AREF引脚外接标准参考电压源来作为ADC的参考电源。 
3. ADC通道带宽和输入阻抗 
不管使用单端输入转换还是差分输入转换方式,所有模拟输入口的输入电压应在AVcc-GNG之间。 
在单端ADC转换方式时,ADC通道的输入频率带宽取决于ADC转换时钟频率。一次常规的ADC转换需要13个ADC时钟,当ADC转换时钟为1MHz时,一秒种内ADC采样转换的次数约77K。根据采样定理,此时ADC通道的带宽为38.5KHz。 
差分方式ADC转换的带宽是由芯片内部的差分放大器的带宽决定,为4KHz。 
AVR的ADC输入阻抗典型值为100MΩ,为保证测量的准确,被测信号源的输出阻抗要尽可能的低,应在10K以下。 
4. ADC采样时钟的选择 
通常条件下,AVR的ADC逐次比较电路要达到转换的最大精度,需要一个50K~200KHz的采样时钟。一次正常的ADC转换过程需要13个采样时钟,假定ADC采样时钟为200KHz,那么最高的采样速率为200K/13=15.384K。因此根据采样定理,理论上被测模拟信号的最高频率为7.7K! 
尽管可以设置ADC的采样时钟为1M,但并不能提高ADC转换精度,反而会降低转换精度(受逐次比较硬件电路的限制),因此AVR的ADC不能完成高速ADC的任务。如果所需的转换精度低于10位,那么采样时钟可以高于200KHz,以达到更高的采样频率。 
ADC采样时钟的选择方式为:给出或估计被测模拟信号的最高频率fs,取采样频率为fs的4-10倍,再乘上13为ADC采样时钟频率,该频率应在50K~200KHz之间。 如果该频率大于200KHz,则ADC的10位精度不能保证。如果该频率小于50Khz,则可选择50K~200KHz之间的数值。 
5.模拟噪声的抑制 
器件外部和内部的数字电路会产生电磁干扰,并会影响模拟测量的精度。如果ADC转换精度要求很高,可以采用以下的技术来降低噪声的影响: 
(1)使模拟信号的通路尽可能的短。模拟信号连线应从模拟地的布线盘上通过,并使它们尽可能远离高速开关数字信号线。 
(2)AVR的AVcc引脚应该通过LC网络与数字端电源Vcc相连。
(3)采用ADC噪声抑制器功能来降低来自MCU内部的噪声。 
(4)如果某些ADC引脚是作为通用数字输出口使用,那么在ADC转换过程中,不要改变这些引脚的状态。
6.ADC的校正 
由于AVD内部ADC部分的放大器非线性等客观原因,ADC的转换结果会有误差的。如果要获得高精度的ADC转换,还需要对ADC结果进行校正。具体的方法请参考AVR应用笔记AVR120(avr_app_120.pdf),在这篇应用设计参考中详细介绍了误差的种类,以及校正方案。 
7.ADC精度的提高 
在有了上述几点的保证后,通过软件的手段也能适当的提高ADC的精度。如采用多次测量取平均,软件滤波算法等。在AVR应用笔记AVR121(avr_app_121.pdf)中介绍了一种使用过采样算法的软件实现,可以将ADC的精度提高到11位或更高,当然这是在花费更多的时间基础上实现的。6.2 模数转换的ADC实验 

6.2.1、实例功能 
AVR的模数转换器ADC具有下列特点:

  1. 10位精度;
  2. 0.5LSB积分非线形误差
  3. ±2LSB的绝对精度;
  4. 13µs~260µs的转换时间;
  5. 在最大精度下可达到每秒15kSPS的采样速率;
  6. 8路可选的单端输入通道;
  7. 7路差分输入通道;
  8. 2路差分输入通道带有可选的10×和200×增益;
  9. ADC转换结果的读取可设置为左端对齐(LEFT ADJUSTMENT);
  10. ADC的电压输入范围0~Vcc;
  11. 可选择的内部2.56V的ADC参考电压源;
  12. 自由连续转换模式和单次转换模式;
  13. ADC自动转换触发模式选择;
  14. ADC转换完成中断;
  15. 休眠模式下的噪声抑制器(NOISE CANCELER)。

在本实例中,我们将编写程序实现将模数转换后获得的电压值通过单片机的串口发送到计算机,然后通过计算机上的串口助手显示测量的电压值。 
本实例共有3个功能模块,分别描述如下: 
● 单片机系统:使用单片机的串口实现将模数转换后获得的电压值通过串口发送到计算机。 
● 外围电路:RS232电平转换电路,DB9串行接口插座,模拟电压输入采集电路。
● 软件程序:进一步熟悉单片机的串行通信,并掌握单片机的模数转换的方法。
6.2.2、器件和原理 
关于串行接口的原理已接单片机与计算机的串口的连接在上一实例中进行了描述,在本实例中不再重复。
本实例只介绍ATmega16单片机如何通过内置的模数转换模块采集外界输入的模拟电压。
1、ATmega16单片机的模数转换器ADC介绍 
由于单片机只能处理数字信号,所以外部的模拟信号量需要转变成数字量才能进一步的由单片机进行处理。ATmega16内部集成有一个10位逐次比较(successive approximation)ADC电路。因此使用AVR可以非常方便的处理输入的模拟信号量。 
ATmega16的ADC与一个8通道的模拟多路选择器连接,能够对以PORTA作为ADC输入引脚的8路单端模拟输入电压进行采样,单端电压输入以0V(GND)为参考。另外还支持16种差分电压输入组合,其中2种差分输入方式(ADC1,ADC0和ACD3,ADC2)带有可编程增益放大器,能在A/D转换前对差分输入电压进行0dB(1×),20dB(10×)或46dB(200×)的放大。还有七种差分输入方式的模拟输入通道共用一个负极(ADC1),此时其它任意一个ADC引脚都可作为相应的正极。若增益为1×或10×,则可获得8位的精度。如果增益为200×,那么转换精度为7位。 
AVR的ADC功能单元由独立的专用模拟电源引脚AVcc供电。AVcc和Vcc的电压差别不能大于±0.3V。ADC转换的参考电源可采用芯片内部的2.56V参考电源,或采用AVcc,也可使用外部参考电源。使用外部参考电源时,外部参考电源由引脚ARFE接入。使用内部电压参考源时,可以通过在AREF引脚外部并接一个电容来提高ADC的抗噪性能。
ADC功能单元包括采样保持电路,以确保输入电压在ADC转换过程中保持恒定。ADC通过逐次比较(successive approximation)方式,将输入端的模拟电压转换成10位的数字量。最小值代表地,最大值为AREF引脚上的电压值减1个LSB。可以通过ADMUX寄存器中REFSn位的设置,选择将芯片内部参考电源(2.56V)或AVcc连接到AREF,作为A/D转换的参考电压。这时,内部电压参考源可以通过外接于AREF引脚的电容来稳定,以改进抗噪特性。 
模拟输入通道和差分增益的选择是通过ADMUX寄存器中的MUX位设定的。任何一个ADC的输入引脚,包括地(GND)以及内部的恒定能隙(fixed bandgap)电压参考源,都可以被选择用来作为ADC的单端输入信号。而ADC的某些输入引脚则可选择作为差分增益放大器的正、负极输入端。当选定了差分输入通道后,差分增益放大器将两输入通道上的电压差按选定增益系数放大,然后输入到ADC中。若选定使用单端输入通道,则增益放大器无效。 
通过设置ADCSRA寄存器中的ADC使能位ADEN来使能ADC。在ADEN没有置“1”前,参考电压源和输入通道的选定将不起作用。当ADEN位清“0”后,ADC将不消耗能量,因此建议在进入节电休眠模式前将ADC关掉。 
ADC将10位的转换结果放在ADC数据寄存器中(ADCH和ADCL)。默认情况下,转换结果为右端对齐(RIGHT ADJUSTED)的。但可以通过设置ADMUX寄存器中ADLAR位,调整为左端对齐(LEFT ADJUSTED)。如果转换结果是左端对齐,并且只需要8位的精度,那么只需读取ADCH寄存器的数据作为转换结果就达到要求了。否则,必须先读取ADCL寄存器,然后再读取ADCH寄存器,以保证数据寄存器中的内容是同一次转换的结果。因为一旦ADCL寄存器被读取,就阻断了ADC对ADC数据寄存器的操作。这就意味着,一旦指令读取了ADCL,那么必须紧接着读取一次ADCH;如果在读取ADCL和读取ADCH的过程中正好有一次ADC转换完成,ADC的2个数据寄存器的内容是不会被更新的,该次转换的结果将丢失。只有当ADCH寄存器被读取后,ADC才可以继续对ADCL和ADCH寄存器操作更新。 
ADC有自己的中断,当转换完成时中断将被触发。尽管在顺序读取ADCL和ADCH寄存器过程中,ADC对ADC数据寄存器的更新被禁止,转换的结果丢失,但仍会触发ADC中断。 
2、ATmwga16单片机的模数转换器ADC相关的I/O寄存器 
1.ADC多路复用器选择寄存器—ADMUX

  1. 位7,6—REFS[1:0]:ADC参考电源选择

REFS1、REFS2用于选择ADC的参考电压源,见表6.2.1。如果这些位在ADC转换过程中被改变,新的选择将在该次ADC转换完成后(ADCSRA中的ADIF被置位)才生效。一旦选择内部参考源(AVcc、2.56V)为ADC的参考电压后,AREF引脚上不得施加外部的参考电源,只能与GND之间并接抗干扰电容。


表6.2.1  ADC参考电源选择

REFS1

REFS0

ADC参考电源

0

0

外部引脚AREF,断开内部参考源连接

0

1

AVcc,AREF外部并接电容

1

0

保留

1

1

内部2.56V,AREF外部并接电容

  1. 位5—ADLAR:ADC结果左对齐选择

ADLAR位决定转换结果在ADC数据寄存器中的存放形式。写“1”到ADLAR位,将使转换结果左对齐(LEFT ADJUST);否则,转换结果为右对齐(RIGHT ADJUST)。无论ADC是否正在进行转换,改变ADLAR位都将会立即影响ADC数据寄存器。

  1. 位4..0—MUX4:0:模拟通道和增益选择

这5个位用于对连接到ADC的输入通道和差分通道的增益进行选择设置,详见表6.2.2。注意,只有转换结束后(ADCSRA的ADIF是“1”),改变这些位才会有效。


表6.2.2   ADC输入通道和增益选择

MUX[4:0]

单端输入

差分正极输入

差分负极输入

增益

00000

ADC0

N/A

00001

ADC1

00010

ADC2

00011

ADC3

00100

ADC4

00101

ADC5

00110

ADC6

00111

ADC7

01000

N/A

ADC0

ADC0

10×

01001

ADC1

ADC0

10×

01010

ADC0

ADC0

200×

01011

ADC1

ADC0

200×

01100

ADC2

ADC2

10×

01101

ADC3

ADC2

10×

01110

ADC2

ADC2

200×

01111

ADC3

ADC2

200×

10000

ADC0

ADC1

10001

ADC1

ADC1

10010

ADC2

ADC1

10011

ADC3

ADC1

10100

ADC4

ADC1

10101

ADC5

ADC1

10110

ADC6

ADC1

10111

ADC7

ADC1

11000

ADC0

ADC2

11001

ADC1

ADC2

11010

ADC2

ADC2

11011

ADC3

ADC2

11100

ADC4

ADC2

11101

ADC5

ADC2

11110

1.22V(VBG)

N/A

11111

0V(GND)

   本实例中我们需要设置ADC的参考电压源为AVcc,即REFS0设置为1,ADC默认转换结果为右对齐,我们不需要改变,模拟通道选择ADC0通道单端输入,即MUX4:0。 
2.ADC控制和状态寄存器A—ADCSRA

  1. 位7—ADEN:ADC使能

该位写入“1”时使能ADC,写入“0”关闭ADC。如在ADC转换过程中将ADC关闭,该次转换随即停止。

  1. 位6—ADSC:ADC转换开始

在单次转换模式下,置该位为“1”,将启动一次转换。在自由连续转换模式下,该位写入“1”将启动第一次转换。先置位ADEN位使能ADC,再置位ADSC;或置位ADSC的同时使能ADC,都会使能ADC开始进行第一次转换。第一次ADC转换将需要25个ADC时钟周期,而不是常规转换的13个ADC时钟周期,这是因为第一次转换需要完成对ADC的初始化。 
在ADC转换的过程中,ADSC将始终读出为“1”。当转换完成时,它将转变为“0”。强制写入“0”是无效的。

  1. 位5—ADATE:ADC自动转换触发允许

当该位被置为“1”时,允许ADC工作在自动转换触发工作模式下。在该模式下,在触发信号的上升沿时ADC将自动开始一次ADC转换过程。ADC的自动转换触发信号源由SFIOR寄存器中的ADTS位选择确定。

  1. 位4—ADIF:ADC中断标志位

当ADC转换完成并且ADC数据寄存器被更新后该位被置位。如果ADIE位(ADC转换结束中断允许)和SREG寄存器中的I位被置“1”,ADC中断服务程序将被执行。ADIF在执行相应的中断处理向量时被硬件自动清零。此外,ADIF位可以通过写入逻辑“1”来清零。

  1. 位3—ADIE:ADC中断允许

当该位和SREG寄存器中的I位同时被置位时,允许ADC转换完成中断。

  1. 位2,0—ADPS[2:0]:ADC预分频选择

这些位决定了XTAL时钟与输入到ADC的ADC时钟之间分频数,见表6.2.3。

表6.2.3   ADC时钟分频

ADPS[2:0]

分  频  系  数

000

2

001

2

010

4

011

8

100

16

101

32

110

64

111

128

本实例中我们需要使能ADC,即ADEN设置为1,我们不用自动转换,也不需要中断,所以,ADTE、ADIE位不需要设置。在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。本例中使用的时钟是12M的,所以要将时钟64分频,分频后ADC频率为188KHz,即时钟分频选择ADPS[2:0]=6。

3.ADC数据寄存器—ADCL和ADCH

  1. ADLAR = 0,ADC转换结果右对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第7张图片

  1. ADLAR = 1,ADC转换结果左对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第8张图片
当ADC转换完成后,可以读取ADC寄存器的ADC0-ADC9得到ADC的转换的结果。如果是差分输入,转换值为二进制的补码形式。一旦开始读取ADCL后,ADC数据寄存器就不能被ADC更新,直到ADCH寄存器被读取为止。因此,如果结果是左对齐(ADLAR=1),且不需要大于8位的精度的话,仅仅读取ADCH寄存器就足够了。否则,必须先读取ADCL寄存器,再读取ADCH寄存器。ADMUX寄存器中的ADLAR位决定了从ADC数据寄存器中读取结果的格式。如果ADLAR位为“1”,结果将是左对齐;如果ADLAR位为“0”(默认情况),结果将是右对齐。 
4.特殊功能I/O寄存器—SFIOR

  1. 位7..5—ADTS[2:0]:ADC自动转换触发源选择

当ADCSRA寄存器中的ADATE为“1”,允许ADC工作在自动转换触发工作模式时,这3位的设置用于选择ADC的自动转换触发源。如果禁止了ADC的自动转换触发(ADATE为“0”),这3个位的设置值将不起任何作用。

表10-6   ADC自动转换触发源的选择

ADTS[2:0]

触  发  源

000

连续自由转换

001

模拟比较器

010

外部中断0

011

T/C0比较匹配

100

T/C0溢出

101

T/C1比较匹配B

110

T/C1溢出

111

T/C1输入捕捉

本例中我们不使用自动转换功能,所以该寄存器可以不必设置。
6.2.3、电路 
本实例的电路包括232电平转换电路和电阻分压电路,这两种电路在前面的实例中均做过介绍,这里不再重复。
1、电路原理
在本实例中利用MAX3232芯片使单片机输出的TTL电平转换为标准的RS232电平,从而使计算机能够识别。同时将计算机输出的RS232电平转换为单片机可以识别的TTL电平。
利用电位器产生电阻分压电路,从而产生变化的模拟电压加到单片机的模拟信号采集端口,供单片机采集。
2、电路连接 
电路中MAX3232芯片的9、10引脚分别连接单片机的PD0、PD1端口,MAX3232的13、14引脚分别连接计算机串口线的3、2脚。
电位器RP2的动片引脚连接单片机的模拟信号采集通道PA0(ADC0)。
3、特别说明
本学习板采用的是串口插座是公头的,所以与计算机相连的串口连接线应该是交叉串口线,而不是串口延长线。
6.2.4、程序设计 
1、程序功能 

程序的功能是通过单片机的串行接口,将单片机采集到的模拟电压值发送到计算机中,通过计算机上的串口助手显示采集的电压值。    
● 单片机串行接收中断的编程
在本例中,我们用到了单片机的串行接收中断,需要编写串行接收中断服务程序,通过查询WINAVR(GCC)的中断库函数手册,可以查找到ATmega16单片机串行接收中断的中断向量为USART_RXC_vect。据此我们可以编写串行接收中断服务程序,如下:
//接收中断函数 
ISR(USART_RXC_vect )
{
unsigned char Rev;
Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 
Usart_PutChar(Rev);    //将接收到的数据发送 
}
在中断服务程序中,我们首先把单片机串口接收到的数据放入变量Rev中,然后调用上一实例中编写的串行接口字节发送函数将变量Rev中的数据发送到计算机。 
2、 单片机与计算机串行通信结果的观察 
在观察本例运行结果时,我们同样要用到串口助手,本例中,单片机发送串口数据采用的波特率是9600bps,数据格式是8位数据位,1位停止位,无奇偶校验。在串口助手里面,我们也要将波特率和数据格式设置成一样的。 
3、函数说明 
本实例用到了6个函数,分别是: 
void Port_Init(void);   //端口初始化配置 
void Usart_Init(void);  //USART寄存器设置 
void AD_Init(void);    //AD初始化 
void Usart_PutChar(unsigned char cTXData);  //字节发送函数 
void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 
unsigned int AD_GetData(void);    //AD转换函数   
4、使用WINAVR开发环境,在本例中我们使用的是外部12M的晶振,所以需要将MAKEFILE文件中的时钟频率修改为12M。另外在程序烧录到单片机的时候,熔丝位也要选择为外部12M晶振(注意是晶振,不是外部振荡器,一定不要选择错了,否则会导致单片机不能再烧写程序)。
5、程序代码

#include         #include #include   //中断函数头文件

//常量声明 #define BAUD 9600         //波特率设置值

//全局变量声明 unsigned int ADData;          //AD转换获得的数据

//函数声明 void Port_Init(void);   //端口初始化配置 void Usart_Init(void);  //USART寄存器设置 void AD_Init(void);    //AD初始化 void Usart_PutChar(unsigned char cTXData);  //字节发送函数 void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 unsigned int AD_GetData(void);    //AD转换函数

int main(void)             { unsigned char Delay3s;

      Port_Init(); Usart_Init(); AD_Init(); Usart_PutString("AD转换测试程序"); Usart_PutString("测得ADC0通道的电压值为:"); sei();          //使能全局中断   while(1) { ADData = (int)((long)AD_GetData() * 5010 / 1024);         //将获得的AD值转换为电压值 //单位为mv。 Usart_PutChar(ADData / 1000 + 0x30);      //得到电压值的千位并发送 Usart_PutChar('.');                          //发送小数点 Usart_PutChar(ADData % 1000 / 100 + 0x30);   //得到电压值的百位并发送 Usart_PutChar(ADData % 100 / 10 + 0x30);    //得到电压值的十位并发送 Usart_PutChar(ADData % 10 + 0x30);     //得到电压值的个位并发送 Usart_PutChar('V');                  //发送电压符号“V” Usart_PutChar(0x0d);     // Usart_PutChar(0x0a);     //  AD值发送结束,回车换行 for(Delay3s = 0;Delay3s < 30;Delay3s++)     //延时3S { _delay_ms(90); } } }

//端口状态初始化设置函数 void Port_Init() { PORTA = 0X00;         DDRA = 0x00;   //ADC通道设置为输入口,高阻态     }

//USART寄存器配置函数 void Usart_Init() { UCSRA = 0X00;     UCSRC |= (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0);  //异步,数据格式8,N,1 //UCSRC寄存器与UBRRH寄存器共用相同的I/O地址,写 UCSRC 时, URSEL 应设置为 1。 UBRRL = (F_CPU / BAUD / 16 - 1) % 256;    //波特率设置 UBRRH = (F_CPU / BAUD / 16 - 1) / 256;         UCSRB |= (1 << RXCIE) | (1 << RXEN) | (1 << TXEN);    //发送使能 }

//字节发送函数 void Usart_PutChar(unsigned char cTXData) { while( !(UCSRA & (1 << UDRE)) );  //只有数据寄存器为空时才能发送数据 UDR = cTXData;                   //发送数据送USART I/O数据寄存器-UDR }

//接收中断函数 ISR(USART_RXC_vect ) { unsigned char Rev; Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 Usart_PutChar(Rev);    //将接收到的数据发送 }

void Usart_PutString(unsigned char *pcString) { while (*pcString) { Usart_PutChar(*pcString++);   } Usart_PutChar(0x0D); Usart_PutChar(0x0A);  //结尾发送回车换行 }

//AD转换初始化函数 void AD_Init() { ADMUX |= (1 << REFS0);     //ADC参考电压为AVcc,ADC结果右对齐,选择通道ADC0 ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); //使能AD转换,ADC时钟64分频 }

//AD转换函数 unsigned int AD_GetData() { ADCSRA |= (1 << ADSC);     //开始AD转换 while(!(ADCSRA & (1 << ADIF)));    //等待转换完成 ADCSRA |= (1 << ADIF);              //清零ADC中断标志位 return ADC;        //返回ADC值 }

附录:ADC应用设计要点

1.预分频与转换时间 
在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。在要求转换精度低于10位的情况下,ADC的采样时钟可以高于200kHz,以获得更高的采样率。 
ADC模块中包含一个预分频器的ADC时钟源,它可以对大于100KHz的系统时钟进行分频,以获得合适的ADC时钟提供ADC使用。预分频器的分频系数由ADCSRA寄存器中的ADPS位设置的。一旦寄存器ADCSRA中的ADEN位置“1”(ADC开始工作),预分频器就启动开始计数。ADEN位为“1”时,预分频器将一直工作;ADEN位为“0”时,预分频器一直处在复位状态。 
AVR的ADC完成一次转换的时间见表6.2.5。从表中可以看出,完成一次ADC转换通常需要13-14个ADC时钟。而启动ADC开始第一次转换到完成的时间需要25个ADC时钟,这是因为要对ADC单元的模拟电路部分进行初始化。


表6.2.5  ADC转换和采样保持时间

转 换 形 式

采样保持时间

完 成 转 换 总 时 间

启动ADC后的第一次转换

13.5个ADC时钟

25个ADC时钟

正常转换,单端输入

1.5个ADC时钟

13个ADC时钟

自动触发方式

2个ADC时钟

13.5个ADC时钟

正常转换,差分输入

1.5/2.5个ADC时钟

13/14个ADC时钟

当ADCSRA寄存器中的ADSC位置位,启动ADC转换时,A/D转换将在随后ADC时钟的上升沿开始。一次正常的A/D转换开始时,需要1.5个ADC时钟周期的采样保持时间(ADC首次启动后需要13.5个ADC时钟周期的采样保持时间)。当一次A/D转换完成后,转换结果写入ADC数据寄存器,ADIF(ADC中断标志位)将被置位。在单次转换模式下,ADSC也同时被清零。用户程序可以再次置位ADSC位,新的一次转换将在下一个ADC时钟的上升沿开始。 
当ADC设置为自动触发方式时,触发信号的上升沿将启动一次ADC转换。转换完成的结果将一直保持到下一次触发信号的上升沿出现,然后开始新的一次ADC转换。这就保证了使ADC每隔一定的时间间隔进行一次转换。在这种方式下,ADC需要2个ADC时钟周期的采样保持时间。 
在自由连续转换模式下,一次转换完毕后马上开始一次新的转换,此时,ADSC位一直保持为“1”。

2.ADC输入通道和参考电源的选择 
寄存器ADMUX中的MUXn和REFS1、REFS0位实际上是一个缓冲器,该缓冲器与一个MCU可以随机读取的临时寄存器相连通。采用这种结构,保证了ADC输入通道和参考电源只能在ADC转换过程中的安全点被改变。在ADC转换开始前,通道和参考电源可以不断被更新,一旦转换开始,通道和参考电源将被锁定,并保持足够时间,以确保ADC转换的正常进行。在转换完成前的最后一个ADC时钟周期(ADCSRA的ADIF位置“1”时),通道和参考电源又开始重新更新。注意,由于A/D转换开始于置位ADSC后的第一个ADC时钟的上升沿,因此,在置位ADSC后的一个ADC时钟周期内不要将一个新的通道或参考电源写入到ADMUX寄存器中。 
改变差分输入通道时需特别当心。一旦确定了差分输入通道,增益放大器需要125µs的稳定时间。所以在选择了新的差分输入通道后的125µs内不要启动A/D转换,或将这段时间内转换结果丢弃。通过改变ADMUX中的REFS1、REFS0来更改参考电源后,第一次差分转换同样要遵循以上的时间处理过程。

  1. 当要改变ADC输入通道时,应该遵守以下方式,以保证能够选择到正确的通道:

在单次转换模式下,总是在开始转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到转换完成后,再改变通道选择。 
在连续转换模式下,总是在启动ADC开始第一次转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到第一次转换完成后再改变通道的设置。然而由于此时新一次的转换已经自动开始,所以,当前这次的转换结果仍反映前一通道的转换值,而下一次的转换结果将为新设置通道的值。

  1. ADC电压参考源

ADC的参考电压(VREF)决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果接近于0x3FF。ADC的参考电压VREF可以选择为AVCC或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。 
AVCC通过一个无源开关连接到ADC。内部2.56V参考源是由内部能隙参考源(VBC)通过内部的放大器产生的。注意,无论选用什么内部参考电源,外部AREF引脚都是直接与ADC相连的,因此,可以通过外部在AREF引脚和地之间并接一个电容,使内部参考电源更加稳定和抗噪。可以通过使用高阻电压表测量AREF引脚,来获得参考电源VREF的电压值。由于VREF是一个高阻源,因此,只有容性负载可以连接到该引脚。 
如果将一个外部固定的电压源连接到AREF引脚,那就不能使用任何的内部参考电源,否则就会使外部电压源短路。外部参考电源的范围应在2.0V到AVCC-0.2V之间。参考电源改变后的第一次ADC转换结果可能不太准确,建议抛弃该次转换结果。 
3.ADC转换结果 
A/D转换结束后(ADIF = 1),在ADC数据寄存器(ADCL和ADCH)中可以取得转换的结果。对于单端输入的A/D转换,其转换结果为: 
ADC =(VIN×1024)/ VREF 
其中VIN表示选定的输入引脚上的电压,VREF表示选定的参考电源的电压。0x000表示输入引脚的电压为模拟地,0x3FF表示输入引脚的电压为参考电压值减去一个LSB。 
对于差分转换,其结果为: 
ADC=(VPOS-VNEG) ×GAIN×512/VREF 
例:若差分输入通道选择为ADC3-ADC2,10倍增益,参考电压2.56V,左端对齐(ADMUX=0xED),ADC3引脚上电压300mV,ADC2引脚上电压500mV。 
则ADCR =(300-500)×10×512 / 2560 = -400 = 0x270
ADCL=0x00,ADCH=0x9C。 
若结果为右端对齐时(ADLAR=“0”),则ADCL=0x70,ADCH=0x02。

附录2、ADC应用设计的深入讨论 
尽管AVR内部集成了10位的ADC,但是在实际应用中,要想真正实现10位精度,比较稳定的ADC的话,并不象上一节中的例子那么简单。需要进一步从硬件、软件等方面进行综合的、细致的考虑。下面介绍一些在ADC设计应用中应该考虑的几个要点。 
1.AVcc的稳定性。 
AVcc是提供给ADC工作的电源,如果AVcc不稳定,就会影响ADC的转换精度。在图10-5中,系统电源通过一个LC滤波后接入AVcc,这样就能很好的抑制掉系统电源中的高频躁声,提高了AVcc的稳定性。另外在要求比较高的场合使用ADC时,PA口上的那些没被用做ADC输入的端口尽量不要作为数字I/O口使用。因为PA口的工作电源是由AVcc提供的,如果PA口上有比较大的电流波动,也会影响AVcc的稳定。 
2.参考电压VREF的选择确定 
在实际应用中,要根据输入测量电压的范围选择正确的参考电压VREF,以求得到比较好的转换精度。ADC的参考电压VREF还决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果全部接近于0x3FF,因此ADC的参考电压应稍大于模拟输入电压的最高值。 
ADC的参考电压VREF可以选择为AVCC,或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。外接参考电压应该稳定,并大于2.0V(芯片的工作电压为1.8V时,外接参考电压应大于1.0V)。要求比较高的场合,建议在AREF引脚外接标准参考电压源来作为ADC的参考电源。 
3. ADC通道带宽和输入阻抗 
不管使用单端输入转换还是差分输入转换方式,所有模拟输入口的输入电压应在AVcc-GNG之间。 
在单端ADC转换方式时,ADC通道的输入频率带宽取决于ADC转换时钟频率。一次常规的ADC转换需要13个ADC时钟,当ADC转换时钟为1MHz时,一秒种内ADC采样转换的次数约77K。根据采样定理,此时ADC通道的带宽为38.5KHz。 
差分方式ADC转换的带宽是由芯片内部的差分放大器的带宽决定,为4KHz。 
AVR的ADC输入阻抗典型值为100MΩ,为保证测量的准确,被测信号源的输出阻抗要尽可能的低,应在10K以下。 
4. ADC采样时钟的选择 
通常条件下,AVR的ADC逐次比较电路要达到转换的最大精度,需要一个50K~200KHz的采样时钟。一次正常的ADC转换过程需要13个采样时钟,假定ADC采样时钟为200KHz,那么最高的采样速率为200K/13=15.384K。因此根据采样定理,理论上被测模拟信号的最高频率为7.7K! 
尽管可以设置ADC的采样时钟为1M,但并不能提高ADC转换精度,反而会降低转换精度(受逐次比较硬件电路的限制),因此AVR的ADC不能完成高速ADC的任务。如果所需的转换精度低于10位,那么采样时钟可以高于200KHz,以达到更高的采样频率。 
ADC采样时钟的选择方式为:给出或估计被测模拟信号的最高频率fs,取采样频率为fs的4-10倍,再乘上13为ADC采样时钟频率,该频率应在50K~200KHz之间。 如果该频率大于200KHz,则ADC的10位精度不能保证。如果该频率小于50Khz,则可选择50K~200KHz之间的数值。 
5.模拟噪声的抑制 
器件外部和内部的数字电路会产生电磁干扰,并会影响模拟测量的精度。如果ADC转换精度要求很高,可以采用以下的技术来降低噪声的影响: 
(1)使模拟信号的通路尽可能的短。模拟信号连线应从模拟地的布线盘上通过,并使它们尽可能远离高速开关数字信号线。 
(2)AVR的AVcc引脚应该通过LC网络与数字端电源Vcc相连。
(3)采用ADC噪声抑制器功能来降低来自MCU内部的噪声。 
(4)如果某些ADC引脚是作为通用数字输出口使用,那么在ADC转换过程中,不要改变这些引脚的状态。
6.ADC的校正 
由于AVD内部ADC部分的放大器非线性等客观原因,ADC的转换结果会有误差的。如果要获得高精度的ADC转换,还需要对ADC结果进行校正。具体的方法请参考AVR应用笔记AVR120(avr_app_120.pdf),在这篇应用设计参考中详细介绍了误差的种类,以及校正方案。 
7.ADC精度的提高 
在有了上述几点的保证后,通过软件的手段也能适当的提高ADC的精度。如采用多次测量取平均,软件滤波算法等。在AVR应用笔记AVR121(avr_app_121.pdf)中介绍了一种使用过采样算法的软件实现,可以将ADC的精度提高到11位或更高,当然这是在花费更多的时间基础上实现的。6.2 模数转换的ADC实验 

6.2.1、实例功能 
AVR的模数转换器ADC具有下列特点:

  1. 10位精度;
  2. 0.5LSB积分非线形误差
  3. ±2LSB的绝对精度;
  4. 13µs~260µs的转换时间;
  5. 在最大精度下可达到每秒15kSPS的采样速率;
  6. 8路可选的单端输入通道;
  7. 7路差分输入通道;
  8. 2路差分输入通道带有可选的10×和200×增益;
  9. ADC转换结果的读取可设置为左端对齐(LEFT ADJUSTMENT);
  10. ADC的电压输入范围0~Vcc;
  11. 可选择的内部2.56V的ADC参考电压源;
  12. 自由连续转换模式和单次转换模式;
  13. ADC自动转换触发模式选择;
  14. ADC转换完成中断;
  15. 休眠模式下的噪声抑制器(NOISE CANCELER)。

在本实例中,我们将编写程序实现将模数转换后获得的电压值通过单片机的串口发送到计算机,然后通过计算机上的串口助手显示测量的电压值。 
本实例共有3个功能模块,分别描述如下: 
● 单片机系统:使用单片机的串口实现将模数转换后获得的电压值通过串口发送到计算机。 
● 外围电路:RS232电平转换电路,DB9串行接口插座,模拟电压输入采集电路。
● 软件程序:进一步熟悉单片机的串行通信,并掌握单片机的模数转换的方法。
6.2.2、器件和原理 
关于串行接口的原理已接单片机与计算机的串口的连接在上一实例中进行了描述,在本实例中不再重复。
本实例只介绍ATmega16单片机如何通过内置的模数转换模块采集外界输入的模拟电压。
1、ATmega16单片机的模数转换器ADC介绍 
由于单片机只能处理数字信号,所以外部的模拟信号量需要转变成数字量才能进一步的由单片机进行处理。ATmega16内部集成有一个10位逐次比较(successive approximation)ADC电路。因此使用AVR可以非常方便的处理输入的模拟信号量。 
ATmega16的ADC与一个8通道的模拟多路选择器连接,能够对以PORTA作为ADC输入引脚的8路单端模拟输入电压进行采样,单端电压输入以0V(GND)为参考。另外还支持16种差分电压输入组合,其中2种差分输入方式(ADC1,ADC0和ACD3,ADC2)带有可编程增益放大器,能在A/D转换前对差分输入电压进行0dB(1×),20dB(10×)或46dB(200×)的放大。还有七种差分输入方式的模拟输入通道共用一个负极(ADC1),此时其它任意一个ADC引脚都可作为相应的正极。若增益为1×或10×,则可获得8位的精度。如果增益为200×,那么转换精度为7位。 
AVR的ADC功能单元由独立的专用模拟电源引脚AVcc供电。AVcc和Vcc的电压差别不能大于±0.3V。ADC转换的参考电源可采用芯片内部的2.56V参考电源,或采用AVcc,也可使用外部参考电源。使用外部参考电源时,外部参考电源由引脚ARFE接入。使用内部电压参考源时,可以通过在AREF引脚外部并接一个电容来提高ADC的抗噪性能。
ADC功能单元包括采样保持电路,以确保输入电压在ADC转换过程中保持恒定。ADC通过逐次比较(successive approximation)方式,将输入端的模拟电压转换成10位的数字量。最小值代表地,最大值为AREF引脚上的电压值减1个LSB。可以通过ADMUX寄存器中REFSn位的设置,选择将芯片内部参考电源(2.56V)或AVcc连接到AREF,作为A/D转换的参考电压。这时,内部电压参考源可以通过外接于AREF引脚的电容来稳定,以改进抗噪特性。 
模拟输入通道和差分增益的选择是通过ADMUX寄存器中的MUX位设定的。任何一个ADC的输入引脚,包括地(GND)以及内部的恒定能隙(fixed bandgap)电压参考源,都可以被选择用来作为ADC的单端输入信号。而ADC的某些输入引脚则可选择作为差分增益放大器的正、负极输入端。当选定了差分输入通道后,差分增益放大器将两输入通道上的电压差按选定增益系数放大,然后输入到ADC中。若选定使用单端输入通道,则增益放大器无效。 
通过设置ADCSRA寄存器中的ADC使能位ADEN来使能ADC。在ADEN没有置“1”前,参考电压源和输入通道的选定将不起作用。当ADEN位清“0”后,ADC将不消耗能量,因此建议在进入节电休眠模式前将ADC关掉。 
ADC将10位的转换结果放在ADC数据寄存器中(ADCH和ADCL)。默认情况下,转换结果为右端对齐(RIGHT ADJUSTED)的。但可以通过设置ADMUX寄存器中ADLAR位,调整为左端对齐(LEFT ADJUSTED)。如果转换结果是左端对齐,并且只需要8位的精度,那么只需读取ADCH寄存器的数据作为转换结果就达到要求了。否则,必须先读取ADCL寄存器,然后再读取ADCH寄存器,以保证数据寄存器中的内容是同一次转换的结果。因为一旦ADCL寄存器被读取,就阻断了ADC对ADC数据寄存器的操作。这就意味着,一旦指令读取了ADCL,那么必须紧接着读取一次ADCH;如果在读取ADCL和读取ADCH的过程中正好有一次ADC转换完成,ADC的2个数据寄存器的内容是不会被更新的,该次转换的结果将丢失。只有当ADCH寄存器被读取后,ADC才可以继续对ADCL和ADCH寄存器操作更新。 
ADC有自己的中断,当转换完成时中断将被触发。尽管在顺序读取ADCL和ADCH寄存器过程中,ADC对ADC数据寄存器的更新被禁止,转换的结果丢失,但仍会触发ADC中断。 
2、ATmwga16单片机的模数转换器ADC相关的I/O寄存器 
1.ADC多路复用器选择寄存器—ADMUX

  1. 位7,6—REFS[1:0]:ADC参考电源选择

REFS1、REFS2用于选择ADC的参考电压源,见表6.2.1。如果这些位在ADC转换过程中被改变,新的选择将在该次ADC转换完成后(ADCSRA中的ADIF被置位)才生效。一旦选择内部参考源(AVcc、2.56V)为ADC的参考电压后,AREF引脚上不得施加外部的参考电源,只能与GND之间并接抗干扰电容。


表6.2.1  ADC参考电源选择

REFS1

REFS0

ADC参考电源

0

0

外部引脚AREF,断开内部参考源连接

0

1

AVcc,AREF外部并接电容

1

0

保留

1

1

内部2.56V,AREF外部并接电容

  1. 位5—ADLAR:ADC结果左对齐选择

ADLAR位决定转换结果在ADC数据寄存器中的存放形式。写“1”到ADLAR位,将使转换结果左对齐(LEFT ADJUST);否则,转换结果为右对齐(RIGHT ADJUST)。无论ADC是否正在进行转换,改变ADLAR位都将会立即影响ADC数据寄存器。

  1. 位4..0—MUX4:0:模拟通道和增益选择

这5个位用于对连接到ADC的输入通道和差分通道的增益进行选择设置,详见表6.2.2。注意,只有转换结束后(ADCSRA的ADIF是“1”),改变这些位才会有效。


表6.2.2   ADC输入通道和增益选择

MUX[4:0]

单端输入

差分正极输入

差分负极输入

增益

00000

ADC0

N/A

00001

ADC1

00010

ADC2

00011

ADC3

00100

ADC4

00101

ADC5

00110

ADC6

00111

ADC7

01000

N/A

ADC0

ADC0

10×

01001

ADC1

ADC0

10×

01010

ADC0

ADC0

200×

01011

ADC1

ADC0

200×

01100

ADC2

ADC2

10×

01101

ADC3

ADC2

10×

01110

ADC2

ADC2

200×

01111

ADC3

ADC2

200×

10000

ADC0

ADC1

10001

ADC1

ADC1

10010

ADC2

ADC1

10011

ADC3

ADC1

10100

ADC4

ADC1

10101

ADC5

ADC1

10110

ADC6

ADC1

10111

ADC7

ADC1

11000

ADC0

ADC2

11001

ADC1

ADC2

11010

ADC2

ADC2

11011

ADC3

ADC2

11100

ADC4

ADC2

11101

ADC5

ADC2

11110

1.22V(VBG)

N/A

11111

0V(GND)

   本实例中我们需要设置ADC的参考电压源为AVcc,即REFS0设置为1,ADC默认转换结果为右对齐,我们不需要改变,模拟通道选择ADC0通道单端输入,即MUX4:0。 
2.ADC控制和状态寄存器A—ADCSRA

  1. 位7—ADEN:ADC使能

该位写入“1”时使能ADC,写入“0”关闭ADC。如在ADC转换过程中将ADC关闭,该次转换随即停止。

  1. 位6—ADSC:ADC转换开始

在单次转换模式下,置该位为“1”,将启动一次转换。在自由连续转换模式下,该位写入“1”将启动第一次转换。先置位ADEN位使能ADC,再置位ADSC;或置位ADSC的同时使能ADC,都会使能ADC开始进行第一次转换。第一次ADC转换将需要25个ADC时钟周期,而不是常规转换的13个ADC时钟周期,这是因为第一次转换需要完成对ADC的初始化。 
在ADC转换的过程中,ADSC将始终读出为“1”。当转换完成时,它将转变为“0”。强制写入“0”是无效的。

  1. 位5—ADATE:ADC自动转换触发允许

当该位被置为“1”时,允许ADC工作在自动转换触发工作模式下。在该模式下,在触发信号的上升沿时ADC将自动开始一次ADC转换过程。ADC的自动转换触发信号源由SFIOR寄存器中的ADTS位选择确定。

  1. 位4—ADIF:ADC中断标志位

当ADC转换完成并且ADC数据寄存器被更新后该位被置位。如果ADIE位(ADC转换结束中断允许)和SREG寄存器中的I位被置“1”,ADC中断服务程序将被执行。ADIF在执行相应的中断处理向量时被硬件自动清零。此外,ADIF位可以通过写入逻辑“1”来清零。

  1. 位3—ADIE:ADC中断允许

当该位和SREG寄存器中的I位同时被置位时,允许ADC转换完成中断。

  1. 位2,0—ADPS[2:0]:ADC预分频选择

这些位决定了XTAL时钟与输入到ADC的ADC时钟之间分频数,见表6.2.3。

表6.2.3   ADC时钟分频

ADPS[2:0]

分  频  系  数

000

2

001

2

010

4

011

8

100

16

101

32

110

64

111

128

本实例中我们需要使能ADC,即ADEN设置为1,我们不用自动转换,也不需要中断,所以,ADTE、ADIE位不需要设置。在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。本例中使用的时钟是12M的,所以要将时钟64分频,分频后ADC频率为188KHz,即时钟分频选择ADPS[2:0]=6。

3.ADC数据寄存器—ADCL和ADCH

  1. ADLAR = 0,ADC转换结果右对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第9张图片

  1. ADLAR = 1,ADC转换结果左对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第10张图片
当ADC转换完成后,可以读取ADC寄存器的ADC0-ADC9得到ADC的转换的结果。如果是差分输入,转换值为二进制的补码形式。一旦开始读取ADCL后,ADC数据寄存器就不能被ADC更新,直到ADCH寄存器被读取为止。因此,如果结果是左对齐(ADLAR=1),且不需要大于8位的精度的话,仅仅读取ADCH寄存器就足够了。否则,必须先读取ADCL寄存器,再读取ADCH寄存器。ADMUX寄存器中的ADLAR位决定了从ADC数据寄存器中读取结果的格式。如果ADLAR位为“1”,结果将是左对齐;如果ADLAR位为“0”(默认情况),结果将是右对齐。 
4.特殊功能I/O寄存器—SFIOR

  1. 位7..5—ADTS[2:0]:ADC自动转换触发源选择

当ADCSRA寄存器中的ADATE为“1”,允许ADC工作在自动转换触发工作模式时,这3位的设置用于选择ADC的自动转换触发源。如果禁止了ADC的自动转换触发(ADATE为“0”),这3个位的设置值将不起任何作用。

表10-6   ADC自动转换触发源的选择

ADTS[2:0]

触  发  源

000

连续自由转换

001

模拟比较器

010

外部中断0

011

T/C0比较匹配

100

T/C0溢出

101

T/C1比较匹配B

110

T/C1溢出

111

T/C1输入捕捉

本例中我们不使用自动转换功能,所以该寄存器可以不必设置。
6.2.3、电路 
本实例的电路包括232电平转换电路和电阻分压电路,这两种电路在前面的实例中均做过介绍,这里不再重复。
1、电路原理
在本实例中利用MAX3232芯片使单片机输出的TTL电平转换为标准的RS232电平,从而使计算机能够识别。同时将计算机输出的RS232电平转换为单片机可以识别的TTL电平。
利用电位器产生电阻分压电路,从而产生变化的模拟电压加到单片机的模拟信号采集端口,供单片机采集。
2、电路连接 
电路中MAX3232芯片的9、10引脚分别连接单片机的PD0、PD1端口,MAX3232的13、14引脚分别连接计算机串口线的3、2脚。
电位器RP2的动片引脚连接单片机的模拟信号采集通道PA0(ADC0)。
3、特别说明
本学习板采用的是串口插座是公头的,所以与计算机相连的串口连接线应该是交叉串口线,而不是串口延长线。
6.2.4、程序设计 
1、程序功能 

程序的功能是通过单片机的串行接口,将单片机采集到的模拟电压值发送到计算机中,通过计算机上的串口助手显示采集的电压值。    
● 单片机串行接收中断的编程
在本例中,我们用到了单片机的串行接收中断,需要编写串行接收中断服务程序,通过查询WINAVR(GCC)的中断库函数手册,可以查找到ATmega16单片机串行接收中断的中断向量为USART_RXC_vect。据此我们可以编写串行接收中断服务程序,如下:
//接收中断函数 
ISR(USART_RXC_vect )
{
unsigned char Rev;
Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 
Usart_PutChar(Rev);    //将接收到的数据发送 
}
在中断服务程序中,我们首先把单片机串口接收到的数据放入变量Rev中,然后调用上一实例中编写的串行接口字节发送函数将变量Rev中的数据发送到计算机。 
2、 单片机与计算机串行通信结果的观察 
在观察本例运行结果时,我们同样要用到串口助手,本例中,单片机发送串口数据采用的波特率是9600bps,数据格式是8位数据位,1位停止位,无奇偶校验。在串口助手里面,我们也要将波特率和数据格式设置成一样的。 
3、函数说明 
本实例用到了6个函数,分别是: 
void Port_Init(void);   //端口初始化配置 
void Usart_Init(void);  //USART寄存器设置 
void AD_Init(void);    //AD初始化 
void Usart_PutChar(unsigned char cTXData);  //字节发送函数 
void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 
unsigned int AD_GetData(void);    //AD转换函数   
4、使用WINAVR开发环境,在本例中我们使用的是外部12M的晶振,所以需要将MAKEFILE文件中的时钟频率修改为12M。另外在程序烧录到单片机的时候,熔丝位也要选择为外部12M晶振(注意是晶振,不是外部振荡器,一定不要选择错了,否则会导致单片机不能再烧写程序)。
5、程序代码

#include         #include #include   //中断函数头文件

//常量声明 #define BAUD 9600         //波特率设置值

//全局变量声明 unsigned int ADData;          //AD转换获得的数据

//函数声明 void Port_Init(void);   //端口初始化配置 void Usart_Init(void);  //USART寄存器设置 void AD_Init(void);    //AD初始化 void Usart_PutChar(unsigned char cTXData);  //字节发送函数 void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 unsigned int AD_GetData(void);    //AD转换函数

int main(void)             { unsigned char Delay3s;

      Port_Init(); Usart_Init(); AD_Init(); Usart_PutString("AD转换测试程序"); Usart_PutString("测得ADC0通道的电压值为:"); sei();          //使能全局中断   while(1) { ADData = (int)((long)AD_GetData() * 5010 / 1024);         //将获得的AD值转换为电压值 //单位为mv。 Usart_PutChar(ADData / 1000 + 0x30);      //得到电压值的千位并发送 Usart_PutChar('.');                          //发送小数点 Usart_PutChar(ADData % 1000 / 100 + 0x30);   //得到电压值的百位并发送 Usart_PutChar(ADData % 100 / 10 + 0x30);    //得到电压值的十位并发送 Usart_PutChar(ADData % 10 + 0x30);     //得到电压值的个位并发送 Usart_PutChar('V');                  //发送电压符号“V” Usart_PutChar(0x0d);     // Usart_PutChar(0x0a);     //  AD值发送结束,回车换行 for(Delay3s = 0;Delay3s < 30;Delay3s++)     //延时3S { _delay_ms(90); } } }

//端口状态初始化设置函数 void Port_Init() { PORTA = 0X00;         DDRA = 0x00;   //ADC通道设置为输入口,高阻态     }

//USART寄存器配置函数 void Usart_Init() { UCSRA = 0X00;     UCSRC |= (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0);  //异步,数据格式8,N,1 //UCSRC寄存器与UBRRH寄存器共用相同的I/O地址,写 UCSRC 时, URSEL 应设置为 1。 UBRRL = (F_CPU / BAUD / 16 - 1) % 256;    //波特率设置 UBRRH = (F_CPU / BAUD / 16 - 1) / 256;         UCSRB |= (1 << RXCIE) | (1 << RXEN) | (1 << TXEN);    //发送使能 }

//字节发送函数 void Usart_PutChar(unsigned char cTXData) { while( !(UCSRA & (1 << UDRE)) );  //只有数据寄存器为空时才能发送数据 UDR = cTXData;                   //发送数据送USART I/O数据寄存器-UDR }

//接收中断函数 ISR(USART_RXC_vect ) { unsigned char Rev; Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 Usart_PutChar(Rev);    //将接收到的数据发送 }

void Usart_PutString(unsigned char *pcString) { while (*pcString) { Usart_PutChar(*pcString++);   } Usart_PutChar(0x0D); Usart_PutChar(0x0A);  //结尾发送回车换行 }

//AD转换初始化函数 void AD_Init() { ADMUX |= (1 << REFS0);     //ADC参考电压为AVcc,ADC结果右对齐,选择通道ADC0 ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); //使能AD转换,ADC时钟64分频 }

//AD转换函数 unsigned int AD_GetData() { ADCSRA |= (1 << ADSC);     //开始AD转换 while(!(ADCSRA & (1 << ADIF)));    //等待转换完成 ADCSRA |= (1 << ADIF);              //清零ADC中断标志位 return ADC;        //返回ADC值 }

附录:ADC应用设计要点

1.预分频与转换时间 
在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。在要求转换精度低于10位的情况下,ADC的采样时钟可以高于200kHz,以获得更高的采样率。 
ADC模块中包含一个预分频器的ADC时钟源,它可以对大于100KHz的系统时钟进行分频,以获得合适的ADC时钟提供ADC使用。预分频器的分频系数由ADCSRA寄存器中的ADPS位设置的。一旦寄存器ADCSRA中的ADEN位置“1”(ADC开始工作),预分频器就启动开始计数。ADEN位为“1”时,预分频器将一直工作;ADEN位为“0”时,预分频器一直处在复位状态。 
AVR的ADC完成一次转换的时间见表6.2.5。从表中可以看出,完成一次ADC转换通常需要13-14个ADC时钟。而启动ADC开始第一次转换到完成的时间需要25个ADC时钟,这是因为要对ADC单元的模拟电路部分进行初始化。


表6.2.5  ADC转换和采样保持时间

转 换 形 式

采样保持时间

完 成 转 换 总 时 间

启动ADC后的第一次转换

13.5个ADC时钟

25个ADC时钟

正常转换,单端输入

1.5个ADC时钟

13个ADC时钟

自动触发方式

2个ADC时钟

13.5个ADC时钟

正常转换,差分输入

1.5/2.5个ADC时钟

13/14个ADC时钟

当ADCSRA寄存器中的ADSC位置位,启动ADC转换时,A/D转换将在随后ADC时钟的上升沿开始。一次正常的A/D转换开始时,需要1.5个ADC时钟周期的采样保持时间(ADC首次启动后需要13.5个ADC时钟周期的采样保持时间)。当一次A/D转换完成后,转换结果写入ADC数据寄存器,ADIF(ADC中断标志位)将被置位。在单次转换模式下,ADSC也同时被清零。用户程序可以再次置位ADSC位,新的一次转换将在下一个ADC时钟的上升沿开始。 
当ADC设置为自动触发方式时,触发信号的上升沿将启动一次ADC转换。转换完成的结果将一直保持到下一次触发信号的上升沿出现,然后开始新的一次ADC转换。这就保证了使ADC每隔一定的时间间隔进行一次转换。在这种方式下,ADC需要2个ADC时钟周期的采样保持时间。 
在自由连续转换模式下,一次转换完毕后马上开始一次新的转换,此时,ADSC位一直保持为“1”。

2.ADC输入通道和参考电源的选择 
寄存器ADMUX中的MUXn和REFS1、REFS0位实际上是一个缓冲器,该缓冲器与一个MCU可以随机读取的临时寄存器相连通。采用这种结构,保证了ADC输入通道和参考电源只能在ADC转换过程中的安全点被改变。在ADC转换开始前,通道和参考电源可以不断被更新,一旦转换开始,通道和参考电源将被锁定,并保持足够时间,以确保ADC转换的正常进行。在转换完成前的最后一个ADC时钟周期(ADCSRA的ADIF位置“1”时),通道和参考电源又开始重新更新。注意,由于A/D转换开始于置位ADSC后的第一个ADC时钟的上升沿,因此,在置位ADSC后的一个ADC时钟周期内不要将一个新的通道或参考电源写入到ADMUX寄存器中。 
改变差分输入通道时需特别当心。一旦确定了差分输入通道,增益放大器需要125µs的稳定时间。所以在选择了新的差分输入通道后的125µs内不要启动A/D转换,或将这段时间内转换结果丢弃。通过改变ADMUX中的REFS1、REFS0来更改参考电源后,第一次差分转换同样要遵循以上的时间处理过程。

  1. 当要改变ADC输入通道时,应该遵守以下方式,以保证能够选择到正确的通道:

在单次转换模式下,总是在开始转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到转换完成后,再改变通道选择。 
在连续转换模式下,总是在启动ADC开始第一次转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到第一次转换完成后再改变通道的设置。然而由于此时新一次的转换已经自动开始,所以,当前这次的转换结果仍反映前一通道的转换值,而下一次的转换结果将为新设置通道的值。

  1. ADC电压参考源

ADC的参考电压(VREF)决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果接近于0x3FF。ADC的参考电压VREF可以选择为AVCC或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。 
AVCC通过一个无源开关连接到ADC。内部2.56V参考源是由内部能隙参考源(VBC)通过内部的放大器产生的。注意,无论选用什么内部参考电源,外部AREF引脚都是直接与ADC相连的,因此,可以通过外部在AREF引脚和地之间并接一个电容,使内部参考电源更加稳定和抗噪。可以通过使用高阻电压表测量AREF引脚,来获得参考电源VREF的电压值。由于VREF是一个高阻源,因此,只有容性负载可以连接到该引脚。 
如果将一个外部固定的电压源连接到AREF引脚,那就不能使用任何的内部参考电源,否则就会使外部电压源短路。外部参考电源的范围应在2.0V到AVCC-0.2V之间。参考电源改变后的第一次ADC转换结果可能不太准确,建议抛弃该次转换结果。 
3.ADC转换结果 
A/D转换结束后(ADIF = 1),在ADC数据寄存器(ADCL和ADCH)中可以取得转换的结果。对于单端输入的A/D转换,其转换结果为: 
ADC =(VIN×1024)/ VREF 
其中VIN表示选定的输入引脚上的电压,VREF表示选定的参考电源的电压。0x000表示输入引脚的电压为模拟地,0x3FF表示输入引脚的电压为参考电压值减去一个LSB。 
对于差分转换,其结果为: 
ADC=(VPOS-VNEG) ×GAIN×512/VREF 
例:若差分输入通道选择为ADC3-ADC2,10倍增益,参考电压2.56V,左端对齐(ADMUX=0xED),ADC3引脚上电压300mV,ADC2引脚上电压500mV。 
则ADCR =(300-500)×10×512 / 2560 = -400 = 0x270
ADCL=0x00,ADCH=0x9C。 
若结果为右端对齐时(ADLAR=“0”),则ADCL=0x70,ADCH=0x02。

附录2、ADC应用设计的深入讨论 
尽管AVR内部集成了10位的ADC,但是在实际应用中,要想真正实现10位精度,比较稳定的ADC的话,并不象上一节中的例子那么简单。需要进一步从硬件、软件等方面进行综合的、细致的考虑。下面介绍一些在ADC设计应用中应该考虑的几个要点。 
1.AVcc的稳定性。 
AVcc是提供给ADC工作的电源,如果AVcc不稳定,就会影响ADC的转换精度。在图10-5中,系统电源通过一个LC滤波后接入AVcc,这样就能很好的抑制掉系统电源中的高频躁声,提高了AVcc的稳定性。另外在要求比较高的场合使用ADC时,PA口上的那些没被用做ADC输入的端口尽量不要作为数字I/O口使用。因为PA口的工作电源是由AVcc提供的,如果PA口上有比较大的电流波动,也会影响AVcc的稳定。 
2.参考电压VREF的选择确定 
在实际应用中,要根据输入测量电压的范围选择正确的参考电压VREF,以求得到比较好的转换精度。ADC的参考电压VREF还决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果全部接近于0x3FF,因此ADC的参考电压应稍大于模拟输入电压的最高值。 
ADC的参考电压VREF可以选择为AVCC,或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。外接参考电压应该稳定,并大于2.0V(芯片的工作电压为1.8V时,外接参考电压应大于1.0V)。要求比较高的场合,建议在AREF引脚外接标准参考电压源来作为ADC的参考电源。 
3. ADC通道带宽和输入阻抗 
不管使用单端输入转换还是差分输入转换方式,所有模拟输入口的输入电压应在AVcc-GNG之间。 
在单端ADC转换方式时,ADC通道的输入频率带宽取决于ADC转换时钟频率。一次常规的ADC转换需要13个ADC时钟,当ADC转换时钟为1MHz时,一秒种内ADC采样转换的次数约77K。根据采样定理,此时ADC通道的带宽为38.5KHz。 
差分方式ADC转换的带宽是由芯片内部的差分放大器的带宽决定,为4KHz。 
AVR的ADC输入阻抗典型值为100MΩ,为保证测量的准确,被测信号源的输出阻抗要尽可能的低,应在10K以下。 
4. ADC采样时钟的选择 
通常条件下,AVR的ADC逐次比较电路要达到转换的最大精度,需要一个50K~200KHz的采样时钟。一次正常的ADC转换过程需要13个采样时钟,假定ADC采样时钟为200KHz,那么最高的采样速率为200K/13=15.384K。因此根据采样定理,理论上被测模拟信号的最高频率为7.7K! 
尽管可以设置ADC的采样时钟为1M,但并不能提高ADC转换精度,反而会降低转换精度(受逐次比较硬件电路的限制),因此AVR的ADC不能完成高速ADC的任务。如果所需的转换精度低于10位,那么采样时钟可以高于200KHz,以达到更高的采样频率。 
ADC采样时钟的选择方式为:给出或估计被测模拟信号的最高频率fs,取采样频率为fs的4-10倍,再乘上13为ADC采样时钟频率,该频率应在50K~200KHz之间。 如果该频率大于200KHz,则ADC的10位精度不能保证。如果该频率小于50Khz,则可选择50K~200KHz之间的数值。 
5.模拟噪声的抑制 
器件外部和内部的数字电路会产生电磁干扰,并会影响模拟测量的精度。如果ADC转换精度要求很高,可以采用以下的技术来降低噪声的影响: 
(1)使模拟信号的通路尽可能的短。模拟信号连线应从模拟地的布线盘上通过,并使它们尽可能远离高速开关数字信号线。 
(2)AVR的AVcc引脚应该通过LC网络与数字端电源Vcc相连。
(3)采用ADC噪声抑制器功能来降低来自MCU内部的噪声。 
(4)如果某些ADC引脚是作为通用数字输出口使用,那么在ADC转换过程中,不要改变这些引脚的状态。
6.ADC的校正 
由于AVD内部ADC部分的放大器非线性等客观原因,ADC的转换结果会有误差的。如果要获得高精度的ADC转换,还需要对ADC结果进行校正。具体的方法请参考AVR应用笔记AVR120(avr_app_120.pdf),在这篇应用设计参考中详细介绍了误差的种类,以及校正方案。 
7.ADC精度的提高 
在有了上述几点的保证后,通过软件的手段也能适当的提高ADC的精度。如采用多次测量取平均,软件滤波算法等。在AVR应用笔记AVR121(avr_app_121.pdf)中介绍了一种使用过采样算法的软件实现,可以将ADC的精度提高到11位或更高,当然这是在花费更多的时间基础上实现的。6.2 模数转换的ADC实验 

6.2.1、实例功能 
AVR的模数转换器ADC具有下列特点:

  1. 10位精度;
  2. 0.5LSB积分非线形误差
  3. ±2LSB的绝对精度;
  4. 13µs~260µs的转换时间;
  5. 在最大精度下可达到每秒15kSPS的采样速率;
  6. 8路可选的单端输入通道;
  7. 7路差分输入通道;
  8. 2路差分输入通道带有可选的10×和200×增益;
  9. ADC转换结果的读取可设置为左端对齐(LEFT ADJUSTMENT);
  10. ADC的电压输入范围0~Vcc;
  11. 可选择的内部2.56V的ADC参考电压源;
  12. 自由连续转换模式和单次转换模式;
  13. ADC自动转换触发模式选择;
  14. ADC转换完成中断;
  15. 休眠模式下的噪声抑制器(NOISE CANCELER)。

在本实例中,我们将编写程序实现将模数转换后获得的电压值通过单片机的串口发送到计算机,然后通过计算机上的串口助手显示测量的电压值。 
本实例共有3个功能模块,分别描述如下: 
● 单片机系统:使用单片机的串口实现将模数转换后获得的电压值通过串口发送到计算机。 
● 外围电路:RS232电平转换电路,DB9串行接口插座,模拟电压输入采集电路。
● 软件程序:进一步熟悉单片机的串行通信,并掌握单片机的模数转换的方法。
6.2.2、器件和原理 
关于串行接口的原理已接单片机与计算机的串口的连接在上一实例中进行了描述,在本实例中不再重复。
本实例只介绍ATmega16单片机如何通过内置的模数转换模块采集外界输入的模拟电压。
1、ATmega16单片机的模数转换器ADC介绍 
由于单片机只能处理数字信号,所以外部的模拟信号量需要转变成数字量才能进一步的由单片机进行处理。ATmega16内部集成有一个10位逐次比较(successive approximation)ADC电路。因此使用AVR可以非常方便的处理输入的模拟信号量。 
ATmega16的ADC与一个8通道的模拟多路选择器连接,能够对以PORTA作为ADC输入引脚的8路单端模拟输入电压进行采样,单端电压输入以0V(GND)为参考。另外还支持16种差分电压输入组合,其中2种差分输入方式(ADC1,ADC0和ACD3,ADC2)带有可编程增益放大器,能在A/D转换前对差分输入电压进行0dB(1×),20dB(10×)或46dB(200×)的放大。还有七种差分输入方式的模拟输入通道共用一个负极(ADC1),此时其它任意一个ADC引脚都可作为相应的正极。若增益为1×或10×,则可获得8位的精度。如果增益为200×,那么转换精度为7位。 
AVR的ADC功能单元由独立的专用模拟电源引脚AVcc供电。AVcc和Vcc的电压差别不能大于±0.3V。ADC转换的参考电源可采用芯片内部的2.56V参考电源,或采用AVcc,也可使用外部参考电源。使用外部参考电源时,外部参考电源由引脚ARFE接入。使用内部电压参考源时,可以通过在AREF引脚外部并接一个电容来提高ADC的抗噪性能。
ADC功能单元包括采样保持电路,以确保输入电压在ADC转换过程中保持恒定。ADC通过逐次比较(successive approximation)方式,将输入端的模拟电压转换成10位的数字量。最小值代表地,最大值为AREF引脚上的电压值减1个LSB。可以通过ADMUX寄存器中REFSn位的设置,选择将芯片内部参考电源(2.56V)或AVcc连接到AREF,作为A/D转换的参考电压。这时,内部电压参考源可以通过外接于AREF引脚的电容来稳定,以改进抗噪特性。 
模拟输入通道和差分增益的选择是通过ADMUX寄存器中的MUX位设定的。任何一个ADC的输入引脚,包括地(GND)以及内部的恒定能隙(fixed bandgap)电压参考源,都可以被选择用来作为ADC的单端输入信号。而ADC的某些输入引脚则可选择作为差分增益放大器的正、负极输入端。当选定了差分输入通道后,差分增益放大器将两输入通道上的电压差按选定增益系数放大,然后输入到ADC中。若选定使用单端输入通道,则增益放大器无效。 
通过设置ADCSRA寄存器中的ADC使能位ADEN来使能ADC。在ADEN没有置“1”前,参考电压源和输入通道的选定将不起作用。当ADEN位清“0”后,ADC将不消耗能量,因此建议在进入节电休眠模式前将ADC关掉。 
ADC将10位的转换结果放在ADC数据寄存器中(ADCH和ADCL)。默认情况下,转换结果为右端对齐(RIGHT ADJUSTED)的。但可以通过设置ADMUX寄存器中ADLAR位,调整为左端对齐(LEFT ADJUSTED)。如果转换结果是左端对齐,并且只需要8位的精度,那么只需读取ADCH寄存器的数据作为转换结果就达到要求了。否则,必须先读取ADCL寄存器,然后再读取ADCH寄存器,以保证数据寄存器中的内容是同一次转换的结果。因为一旦ADCL寄存器被读取,就阻断了ADC对ADC数据寄存器的操作。这就意味着,一旦指令读取了ADCL,那么必须紧接着读取一次ADCH;如果在读取ADCL和读取ADCH的过程中正好有一次ADC转换完成,ADC的2个数据寄存器的内容是不会被更新的,该次转换的结果将丢失。只有当ADCH寄存器被读取后,ADC才可以继续对ADCL和ADCH寄存器操作更新。 
ADC有自己的中断,当转换完成时中断将被触发。尽管在顺序读取ADCL和ADCH寄存器过程中,ADC对ADC数据寄存器的更新被禁止,转换的结果丢失,但仍会触发ADC中断。 
2、ATmwga16单片机的模数转换器ADC相关的I/O寄存器 
1.ADC多路复用器选择寄存器—ADMUX

  1. 位7,6—REFS[1:0]:ADC参考电源选择

REFS1、REFS2用于选择ADC的参考电压源,见表6.2.1。如果这些位在ADC转换过程中被改变,新的选择将在该次ADC转换完成后(ADCSRA中的ADIF被置位)才生效。一旦选择内部参考源(AVcc、2.56V)为ADC的参考电压后,AREF引脚上不得施加外部的参考电源,只能与GND之间并接抗干扰电容。


表6.2.1  ADC参考电源选择

REFS1

REFS0

ADC参考电源

0

0

外部引脚AREF,断开内部参考源连接

0

1

AVcc,AREF外部并接电容

1

0

保留

1

1

内部2.56V,AREF外部并接电容

  1. 位5—ADLAR:ADC结果左对齐选择

ADLAR位决定转换结果在ADC数据寄存器中的存放形式。写“1”到ADLAR位,将使转换结果左对齐(LEFT ADJUST);否则,转换结果为右对齐(RIGHT ADJUST)。无论ADC是否正在进行转换,改变ADLAR位都将会立即影响ADC数据寄存器。

  1. 位4..0—MUX4:0:模拟通道和增益选择

这5个位用于对连接到ADC的输入通道和差分通道的增益进行选择设置,详见表6.2.2。注意,只有转换结束后(ADCSRA的ADIF是“1”),改变这些位才会有效。


表6.2.2   ADC输入通道和增益选择

MUX[4:0]

单端输入

差分正极输入

差分负极输入

增益

00000

ADC0

N/A

00001

ADC1

00010

ADC2

00011

ADC3

00100

ADC4

00101

ADC5

00110

ADC6

00111

ADC7

01000

N/A

ADC0

ADC0

10×

01001

ADC1

ADC0

10×

01010

ADC0

ADC0

200×

01011

ADC1

ADC0

200×

01100

ADC2

ADC2

10×

01101

ADC3

ADC2

10×

01110

ADC2

ADC2

200×

01111

ADC3

ADC2

200×

10000

ADC0

ADC1

10001

ADC1

ADC1

10010

ADC2

ADC1

10011

ADC3

ADC1

10100

ADC4

ADC1

10101

ADC5

ADC1

10110

ADC6

ADC1

10111

ADC7

ADC1

11000

ADC0

ADC2

11001

ADC1

ADC2

11010

ADC2

ADC2

11011

ADC3

ADC2

11100

ADC4

ADC2

11101

ADC5

ADC2

11110

1.22V(VBG)

N/A

11111

0V(GND)

   本实例中我们需要设置ADC的参考电压源为AVcc,即REFS0设置为1,ADC默认转换结果为右对齐,我们不需要改变,模拟通道选择ADC0通道单端输入,即MUX4:0。 
2.ADC控制和状态寄存器A—ADCSRA

  1. 位7—ADEN:ADC使能

该位写入“1”时使能ADC,写入“0”关闭ADC。如在ADC转换过程中将ADC关闭,该次转换随即停止。

  1. 位6—ADSC:ADC转换开始

在单次转换模式下,置该位为“1”,将启动一次转换。在自由连续转换模式下,该位写入“1”将启动第一次转换。先置位ADEN位使能ADC,再置位ADSC;或置位ADSC的同时使能ADC,都会使能ADC开始进行第一次转换。第一次ADC转换将需要25个ADC时钟周期,而不是常规转换的13个ADC时钟周期,这是因为第一次转换需要完成对ADC的初始化。 
在ADC转换的过程中,ADSC将始终读出为“1”。当转换完成时,它将转变为“0”。强制写入“0”是无效的。

  1. 位5—ADATE:ADC自动转换触发允许

当该位被置为“1”时,允许ADC工作在自动转换触发工作模式下。在该模式下,在触发信号的上升沿时ADC将自动开始一次ADC转换过程。ADC的自动转换触发信号源由SFIOR寄存器中的ADTS位选择确定。

  1. 位4—ADIF:ADC中断标志位

当ADC转换完成并且ADC数据寄存器被更新后该位被置位。如果ADIE位(ADC转换结束中断允许)和SREG寄存器中的I位被置“1”,ADC中断服务程序将被执行。ADIF在执行相应的中断处理向量时被硬件自动清零。此外,ADIF位可以通过写入逻辑“1”来清零。

  1. 位3—ADIE:ADC中断允许

当该位和SREG寄存器中的I位同时被置位时,允许ADC转换完成中断。

  1. 位2,0—ADPS[2:0]:ADC预分频选择

这些位决定了XTAL时钟与输入到ADC的ADC时钟之间分频数,见表6.2.3。

表6.2.3   ADC时钟分频

ADPS[2:0]

分  频  系  数

000

2

001

2

010

4

011

8

100

16

101

32

110

64

111

128

本实例中我们需要使能ADC,即ADEN设置为1,我们不用自动转换,也不需要中断,所以,ADTE、ADIE位不需要设置。在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。本例中使用的时钟是12M的,所以要将时钟64分频,分频后ADC频率为188KHz,即时钟分频选择ADPS[2:0]=6。

3.ADC数据寄存器—ADCL和ADCH

  1. ADLAR = 0,ADC转换结果右对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第11张图片

  1. ADLAR = 1,ADC转换结果左对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第12张图片
当ADC转换完成后,可以读取ADC寄存器的ADC0-ADC9得到ADC的转换的结果。如果是差分输入,转换值为二进制的补码形式。一旦开始读取ADCL后,ADC数据寄存器就不能被ADC更新,直到ADCH寄存器被读取为止。因此,如果结果是左对齐(ADLAR=1),且不需要大于8位的精度的话,仅仅读取ADCH寄存器就足够了。否则,必须先读取ADCL寄存器,再读取ADCH寄存器。ADMUX寄存器中的ADLAR位决定了从ADC数据寄存器中读取结果的格式。如果ADLAR位为“1”,结果将是左对齐;如果ADLAR位为“0”(默认情况),结果将是右对齐。 
4.特殊功能I/O寄存器—SFIOR

  1. 位7..5—ADTS[2:0]:ADC自动转换触发源选择

当ADCSRA寄存器中的ADATE为“1”,允许ADC工作在自动转换触发工作模式时,这3位的设置用于选择ADC的自动转换触发源。如果禁止了ADC的自动转换触发(ADATE为“0”),这3个位的设置值将不起任何作用。

表10-6   ADC自动转换触发源的选择

ADTS[2:0]

触  发  源

000

连续自由转换

001

模拟比较器

010

外部中断0

011

T/C0比较匹配

100

T/C0溢出

101

T/C1比较匹配B

110

T/C1溢出

111

T/C1输入捕捉

本例中我们不使用自动转换功能,所以该寄存器可以不必设置。
6.2.3、电路 
本实例的电路包括232电平转换电路和电阻分压电路,这两种电路在前面的实例中均做过介绍,这里不再重复。
1、电路原理
在本实例中利用MAX3232芯片使单片机输出的TTL电平转换为标准的RS232电平,从而使计算机能够识别。同时将计算机输出的RS232电平转换为单片机可以识别的TTL电平。
利用电位器产生电阻分压电路,从而产生变化的模拟电压加到单片机的模拟信号采集端口,供单片机采集。
2、电路连接 
电路中MAX3232芯片的9、10引脚分别连接单片机的PD0、PD1端口,MAX3232的13、14引脚分别连接计算机串口线的3、2脚。
电位器RP2的动片引脚连接单片机的模拟信号采集通道PA0(ADC0)。
3、特别说明
本学习板采用的是串口插座是公头的,所以与计算机相连的串口连接线应该是交叉串口线,而不是串口延长线。
6.2.4、程序设计 
1、程序功能 

程序的功能是通过单片机的串行接口,将单片机采集到的模拟电压值发送到计算机中,通过计算机上的串口助手显示采集的电压值。    
● 单片机串行接收中断的编程
在本例中,我们用到了单片机的串行接收中断,需要编写串行接收中断服务程序,通过查询WINAVR(GCC)的中断库函数手册,可以查找到ATmega16单片机串行接收中断的中断向量为USART_RXC_vect。据此我们可以编写串行接收中断服务程序,如下:
//接收中断函数 
ISR(USART_RXC_vect )
{
unsigned char Rev;
Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 
Usart_PutChar(Rev);    //将接收到的数据发送 
}
在中断服务程序中,我们首先把单片机串口接收到的数据放入变量Rev中,然后调用上一实例中编写的串行接口字节发送函数将变量Rev中的数据发送到计算机。 
2、 单片机与计算机串行通信结果的观察 
在观察本例运行结果时,我们同样要用到串口助手,本例中,单片机发送串口数据采用的波特率是9600bps,数据格式是8位数据位,1位停止位,无奇偶校验。在串口助手里面,我们也要将波特率和数据格式设置成一样的。 
3、函数说明 
本实例用到了6个函数,分别是: 
void Port_Init(void);   //端口初始化配置 
void Usart_Init(void);  //USART寄存器设置 
void AD_Init(void);    //AD初始化 
void Usart_PutChar(unsigned char cTXData);  //字节发送函数 
void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 
unsigned int AD_GetData(void);    //AD转换函数   
4、使用WINAVR开发环境,在本例中我们使用的是外部12M的晶振,所以需要将MAKEFILE文件中的时钟频率修改为12M。另外在程序烧录到单片机的时候,熔丝位也要选择为外部12M晶振(注意是晶振,不是外部振荡器,一定不要选择错了,否则会导致单片机不能再烧写程序)。
5、程序代码

#include         #include #include   //中断函数头文件

//常量声明 #define BAUD 9600         //波特率设置值

//全局变量声明 unsigned int ADData;          //AD转换获得的数据

//函数声明 void Port_Init(void);   //端口初始化配置 void Usart_Init(void);  //USART寄存器设置 void AD_Init(void);    //AD初始化 void Usart_PutChar(unsigned char cTXData);  //字节发送函数 void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 unsigned int AD_GetData(void);    //AD转换函数

int main(void)             { unsigned char Delay3s;

      Port_Init(); Usart_Init(); AD_Init(); Usart_PutString("AD转换测试程序"); Usart_PutString("测得ADC0通道的电压值为:"); sei();          //使能全局中断   while(1) { ADData = (int)((long)AD_GetData() * 5010 / 1024);         //将获得的AD值转换为电压值 //单位为mv。 Usart_PutChar(ADData / 1000 + 0x30);      //得到电压值的千位并发送 Usart_PutChar('.');                          //发送小数点 Usart_PutChar(ADData % 1000 / 100 + 0x30);   //得到电压值的百位并发送 Usart_PutChar(ADData % 100 / 10 + 0x30);    //得到电压值的十位并发送 Usart_PutChar(ADData % 10 + 0x30);     //得到电压值的个位并发送 Usart_PutChar('V');                  //发送电压符号“V” Usart_PutChar(0x0d);     // Usart_PutChar(0x0a);     //  AD值发送结束,回车换行 for(Delay3s = 0;Delay3s < 30;Delay3s++)     //延时3S { _delay_ms(90); } } }

//端口状态初始化设置函数 void Port_Init() { PORTA = 0X00;         DDRA = 0x00;   //ADC通道设置为输入口,高阻态     }

//USART寄存器配置函数 void Usart_Init() { UCSRA = 0X00;     UCSRC |= (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0);  //异步,数据格式8,N,1 //UCSRC寄存器与UBRRH寄存器共用相同的I/O地址,写 UCSRC 时, URSEL 应设置为 1。 UBRRL = (F_CPU / BAUD / 16 - 1) % 256;    //波特率设置 UBRRH = (F_CPU / BAUD / 16 - 1) / 256;         UCSRB |= (1 << RXCIE) | (1 << RXEN) | (1 << TXEN);    //发送使能 }

//字节发送函数 void Usart_PutChar(unsigned char cTXData) { while( !(UCSRA & (1 << UDRE)) );  //只有数据寄存器为空时才能发送数据 UDR = cTXData;                   //发送数据送USART I/O数据寄存器-UDR }

//接收中断函数 ISR(USART_RXC_vect ) { unsigned char Rev; Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 Usart_PutChar(Rev);    //将接收到的数据发送 }

void Usart_PutString(unsigned char *pcString) { while (*pcString) { Usart_PutChar(*pcString++);   } Usart_PutChar(0x0D); Usart_PutChar(0x0A);  //结尾发送回车换行 }

//AD转换初始化函数 void AD_Init() { ADMUX |= (1 << REFS0);     //ADC参考电压为AVcc,ADC结果右对齐,选择通道ADC0 ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); //使能AD转换,ADC时钟64分频 }

//AD转换函数 unsigned int AD_GetData() { ADCSRA |= (1 << ADSC);     //开始AD转换 while(!(ADCSRA & (1 << ADIF)));    //等待转换完成 ADCSRA |= (1 << ADIF);              //清零ADC中断标志位 return ADC;        //返回ADC值 }

附录:ADC应用设计要点

1.预分频与转换时间 
在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。在要求转换精度低于10位的情况下,ADC的采样时钟可以高于200kHz,以获得更高的采样率。 
ADC模块中包含一个预分频器的ADC时钟源,它可以对大于100KHz的系统时钟进行分频,以获得合适的ADC时钟提供ADC使用。预分频器的分频系数由ADCSRA寄存器中的ADPS位设置的。一旦寄存器ADCSRA中的ADEN位置“1”(ADC开始工作),预分频器就启动开始计数。ADEN位为“1”时,预分频器将一直工作;ADEN位为“0”时,预分频器一直处在复位状态。 
AVR的ADC完成一次转换的时间见表6.2.5。从表中可以看出,完成一次ADC转换通常需要13-14个ADC时钟。而启动ADC开始第一次转换到完成的时间需要25个ADC时钟,这是因为要对ADC单元的模拟电路部分进行初始化。


表6.2.5  ADC转换和采样保持时间

转 换 形 式

采样保持时间

完 成 转 换 总 时 间

启动ADC后的第一次转换

13.5个ADC时钟

25个ADC时钟

正常转换,单端输入

1.5个ADC时钟

13个ADC时钟

自动触发方式

2个ADC时钟

13.5个ADC时钟

正常转换,差分输入

1.5/2.5个ADC时钟

13/14个ADC时钟

当ADCSRA寄存器中的ADSC位置位,启动ADC转换时,A/D转换将在随后ADC时钟的上升沿开始。一次正常的A/D转换开始时,需要1.5个ADC时钟周期的采样保持时间(ADC首次启动后需要13.5个ADC时钟周期的采样保持时间)。当一次A/D转换完成后,转换结果写入ADC数据寄存器,ADIF(ADC中断标志位)将被置位。在单次转换模式下,ADSC也同时被清零。用户程序可以再次置位ADSC位,新的一次转换将在下一个ADC时钟的上升沿开始。 
当ADC设置为自动触发方式时,触发信号的上升沿将启动一次ADC转换。转换完成的结果将一直保持到下一次触发信号的上升沿出现,然后开始新的一次ADC转换。这就保证了使ADC每隔一定的时间间隔进行一次转换。在这种方式下,ADC需要2个ADC时钟周期的采样保持时间。 
在自由连续转换模式下,一次转换完毕后马上开始一次新的转换,此时,ADSC位一直保持为“1”。

2.ADC输入通道和参考电源的选择 
寄存器ADMUX中的MUXn和REFS1、REFS0位实际上是一个缓冲器,该缓冲器与一个MCU可以随机读取的临时寄存器相连通。采用这种结构,保证了ADC输入通道和参考电源只能在ADC转换过程中的安全点被改变。在ADC转换开始前,通道和参考电源可以不断被更新,一旦转换开始,通道和参考电源将被锁定,并保持足够时间,以确保ADC转换的正常进行。在转换完成前的最后一个ADC时钟周期(ADCSRA的ADIF位置“1”时),通道和参考电源又开始重新更新。注意,由于A/D转换开始于置位ADSC后的第一个ADC时钟的上升沿,因此,在置位ADSC后的一个ADC时钟周期内不要将一个新的通道或参考电源写入到ADMUX寄存器中。 
改变差分输入通道时需特别当心。一旦确定了差分输入通道,增益放大器需要125µs的稳定时间。所以在选择了新的差分输入通道后的125µs内不要启动A/D转换,或将这段时间内转换结果丢弃。通过改变ADMUX中的REFS1、REFS0来更改参考电源后,第一次差分转换同样要遵循以上的时间处理过程。

  1. 当要改变ADC输入通道时,应该遵守以下方式,以保证能够选择到正确的通道:

在单次转换模式下,总是在开始转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到转换完成后,再改变通道选择。 
在连续转换模式下,总是在启动ADC开始第一次转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到第一次转换完成后再改变通道的设置。然而由于此时新一次的转换已经自动开始,所以,当前这次的转换结果仍反映前一通道的转换值,而下一次的转换结果将为新设置通道的值。

  1. ADC电压参考源

ADC的参考电压(VREF)决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果接近于0x3FF。ADC的参考电压VREF可以选择为AVCC或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。 
AVCC通过一个无源开关连接到ADC。内部2.56V参考源是由内部能隙参考源(VBC)通过内部的放大器产生的。注意,无论选用什么内部参考电源,外部AREF引脚都是直接与ADC相连的,因此,可以通过外部在AREF引脚和地之间并接一个电容,使内部参考电源更加稳定和抗噪。可以通过使用高阻电压表测量AREF引脚,来获得参考电源VREF的电压值。由于VREF是一个高阻源,因此,只有容性负载可以连接到该引脚。 
如果将一个外部固定的电压源连接到AREF引脚,那就不能使用任何的内部参考电源,否则就会使外部电压源短路。外部参考电源的范围应在2.0V到AVCC-0.2V之间。参考电源改变后的第一次ADC转换结果可能不太准确,建议抛弃该次转换结果。 
3.ADC转换结果 
A/D转换结束后(ADIF = 1),在ADC数据寄存器(ADCL和ADCH)中可以取得转换的结果。对于单端输入的A/D转换,其转换结果为: 
ADC =(VIN×1024)/ VREF 
其中VIN表示选定的输入引脚上的电压,VREF表示选定的参考电源的电压。0x000表示输入引脚的电压为模拟地,0x3FF表示输入引脚的电压为参考电压值减去一个LSB。 
对于差分转换,其结果为: 
ADC=(VPOS-VNEG) ×GAIN×512/VREF 
例:若差分输入通道选择为ADC3-ADC2,10倍增益,参考电压2.56V,左端对齐(ADMUX=0xED),ADC3引脚上电压300mV,ADC2引脚上电压500mV。 
则ADCR =(300-500)×10×512 / 2560 = -400 = 0x270
ADCL=0x00,ADCH=0x9C。 
若结果为右端对齐时(ADLAR=“0”),则ADCL=0x70,ADCH=0x02。

附录2、ADC应用设计的深入讨论 
尽管AVR内部集成了10位的ADC,但是在实际应用中,要想真正实现10位精度,比较稳定的ADC的话,并不象上一节中的例子那么简单。需要进一步从硬件、软件等方面进行综合的、细致的考虑。下面介绍一些在ADC设计应用中应该考虑的几个要点。 
1.AVcc的稳定性。 
AVcc是提供给ADC工作的电源,如果AVcc不稳定,就会影响ADC的转换精度。在图10-5中,系统电源通过一个LC滤波后接入AVcc,这样就能很好的抑制掉系统电源中的高频躁声,提高了AVcc的稳定性。另外在要求比较高的场合使用ADC时,PA口上的那些没被用做ADC输入的端口尽量不要作为数字I/O口使用。因为PA口的工作电源是由AVcc提供的,如果PA口上有比较大的电流波动,也会影响AVcc的稳定。 
2.参考电压VREF的选择确定 
在实际应用中,要根据输入测量电压的范围选择正确的参考电压VREF,以求得到比较好的转换精度。ADC的参考电压VREF还决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果全部接近于0x3FF,因此ADC的参考电压应稍大于模拟输入电压的最高值。 
ADC的参考电压VREF可以选择为AVCC,或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。外接参考电压应该稳定,并大于2.0V(芯片的工作电压为1.8V时,外接参考电压应大于1.0V)。要求比较高的场合,建议在AREF引脚外接标准参考电压源来作为ADC的参考电源。 
3. ADC通道带宽和输入阻抗 
不管使用单端输入转换还是差分输入转换方式,所有模拟输入口的输入电压应在AVcc-GNG之间。 
在单端ADC转换方式时,ADC通道的输入频率带宽取决于ADC转换时钟频率。一次常规的ADC转换需要13个ADC时钟,当ADC转换时钟为1MHz时,一秒种内ADC采样转换的次数约77K。根据采样定理,此时ADC通道的带宽为38.5KHz。 
差分方式ADC转换的带宽是由芯片内部的差分放大器的带宽决定,为4KHz。 
AVR的ADC输入阻抗典型值为100MΩ,为保证测量的准确,被测信号源的输出阻抗要尽可能的低,应在10K以下。 
4. ADC采样时钟的选择 
通常条件下,AVR的ADC逐次比较电路要达到转换的最大精度,需要一个50K~200KHz的采样时钟。一次正常的ADC转换过程需要13个采样时钟,假定ADC采样时钟为200KHz,那么最高的采样速率为200K/13=15.384K。因此根据采样定理,理论上被测模拟信号的最高频率为7.7K! 
尽管可以设置ADC的采样时钟为1M,但并不能提高ADC转换精度,反而会降低转换精度(受逐次比较硬件电路的限制),因此AVR的ADC不能完成高速ADC的任务。如果所需的转换精度低于10位,那么采样时钟可以高于200KHz,以达到更高的采样频率。 
ADC采样时钟的选择方式为:给出或估计被测模拟信号的最高频率fs,取采样频率为fs的4-10倍,再乘上13为ADC采样时钟频率,该频率应在50K~200KHz之间。 如果该频率大于200KHz,则ADC的10位精度不能保证。如果该频率小于50Khz,则可选择50K~200KHz之间的数值。 
5.模拟噪声的抑制 
器件外部和内部的数字电路会产生电磁干扰,并会影响模拟测量的精度。如果ADC转换精度要求很高,可以采用以下的技术来降低噪声的影响: 
(1)使模拟信号的通路尽可能的短。模拟信号连线应从模拟地的布线盘上通过,并使它们尽可能远离高速开关数字信号线。 
(2)AVR的AVcc引脚应该通过LC网络与数字端电源Vcc相连。
(3)采用ADC噪声抑制器功能来降低来自MCU内部的噪声。 
(4)如果某些ADC引脚是作为通用数字输出口使用,那么在ADC转换过程中,不要改变这些引脚的状态。
6.ADC的校正 
由于AVD内部ADC部分的放大器非线性等客观原因,ADC的转换结果会有误差的。如果要获得高精度的ADC转换,还需要对ADC结果进行校正。具体的方法请参考AVR应用笔记AVR120(avr_app_120.pdf),在这篇应用设计参考中详细介绍了误差的种类,以及校正方案。 
7.ADC精度的提高 
在有了上述几点的保证后,通过软件的手段也能适当的提高ADC的精度。如采用多次测量取平均,软件滤波算法等。在AVR应用笔记AVR121(avr_app_121.pdf)中介绍了一种使用过采样算法的软件实现,可以将ADC的精度提高到11位或更高,当然这是在花费更多的时间基础上实现的。6.2 模数转换的ADC实验 

6.2.1、实例功能 
AVR的模数转换器ADC具有下列特点:

  1. 10位精度;
  2. 0.5LSB积分非线形误差
  3. ±2LSB的绝对精度;
  4. 13µs~260µs的转换时间;
  5. 在最大精度下可达到每秒15kSPS的采样速率;
  6. 8路可选的单端输入通道;
  7. 7路差分输入通道;
  8. 2路差分输入通道带有可选的10×和200×增益;
  9. ADC转换结果的读取可设置为左端对齐(LEFT ADJUSTMENT);
  10. ADC的电压输入范围0~Vcc;
  11. 可选择的内部2.56V的ADC参考电压源;
  12. 自由连续转换模式和单次转换模式;
  13. ADC自动转换触发模式选择;
  14. ADC转换完成中断;
  15. 休眠模式下的噪声抑制器(NOISE CANCELER)。

在本实例中,我们将编写程序实现将模数转换后获得的电压值通过单片机的串口发送到计算机,然后通过计算机上的串口助手显示测量的电压值。 
本实例共有3个功能模块,分别描述如下: 
● 单片机系统:使用单片机的串口实现将模数转换后获得的电压值通过串口发送到计算机。 
● 外围电路:RS232电平转换电路,DB9串行接口插座,模拟电压输入采集电路。
● 软件程序:进一步熟悉单片机的串行通信,并掌握单片机的模数转换的方法。
6.2.2、器件和原理 
关于串行接口的原理已接单片机与计算机的串口的连接在上一实例中进行了描述,在本实例中不再重复。
本实例只介绍ATmega16单片机如何通过内置的模数转换模块采集外界输入的模拟电压。
1、ATmega16单片机的模数转换器ADC介绍 
由于单片机只能处理数字信号,所以外部的模拟信号量需要转变成数字量才能进一步的由单片机进行处理。ATmega16内部集成有一个10位逐次比较(successive approximation)ADC电路。因此使用AVR可以非常方便的处理输入的模拟信号量。 
ATmega16的ADC与一个8通道的模拟多路选择器连接,能够对以PORTA作为ADC输入引脚的8路单端模拟输入电压进行采样,单端电压输入以0V(GND)为参考。另外还支持16种差分电压输入组合,其中2种差分输入方式(ADC1,ADC0和ACD3,ADC2)带有可编程增益放大器,能在A/D转换前对差分输入电压进行0dB(1×),20dB(10×)或46dB(200×)的放大。还有七种差分输入方式的模拟输入通道共用一个负极(ADC1),此时其它任意一个ADC引脚都可作为相应的正极。若增益为1×或10×,则可获得8位的精度。如果增益为200×,那么转换精度为7位。 
AVR的ADC功能单元由独立的专用模拟电源引脚AVcc供电。AVcc和Vcc的电压差别不能大于±0.3V。ADC转换的参考电源可采用芯片内部的2.56V参考电源,或采用AVcc,也可使用外部参考电源。使用外部参考电源时,外部参考电源由引脚ARFE接入。使用内部电压参考源时,可以通过在AREF引脚外部并接一个电容来提高ADC的抗噪性能。
ADC功能单元包括采样保持电路,以确保输入电压在ADC转换过程中保持恒定。ADC通过逐次比较(successive approximation)方式,将输入端的模拟电压转换成10位的数字量。最小值代表地,最大值为AREF引脚上的电压值减1个LSB。可以通过ADMUX寄存器中REFSn位的设置,选择将芯片内部参考电源(2.56V)或AVcc连接到AREF,作为A/D转换的参考电压。这时,内部电压参考源可以通过外接于AREF引脚的电容来稳定,以改进抗噪特性。 
模拟输入通道和差分增益的选择是通过ADMUX寄存器中的MUX位设定的。任何一个ADC的输入引脚,包括地(GND)以及内部的恒定能隙(fixed bandgap)电压参考源,都可以被选择用来作为ADC的单端输入信号。而ADC的某些输入引脚则可选择作为差分增益放大器的正、负极输入端。当选定了差分输入通道后,差分增益放大器将两输入通道上的电压差按选定增益系数放大,然后输入到ADC中。若选定使用单端输入通道,则增益放大器无效。 
通过设置ADCSRA寄存器中的ADC使能位ADEN来使能ADC。在ADEN没有置“1”前,参考电压源和输入通道的选定将不起作用。当ADEN位清“0”后,ADC将不消耗能量,因此建议在进入节电休眠模式前将ADC关掉。 
ADC将10位的转换结果放在ADC数据寄存器中(ADCH和ADCL)。默认情况下,转换结果为右端对齐(RIGHT ADJUSTED)的。但可以通过设置ADMUX寄存器中ADLAR位,调整为左端对齐(LEFT ADJUSTED)。如果转换结果是左端对齐,并且只需要8位的精度,那么只需读取ADCH寄存器的数据作为转换结果就达到要求了。否则,必须先读取ADCL寄存器,然后再读取ADCH寄存器,以保证数据寄存器中的内容是同一次转换的结果。因为一旦ADCL寄存器被读取,就阻断了ADC对ADC数据寄存器的操作。这就意味着,一旦指令读取了ADCL,那么必须紧接着读取一次ADCH;如果在读取ADCL和读取ADCH的过程中正好有一次ADC转换完成,ADC的2个数据寄存器的内容是不会被更新的,该次转换的结果将丢失。只有当ADCH寄存器被读取后,ADC才可以继续对ADCL和ADCH寄存器操作更新。 
ADC有自己的中断,当转换完成时中断将被触发。尽管在顺序读取ADCL和ADCH寄存器过程中,ADC对ADC数据寄存器的更新被禁止,转换的结果丢失,但仍会触发ADC中断。 
2、ATmwga16单片机的模数转换器ADC相关的I/O寄存器 
1.ADC多路复用器选择寄存器—ADMUX

  1. 位7,6—REFS[1:0]:ADC参考电源选择

REFS1、REFS2用于选择ADC的参考电压源,见表6.2.1。如果这些位在ADC转换过程中被改变,新的选择将在该次ADC转换完成后(ADCSRA中的ADIF被置位)才生效。一旦选择内部参考源(AVcc、2.56V)为ADC的参考电压后,AREF引脚上不得施加外部的参考电源,只能与GND之间并接抗干扰电容。


表6.2.1  ADC参考电源选择

REFS1

REFS0

ADC参考电源

0

0

外部引脚AREF,断开内部参考源连接

0

1

AVcc,AREF外部并接电容

1

0

保留

1

1

内部2.56V,AREF外部并接电容

  1. 位5—ADLAR:ADC结果左对齐选择

ADLAR位决定转换结果在ADC数据寄存器中的存放形式。写“1”到ADLAR位,将使转换结果左对齐(LEFT ADJUST);否则,转换结果为右对齐(RIGHT ADJUST)。无论ADC是否正在进行转换,改变ADLAR位都将会立即影响ADC数据寄存器。

  1. 位4..0—MUX4:0:模拟通道和增益选择

这5个位用于对连接到ADC的输入通道和差分通道的增益进行选择设置,详见表6.2.2。注意,只有转换结束后(ADCSRA的ADIF是“1”),改变这些位才会有效。


表6.2.2   ADC输入通道和增益选择

MUX[4:0]

单端输入

差分正极输入

差分负极输入

增益

00000

ADC0

N/A

00001

ADC1

00010

ADC2

00011

ADC3

00100

ADC4

00101

ADC5

00110

ADC6

00111

ADC7

01000

N/A

ADC0

ADC0

10×

01001

ADC1

ADC0

10×

01010

ADC0

ADC0

200×

01011

ADC1

ADC0

200×

01100

ADC2

ADC2

10×

01101

ADC3

ADC2

10×

01110

ADC2

ADC2

200×

01111

ADC3

ADC2

200×

10000

ADC0

ADC1

10001

ADC1

ADC1

10010

ADC2

ADC1

10011

ADC3

ADC1

10100

ADC4

ADC1

10101

ADC5

ADC1

10110

ADC6

ADC1

10111

ADC7

ADC1

11000

ADC0

ADC2

11001

ADC1

ADC2

11010

ADC2

ADC2

11011

ADC3

ADC2

11100

ADC4

ADC2

11101

ADC5

ADC2

11110

1.22V(VBG)

N/A

11111

0V(GND)

   本实例中我们需要设置ADC的参考电压源为AVcc,即REFS0设置为1,ADC默认转换结果为右对齐,我们不需要改变,模拟通道选择ADC0通道单端输入,即MUX4:0。 
2.ADC控制和状态寄存器A—ADCSRA

  1. 位7—ADEN:ADC使能

该位写入“1”时使能ADC,写入“0”关闭ADC。如在ADC转换过程中将ADC关闭,该次转换随即停止。

  1. 位6—ADSC:ADC转换开始

在单次转换模式下,置该位为“1”,将启动一次转换。在自由连续转换模式下,该位写入“1”将启动第一次转换。先置位ADEN位使能ADC,再置位ADSC;或置位ADSC的同时使能ADC,都会使能ADC开始进行第一次转换。第一次ADC转换将需要25个ADC时钟周期,而不是常规转换的13个ADC时钟周期,这是因为第一次转换需要完成对ADC的初始化。 
在ADC转换的过程中,ADSC将始终读出为“1”。当转换完成时,它将转变为“0”。强制写入“0”是无效的。

  1. 位5—ADATE:ADC自动转换触发允许

当该位被置为“1”时,允许ADC工作在自动转换触发工作模式下。在该模式下,在触发信号的上升沿时ADC将自动开始一次ADC转换过程。ADC的自动转换触发信号源由SFIOR寄存器中的ADTS位选择确定。

  1. 位4—ADIF:ADC中断标志位

当ADC转换完成并且ADC数据寄存器被更新后该位被置位。如果ADIE位(ADC转换结束中断允许)和SREG寄存器中的I位被置“1”,ADC中断服务程序将被执行。ADIF在执行相应的中断处理向量时被硬件自动清零。此外,ADIF位可以通过写入逻辑“1”来清零。

  1. 位3—ADIE:ADC中断允许

当该位和SREG寄存器中的I位同时被置位时,允许ADC转换完成中断。

  1. 位2,0—ADPS[2:0]:ADC预分频选择

这些位决定了XTAL时钟与输入到ADC的ADC时钟之间分频数,见表6.2.3。

表6.2.3   ADC时钟分频

ADPS[2:0]

分  频  系  数

000

2

001

2

010

4

011

8

100

16

101

32

110

64

111

128

本实例中我们需要使能ADC,即ADEN设置为1,我们不用自动转换,也不需要中断,所以,ADTE、ADIE位不需要设置。在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。本例中使用的时钟是12M的,所以要将时钟64分频,分频后ADC频率为188KHz,即时钟分频选择ADPS[2:0]=6。

3.ADC数据寄存器—ADCL和ADCH

  1. ADLAR = 0,ADC转换结果右对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第13张图片

  1. ADLAR = 1,ADC转换结果左对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第14张图片
当ADC转换完成后,可以读取ADC寄存器的ADC0-ADC9得到ADC的转换的结果。如果是差分输入,转换值为二进制的补码形式。一旦开始读取ADCL后,ADC数据寄存器就不能被ADC更新,直到ADCH寄存器被读取为止。因此,如果结果是左对齐(ADLAR=1),且不需要大于8位的精度的话,仅仅读取ADCH寄存器就足够了。否则,必须先读取ADCL寄存器,再读取ADCH寄存器。ADMUX寄存器中的ADLAR位决定了从ADC数据寄存器中读取结果的格式。如果ADLAR位为“1”,结果将是左对齐;如果ADLAR位为“0”(默认情况),结果将是右对齐。 
4.特殊功能I/O寄存器—SFIOR

  1. 位7..5—ADTS[2:0]:ADC自动转换触发源选择

当ADCSRA寄存器中的ADATE为“1”,允许ADC工作在自动转换触发工作模式时,这3位的设置用于选择ADC的自动转换触发源。如果禁止了ADC的自动转换触发(ADATE为“0”),这3个位的设置值将不起任何作用。

表10-6   ADC自动转换触发源的选择

ADTS[2:0]

触  发  源

000

连续自由转换

001

模拟比较器

010

外部中断0

011

T/C0比较匹配

100

T/C0溢出

101

T/C1比较匹配B

110

T/C1溢出

111

T/C1输入捕捉

本例中我们不使用自动转换功能,所以该寄存器可以不必设置。
6.2.3、电路 
本实例的电路包括232电平转换电路和电阻分压电路,这两种电路在前面的实例中均做过介绍,这里不再重复。
1、电路原理
在本实例中利用MAX3232芯片使单片机输出的TTL电平转换为标准的RS232电平,从而使计算机能够识别。同时将计算机输出的RS232电平转换为单片机可以识别的TTL电平。
利用电位器产生电阻分压电路,从而产生变化的模拟电压加到单片机的模拟信号采集端口,供单片机采集。
2、电路连接 
电路中MAX3232芯片的9、10引脚分别连接单片机的PD0、PD1端口,MAX3232的13、14引脚分别连接计算机串口线的3、2脚。
电位器RP2的动片引脚连接单片机的模拟信号采集通道PA0(ADC0)。
3、特别说明
本学习板采用的是串口插座是公头的,所以与计算机相连的串口连接线应该是交叉串口线,而不是串口延长线。
6.2.4、程序设计 
1、程序功能 

程序的功能是通过单片机的串行接口,将单片机采集到的模拟电压值发送到计算机中,通过计算机上的串口助手显示采集的电压值。    
● 单片机串行接收中断的编程
在本例中,我们用到了单片机的串行接收中断,需要编写串行接收中断服务程序,通过查询WINAVR(GCC)的中断库函数手册,可以查找到ATmega16单片机串行接收中断的中断向量为USART_RXC_vect。据此我们可以编写串行接收中断服务程序,如下:
//接收中断函数 
ISR(USART_RXC_vect )
{
unsigned char Rev;
Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 
Usart_PutChar(Rev);    //将接收到的数据发送 
}
在中断服务程序中,我们首先把单片机串口接收到的数据放入变量Rev中,然后调用上一实例中编写的串行接口字节发送函数将变量Rev中的数据发送到计算机。 
2、 单片机与计算机串行通信结果的观察 
在观察本例运行结果时,我们同样要用到串口助手,本例中,单片机发送串口数据采用的波特率是9600bps,数据格式是8位数据位,1位停止位,无奇偶校验。在串口助手里面,我们也要将波特率和数据格式设置成一样的。 
3、函数说明 
本实例用到了6个函数,分别是: 
void Port_Init(void);   //端口初始化配置 
void Usart_Init(void);  //USART寄存器设置 
void AD_Init(void);    //AD初始化 
void Usart_PutChar(unsigned char cTXData);  //字节发送函数 
void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 
unsigned int AD_GetData(void);    //AD转换函数   
4、使用WINAVR开发环境,在本例中我们使用的是外部12M的晶振,所以需要将MAKEFILE文件中的时钟频率修改为12M。另外在程序烧录到单片机的时候,熔丝位也要选择为外部12M晶振(注意是晶振,不是外部振荡器,一定不要选择错了,否则会导致单片机不能再烧写程序)。
5、程序代码

#include         #include #include   //中断函数头文件

//常量声明 #define BAUD 9600         //波特率设置值

//全局变量声明 unsigned int ADData;          //AD转换获得的数据

//函数声明 void Port_Init(void);   //端口初始化配置 void Usart_Init(void);  //USART寄存器设置 void AD_Init(void);    //AD初始化 void Usart_PutChar(unsigned char cTXData);  //字节发送函数 void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 unsigned int AD_GetData(void);    //AD转换函数

int main(void)             { unsigned char Delay3s;

      Port_Init(); Usart_Init(); AD_Init(); Usart_PutString("AD转换测试程序"); Usart_PutString("测得ADC0通道的电压值为:"); sei();          //使能全局中断   while(1) { ADData = (int)((long)AD_GetData() * 5010 / 1024);         //将获得的AD值转换为电压值 //单位为mv。 Usart_PutChar(ADData / 1000 + 0x30);      //得到电压值的千位并发送 Usart_PutChar('.');                          //发送小数点 Usart_PutChar(ADData % 1000 / 100 + 0x30);   //得到电压值的百位并发送 Usart_PutChar(ADData % 100 / 10 + 0x30);    //得到电压值的十位并发送 Usart_PutChar(ADData % 10 + 0x30);     //得到电压值的个位并发送 Usart_PutChar('V');                  //发送电压符号“V” Usart_PutChar(0x0d);     // Usart_PutChar(0x0a);     //  AD值发送结束,回车换行 for(Delay3s = 0;Delay3s < 30;Delay3s++)     //延时3S { _delay_ms(90); } } }

//端口状态初始化设置函数 void Port_Init() { PORTA = 0X00;         DDRA = 0x00;   //ADC通道设置为输入口,高阻态     }

//USART寄存器配置函数 void Usart_Init() { UCSRA = 0X00;     UCSRC |= (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0);  //异步,数据格式8,N,1 //UCSRC寄存器与UBRRH寄存器共用相同的I/O地址,写 UCSRC 时, URSEL 应设置为 1。 UBRRL = (F_CPU / BAUD / 16 - 1) % 256;    //波特率设置 UBRRH = (F_CPU / BAUD / 16 - 1) / 256;         UCSRB |= (1 << RXCIE) | (1 << RXEN) | (1 << TXEN);    //发送使能 }

//字节发送函数 void Usart_PutChar(unsigned char cTXData) { while( !(UCSRA & (1 << UDRE)) );  //只有数据寄存器为空时才能发送数据 UDR = cTXData;                   //发送数据送USART I/O数据寄存器-UDR }

//接收中断函数 ISR(USART_RXC_vect ) { unsigned char Rev; Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 Usart_PutChar(Rev);    //将接收到的数据发送 }

void Usart_PutString(unsigned char *pcString) { while (*pcString) { Usart_PutChar(*pcString++);   } Usart_PutChar(0x0D); Usart_PutChar(0x0A);  //结尾发送回车换行 }

//AD转换初始化函数 void AD_Init() { ADMUX |= (1 << REFS0);     //ADC参考电压为AVcc,ADC结果右对齐,选择通道ADC0 ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); //使能AD转换,ADC时钟64分频 }

//AD转换函数 unsigned int AD_GetData() { ADCSRA |= (1 << ADSC);     //开始AD转换 while(!(ADCSRA & (1 << ADIF)));    //等待转换完成 ADCSRA |= (1 << ADIF);              //清零ADC中断标志位 return ADC;        //返回ADC值 }

附录:ADC应用设计要点

1.预分频与转换时间 
在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。在要求转换精度低于10位的情况下,ADC的采样时钟可以高于200kHz,以获得更高的采样率。 
ADC模块中包含一个预分频器的ADC时钟源,它可以对大于100KHz的系统时钟进行分频,以获得合适的ADC时钟提供ADC使用。预分频器的分频系数由ADCSRA寄存器中的ADPS位设置的。一旦寄存器ADCSRA中的ADEN位置“1”(ADC开始工作),预分频器就启动开始计数。ADEN位为“1”时,预分频器将一直工作;ADEN位为“0”时,预分频器一直处在复位状态。 
AVR的ADC完成一次转换的时间见表6.2.5。从表中可以看出,完成一次ADC转换通常需要13-14个ADC时钟。而启动ADC开始第一次转换到完成的时间需要25个ADC时钟,这是因为要对ADC单元的模拟电路部分进行初始化。


表6.2.5  ADC转换和采样保持时间

转 换 形 式

采样保持时间

完 成 转 换 总 时 间

启动ADC后的第一次转换

13.5个ADC时钟

25个ADC时钟

正常转换,单端输入

1.5个ADC时钟

13个ADC时钟

自动触发方式

2个ADC时钟

13.5个ADC时钟

正常转换,差分输入

1.5/2.5个ADC时钟

13/14个ADC时钟

当ADCSRA寄存器中的ADSC位置位,启动ADC转换时,A/D转换将在随后ADC时钟的上升沿开始。一次正常的A/D转换开始时,需要1.5个ADC时钟周期的采样保持时间(ADC首次启动后需要13.5个ADC时钟周期的采样保持时间)。当一次A/D转换完成后,转换结果写入ADC数据寄存器,ADIF(ADC中断标志位)将被置位。在单次转换模式下,ADSC也同时被清零。用户程序可以再次置位ADSC位,新的一次转换将在下一个ADC时钟的上升沿开始。 
当ADC设置为自动触发方式时,触发信号的上升沿将启动一次ADC转换。转换完成的结果将一直保持到下一次触发信号的上升沿出现,然后开始新的一次ADC转换。这就保证了使ADC每隔一定的时间间隔进行一次转换。在这种方式下,ADC需要2个ADC时钟周期的采样保持时间。 
在自由连续转换模式下,一次转换完毕后马上开始一次新的转换,此时,ADSC位一直保持为“1”。

2.ADC输入通道和参考电源的选择 
寄存器ADMUX中的MUXn和REFS1、REFS0位实际上是一个缓冲器,该缓冲器与一个MCU可以随机读取的临时寄存器相连通。采用这种结构,保证了ADC输入通道和参考电源只能在ADC转换过程中的安全点被改变。在ADC转换开始前,通道和参考电源可以不断被更新,一旦转换开始,通道和参考电源将被锁定,并保持足够时间,以确保ADC转换的正常进行。在转换完成前的最后一个ADC时钟周期(ADCSRA的ADIF位置“1”时),通道和参考电源又开始重新更新。注意,由于A/D转换开始于置位ADSC后的第一个ADC时钟的上升沿,因此,在置位ADSC后的一个ADC时钟周期内不要将一个新的通道或参考电源写入到ADMUX寄存器中。 
改变差分输入通道时需特别当心。一旦确定了差分输入通道,增益放大器需要125µs的稳定时间。所以在选择了新的差分输入通道后的125µs内不要启动A/D转换,或将这段时间内转换结果丢弃。通过改变ADMUX中的REFS1、REFS0来更改参考电源后,第一次差分转换同样要遵循以上的时间处理过程。

  1. 当要改变ADC输入通道时,应该遵守以下方式,以保证能够选择到正确的通道:

在单次转换模式下,总是在开始转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到转换完成后,再改变通道选择。 
在连续转换模式下,总是在启动ADC开始第一次转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到第一次转换完成后再改变通道的设置。然而由于此时新一次的转换已经自动开始,所以,当前这次的转换结果仍反映前一通道的转换值,而下一次的转换结果将为新设置通道的值。

  1. ADC电压参考源

ADC的参考电压(VREF)决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果接近于0x3FF。ADC的参考电压VREF可以选择为AVCC或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。 
AVCC通过一个无源开关连接到ADC。内部2.56V参考源是由内部能隙参考源(VBC)通过内部的放大器产生的。注意,无论选用什么内部参考电源,外部AREF引脚都是直接与ADC相连的,因此,可以通过外部在AREF引脚和地之间并接一个电容,使内部参考电源更加稳定和抗噪。可以通过使用高阻电压表测量AREF引脚,来获得参考电源VREF的电压值。由于VREF是一个高阻源,因此,只有容性负载可以连接到该引脚。 
如果将一个外部固定的电压源连接到AREF引脚,那就不能使用任何的内部参考电源,否则就会使外部电压源短路。外部参考电源的范围应在2.0V到AVCC-0.2V之间。参考电源改变后的第一次ADC转换结果可能不太准确,建议抛弃该次转换结果。 
3.ADC转换结果 
A/D转换结束后(ADIF = 1),在ADC数据寄存器(ADCL和ADCH)中可以取得转换的结果。对于单端输入的A/D转换,其转换结果为: 
ADC =(VIN×1024)/ VREF 
其中VIN表示选定的输入引脚上的电压,VREF表示选定的参考电源的电压。0x000表示输入引脚的电压为模拟地,0x3FF表示输入引脚的电压为参考电压值减去一个LSB。 
对于差分转换,其结果为: 
ADC=(VPOS-VNEG) ×GAIN×512/VREF 
例:若差分输入通道选择为ADC3-ADC2,10倍增益,参考电压2.56V,左端对齐(ADMUX=0xED),ADC3引脚上电压300mV,ADC2引脚上电压500mV。 
则ADCR =(300-500)×10×512 / 2560 = -400 = 0x270
ADCL=0x00,ADCH=0x9C。 
若结果为右端对齐时(ADLAR=“0”),则ADCL=0x70,ADCH=0x02。

附录2、ADC应用设计的深入讨论 
尽管AVR内部集成了10位的ADC,但是在实际应用中,要想真正实现10位精度,比较稳定的ADC的话,并不象上一节中的例子那么简单。需要进一步从硬件、软件等方面进行综合的、细致的考虑。下面介绍一些在ADC设计应用中应该考虑的几个要点。 
1.AVcc的稳定性。 
AVcc是提供给ADC工作的电源,如果AVcc不稳定,就会影响ADC的转换精度。在图10-5中,系统电源通过一个LC滤波后接入AVcc,这样就能很好的抑制掉系统电源中的高频躁声,提高了AVcc的稳定性。另外在要求比较高的场合使用ADC时,PA口上的那些没被用做ADC输入的端口尽量不要作为数字I/O口使用。因为PA口的工作电源是由AVcc提供的,如果PA口上有比较大的电流波动,也会影响AVcc的稳定。 
2.参考电压VREF的选择确定 
在实际应用中,要根据输入测量电压的范围选择正确的参考电压VREF,以求得到比较好的转换精度。ADC的参考电压VREF还决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果全部接近于0x3FF,因此ADC的参考电压应稍大于模拟输入电压的最高值。 
ADC的参考电压VREF可以选择为AVCC,或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。外接参考电压应该稳定,并大于2.0V(芯片的工作电压为1.8V时,外接参考电压应大于1.0V)。要求比较高的场合,建议在AREF引脚外接标准参考电压源来作为ADC的参考电源。 
3. ADC通道带宽和输入阻抗 
不管使用单端输入转换还是差分输入转换方式,所有模拟输入口的输入电压应在AVcc-GNG之间。 
在单端ADC转换方式时,ADC通道的输入频率带宽取决于ADC转换时钟频率。一次常规的ADC转换需要13个ADC时钟,当ADC转换时钟为1MHz时,一秒种内ADC采样转换的次数约77K。根据采样定理,此时ADC通道的带宽为38.5KHz。 
差分方式ADC转换的带宽是由芯片内部的差分放大器的带宽决定,为4KHz。 
AVR的ADC输入阻抗典型值为100MΩ,为保证测量的准确,被测信号源的输出阻抗要尽可能的低,应在10K以下。 
4. ADC采样时钟的选择 
通常条件下,AVR的ADC逐次比较电路要达到转换的最大精度,需要一个50K~200KHz的采样时钟。一次正常的ADC转换过程需要13个采样时钟,假定ADC采样时钟为200KHz,那么最高的采样速率为200K/13=15.384K。因此根据采样定理,理论上被测模拟信号的最高频率为7.7K! 
尽管可以设置ADC的采样时钟为1M,但并不能提高ADC转换精度,反而会降低转换精度(受逐次比较硬件电路的限制),因此AVR的ADC不能完成高速ADC的任务。如果所需的转换精度低于10位,那么采样时钟可以高于200KHz,以达到更高的采样频率。 
ADC采样时钟的选择方式为:给出或估计被测模拟信号的最高频率fs,取采样频率为fs的4-10倍,再乘上13为ADC采样时钟频率,该频率应在50K~200KHz之间。 如果该频率大于200KHz,则ADC的10位精度不能保证。如果该频率小于50Khz,则可选择50K~200KHz之间的数值。 
5.模拟噪声的抑制 
器件外部和内部的数字电路会产生电磁干扰,并会影响模拟测量的精度。如果ADC转换精度要求很高,可以采用以下的技术来降低噪声的影响: 
(1)使模拟信号的通路尽可能的短。模拟信号连线应从模拟地的布线盘上通过,并使它们尽可能远离高速开关数字信号线。 
(2)AVR的AVcc引脚应该通过LC网络与数字端电源Vcc相连。
(3)采用ADC噪声抑制器功能来降低来自MCU内部的噪声。 
(4)如果某些ADC引脚是作为通用数字输出口使用,那么在ADC转换过程中,不要改变这些引脚的状态。
6.ADC的校正 
由于AVD内部ADC部分的放大器非线性等客观原因,ADC的转换结果会有误差的。如果要获得高精度的ADC转换,还需要对ADC结果进行校正。具体的方法请参考AVR应用笔记AVR120(avr_app_120.pdf),在这篇应用设计参考中详细介绍了误差的种类,以及校正方案。 
7.ADC精度的提高 
在有了上述几点的保证后,通过软件的手段也能适当的提高ADC的精度。如采用多次测量取平均,软件滤波算法等。在AVR应用笔记AVR121(avr_app_121.pdf)中介绍了一种使用过采样算法的软件实现,可以将ADC的精度提高到11位或更高,当然这是在花费更多的时间基础上实现的。6.2 模数转换的ADC实验 

6.2.1、实例功能 
AVR的模数转换器ADC具有下列特点:

  1. 10位精度;
  2. 0.5LSB积分非线形误差
  3. ±2LSB的绝对精度;
  4. 13µs~260µs的转换时间;
  5. 在最大精度下可达到每秒15kSPS的采样速率;
  6. 8路可选的单端输入通道;
  7. 7路差分输入通道;
  8. 2路差分输入通道带有可选的10×和200×增益;
  9. ADC转换结果的读取可设置为左端对齐(LEFT ADJUSTMENT);
  10. ADC的电压输入范围0~Vcc;
  11. 可选择的内部2.56V的ADC参考电压源;
  12. 自由连续转换模式和单次转换模式;
  13. ADC自动转换触发模式选择;
  14. ADC转换完成中断;
  15. 休眠模式下的噪声抑制器(NOISE CANCELER)。

在本实例中,我们将编写程序实现将模数转换后获得的电压值通过单片机的串口发送到计算机,然后通过计算机上的串口助手显示测量的电压值。 
本实例共有3个功能模块,分别描述如下: 
● 单片机系统:使用单片机的串口实现将模数转换后获得的电压值通过串口发送到计算机。 
● 外围电路:RS232电平转换电路,DB9串行接口插座,模拟电压输入采集电路。
● 软件程序:进一步熟悉单片机的串行通信,并掌握单片机的模数转换的方法。
6.2.2、器件和原理 
关于串行接口的原理已接单片机与计算机的串口的连接在上一实例中进行了描述,在本实例中不再重复。
本实例只介绍ATmega16单片机如何通过内置的模数转换模块采集外界输入的模拟电压。
1、ATmega16单片机的模数转换器ADC介绍 
由于单片机只能处理数字信号,所以外部的模拟信号量需要转变成数字量才能进一步的由单片机进行处理。ATmega16内部集成有一个10位逐次比较(successive approximation)ADC电路。因此使用AVR可以非常方便的处理输入的模拟信号量。 
ATmega16的ADC与一个8通道的模拟多路选择器连接,能够对以PORTA作为ADC输入引脚的8路单端模拟输入电压进行采样,单端电压输入以0V(GND)为参考。另外还支持16种差分电压输入组合,其中2种差分输入方式(ADC1,ADC0和ACD3,ADC2)带有可编程增益放大器,能在A/D转换前对差分输入电压进行0dB(1×),20dB(10×)或46dB(200×)的放大。还有七种差分输入方式的模拟输入通道共用一个负极(ADC1),此时其它任意一个ADC引脚都可作为相应的正极。若增益为1×或10×,则可获得8位的精度。如果增益为200×,那么转换精度为7位。 
AVR的ADC功能单元由独立的专用模拟电源引脚AVcc供电。AVcc和Vcc的电压差别不能大于±0.3V。ADC转换的参考电源可采用芯片内部的2.56V参考电源,或采用AVcc,也可使用外部参考电源。使用外部参考电源时,外部参考电源由引脚ARFE接入。使用内部电压参考源时,可以通过在AREF引脚外部并接一个电容来提高ADC的抗噪性能。
ADC功能单元包括采样保持电路,以确保输入电压在ADC转换过程中保持恒定。ADC通过逐次比较(successive approximation)方式,将输入端的模拟电压转换成10位的数字量。最小值代表地,最大值为AREF引脚上的电压值减1个LSB。可以通过ADMUX寄存器中REFSn位的设置,选择将芯片内部参考电源(2.56V)或AVcc连接到AREF,作为A/D转换的参考电压。这时,内部电压参考源可以通过外接于AREF引脚的电容来稳定,以改进抗噪特性。 
模拟输入通道和差分增益的选择是通过ADMUX寄存器中的MUX位设定的。任何一个ADC的输入引脚,包括地(GND)以及内部的恒定能隙(fixed bandgap)电压参考源,都可以被选择用来作为ADC的单端输入信号。而ADC的某些输入引脚则可选择作为差分增益放大器的正、负极输入端。当选定了差分输入通道后,差分增益放大器将两输入通道上的电压差按选定增益系数放大,然后输入到ADC中。若选定使用单端输入通道,则增益放大器无效。 
通过设置ADCSRA寄存器中的ADC使能位ADEN来使能ADC。在ADEN没有置“1”前,参考电压源和输入通道的选定将不起作用。当ADEN位清“0”后,ADC将不消耗能量,因此建议在进入节电休眠模式前将ADC关掉。 
ADC将10位的转换结果放在ADC数据寄存器中(ADCH和ADCL)。默认情况下,转换结果为右端对齐(RIGHT ADJUSTED)的。但可以通过设置ADMUX寄存器中ADLAR位,调整为左端对齐(LEFT ADJUSTED)。如果转换结果是左端对齐,并且只需要8位的精度,那么只需读取ADCH寄存器的数据作为转换结果就达到要求了。否则,必须先读取ADCL寄存器,然后再读取ADCH寄存器,以保证数据寄存器中的内容是同一次转换的结果。因为一旦ADCL寄存器被读取,就阻断了ADC对ADC数据寄存器的操作。这就意味着,一旦指令读取了ADCL,那么必须紧接着读取一次ADCH;如果在读取ADCL和读取ADCH的过程中正好有一次ADC转换完成,ADC的2个数据寄存器的内容是不会被更新的,该次转换的结果将丢失。只有当ADCH寄存器被读取后,ADC才可以继续对ADCL和ADCH寄存器操作更新。 
ADC有自己的中断,当转换完成时中断将被触发。尽管在顺序读取ADCL和ADCH寄存器过程中,ADC对ADC数据寄存器的更新被禁止,转换的结果丢失,但仍会触发ADC中断。 
2、ATmwga16单片机的模数转换器ADC相关的I/O寄存器 
1.ADC多路复用器选择寄存器—ADMUX

  1. 位7,6—REFS[1:0]:ADC参考电源选择

REFS1、REFS2用于选择ADC的参考电压源,见表6.2.1。如果这些位在ADC转换过程中被改变,新的选择将在该次ADC转换完成后(ADCSRA中的ADIF被置位)才生效。一旦选择内部参考源(AVcc、2.56V)为ADC的参考电压后,AREF引脚上不得施加外部的参考电源,只能与GND之间并接抗干扰电容。


表6.2.1  ADC参考电源选择

REFS1

REFS0

ADC参考电源

0

0

外部引脚AREF,断开内部参考源连接

0

1

AVcc,AREF外部并接电容

1

0

保留

1

1

内部2.56V,AREF外部并接电容

  1. 位5—ADLAR:ADC结果左对齐选择

ADLAR位决定转换结果在ADC数据寄存器中的存放形式。写“1”到ADLAR位,将使转换结果左对齐(LEFT ADJUST);否则,转换结果为右对齐(RIGHT ADJUST)。无论ADC是否正在进行转换,改变ADLAR位都将会立即影响ADC数据寄存器。

  1. 位4..0—MUX4:0:模拟通道和增益选择

这5个位用于对连接到ADC的输入通道和差分通道的增益进行选择设置,详见表6.2.2。注意,只有转换结束后(ADCSRA的ADIF是“1”),改变这些位才会有效。


表6.2.2   ADC输入通道和增益选择

MUX[4:0]

单端输入

差分正极输入

差分负极输入

增益

00000

ADC0

N/A

00001

ADC1

00010

ADC2

00011

ADC3

00100

ADC4

00101

ADC5

00110

ADC6

00111

ADC7

01000

N/A

ADC0

ADC0

10×

01001

ADC1

ADC0

10×

01010

ADC0

ADC0

200×

01011

ADC1

ADC0

200×

01100

ADC2

ADC2

10×

01101

ADC3

ADC2

10×

01110

ADC2

ADC2

200×

01111

ADC3

ADC2

200×

10000

ADC0

ADC1

10001

ADC1

ADC1

10010

ADC2

ADC1

10011

ADC3

ADC1

10100

ADC4

ADC1

10101

ADC5

ADC1

10110

ADC6

ADC1

10111

ADC7

ADC1

11000

ADC0

ADC2

11001

ADC1

ADC2

11010

ADC2

ADC2

11011

ADC3

ADC2

11100

ADC4

ADC2

11101

ADC5

ADC2

11110

1.22V(VBG)

N/A

11111

0V(GND)

   本实例中我们需要设置ADC的参考电压源为AVcc,即REFS0设置为1,ADC默认转换结果为右对齐,我们不需要改变,模拟通道选择ADC0通道单端输入,即MUX4:0。 
2.ADC控制和状态寄存器A—ADCSRA

  1. 位7—ADEN:ADC使能

该位写入“1”时使能ADC,写入“0”关闭ADC。如在ADC转换过程中将ADC关闭,该次转换随即停止。

  1. 位6—ADSC:ADC转换开始

在单次转换模式下,置该位为“1”,将启动一次转换。在自由连续转换模式下,该位写入“1”将启动第一次转换。先置位ADEN位使能ADC,再置位ADSC;或置位ADSC的同时使能ADC,都会使能ADC开始进行第一次转换。第一次ADC转换将需要25个ADC时钟周期,而不是常规转换的13个ADC时钟周期,这是因为第一次转换需要完成对ADC的初始化。 
在ADC转换的过程中,ADSC将始终读出为“1”。当转换完成时,它将转变为“0”。强制写入“0”是无效的。

  1. 位5—ADATE:ADC自动转换触发允许

当该位被置为“1”时,允许ADC工作在自动转换触发工作模式下。在该模式下,在触发信号的上升沿时ADC将自动开始一次ADC转换过程。ADC的自动转换触发信号源由SFIOR寄存器中的ADTS位选择确定。

  1. 位4—ADIF:ADC中断标志位

当ADC转换完成并且ADC数据寄存器被更新后该位被置位。如果ADIE位(ADC转换结束中断允许)和SREG寄存器中的I位被置“1”,ADC中断服务程序将被执行。ADIF在执行相应的中断处理向量时被硬件自动清零。此外,ADIF位可以通过写入逻辑“1”来清零。

  1. 位3—ADIE:ADC中断允许

当该位和SREG寄存器中的I位同时被置位时,允许ADC转换完成中断。

  1. 位2,0—ADPS[2:0]:ADC预分频选择

这些位决定了XTAL时钟与输入到ADC的ADC时钟之间分频数,见表6.2.3。

表6.2.3   ADC时钟分频

ADPS[2:0]

分  频  系  数

000

2

001

2

010

4

011

8

100

16

101

32

110

64

111

128

本实例中我们需要使能ADC,即ADEN设置为1,我们不用自动转换,也不需要中断,所以,ADTE、ADIE位不需要设置。在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。本例中使用的时钟是12M的,所以要将时钟64分频,分频后ADC频率为188KHz,即时钟分频选择ADPS[2:0]=6。

3.ADC数据寄存器—ADCL和ADCH

  1. ADLAR = 0,ADC转换结果右对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第15张图片

  1. ADLAR = 1,ADC转换结果左对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第16张图片
当ADC转换完成后,可以读取ADC寄存器的ADC0-ADC9得到ADC的转换的结果。如果是差分输入,转换值为二进制的补码形式。一旦开始读取ADCL后,ADC数据寄存器就不能被ADC更新,直到ADCH寄存器被读取为止。因此,如果结果是左对齐(ADLAR=1),且不需要大于8位的精度的话,仅仅读取ADCH寄存器就足够了。否则,必须先读取ADCL寄存器,再读取ADCH寄存器。ADMUX寄存器中的ADLAR位决定了从ADC数据寄存器中读取结果的格式。如果ADLAR位为“1”,结果将是左对齐;如果ADLAR位为“0”(默认情况),结果将是右对齐。 
4.特殊功能I/O寄存器—SFIOR

  1. 位7..5—ADTS[2:0]:ADC自动转换触发源选择

当ADCSRA寄存器中的ADATE为“1”,允许ADC工作在自动转换触发工作模式时,这3位的设置用于选择ADC的自动转换触发源。如果禁止了ADC的自动转换触发(ADATE为“0”),这3个位的设置值将不起任何作用。

表10-6   ADC自动转换触发源的选择

ADTS[2:0]

触  发  源

000

连续自由转换

001

模拟比较器

010

外部中断0

011

T/C0比较匹配

100

T/C0溢出

101

T/C1比较匹配B

110

T/C1溢出

111

T/C1输入捕捉

本例中我们不使用自动转换功能,所以该寄存器可以不必设置。
6.2.3、电路 
本实例的电路包括232电平转换电路和电阻分压电路,这两种电路在前面的实例中均做过介绍,这里不再重复。
1、电路原理
在本实例中利用MAX3232芯片使单片机输出的TTL电平转换为标准的RS232电平,从而使计算机能够识别。同时将计算机输出的RS232电平转换为单片机可以识别的TTL电平。
利用电位器产生电阻分压电路,从而产生变化的模拟电压加到单片机的模拟信号采集端口,供单片机采集。
2、电路连接 
电路中MAX3232芯片的9、10引脚分别连接单片机的PD0、PD1端口,MAX3232的13、14引脚分别连接计算机串口线的3、2脚。
电位器RP2的动片引脚连接单片机的模拟信号采集通道PA0(ADC0)。
3、特别说明
本学习板采用的是串口插座是公头的,所以与计算机相连的串口连接线应该是交叉串口线,而不是串口延长线。
6.2.4、程序设计 
1、程序功能 

程序的功能是通过单片机的串行接口,将单片机采集到的模拟电压值发送到计算机中,通过计算机上的串口助手显示采集的电压值。    
● 单片机串行接收中断的编程
在本例中,我们用到了单片机的串行接收中断,需要编写串行接收中断服务程序,通过查询WINAVR(GCC)的中断库函数手册,可以查找到ATmega16单片机串行接收中断的中断向量为USART_RXC_vect。据此我们可以编写串行接收中断服务程序,如下:
//接收中断函数 
ISR(USART_RXC_vect )
{
unsigned char Rev;
Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 
Usart_PutChar(Rev);    //将接收到的数据发送 
}
在中断服务程序中,我们首先把单片机串口接收到的数据放入变量Rev中,然后调用上一实例中编写的串行接口字节发送函数将变量Rev中的数据发送到计算机。 
2、 单片机与计算机串行通信结果的观察 
在观察本例运行结果时,我们同样要用到串口助手,本例中,单片机发送串口数据采用的波特率是9600bps,数据格式是8位数据位,1位停止位,无奇偶校验。在串口助手里面,我们也要将波特率和数据格式设置成一样的。 
3、函数说明 
本实例用到了6个函数,分别是: 
void Port_Init(void);   //端口初始化配置 
void Usart_Init(void);  //USART寄存器设置 
void AD_Init(void);    //AD初始化 
void Usart_PutChar(unsigned char cTXData);  //字节发送函数 
void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 
unsigned int AD_GetData(void);    //AD转换函数   
4、使用WINAVR开发环境,在本例中我们使用的是外部12M的晶振,所以需要将MAKEFILE文件中的时钟频率修改为12M。另外在程序烧录到单片机的时候,熔丝位也要选择为外部12M晶振(注意是晶振,不是外部振荡器,一定不要选择错了,否则会导致单片机不能再烧写程序)。
5、程序代码

#include         #include #include   //中断函数头文件

//常量声明 #define BAUD 9600         //波特率设置值

//全局变量声明 unsigned int ADData;          //AD转换获得的数据

//函数声明 void Port_Init(void);   //端口初始化配置 void Usart_Init(void);  //USART寄存器设置 void AD_Init(void);    //AD初始化 void Usart_PutChar(unsigned char cTXData);  //字节发送函数 void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 unsigned int AD_GetData(void);    //AD转换函数

int main(void)             { unsigned char Delay3s;

      Port_Init(); Usart_Init(); AD_Init(); Usart_PutString("AD转换测试程序"); Usart_PutString("测得ADC0通道的电压值为:"); sei();          //使能全局中断   while(1) { ADData = (int)((long)AD_GetData() * 5010 / 1024);         //将获得的AD值转换为电压值 //单位为mv。 Usart_PutChar(ADData / 1000 + 0x30);      //得到电压值的千位并发送 Usart_PutChar('.');                          //发送小数点 Usart_PutChar(ADData % 1000 / 100 + 0x30);   //得到电压值的百位并发送 Usart_PutChar(ADData % 100 / 10 + 0x30);    //得到电压值的十位并发送 Usart_PutChar(ADData % 10 + 0x30);     //得到电压值的个位并发送 Usart_PutChar('V');                  //发送电压符号“V” Usart_PutChar(0x0d);     // Usart_PutChar(0x0a);     //  AD值发送结束,回车换行 for(Delay3s = 0;Delay3s < 30;Delay3s++)     //延时3S { _delay_ms(90); } } }

//端口状态初始化设置函数 void Port_Init() { PORTA = 0X00;         DDRA = 0x00;   //ADC通道设置为输入口,高阻态     }

//USART寄存器配置函数 void Usart_Init() { UCSRA = 0X00;     UCSRC |= (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0);  //异步,数据格式8,N,1 //UCSRC寄存器与UBRRH寄存器共用相同的I/O地址,写 UCSRC 时, URSEL 应设置为 1。 UBRRL = (F_CPU / BAUD / 16 - 1) % 256;    //波特率设置 UBRRH = (F_CPU / BAUD / 16 - 1) / 256;         UCSRB |= (1 << RXCIE) | (1 << RXEN) | (1 << TXEN);    //发送使能 }

//字节发送函数 void Usart_PutChar(unsigned char cTXData) { while( !(UCSRA & (1 << UDRE)) );  //只有数据寄存器为空时才能发送数据 UDR = cTXData;                   //发送数据送USART I/O数据寄存器-UDR }

//接收中断函数 ISR(USART_RXC_vect ) { unsigned char Rev; Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 Usart_PutChar(Rev);    //将接收到的数据发送 }

void Usart_PutString(unsigned char *pcString) { while (*pcString) { Usart_PutChar(*pcString++);   } Usart_PutChar(0x0D); Usart_PutChar(0x0A);  //结尾发送回车换行 }

//AD转换初始化函数 void AD_Init() { ADMUX |= (1 << REFS0);     //ADC参考电压为AVcc,ADC结果右对齐,选择通道ADC0 ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); //使能AD转换,ADC时钟64分频 }

//AD转换函数 unsigned int AD_GetData() { ADCSRA |= (1 << ADSC);     //开始AD转换 while(!(ADCSRA & (1 << ADIF)));    //等待转换完成 ADCSRA |= (1 << ADIF);              //清零ADC中断标志位 return ADC;        //返回ADC值 }

附录:ADC应用设计要点

1.预分频与转换时间 
在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。在要求转换精度低于10位的情况下,ADC的采样时钟可以高于200kHz,以获得更高的采样率。 
ADC模块中包含一个预分频器的ADC时钟源,它可以对大于100KHz的系统时钟进行分频,以获得合适的ADC时钟提供ADC使用。预分频器的分频系数由ADCSRA寄存器中的ADPS位设置的。一旦寄存器ADCSRA中的ADEN位置“1”(ADC开始工作),预分频器就启动开始计数。ADEN位为“1”时,预分频器将一直工作;ADEN位为“0”时,预分频器一直处在复位状态。 
AVR的ADC完成一次转换的时间见表6.2.5。从表中可以看出,完成一次ADC转换通常需要13-14个ADC时钟。而启动ADC开始第一次转换到完成的时间需要25个ADC时钟,这是因为要对ADC单元的模拟电路部分进行初始化。


表6.2.5  ADC转换和采样保持时间

转 换 形 式

采样保持时间

完 成 转 换 总 时 间

启动ADC后的第一次转换

13.5个ADC时钟

25个ADC时钟

正常转换,单端输入

1.5个ADC时钟

13个ADC时钟

自动触发方式

2个ADC时钟

13.5个ADC时钟

正常转换,差分输入

1.5/2.5个ADC时钟

13/14个ADC时钟

当ADCSRA寄存器中的ADSC位置位,启动ADC转换时,A/D转换将在随后ADC时钟的上升沿开始。一次正常的A/D转换开始时,需要1.5个ADC时钟周期的采样保持时间(ADC首次启动后需要13.5个ADC时钟周期的采样保持时间)。当一次A/D转换完成后,转换结果写入ADC数据寄存器,ADIF(ADC中断标志位)将被置位。在单次转换模式下,ADSC也同时被清零。用户程序可以再次置位ADSC位,新的一次转换将在下一个ADC时钟的上升沿开始。 
当ADC设置为自动触发方式时,触发信号的上升沿将启动一次ADC转换。转换完成的结果将一直保持到下一次触发信号的上升沿出现,然后开始新的一次ADC转换。这就保证了使ADC每隔一定的时间间隔进行一次转换。在这种方式下,ADC需要2个ADC时钟周期的采样保持时间。 
在自由连续转换模式下,一次转换完毕后马上开始一次新的转换,此时,ADSC位一直保持为“1”。

2.ADC输入通道和参考电源的选择 
寄存器ADMUX中的MUXn和REFS1、REFS0位实际上是一个缓冲器,该缓冲器与一个MCU可以随机读取的临时寄存器相连通。采用这种结构,保证了ADC输入通道和参考电源只能在ADC转换过程中的安全点被改变。在ADC转换开始前,通道和参考电源可以不断被更新,一旦转换开始,通道和参考电源将被锁定,并保持足够时间,以确保ADC转换的正常进行。在转换完成前的最后一个ADC时钟周期(ADCSRA的ADIF位置“1”时),通道和参考电源又开始重新更新。注意,由于A/D转换开始于置位ADSC后的第一个ADC时钟的上升沿,因此,在置位ADSC后的一个ADC时钟周期内不要将一个新的通道或参考电源写入到ADMUX寄存器中。 
改变差分输入通道时需特别当心。一旦确定了差分输入通道,增益放大器需要125µs的稳定时间。所以在选择了新的差分输入通道后的125µs内不要启动A/D转换,或将这段时间内转换结果丢弃。通过改变ADMUX中的REFS1、REFS0来更改参考电源后,第一次差分转换同样要遵循以上的时间处理过程。

  1. 当要改变ADC输入通道时,应该遵守以下方式,以保证能够选择到正确的通道:

在单次转换模式下,总是在开始转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到转换完成后,再改变通道选择。 
在连续转换模式下,总是在启动ADC开始第一次转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到第一次转换完成后再改变通道的设置。然而由于此时新一次的转换已经自动开始,所以,当前这次的转换结果仍反映前一通道的转换值,而下一次的转换结果将为新设置通道的值。

  1. ADC电压参考源

ADC的参考电压(VREF)决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果接近于0x3FF。ADC的参考电压VREF可以选择为AVCC或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。 
AVCC通过一个无源开关连接到ADC。内部2.56V参考源是由内部能隙参考源(VBC)通过内部的放大器产生的。注意,无论选用什么内部参考电源,外部AREF引脚都是直接与ADC相连的,因此,可以通过外部在AREF引脚和地之间并接一个电容,使内部参考电源更加稳定和抗噪。可以通过使用高阻电压表测量AREF引脚,来获得参考电源VREF的电压值。由于VREF是一个高阻源,因此,只有容性负载可以连接到该引脚。 
如果将一个外部固定的电压源连接到AREF引脚,那就不能使用任何的内部参考电源,否则就会使外部电压源短路。外部参考电源的范围应在2.0V到AVCC-0.2V之间。参考电源改变后的第一次ADC转换结果可能不太准确,建议抛弃该次转换结果。 
3.ADC转换结果 
A/D转换结束后(ADIF = 1),在ADC数据寄存器(ADCL和ADCH)中可以取得转换的结果。对于单端输入的A/D转换,其转换结果为: 
ADC =(VIN×1024)/ VREF 
其中VIN表示选定的输入引脚上的电压,VREF表示选定的参考电源的电压。0x000表示输入引脚的电压为模拟地,0x3FF表示输入引脚的电压为参考电压值减去一个LSB。 
对于差分转换,其结果为: 
ADC=(VPOS-VNEG) ×GAIN×512/VREF 
例:若差分输入通道选择为ADC3-ADC2,10倍增益,参考电压2.56V,左端对齐(ADMUX=0xED),ADC3引脚上电压300mV,ADC2引脚上电压500mV。 
则ADCR =(300-500)×10×512 / 2560 = -400 = 0x270
ADCL=0x00,ADCH=0x9C。 
若结果为右端对齐时(ADLAR=“0”),则ADCL=0x70,ADCH=0x02。

附录2、ADC应用设计的深入讨论 
尽管AVR内部集成了10位的ADC,但是在实际应用中,要想真正实现10位精度,比较稳定的ADC的话,并不象上一节中的例子那么简单。需要进一步从硬件、软件等方面进行综合的、细致的考虑。下面介绍一些在ADC设计应用中应该考虑的几个要点。 
1.AVcc的稳定性。 
AVcc是提供给ADC工作的电源,如果AVcc不稳定,就会影响ADC的转换精度。在图10-5中,系统电源通过一个LC滤波后接入AVcc,这样就能很好的抑制掉系统电源中的高频躁声,提高了AVcc的稳定性。另外在要求比较高的场合使用ADC时,PA口上的那些没被用做ADC输入的端口尽量不要作为数字I/O口使用。因为PA口的工作电源是由AVcc提供的,如果PA口上有比较大的电流波动,也会影响AVcc的稳定。 
2.参考电压VREF的选择确定 
在实际应用中,要根据输入测量电压的范围选择正确的参考电压VREF,以求得到比较好的转换精度。ADC的参考电压VREF还决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果全部接近于0x3FF,因此ADC的参考电压应稍大于模拟输入电压的最高值。 
ADC的参考电压VREF可以选择为AVCC,或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。外接参考电压应该稳定,并大于2.0V(芯片的工作电压为1.8V时,外接参考电压应大于1.0V)。要求比较高的场合,建议在AREF引脚外接标准参考电压源来作为ADC的参考电源。 
3. ADC通道带宽和输入阻抗 
不管使用单端输入转换还是差分输入转换方式,所有模拟输入口的输入电压应在AVcc-GNG之间。 
在单端ADC转换方式时,ADC通道的输入频率带宽取决于ADC转换时钟频率。一次常规的ADC转换需要13个ADC时钟,当ADC转换时钟为1MHz时,一秒种内ADC采样转换的次数约77K。根据采样定理,此时ADC通道的带宽为38.5KHz。 
差分方式ADC转换的带宽是由芯片内部的差分放大器的带宽决定,为4KHz。 
AVR的ADC输入阻抗典型值为100MΩ,为保证测量的准确,被测信号源的输出阻抗要尽可能的低,应在10K以下。 
4. ADC采样时钟的选择 
通常条件下,AVR的ADC逐次比较电路要达到转换的最大精度,需要一个50K~200KHz的采样时钟。一次正常的ADC转换过程需要13个采样时钟,假定ADC采样时钟为200KHz,那么最高的采样速率为200K/13=15.384K。因此根据采样定理,理论上被测模拟信号的最高频率为7.7K! 
尽管可以设置ADC的采样时钟为1M,但并不能提高ADC转换精度,反而会降低转换精度(受逐次比较硬件电路的限制),因此AVR的ADC不能完成高速ADC的任务。如果所需的转换精度低于10位,那么采样时钟可以高于200KHz,以达到更高的采样频率。 
ADC采样时钟的选择方式为:给出或估计被测模拟信号的最高频率fs,取采样频率为fs的4-10倍,再乘上13为ADC采样时钟频率,该频率应在50K~200KHz之间。 如果该频率大于200KHz,则ADC的10位精度不能保证。如果该频率小于50Khz,则可选择50K~200KHz之间的数值。 
5.模拟噪声的抑制 
器件外部和内部的数字电路会产生电磁干扰,并会影响模拟测量的精度。如果ADC转换精度要求很高,可以采用以下的技术来降低噪声的影响: 
(1)使模拟信号的通路尽可能的短。模拟信号连线应从模拟地的布线盘上通过,并使它们尽可能远离高速开关数字信号线。 
(2)AVR的AVcc引脚应该通过LC网络与数字端电源Vcc相连。
(3)采用ADC噪声抑制器功能来降低来自MCU内部的噪声。 
(4)如果某些ADC引脚是作为通用数字输出口使用,那么在ADC转换过程中,不要改变这些引脚的状态。
6.ADC的校正 
由于AVD内部ADC部分的放大器非线性等客观原因,ADC的转换结果会有误差的。如果要获得高精度的ADC转换,还需要对ADC结果进行校正。具体的方法请参考AVR应用笔记AVR120(avr_app_120.pdf),在这篇应用设计参考中详细介绍了误差的种类,以及校正方案。 
7.ADC精度的提高 
在有了上述几点的保证后,通过软件的手段也能适当的提高ADC的精度。如采用多次测量取平均,软件滤波算法等。在AVR应用笔记AVR121(avr_app_121.pdf)中介绍了一种使用过采样算法的软件实现,可以将ADC的精度提高到11位或更高,当然这是在花费更多的时间基础上实现的。6.2 模数转换的ADC实验 

6.2.1、实例功能 
AVR的模数转换器ADC具有下列特点:

  1. 10位精度;
  2. 0.5LSB积分非线形误差
  3. ±2LSB的绝对精度;
  4. 13µs~260µs的转换时间;
  5. 在最大精度下可达到每秒15kSPS的采样速率;
  6. 8路可选的单端输入通道;
  7. 7路差分输入通道;
  8. 2路差分输入通道带有可选的10×和200×增益;
  9. ADC转换结果的读取可设置为左端对齐(LEFT ADJUSTMENT);
  10. ADC的电压输入范围0~Vcc;
  11. 可选择的内部2.56V的ADC参考电压源;
  12. 自由连续转换模式和单次转换模式;
  13. ADC自动转换触发模式选择;
  14. ADC转换完成中断;
  15. 休眠模式下的噪声抑制器(NOISE CANCELER)。

在本实例中,我们将编写程序实现将模数转换后获得的电压值通过单片机的串口发送到计算机,然后通过计算机上的串口助手显示测量的电压值。 
本实例共有3个功能模块,分别描述如下: 
● 单片机系统:使用单片机的串口实现将模数转换后获得的电压值通过串口发送到计算机。 
● 外围电路:RS232电平转换电路,DB9串行接口插座,模拟电压输入采集电路。
● 软件程序:进一步熟悉单片机的串行通信,并掌握单片机的模数转换的方法。
6.2.2、器件和原理 
关于串行接口的原理已接单片机与计算机的串口的连接在上一实例中进行了描述,在本实例中不再重复。
本实例只介绍ATmega16单片机如何通过内置的模数转换模块采集外界输入的模拟电压。
1、ATmega16单片机的模数转换器ADC介绍 
由于单片机只能处理数字信号,所以外部的模拟信号量需要转变成数字量才能进一步的由单片机进行处理。ATmega16内部集成有一个10位逐次比较(successive approximation)ADC电路。因此使用AVR可以非常方便的处理输入的模拟信号量。 
ATmega16的ADC与一个8通道的模拟多路选择器连接,能够对以PORTA作为ADC输入引脚的8路单端模拟输入电压进行采样,单端电压输入以0V(GND)为参考。另外还支持16种差分电压输入组合,其中2种差分输入方式(ADC1,ADC0和ACD3,ADC2)带有可编程增益放大器,能在A/D转换前对差分输入电压进行0dB(1×),20dB(10×)或46dB(200×)的放大。还有七种差分输入方式的模拟输入通道共用一个负极(ADC1),此时其它任意一个ADC引脚都可作为相应的正极。若增益为1×或10×,则可获得8位的精度。如果增益为200×,那么转换精度为7位。 
AVR的ADC功能单元由独立的专用模拟电源引脚AVcc供电。AVcc和Vcc的电压差别不能大于±0.3V。ADC转换的参考电源可采用芯片内部的2.56V参考电源,或采用AVcc,也可使用外部参考电源。使用外部参考电源时,外部参考电源由引脚ARFE接入。使用内部电压参考源时,可以通过在AREF引脚外部并接一个电容来提高ADC的抗噪性能。
ADC功能单元包括采样保持电路,以确保输入电压在ADC转换过程中保持恒定。ADC通过逐次比较(successive approximation)方式,将输入端的模拟电压转换成10位的数字量。最小值代表地,最大值为AREF引脚上的电压值减1个LSB。可以通过ADMUX寄存器中REFSn位的设置,选择将芯片内部参考电源(2.56V)或AVcc连接到AREF,作为A/D转换的参考电压。这时,内部电压参考源可以通过外接于AREF引脚的电容来稳定,以改进抗噪特性。 
模拟输入通道和差分增益的选择是通过ADMUX寄存器中的MUX位设定的。任何一个ADC的输入引脚,包括地(GND)以及内部的恒定能隙(fixed bandgap)电压参考源,都可以被选择用来作为ADC的单端输入信号。而ADC的某些输入引脚则可选择作为差分增益放大器的正、负极输入端。当选定了差分输入通道后,差分增益放大器将两输入通道上的电压差按选定增益系数放大,然后输入到ADC中。若选定使用单端输入通道,则增益放大器无效。 
通过设置ADCSRA寄存器中的ADC使能位ADEN来使能ADC。在ADEN没有置“1”前,参考电压源和输入通道的选定将不起作用。当ADEN位清“0”后,ADC将不消耗能量,因此建议在进入节电休眠模式前将ADC关掉。 
ADC将10位的转换结果放在ADC数据寄存器中(ADCH和ADCL)。默认情况下,转换结果为右端对齐(RIGHT ADJUSTED)的。但可以通过设置ADMUX寄存器中ADLAR位,调整为左端对齐(LEFT ADJUSTED)。如果转换结果是左端对齐,并且只需要8位的精度,那么只需读取ADCH寄存器的数据作为转换结果就达到要求了。否则,必须先读取ADCL寄存器,然后再读取ADCH寄存器,以保证数据寄存器中的内容是同一次转换的结果。因为一旦ADCL寄存器被读取,就阻断了ADC对ADC数据寄存器的操作。这就意味着,一旦指令读取了ADCL,那么必须紧接着读取一次ADCH;如果在读取ADCL和读取ADCH的过程中正好有一次ADC转换完成,ADC的2个数据寄存器的内容是不会被更新的,该次转换的结果将丢失。只有当ADCH寄存器被读取后,ADC才可以继续对ADCL和ADCH寄存器操作更新。 
ADC有自己的中断,当转换完成时中断将被触发。尽管在顺序读取ADCL和ADCH寄存器过程中,ADC对ADC数据寄存器的更新被禁止,转换的结果丢失,但仍会触发ADC中断。 
2、ATmwga16单片机的模数转换器ADC相关的I/O寄存器 
1.ADC多路复用器选择寄存器—ADMUX

  1. 位7,6—REFS[1:0]:ADC参考电源选择

REFS1、REFS2用于选择ADC的参考电压源,见表6.2.1。如果这些位在ADC转换过程中被改变,新的选择将在该次ADC转换完成后(ADCSRA中的ADIF被置位)才生效。一旦选择内部参考源(AVcc、2.56V)为ADC的参考电压后,AREF引脚上不得施加外部的参考电源,只能与GND之间并接抗干扰电容。


表6.2.1  ADC参考电源选择

REFS1

REFS0

ADC参考电源

0

0

外部引脚AREF,断开内部参考源连接

0

1

AVcc,AREF外部并接电容

1

0

保留

1

1

内部2.56V,AREF外部并接电容

  1. 位5—ADLAR:ADC结果左对齐选择

ADLAR位决定转换结果在ADC数据寄存器中的存放形式。写“1”到ADLAR位,将使转换结果左对齐(LEFT ADJUST);否则,转换结果为右对齐(RIGHT ADJUST)。无论ADC是否正在进行转换,改变ADLAR位都将会立即影响ADC数据寄存器。

  1. 位4..0—MUX4:0:模拟通道和增益选择

这5个位用于对连接到ADC的输入通道和差分通道的增益进行选择设置,详见表6.2.2。注意,只有转换结束后(ADCSRA的ADIF是“1”),改变这些位才会有效。


表6.2.2   ADC输入通道和增益选择

MUX[4:0]

单端输入

差分正极输入

差分负极输入

增益

00000

ADC0

N/A

00001

ADC1

00010

ADC2

00011

ADC3

00100

ADC4

00101

ADC5

00110

ADC6

00111

ADC7

01000

N/A

ADC0

ADC0

10×

01001

ADC1

ADC0

10×

01010

ADC0

ADC0

200×

01011

ADC1

ADC0

200×

01100

ADC2

ADC2

10×

01101

ADC3

ADC2

10×

01110

ADC2

ADC2

200×

01111

ADC3

ADC2

200×

10000

ADC0

ADC1

10001

ADC1

ADC1

10010

ADC2

ADC1

10011

ADC3

ADC1

10100

ADC4

ADC1

10101

ADC5

ADC1

10110

ADC6

ADC1

10111

ADC7

ADC1

11000

ADC0

ADC2

11001

ADC1

ADC2

11010

ADC2

ADC2

11011

ADC3

ADC2

11100

ADC4

ADC2

11101

ADC5

ADC2

11110

1.22V(VBG)

N/A

11111

0V(GND)

   本实例中我们需要设置ADC的参考电压源为AVcc,即REFS0设置为1,ADC默认转换结果为右对齐,我们不需要改变,模拟通道选择ADC0通道单端输入,即MUX4:0。 
2.ADC控制和状态寄存器A—ADCSRA

  1. 位7—ADEN:ADC使能

该位写入“1”时使能ADC,写入“0”关闭ADC。如在ADC转换过程中将ADC关闭,该次转换随即停止。

  1. 位6—ADSC:ADC转换开始

在单次转换模式下,置该位为“1”,将启动一次转换。在自由连续转换模式下,该位写入“1”将启动第一次转换。先置位ADEN位使能ADC,再置位ADSC;或置位ADSC的同时使能ADC,都会使能ADC开始进行第一次转换。第一次ADC转换将需要25个ADC时钟周期,而不是常规转换的13个ADC时钟周期,这是因为第一次转换需要完成对ADC的初始化。 
在ADC转换的过程中,ADSC将始终读出为“1”。当转换完成时,它将转变为“0”。强制写入“0”是无效的。

  1. 位5—ADATE:ADC自动转换触发允许

当该位被置为“1”时,允许ADC工作在自动转换触发工作模式下。在该模式下,在触发信号的上升沿时ADC将自动开始一次ADC转换过程。ADC的自动转换触发信号源由SFIOR寄存器中的ADTS位选择确定。

  1. 位4—ADIF:ADC中断标志位

当ADC转换完成并且ADC数据寄存器被更新后该位被置位。如果ADIE位(ADC转换结束中断允许)和SREG寄存器中的I位被置“1”,ADC中断服务程序将被执行。ADIF在执行相应的中断处理向量时被硬件自动清零。此外,ADIF位可以通过写入逻辑“1”来清零。

  1. 位3—ADIE:ADC中断允许

当该位和SREG寄存器中的I位同时被置位时,允许ADC转换完成中断。

  1. 位2,0—ADPS[2:0]:ADC预分频选择

这些位决定了XTAL时钟与输入到ADC的ADC时钟之间分频数,见表6.2.3。

表6.2.3   ADC时钟分频

ADPS[2:0]

分  频  系  数

000

2

001

2

010

4

011

8

100

16

101

32

110

64

111

128

本实例中我们需要使能ADC,即ADEN设置为1,我们不用自动转换,也不需要中断,所以,ADTE、ADIE位不需要设置。在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。本例中使用的时钟是12M的,所以要将时钟64分频,分频后ADC频率为188KHz,即时钟分频选择ADPS[2:0]=6。

3.ADC数据寄存器—ADCL和ADCH

  1. ADLAR = 0,ADC转换结果右对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第17张图片

  1. ADLAR = 1,ADC转换结果左对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第18张图片
当ADC转换完成后,可以读取ADC寄存器的ADC0-ADC9得到ADC的转换的结果。如果是差分输入,转换值为二进制的补码形式。一旦开始读取ADCL后,ADC数据寄存器就不能被ADC更新,直到ADCH寄存器被读取为止。因此,如果结果是左对齐(ADLAR=1),且不需要大于8位的精度的话,仅仅读取ADCH寄存器就足够了。否则,必须先读取ADCL寄存器,再读取ADCH寄存器。ADMUX寄存器中的ADLAR位决定了从ADC数据寄存器中读取结果的格式。如果ADLAR位为“1”,结果将是左对齐;如果ADLAR位为“0”(默认情况),结果将是右对齐。 
4.特殊功能I/O寄存器—SFIOR

  1. 位7..5—ADTS[2:0]:ADC自动转换触发源选择

当ADCSRA寄存器中的ADATE为“1”,允许ADC工作在自动转换触发工作模式时,这3位的设置用于选择ADC的自动转换触发源。如果禁止了ADC的自动转换触发(ADATE为“0”),这3个位的设置值将不起任何作用。

表10-6   ADC自动转换触发源的选择

ADTS[2:0]

触  发  源

000

连续自由转换

001

模拟比较器

010

外部中断0

011

T/C0比较匹配

100

T/C0溢出

101

T/C1比较匹配B

110

T/C1溢出

111

T/C1输入捕捉

本例中我们不使用自动转换功能,所以该寄存器可以不必设置。
6.2.3、电路 
本实例的电路包括232电平转换电路和电阻分压电路,这两种电路在前面的实例中均做过介绍,这里不再重复。
1、电路原理
在本实例中利用MAX3232芯片使单片机输出的TTL电平转换为标准的RS232电平,从而使计算机能够识别。同时将计算机输出的RS232电平转换为单片机可以识别的TTL电平。
利用电位器产生电阻分压电路,从而产生变化的模拟电压加到单片机的模拟信号采集端口,供单片机采集。
2、电路连接 
电路中MAX3232芯片的9、10引脚分别连接单片机的PD0、PD1端口,MAX3232的13、14引脚分别连接计算机串口线的3、2脚。
电位器RP2的动片引脚连接单片机的模拟信号采集通道PA0(ADC0)。
3、特别说明
本学习板采用的是串口插座是公头的,所以与计算机相连的串口连接线应该是交叉串口线,而不是串口延长线。
6.2.4、程序设计 
1、程序功能 

程序的功能是通过单片机的串行接口,将单片机采集到的模拟电压值发送到计算机中,通过计算机上的串口助手显示采集的电压值。    
● 单片机串行接收中断的编程
在本例中,我们用到了单片机的串行接收中断,需要编写串行接收中断服务程序,通过查询WINAVR(GCC)的中断库函数手册,可以查找到ATmega16单片机串行接收中断的中断向量为USART_RXC_vect。据此我们可以编写串行接收中断服务程序,如下:
//接收中断函数 
ISR(USART_RXC_vect )
{
unsigned char Rev;
Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 
Usart_PutChar(Rev);    //将接收到的数据发送 
}
在中断服务程序中,我们首先把单片机串口接收到的数据放入变量Rev中,然后调用上一实例中编写的串行接口字节发送函数将变量Rev中的数据发送到计算机。 
2、 单片机与计算机串行通信结果的观察 
在观察本例运行结果时,我们同样要用到串口助手,本例中,单片机发送串口数据采用的波特率是9600bps,数据格式是8位数据位,1位停止位,无奇偶校验。在串口助手里面,我们也要将波特率和数据格式设置成一样的。 
3、函数说明 
本实例用到了6个函数,分别是: 
void Port_Init(void);   //端口初始化配置 
void Usart_Init(void);  //USART寄存器设置 
void AD_Init(void);    //AD初始化 
void Usart_PutChar(unsigned char cTXData);  //字节发送函数 
void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 
unsigned int AD_GetData(void);    //AD转换函数   
4、使用WINAVR开发环境,在本例中我们使用的是外部12M的晶振,所以需要将MAKEFILE文件中的时钟频率修改为12M。另外在程序烧录到单片机的时候,熔丝位也要选择为外部12M晶振(注意是晶振,不是外部振荡器,一定不要选择错了,否则会导致单片机不能再烧写程序)。
5、程序代码

#include         #include #include   //中断函数头文件

//常量声明 #define BAUD 9600         //波特率设置值

//全局变量声明 unsigned int ADData;          //AD转换获得的数据

//函数声明 void Port_Init(void);   //端口初始化配置 void Usart_Init(void);  //USART寄存器设置 void AD_Init(void);    //AD初始化 void Usart_PutChar(unsigned char cTXData);  //字节发送函数 void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 unsigned int AD_GetData(void);    //AD转换函数

int main(void)             { unsigned char Delay3s;

      Port_Init(); Usart_Init(); AD_Init(); Usart_PutString("AD转换测试程序"); Usart_PutString("测得ADC0通道的电压值为:"); sei();          //使能全局中断   while(1) { ADData = (int)((long)AD_GetData() * 5010 / 1024);         //将获得的AD值转换为电压值 //单位为mv。 Usart_PutChar(ADData / 1000 + 0x30);      //得到电压值的千位并发送 Usart_PutChar('.');                          //发送小数点 Usart_PutChar(ADData % 1000 / 100 + 0x30);   //得到电压值的百位并发送 Usart_PutChar(ADData % 100 / 10 + 0x30);    //得到电压值的十位并发送 Usart_PutChar(ADData % 10 + 0x30);     //得到电压值的个位并发送 Usart_PutChar('V');                  //发送电压符号“V” Usart_PutChar(0x0d);     // Usart_PutChar(0x0a);     //  AD值发送结束,回车换行 for(Delay3s = 0;Delay3s < 30;Delay3s++)     //延时3S { _delay_ms(90); } } }

//端口状态初始化设置函数 void Port_Init() { PORTA = 0X00;         DDRA = 0x00;   //ADC通道设置为输入口,高阻态     }

//USART寄存器配置函数 void Usart_Init() { UCSRA = 0X00;     UCSRC |= (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0);  //异步,数据格式8,N,1 //UCSRC寄存器与UBRRH寄存器共用相同的I/O地址,写 UCSRC 时, URSEL 应设置为 1。 UBRRL = (F_CPU / BAUD / 16 - 1) % 256;    //波特率设置 UBRRH = (F_CPU / BAUD / 16 - 1) / 256;         UCSRB |= (1 << RXCIE) | (1 << RXEN) | (1 << TXEN);    //发送使能 }

//字节发送函数 void Usart_PutChar(unsigned char cTXData) { while( !(UCSRA & (1 << UDRE)) );  //只有数据寄存器为空时才能发送数据 UDR = cTXData;                   //发送数据送USART I/O数据寄存器-UDR }

//接收中断函数 ISR(USART_RXC_vect ) { unsigned char Rev; Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 Usart_PutChar(Rev);    //将接收到的数据发送 }

void Usart_PutString(unsigned char *pcString) { while (*pcString) { Usart_PutChar(*pcString++);   } Usart_PutChar(0x0D); Usart_PutChar(0x0A);  //结尾发送回车换行 }

//AD转换初始化函数 void AD_Init() { ADMUX |= (1 << REFS0);     //ADC参考电压为AVcc,ADC结果右对齐,选择通道ADC0 ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); //使能AD转换,ADC时钟64分频 }

//AD转换函数 unsigned int AD_GetData() { ADCSRA |= (1 << ADSC);     //开始AD转换 while(!(ADCSRA & (1 << ADIF)));    //等待转换完成 ADCSRA |= (1 << ADIF);              //清零ADC中断标志位 return ADC;        //返回ADC值 }

附录:ADC应用设计要点

1.预分频与转换时间 
在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。在要求转换精度低于10位的情况下,ADC的采样时钟可以高于200kHz,以获得更高的采样率。 
ADC模块中包含一个预分频器的ADC时钟源,它可以对大于100KHz的系统时钟进行分频,以获得合适的ADC时钟提供ADC使用。预分频器的分频系数由ADCSRA寄存器中的ADPS位设置的。一旦寄存器ADCSRA中的ADEN位置“1”(ADC开始工作),预分频器就启动开始计数。ADEN位为“1”时,预分频器将一直工作;ADEN位为“0”时,预分频器一直处在复位状态。 
AVR的ADC完成一次转换的时间见表6.2.5。从表中可以看出,完成一次ADC转换通常需要13-14个ADC时钟。而启动ADC开始第一次转换到完成的时间需要25个ADC时钟,这是因为要对ADC单元的模拟电路部分进行初始化。


表6.2.5  ADC转换和采样保持时间

转 换 形 式

采样保持时间

完 成 转 换 总 时 间

启动ADC后的第一次转换

13.5个ADC时钟

25个ADC时钟

正常转换,单端输入

1.5个ADC时钟

13个ADC时钟

自动触发方式

2个ADC时钟

13.5个ADC时钟

正常转换,差分输入

1.5/2.5个ADC时钟

13/14个ADC时钟

当ADCSRA寄存器中的ADSC位置位,启动ADC转换时,A/D转换将在随后ADC时钟的上升沿开始。一次正常的A/D转换开始时,需要1.5个ADC时钟周期的采样保持时间(ADC首次启动后需要13.5个ADC时钟周期的采样保持时间)。当一次A/D转换完成后,转换结果写入ADC数据寄存器,ADIF(ADC中断标志位)将被置位。在单次转换模式下,ADSC也同时被清零。用户程序可以再次置位ADSC位,新的一次转换将在下一个ADC时钟的上升沿开始。 
当ADC设置为自动触发方式时,触发信号的上升沿将启动一次ADC转换。转换完成的结果将一直保持到下一次触发信号的上升沿出现,然后开始新的一次ADC转换。这就保证了使ADC每隔一定的时间间隔进行一次转换。在这种方式下,ADC需要2个ADC时钟周期的采样保持时间。 
在自由连续转换模式下,一次转换完毕后马上开始一次新的转换,此时,ADSC位一直保持为“1”。

2.ADC输入通道和参考电源的选择 
寄存器ADMUX中的MUXn和REFS1、REFS0位实际上是一个缓冲器,该缓冲器与一个MCU可以随机读取的临时寄存器相连通。采用这种结构,保证了ADC输入通道和参考电源只能在ADC转换过程中的安全点被改变。在ADC转换开始前,通道和参考电源可以不断被更新,一旦转换开始,通道和参考电源将被锁定,并保持足够时间,以确保ADC转换的正常进行。在转换完成前的最后一个ADC时钟周期(ADCSRA的ADIF位置“1”时),通道和参考电源又开始重新更新。注意,由于A/D转换开始于置位ADSC后的第一个ADC时钟的上升沿,因此,在置位ADSC后的一个ADC时钟周期内不要将一个新的通道或参考电源写入到ADMUX寄存器中。 
改变差分输入通道时需特别当心。一旦确定了差分输入通道,增益放大器需要125µs的稳定时间。所以在选择了新的差分输入通道后的125µs内不要启动A/D转换,或将这段时间内转换结果丢弃。通过改变ADMUX中的REFS1、REFS0来更改参考电源后,第一次差分转换同样要遵循以上的时间处理过程。

  1. 当要改变ADC输入通道时,应该遵守以下方式,以保证能够选择到正确的通道:

在单次转换模式下,总是在开始转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到转换完成后,再改变通道选择。 
在连续转换模式下,总是在启动ADC开始第一次转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到第一次转换完成后再改变通道的设置。然而由于此时新一次的转换已经自动开始,所以,当前这次的转换结果仍反映前一通道的转换值,而下一次的转换结果将为新设置通道的值。

  1. ADC电压参考源

ADC的参考电压(VREF)决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果接近于0x3FF。ADC的参考电压VREF可以选择为AVCC或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。 
AVCC通过一个无源开关连接到ADC。内部2.56V参考源是由内部能隙参考源(VBC)通过内部的放大器产生的。注意,无论选用什么内部参考电源,外部AREF引脚都是直接与ADC相连的,因此,可以通过外部在AREF引脚和地之间并接一个电容,使内部参考电源更加稳定和抗噪。可以通过使用高阻电压表测量AREF引脚,来获得参考电源VREF的电压值。由于VREF是一个高阻源,因此,只有容性负载可以连接到该引脚。 
如果将一个外部固定的电压源连接到AREF引脚,那就不能使用任何的内部参考电源,否则就会使外部电压源短路。外部参考电源的范围应在2.0V到AVCC-0.2V之间。参考电源改变后的第一次ADC转换结果可能不太准确,建议抛弃该次转换结果。 
3.ADC转换结果 
A/D转换结束后(ADIF = 1),在ADC数据寄存器(ADCL和ADCH)中可以取得转换的结果。对于单端输入的A/D转换,其转换结果为: 
ADC =(VIN×1024)/ VREF 
其中VIN表示选定的输入引脚上的电压,VREF表示选定的参考电源的电压。0x000表示输入引脚的电压为模拟地,0x3FF表示输入引脚的电压为参考电压值减去一个LSB。 
对于差分转换,其结果为: 
ADC=(VPOS-VNEG) ×GAIN×512/VREF 
例:若差分输入通道选择为ADC3-ADC2,10倍增益,参考电压2.56V,左端对齐(ADMUX=0xED),ADC3引脚上电压300mV,ADC2引脚上电压500mV。 
则ADCR =(300-500)×10×512 / 2560 = -400 = 0x270
ADCL=0x00,ADCH=0x9C。 
若结果为右端对齐时(ADLAR=“0”),则ADCL=0x70,ADCH=0x02。

附录2、ADC应用设计的深入讨论 
尽管AVR内部集成了10位的ADC,但是在实际应用中,要想真正实现10位精度,比较稳定的ADC的话,并不象上一节中的例子那么简单。需要进一步从硬件、软件等方面进行综合的、细致的考虑。下面介绍一些在ADC设计应用中应该考虑的几个要点。 
1.AVcc的稳定性。 
AVcc是提供给ADC工作的电源,如果AVcc不稳定,就会影响ADC的转换精度。在图10-5中,系统电源通过一个LC滤波后接入AVcc,这样就能很好的抑制掉系统电源中的高频躁声,提高了AVcc的稳定性。另外在要求比较高的场合使用ADC时,PA口上的那些没被用做ADC输入的端口尽量不要作为数字I/O口使用。因为PA口的工作电源是由AVcc提供的,如果PA口上有比较大的电流波动,也会影响AVcc的稳定。 
2.参考电压VREF的选择确定 
在实际应用中,要根据输入测量电压的范围选择正确的参考电压VREF,以求得到比较好的转换精度。ADC的参考电压VREF还决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果全部接近于0x3FF,因此ADC的参考电压应稍大于模拟输入电压的最高值。 
ADC的参考电压VREF可以选择为AVCC,或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。外接参考电压应该稳定,并大于2.0V(芯片的工作电压为1.8V时,外接参考电压应大于1.0V)。要求比较高的场合,建议在AREF引脚外接标准参考电压源来作为ADC的参考电源。 
3. ADC通道带宽和输入阻抗 
不管使用单端输入转换还是差分输入转换方式,所有模拟输入口的输入电压应在AVcc-GNG之间。 
在单端ADC转换方式时,ADC通道的输入频率带宽取决于ADC转换时钟频率。一次常规的ADC转换需要13个ADC时钟,当ADC转换时钟为1MHz时,一秒种内ADC采样转换的次数约77K。根据采样定理,此时ADC通道的带宽为38.5KHz。 
差分方式ADC转换的带宽是由芯片内部的差分放大器的带宽决定,为4KHz。 
AVR的ADC输入阻抗典型值为100MΩ,为保证测量的准确,被测信号源的输出阻抗要尽可能的低,应在10K以下。 
4. ADC采样时钟的选择 
通常条件下,AVR的ADC逐次比较电路要达到转换的最大精度,需要一个50K~200KHz的采样时钟。一次正常的ADC转换过程需要13个采样时钟,假定ADC采样时钟为200KHz,那么最高的采样速率为200K/13=15.384K。因此根据采样定理,理论上被测模拟信号的最高频率为7.7K! 
尽管可以设置ADC的采样时钟为1M,但并不能提高ADC转换精度,反而会降低转换精度(受逐次比较硬件电路的限制),因此AVR的ADC不能完成高速ADC的任务。如果所需的转换精度低于10位,那么采样时钟可以高于200KHz,以达到更高的采样频率。 
ADC采样时钟的选择方式为:给出或估计被测模拟信号的最高频率fs,取采样频率为fs的4-10倍,再乘上13为ADC采样时钟频率,该频率应在50K~200KHz之间。 如果该频率大于200KHz,则ADC的10位精度不能保证。如果该频率小于50Khz,则可选择50K~200KHz之间的数值。 
5.模拟噪声的抑制 
器件外部和内部的数字电路会产生电磁干扰,并会影响模拟测量的精度。如果ADC转换精度要求很高,可以采用以下的技术来降低噪声的影响: 
(1)使模拟信号的通路尽可能的短。模拟信号连线应从模拟地的布线盘上通过,并使它们尽可能远离高速开关数字信号线。 
(2)AVR的AVcc引脚应该通过LC网络与数字端电源Vcc相连。
(3)采用ADC噪声抑制器功能来降低来自MCU内部的噪声。 
(4)如果某些ADC引脚是作为通用数字输出口使用,那么在ADC转换过程中,不要改变这些引脚的状态。
6.ADC的校正 
由于AVD内部ADC部分的放大器非线性等客观原因,ADC的转换结果会有误差的。如果要获得高精度的ADC转换,还需要对ADC结果进行校正。具体的方法请参考AVR应用笔记AVR120(avr_app_120.pdf),在这篇应用设计参考中详细介绍了误差的种类,以及校正方案。 
7.ADC精度的提高 
在有了上述几点的保证后,通过软件的手段也能适当的提高ADC的精度。如采用多次测量取平均,软件滤波算法等。在AVR应用笔记AVR121(avr_app_121.pdf)中介绍了一种使用过采样算法的软件实现,可以将ADC的精度提高到11位或更高,当然这是在花费更多的时间基础上实现的。6.2 模数转换的ADC实验 

6.2.1、实例功能 
AVR的模数转换器ADC具有下列特点:

  1. 10位精度;
  2. 0.5LSB积分非线形误差
  3. ±2LSB的绝对精度;
  4. 13µs~260µs的转换时间;
  5. 在最大精度下可达到每秒15kSPS的采样速率;
  6. 8路可选的单端输入通道;
  7. 7路差分输入通道;
  8. 2路差分输入通道带有可选的10×和200×增益;
  9. ADC转换结果的读取可设置为左端对齐(LEFT ADJUSTMENT);
  10. ADC的电压输入范围0~Vcc;
  11. 可选择的内部2.56V的ADC参考电压源;
  12. 自由连续转换模式和单次转换模式;
  13. ADC自动转换触发模式选择;
  14. ADC转换完成中断;
  15. 休眠模式下的噪声抑制器(NOISE CANCELER)。

在本实例中,我们将编写程序实现将模数转换后获得的电压值通过单片机的串口发送到计算机,然后通过计算机上的串口助手显示测量的电压值。 
本实例共有3个功能模块,分别描述如下: 
● 单片机系统:使用单片机的串口实现将模数转换后获得的电压值通过串口发送到计算机。 
● 外围电路:RS232电平转换电路,DB9串行接口插座,模拟电压输入采集电路。
● 软件程序:进一步熟悉单片机的串行通信,并掌握单片机的模数转换的方法。
6.2.2、器件和原理 
关于串行接口的原理已接单片机与计算机的串口的连接在上一实例中进行了描述,在本实例中不再重复。
本实例只介绍ATmega16单片机如何通过内置的模数转换模块采集外界输入的模拟电压。
1、ATmega16单片机的模数转换器ADC介绍 
由于单片机只能处理数字信号,所以外部的模拟信号量需要转变成数字量才能进一步的由单片机进行处理。ATmega16内部集成有一个10位逐次比较(successive approximation)ADC电路。因此使用AVR可以非常方便的处理输入的模拟信号量。 
ATmega16的ADC与一个8通道的模拟多路选择器连接,能够对以PORTA作为ADC输入引脚的8路单端模拟输入电压进行采样,单端电压输入以0V(GND)为参考。另外还支持16种差分电压输入组合,其中2种差分输入方式(ADC1,ADC0和ACD3,ADC2)带有可编程增益放大器,能在A/D转换前对差分输入电压进行0dB(1×),20dB(10×)或46dB(200×)的放大。还有七种差分输入方式的模拟输入通道共用一个负极(ADC1),此时其它任意一个ADC引脚都可作为相应的正极。若增益为1×或10×,则可获得8位的精度。如果增益为200×,那么转换精度为7位。 
AVR的ADC功能单元由独立的专用模拟电源引脚AVcc供电。AVcc和Vcc的电压差别不能大于±0.3V。ADC转换的参考电源可采用芯片内部的2.56V参考电源,或采用AVcc,也可使用外部参考电源。使用外部参考电源时,外部参考电源由引脚ARFE接入。使用内部电压参考源时,可以通过在AREF引脚外部并接一个电容来提高ADC的抗噪性能。
ADC功能单元包括采样保持电路,以确保输入电压在ADC转换过程中保持恒定。ADC通过逐次比较(successive approximation)方式,将输入端的模拟电压转换成10位的数字量。最小值代表地,最大值为AREF引脚上的电压值减1个LSB。可以通过ADMUX寄存器中REFSn位的设置,选择将芯片内部参考电源(2.56V)或AVcc连接到AREF,作为A/D转换的参考电压。这时,内部电压参考源可以通过外接于AREF引脚的电容来稳定,以改进抗噪特性。 
模拟输入通道和差分增益的选择是通过ADMUX寄存器中的MUX位设定的。任何一个ADC的输入引脚,包括地(GND)以及内部的恒定能隙(fixed bandgap)电压参考源,都可以被选择用来作为ADC的单端输入信号。而ADC的某些输入引脚则可选择作为差分增益放大器的正、负极输入端。当选定了差分输入通道后,差分增益放大器将两输入通道上的电压差按选定增益系数放大,然后输入到ADC中。若选定使用单端输入通道,则增益放大器无效。 
通过设置ADCSRA寄存器中的ADC使能位ADEN来使能ADC。在ADEN没有置“1”前,参考电压源和输入通道的选定将不起作用。当ADEN位清“0”后,ADC将不消耗能量,因此建议在进入节电休眠模式前将ADC关掉。 
ADC将10位的转换结果放在ADC数据寄存器中(ADCH和ADCL)。默认情况下,转换结果为右端对齐(RIGHT ADJUSTED)的。但可以通过设置ADMUX寄存器中ADLAR位,调整为左端对齐(LEFT ADJUSTED)。如果转换结果是左端对齐,并且只需要8位的精度,那么只需读取ADCH寄存器的数据作为转换结果就达到要求了。否则,必须先读取ADCL寄存器,然后再读取ADCH寄存器,以保证数据寄存器中的内容是同一次转换的结果。因为一旦ADCL寄存器被读取,就阻断了ADC对ADC数据寄存器的操作。这就意味着,一旦指令读取了ADCL,那么必须紧接着读取一次ADCH;如果在读取ADCL和读取ADCH的过程中正好有一次ADC转换完成,ADC的2个数据寄存器的内容是不会被更新的,该次转换的结果将丢失。只有当ADCH寄存器被读取后,ADC才可以继续对ADCL和ADCH寄存器操作更新。 
ADC有自己的中断,当转换完成时中断将被触发。尽管在顺序读取ADCL和ADCH寄存器过程中,ADC对ADC数据寄存器的更新被禁止,转换的结果丢失,但仍会触发ADC中断。 
2、ATmwga16单片机的模数转换器ADC相关的I/O寄存器 
1.ADC多路复用器选择寄存器—ADMUX

  1. 位7,6—REFS[1:0]:ADC参考电源选择

REFS1、REFS2用于选择ADC的参考电压源,见表6.2.1。如果这些位在ADC转换过程中被改变,新的选择将在该次ADC转换完成后(ADCSRA中的ADIF被置位)才生效。一旦选择内部参考源(AVcc、2.56V)为ADC的参考电压后,AREF引脚上不得施加外部的参考电源,只能与GND之间并接抗干扰电容。


表6.2.1  ADC参考电源选择

REFS1

REFS0

ADC参考电源

0

0

外部引脚AREF,断开内部参考源连接

0

1

AVcc,AREF外部并接电容

1

0

保留

1

1

内部2.56V,AREF外部并接电容

  1. 位5—ADLAR:ADC结果左对齐选择

ADLAR位决定转换结果在ADC数据寄存器中的存放形式。写“1”到ADLAR位,将使转换结果左对齐(LEFT ADJUST);否则,转换结果为右对齐(RIGHT ADJUST)。无论ADC是否正在进行转换,改变ADLAR位都将会立即影响ADC数据寄存器。

  1. 位4..0—MUX4:0:模拟通道和增益选择

这5个位用于对连接到ADC的输入通道和差分通道的增益进行选择设置,详见表6.2.2。注意,只有转换结束后(ADCSRA的ADIF是“1”),改变这些位才会有效。


表6.2.2   ADC输入通道和增益选择

MUX[4:0]

单端输入

差分正极输入

差分负极输入

增益

00000

ADC0

N/A

00001

ADC1

00010

ADC2

00011

ADC3

00100

ADC4

00101

ADC5

00110

ADC6

00111

ADC7

01000

N/A

ADC0

ADC0

10×

01001

ADC1

ADC0

10×

01010

ADC0

ADC0

200×

01011

ADC1

ADC0

200×

01100

ADC2

ADC2

10×

01101

ADC3

ADC2

10×

01110

ADC2

ADC2

200×

01111

ADC3

ADC2

200×

10000

ADC0

ADC1

10001

ADC1

ADC1

10010

ADC2

ADC1

10011

ADC3

ADC1

10100

ADC4

ADC1

10101

ADC5

ADC1

10110

ADC6

ADC1

10111

ADC7

ADC1

11000

ADC0

ADC2

11001

ADC1

ADC2

11010

ADC2

ADC2

11011

ADC3

ADC2

11100

ADC4

ADC2

11101

ADC5

ADC2

11110

1.22V(VBG)

N/A

11111

0V(GND)

   本实例中我们需要设置ADC的参考电压源为AVcc,即REFS0设置为1,ADC默认转换结果为右对齐,我们不需要改变,模拟通道选择ADC0通道单端输入,即MUX4:0。 
2.ADC控制和状态寄存器A—ADCSRA

  1. 位7—ADEN:ADC使能

该位写入“1”时使能ADC,写入“0”关闭ADC。如在ADC转换过程中将ADC关闭,该次转换随即停止。

  1. 位6—ADSC:ADC转换开始

在单次转换模式下,置该位为“1”,将启动一次转换。在自由连续转换模式下,该位写入“1”将启动第一次转换。先置位ADEN位使能ADC,再置位ADSC;或置位ADSC的同时使能ADC,都会使能ADC开始进行第一次转换。第一次ADC转换将需要25个ADC时钟周期,而不是常规转换的13个ADC时钟周期,这是因为第一次转换需要完成对ADC的初始化。 
在ADC转换的过程中,ADSC将始终读出为“1”。当转换完成时,它将转变为“0”。强制写入“0”是无效的。

  1. 位5—ADATE:ADC自动转换触发允许

当该位被置为“1”时,允许ADC工作在自动转换触发工作模式下。在该模式下,在触发信号的上升沿时ADC将自动开始一次ADC转换过程。ADC的自动转换触发信号源由SFIOR寄存器中的ADTS位选择确定。

  1. 位4—ADIF:ADC中断标志位

当ADC转换完成并且ADC数据寄存器被更新后该位被置位。如果ADIE位(ADC转换结束中断允许)和SREG寄存器中的I位被置“1”,ADC中断服务程序将被执行。ADIF在执行相应的中断处理向量时被硬件自动清零。此外,ADIF位可以通过写入逻辑“1”来清零。

  1. 位3—ADIE:ADC中断允许

当该位和SREG寄存器中的I位同时被置位时,允许ADC转换完成中断。

  1. 位2,0—ADPS[2:0]:ADC预分频选择

这些位决定了XTAL时钟与输入到ADC的ADC时钟之间分频数,见表6.2.3。

表6.2.3   ADC时钟分频

ADPS[2:0]

分  频  系  数

000

2

001

2

010

4

011

8

100

16

101

32

110

64

111

128

本实例中我们需要使能ADC,即ADEN设置为1,我们不用自动转换,也不需要中断,所以,ADTE、ADIE位不需要设置。在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。本例中使用的时钟是12M的,所以要将时钟64分频,分频后ADC频率为188KHz,即时钟分频选择ADPS[2:0]=6。

3.ADC数据寄存器—ADCL和ADCH

  1. ADLAR = 0,ADC转换结果右对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第19张图片

  1. ADLAR = 1,ADC转换结果左对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第20张图片
当ADC转换完成后,可以读取ADC寄存器的ADC0-ADC9得到ADC的转换的结果。如果是差分输入,转换值为二进制的补码形式。一旦开始读取ADCL后,ADC数据寄存器就不能被ADC更新,直到ADCH寄存器被读取为止。因此,如果结果是左对齐(ADLAR=1),且不需要大于8位的精度的话,仅仅读取ADCH寄存器就足够了。否则,必须先读取ADCL寄存器,再读取ADCH寄存器。ADMUX寄存器中的ADLAR位决定了从ADC数据寄存器中读取结果的格式。如果ADLAR位为“1”,结果将是左对齐;如果ADLAR位为“0”(默认情况),结果将是右对齐。 
4.特殊功能I/O寄存器—SFIOR

  1. 位7..5—ADTS[2:0]:ADC自动转换触发源选择

当ADCSRA寄存器中的ADATE为“1”,允许ADC工作在自动转换触发工作模式时,这3位的设置用于选择ADC的自动转换触发源。如果禁止了ADC的自动转换触发(ADATE为“0”),这3个位的设置值将不起任何作用。

表10-6   ADC自动转换触发源的选择

ADTS[2:0]

触  发  源

000

连续自由转换

001

模拟比较器

010

外部中断0

011

T/C0比较匹配

100

T/C0溢出

101

T/C1比较匹配B

110

T/C1溢出

111

T/C1输入捕捉

本例中我们不使用自动转换功能,所以该寄存器可以不必设置。
6.2.3、电路 
本实例的电路包括232电平转换电路和电阻分压电路,这两种电路在前面的实例中均做过介绍,这里不再重复。
1、电路原理
在本实例中利用MAX3232芯片使单片机输出的TTL电平转换为标准的RS232电平,从而使计算机能够识别。同时将计算机输出的RS232电平转换为单片机可以识别的TTL电平。
利用电位器产生电阻分压电路,从而产生变化的模拟电压加到单片机的模拟信号采集端口,供单片机采集。
2、电路连接 
电路中MAX3232芯片的9、10引脚分别连接单片机的PD0、PD1端口,MAX3232的13、14引脚分别连接计算机串口线的3、2脚。
电位器RP2的动片引脚连接单片机的模拟信号采集通道PA0(ADC0)。
3、特别说明
本学习板采用的是串口插座是公头的,所以与计算机相连的串口连接线应该是交叉串口线,而不是串口延长线。
6.2.4、程序设计 
1、程序功能 

程序的功能是通过单片机的串行接口,将单片机采集到的模拟电压值发送到计算机中,通过计算机上的串口助手显示采集的电压值。    
● 单片机串行接收中断的编程
在本例中,我们用到了单片机的串行接收中断,需要编写串行接收中断服务程序,通过查询WINAVR(GCC)的中断库函数手册,可以查找到ATmega16单片机串行接收中断的中断向量为USART_RXC_vect。据此我们可以编写串行接收中断服务程序,如下:
//接收中断函数 
ISR(USART_RXC_vect )
{
unsigned char Rev;
Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 
Usart_PutChar(Rev);    //将接收到的数据发送 
}
在中断服务程序中,我们首先把单片机串口接收到的数据放入变量Rev中,然后调用上一实例中编写的串行接口字节发送函数将变量Rev中的数据发送到计算机。 
2、 单片机与计算机串行通信结果的观察 
在观察本例运行结果时,我们同样要用到串口助手,本例中,单片机发送串口数据采用的波特率是9600bps,数据格式是8位数据位,1位停止位,无奇偶校验。在串口助手里面,我们也要将波特率和数据格式设置成一样的。 
3、函数说明 
本实例用到了6个函数,分别是: 
void Port_Init(void);   //端口初始化配置 
void Usart_Init(void);  //USART寄存器设置 
void AD_Init(void);    //AD初始化 
void Usart_PutChar(unsigned char cTXData);  //字节发送函数 
void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 
unsigned int AD_GetData(void);    //AD转换函数   
4、使用WINAVR开发环境,在本例中我们使用的是外部12M的晶振,所以需要将MAKEFILE文件中的时钟频率修改为12M。另外在程序烧录到单片机的时候,熔丝位也要选择为外部12M晶振(注意是晶振,不是外部振荡器,一定不要选择错了,否则会导致单片机不能再烧写程序)。
5、程序代码

#include         #include #include   //中断函数头文件

//常量声明 #define BAUD 9600         //波特率设置值

//全局变量声明 unsigned int ADData;          //AD转换获得的数据

//函数声明 void Port_Init(void);   //端口初始化配置 void Usart_Init(void);  //USART寄存器设置 void AD_Init(void);    //AD初始化 void Usart_PutChar(unsigned char cTXData);  //字节发送函数 void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 unsigned int AD_GetData(void);    //AD转换函数

int main(void)             { unsigned char Delay3s;

      Port_Init(); Usart_Init(); AD_Init(); Usart_PutString("AD转换测试程序"); Usart_PutString("测得ADC0通道的电压值为:"); sei();          //使能全局中断   while(1) { ADData = (int)((long)AD_GetData() * 5010 / 1024);         //将获得的AD值转换为电压值 //单位为mv。 Usart_PutChar(ADData / 1000 + 0x30);      //得到电压值的千位并发送 Usart_PutChar('.');                          //发送小数点 Usart_PutChar(ADData % 1000 / 100 + 0x30);   //得到电压值的百位并发送 Usart_PutChar(ADData % 100 / 10 + 0x30);    //得到电压值的十位并发送 Usart_PutChar(ADData % 10 + 0x30);     //得到电压值的个位并发送 Usart_PutChar('V');                  //发送电压符号“V” Usart_PutChar(0x0d);     // Usart_PutChar(0x0a);     //  AD值发送结束,回车换行 for(Delay3s = 0;Delay3s < 30;Delay3s++)     //延时3S { _delay_ms(90); } } }

//端口状态初始化设置函数 void Port_Init() { PORTA = 0X00;         DDRA = 0x00;   //ADC通道设置为输入口,高阻态     }

//USART寄存器配置函数 void Usart_Init() { UCSRA = 0X00;     UCSRC |= (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0);  //异步,数据格式8,N,1 //UCSRC寄存器与UBRRH寄存器共用相同的I/O地址,写 UCSRC 时, URSEL 应设置为 1。 UBRRL = (F_CPU / BAUD / 16 - 1) % 256;    //波特率设置 UBRRH = (F_CPU / BAUD / 16 - 1) / 256;         UCSRB |= (1 << RXCIE) | (1 << RXEN) | (1 << TXEN);    //发送使能 }

//字节发送函数 void Usart_PutChar(unsigned char cTXData) { while( !(UCSRA & (1 << UDRE)) );  //只有数据寄存器为空时才能发送数据 UDR = cTXData;                   //发送数据送USART I/O数据寄存器-UDR }

//接收中断函数 ISR(USART_RXC_vect ) { unsigned char Rev; Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 Usart_PutChar(Rev);    //将接收到的数据发送 }

void Usart_PutString(unsigned char *pcString) { while (*pcString) { Usart_PutChar(*pcString++);   } Usart_PutChar(0x0D); Usart_PutChar(0x0A);  //结尾发送回车换行 }

//AD转换初始化函数 void AD_Init() { ADMUX |= (1 << REFS0);     //ADC参考电压为AVcc,ADC结果右对齐,选择通道ADC0 ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); //使能AD转换,ADC时钟64分频 }

//AD转换函数 unsigned int AD_GetData() { ADCSRA |= (1 << ADSC);     //开始AD转换 while(!(ADCSRA & (1 << ADIF)));    //等待转换完成 ADCSRA |= (1 << ADIF);              //清零ADC中断标志位 return ADC;        //返回ADC值 }

附录:ADC应用设计要点

1.预分频与转换时间 
在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。在要求转换精度低于10位的情况下,ADC的采样时钟可以高于200kHz,以获得更高的采样率。 
ADC模块中包含一个预分频器的ADC时钟源,它可以对大于100KHz的系统时钟进行分频,以获得合适的ADC时钟提供ADC使用。预分频器的分频系数由ADCSRA寄存器中的ADPS位设置的。一旦寄存器ADCSRA中的ADEN位置“1”(ADC开始工作),预分频器就启动开始计数。ADEN位为“1”时,预分频器将一直工作;ADEN位为“0”时,预分频器一直处在复位状态。 
AVR的ADC完成一次转换的时间见表6.2.5。从表中可以看出,完成一次ADC转换通常需要13-14个ADC时钟。而启动ADC开始第一次转换到完成的时间需要25个ADC时钟,这是因为要对ADC单元的模拟电路部分进行初始化。


表6.2.5  ADC转换和采样保持时间

转 换 形 式

采样保持时间

完 成 转 换 总 时 间

启动ADC后的第一次转换

13.5个ADC时钟

25个ADC时钟

正常转换,单端输入

1.5个ADC时钟

13个ADC时钟

自动触发方式

2个ADC时钟

13.5个ADC时钟

正常转换,差分输入

1.5/2.5个ADC时钟

13/14个ADC时钟

当ADCSRA寄存器中的ADSC位置位,启动ADC转换时,A/D转换将在随后ADC时钟的上升沿开始。一次正常的A/D转换开始时,需要1.5个ADC时钟周期的采样保持时间(ADC首次启动后需要13.5个ADC时钟周期的采样保持时间)。当一次A/D转换完成后,转换结果写入ADC数据寄存器,ADIF(ADC中断标志位)将被置位。在单次转换模式下,ADSC也同时被清零。用户程序可以再次置位ADSC位,新的一次转换将在下一个ADC时钟的上升沿开始。 
当ADC设置为自动触发方式时,触发信号的上升沿将启动一次ADC转换。转换完成的结果将一直保持到下一次触发信号的上升沿出现,然后开始新的一次ADC转换。这就保证了使ADC每隔一定的时间间隔进行一次转换。在这种方式下,ADC需要2个ADC时钟周期的采样保持时间。 
在自由连续转换模式下,一次转换完毕后马上开始一次新的转换,此时,ADSC位一直保持为“1”。

2.ADC输入通道和参考电源的选择 
寄存器ADMUX中的MUXn和REFS1、REFS0位实际上是一个缓冲器,该缓冲器与一个MCU可以随机读取的临时寄存器相连通。采用这种结构,保证了ADC输入通道和参考电源只能在ADC转换过程中的安全点被改变。在ADC转换开始前,通道和参考电源可以不断被更新,一旦转换开始,通道和参考电源将被锁定,并保持足够时间,以确保ADC转换的正常进行。在转换完成前的最后一个ADC时钟周期(ADCSRA的ADIF位置“1”时),通道和参考电源又开始重新更新。注意,由于A/D转换开始于置位ADSC后的第一个ADC时钟的上升沿,因此,在置位ADSC后的一个ADC时钟周期内不要将一个新的通道或参考电源写入到ADMUX寄存器中。 
改变差分输入通道时需特别当心。一旦确定了差分输入通道,增益放大器需要125µs的稳定时间。所以在选择了新的差分输入通道后的125µs内不要启动A/D转换,或将这段时间内转换结果丢弃。通过改变ADMUX中的REFS1、REFS0来更改参考电源后,第一次差分转换同样要遵循以上的时间处理过程。

  1. 当要改变ADC输入通道时,应该遵守以下方式,以保证能够选择到正确的通道:

在单次转换模式下,总是在开始转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到转换完成后,再改变通道选择。 
在连续转换模式下,总是在启动ADC开始第一次转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到第一次转换完成后再改变通道的设置。然而由于此时新一次的转换已经自动开始,所以,当前这次的转换结果仍反映前一通道的转换值,而下一次的转换结果将为新设置通道的值。

  1. ADC电压参考源

ADC的参考电压(VREF)决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果接近于0x3FF。ADC的参考电压VREF可以选择为AVCC或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。 
AVCC通过一个无源开关连接到ADC。内部2.56V参考源是由内部能隙参考源(VBC)通过内部的放大器产生的。注意,无论选用什么内部参考电源,外部AREF引脚都是直接与ADC相连的,因此,可以通过外部在AREF引脚和地之间并接一个电容,使内部参考电源更加稳定和抗噪。可以通过使用高阻电压表测量AREF引脚,来获得参考电源VREF的电压值。由于VREF是一个高阻源,因此,只有容性负载可以连接到该引脚。 
如果将一个外部固定的电压源连接到AREF引脚,那就不能使用任何的内部参考电源,否则就会使外部电压源短路。外部参考电源的范围应在2.0V到AVCC-0.2V之间。参考电源改变后的第一次ADC转换结果可能不太准确,建议抛弃该次转换结果。 
3.ADC转换结果 
A/D转换结束后(ADIF = 1),在ADC数据寄存器(ADCL和ADCH)中可以取得转换的结果。对于单端输入的A/D转换,其转换结果为: 
ADC =(VIN×1024)/ VREF 
其中VIN表示选定的输入引脚上的电压,VREF表示选定的参考电源的电压。0x000表示输入引脚的电压为模拟地,0x3FF表示输入引脚的电压为参考电压值减去一个LSB。 
对于差分转换,其结果为: 
ADC=(VPOS-VNEG) ×GAIN×512/VREF 
例:若差分输入通道选择为ADC3-ADC2,10倍增益,参考电压2.56V,左端对齐(ADMUX=0xED),ADC3引脚上电压300mV,ADC2引脚上电压500mV。 
则ADCR =(300-500)×10×512 / 2560 = -400 = 0x270
ADCL=0x00,ADCH=0x9C。 
若结果为右端对齐时(ADLAR=“0”),则ADCL=0x70,ADCH=0x02。

附录2、ADC应用设计的深入讨论 
尽管AVR内部集成了10位的ADC,但是在实际应用中,要想真正实现10位精度,比较稳定的ADC的话,并不象上一节中的例子那么简单。需要进一步从硬件、软件等方面进行综合的、细致的考虑。下面介绍一些在ADC设计应用中应该考虑的几个要点。 
1.AVcc的稳定性。 
AVcc是提供给ADC工作的电源,如果AVcc不稳定,就会影响ADC的转换精度。在图10-5中,系统电源通过一个LC滤波后接入AVcc,这样就能很好的抑制掉系统电源中的高频躁声,提高了AVcc的稳定性。另外在要求比较高的场合使用ADC时,PA口上的那些没被用做ADC输入的端口尽量不要作为数字I/O口使用。因为PA口的工作电源是由AVcc提供的,如果PA口上有比较大的电流波动,也会影响AVcc的稳定。 
2.参考电压VREF的选择确定 
在实际应用中,要根据输入测量电压的范围选择正确的参考电压VREF,以求得到比较好的转换精度。ADC的参考电压VREF还决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果全部接近于0x3FF,因此ADC的参考电压应稍大于模拟输入电压的最高值。 
ADC的参考电压VREF可以选择为AVCC,或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。外接参考电压应该稳定,并大于2.0V(芯片的工作电压为1.8V时,外接参考电压应大于1.0V)。要求比较高的场合,建议在AREF引脚外接标准参考电压源来作为ADC的参考电源。 
3. ADC通道带宽和输入阻抗 
不管使用单端输入转换还是差分输入转换方式,所有模拟输入口的输入电压应在AVcc-GNG之间。 
在单端ADC转换方式时,ADC通道的输入频率带宽取决于ADC转换时钟频率。一次常规的ADC转换需要13个ADC时钟,当ADC转换时钟为1MHz时,一秒种内ADC采样转换的次数约77K。根据采样定理,此时ADC通道的带宽为38.5KHz。 
差分方式ADC转换的带宽是由芯片内部的差分放大器的带宽决定,为4KHz。 
AVR的ADC输入阻抗典型值为100MΩ,为保证测量的准确,被测信号源的输出阻抗要尽可能的低,应在10K以下。 
4. ADC采样时钟的选择 
通常条件下,AVR的ADC逐次比较电路要达到转换的最大精度,需要一个50K~200KHz的采样时钟。一次正常的ADC转换过程需要13个采样时钟,假定ADC采样时钟为200KHz,那么最高的采样速率为200K/13=15.384K。因此根据采样定理,理论上被测模拟信号的最高频率为7.7K! 
尽管可以设置ADC的采样时钟为1M,但并不能提高ADC转换精度,反而会降低转换精度(受逐次比较硬件电路的限制),因此AVR的ADC不能完成高速ADC的任务。如果所需的转换精度低于10位,那么采样时钟可以高于200KHz,以达到更高的采样频率。 
ADC采样时钟的选择方式为:给出或估计被测模拟信号的最高频率fs,取采样频率为fs的4-10倍,再乘上13为ADC采样时钟频率,该频率应在50K~200KHz之间。 如果该频率大于200KHz,则ADC的10位精度不能保证。如果该频率小于50Khz,则可选择50K~200KHz之间的数值。 
5.模拟噪声的抑制 
器件外部和内部的数字电路会产生电磁干扰,并会影响模拟测量的精度。如果ADC转换精度要求很高,可以采用以下的技术来降低噪声的影响: 
(1)使模拟信号的通路尽可能的短。模拟信号连线应从模拟地的布线盘上通过,并使它们尽可能远离高速开关数字信号线。 
(2)AVR的AVcc引脚应该通过LC网络与数字端电源Vcc相连。
(3)采用ADC噪声抑制器功能来降低来自MCU内部的噪声。 
(4)如果某些ADC引脚是作为通用数字输出口使用,那么在ADC转换过程中,不要改变这些引脚的状态。
6.ADC的校正 
由于AVD内部ADC部分的放大器非线性等客观原因,ADC的转换结果会有误差的。如果要获得高精度的ADC转换,还需要对ADC结果进行校正。具体的方法请参考AVR应用笔记AVR120(avr_app_120.pdf),在这篇应用设计参考中详细介绍了误差的种类,以及校正方案。 
7.ADC精度的提高 
在有了上述几点的保证后,通过软件的手段也能适当的提高ADC的精度。如采用多次测量取平均,软件滤波算法等。在AVR应用笔记AVR121(avr_app_121.pdf)中介绍了一种使用过采样算法的软件实现,可以将ADC的精度提高到11位或更高,当然这是在花费更多的时间基础上实现的。6.2 模数转换的ADC实验 

6.2.1、实例功能 
AVR的模数转换器ADC具有下列特点:

  1. 10位精度;
  2. 0.5LSB积分非线形误差
  3. ±2LSB的绝对精度;
  4. 13µs~260µs的转换时间;
  5. 在最大精度下可达到每秒15kSPS的采样速率;
  6. 8路可选的单端输入通道;
  7. 7路差分输入通道;
  8. 2路差分输入通道带有可选的10×和200×增益;
  9. ADC转换结果的读取可设置为左端对齐(LEFT ADJUSTMENT);
  10. ADC的电压输入范围0~Vcc;
  11. 可选择的内部2.56V的ADC参考电压源;
  12. 自由连续转换模式和单次转换模式;
  13. ADC自动转换触发模式选择;
  14. ADC转换完成中断;
  15. 休眠模式下的噪声抑制器(NOISE CANCELER)。

在本实例中,我们将编写程序实现将模数转换后获得的电压值通过单片机的串口发送到计算机,然后通过计算机上的串口助手显示测量的电压值。 
本实例共有3个功能模块,分别描述如下: 
● 单片机系统:使用单片机的串口实现将模数转换后获得的电压值通过串口发送到计算机。 
● 外围电路:RS232电平转换电路,DB9串行接口插座,模拟电压输入采集电路。
● 软件程序:进一步熟悉单片机的串行通信,并掌握单片机的模数转换的方法。
6.2.2、器件和原理 
关于串行接口的原理已接单片机与计算机的串口的连接在上一实例中进行了描述,在本实例中不再重复。
本实例只介绍ATmega16单片机如何通过内置的模数转换模块采集外界输入的模拟电压。
1、ATmega16单片机的模数转换器ADC介绍 
由于单片机只能处理数字信号,所以外部的模拟信号量需要转变成数字量才能进一步的由单片机进行处理。ATmega16内部集成有一个10位逐次比较(successive approximation)ADC电路。因此使用AVR可以非常方便的处理输入的模拟信号量。 
ATmega16的ADC与一个8通道的模拟多路选择器连接,能够对以PORTA作为ADC输入引脚的8路单端模拟输入电压进行采样,单端电压输入以0V(GND)为参考。另外还支持16种差分电压输入组合,其中2种差分输入方式(ADC1,ADC0和ACD3,ADC2)带有可编程增益放大器,能在A/D转换前对差分输入电压进行0dB(1×),20dB(10×)或46dB(200×)的放大。还有七种差分输入方式的模拟输入通道共用一个负极(ADC1),此时其它任意一个ADC引脚都可作为相应的正极。若增益为1×或10×,则可获得8位的精度。如果增益为200×,那么转换精度为7位。 
AVR的ADC功能单元由独立的专用模拟电源引脚AVcc供电。AVcc和Vcc的电压差别不能大于±0.3V。ADC转换的参考电源可采用芯片内部的2.56V参考电源,或采用AVcc,也可使用外部参考电源。使用外部参考电源时,外部参考电源由引脚ARFE接入。使用内部电压参考源时,可以通过在AREF引脚外部并接一个电容来提高ADC的抗噪性能。
ADC功能单元包括采样保持电路,以确保输入电压在ADC转换过程中保持恒定。ADC通过逐次比较(successive approximation)方式,将输入端的模拟电压转换成10位的数字量。最小值代表地,最大值为AREF引脚上的电压值减1个LSB。可以通过ADMUX寄存器中REFSn位的设置,选择将芯片内部参考电源(2.56V)或AVcc连接到AREF,作为A/D转换的参考电压。这时,内部电压参考源可以通过外接于AREF引脚的电容来稳定,以改进抗噪特性。 
模拟输入通道和差分增益的选择是通过ADMUX寄存器中的MUX位设定的。任何一个ADC的输入引脚,包括地(GND)以及内部的恒定能隙(fixed bandgap)电压参考源,都可以被选择用来作为ADC的单端输入信号。而ADC的某些输入引脚则可选择作为差分增益放大器的正、负极输入端。当选定了差分输入通道后,差分增益放大器将两输入通道上的电压差按选定增益系数放大,然后输入到ADC中。若选定使用单端输入通道,则增益放大器无效。 
通过设置ADCSRA寄存器中的ADC使能位ADEN来使能ADC。在ADEN没有置“1”前,参考电压源和输入通道的选定将不起作用。当ADEN位清“0”后,ADC将不消耗能量,因此建议在进入节电休眠模式前将ADC关掉。 
ADC将10位的转换结果放在ADC数据寄存器中(ADCH和ADCL)。默认情况下,转换结果为右端对齐(RIGHT ADJUSTED)的。但可以通过设置ADMUX寄存器中ADLAR位,调整为左端对齐(LEFT ADJUSTED)。如果转换结果是左端对齐,并且只需要8位的精度,那么只需读取ADCH寄存器的数据作为转换结果就达到要求了。否则,必须先读取ADCL寄存器,然后再读取ADCH寄存器,以保证数据寄存器中的内容是同一次转换的结果。因为一旦ADCL寄存器被读取,就阻断了ADC对ADC数据寄存器的操作。这就意味着,一旦指令读取了ADCL,那么必须紧接着读取一次ADCH;如果在读取ADCL和读取ADCH的过程中正好有一次ADC转换完成,ADC的2个数据寄存器的内容是不会被更新的,该次转换的结果将丢失。只有当ADCH寄存器被读取后,ADC才可以继续对ADCL和ADCH寄存器操作更新。 
ADC有自己的中断,当转换完成时中断将被触发。尽管在顺序读取ADCL和ADCH寄存器过程中,ADC对ADC数据寄存器的更新被禁止,转换的结果丢失,但仍会触发ADC中断。 
2、ATmwga16单片机的模数转换器ADC相关的I/O寄存器 
1.ADC多路复用器选择寄存器—ADMUX

  1. 位7,6—REFS[1:0]:ADC参考电源选择

REFS1、REFS2用于选择ADC的参考电压源,见表6.2.1。如果这些位在ADC转换过程中被改变,新的选择将在该次ADC转换完成后(ADCSRA中的ADIF被置位)才生效。一旦选择内部参考源(AVcc、2.56V)为ADC的参考电压后,AREF引脚上不得施加外部的参考电源,只能与GND之间并接抗干扰电容。


表6.2.1  ADC参考电源选择

REFS1

REFS0

ADC参考电源

0

0

外部引脚AREF,断开内部参考源连接

0

1

AVcc,AREF外部并接电容

1

0

保留

1

1

内部2.56V,AREF外部并接电容

  1. 位5—ADLAR:ADC结果左对齐选择

ADLAR位决定转换结果在ADC数据寄存器中的存放形式。写“1”到ADLAR位,将使转换结果左对齐(LEFT ADJUST);否则,转换结果为右对齐(RIGHT ADJUST)。无论ADC是否正在进行转换,改变ADLAR位都将会立即影响ADC数据寄存器。

  1. 位4..0—MUX4:0:模拟通道和增益选择

这5个位用于对连接到ADC的输入通道和差分通道的增益进行选择设置,详见表6.2.2。注意,只有转换结束后(ADCSRA的ADIF是“1”),改变这些位才会有效。


表6.2.2   ADC输入通道和增益选择

MUX[4:0]

单端输入

差分正极输入

差分负极输入

增益

00000

ADC0

N/A

00001

ADC1

00010

ADC2

00011

ADC3

00100

ADC4

00101

ADC5

00110

ADC6

00111

ADC7

01000

N/A

ADC0

ADC0

10×

01001

ADC1

ADC0

10×

01010

ADC0

ADC0

200×

01011

ADC1

ADC0

200×

01100

ADC2

ADC2

10×

01101

ADC3

ADC2

10×

01110

ADC2

ADC2

200×

01111

ADC3

ADC2

200×

10000

ADC0

ADC1

10001

ADC1

ADC1

10010

ADC2

ADC1

10011

ADC3

ADC1

10100

ADC4

ADC1

10101

ADC5

ADC1

10110

ADC6

ADC1

10111

ADC7

ADC1

11000

ADC0

ADC2

11001

ADC1

ADC2

11010

ADC2

ADC2

11011

ADC3

ADC2

11100

ADC4

ADC2

11101

ADC5

ADC2

11110

1.22V(VBG)

N/A

11111

0V(GND)

   本实例中我们需要设置ADC的参考电压源为AVcc,即REFS0设置为1,ADC默认转换结果为右对齐,我们不需要改变,模拟通道选择ADC0通道单端输入,即MUX4:0。 
2.ADC控制和状态寄存器A—ADCSRA

  1. 位7—ADEN:ADC使能

该位写入“1”时使能ADC,写入“0”关闭ADC。如在ADC转换过程中将ADC关闭,该次转换随即停止。

  1. 位6—ADSC:ADC转换开始

在单次转换模式下,置该位为“1”,将启动一次转换。在自由连续转换模式下,该位写入“1”将启动第一次转换。先置位ADEN位使能ADC,再置位ADSC;或置位ADSC的同时使能ADC,都会使能ADC开始进行第一次转换。第一次ADC转换将需要25个ADC时钟周期,而不是常规转换的13个ADC时钟周期,这是因为第一次转换需要完成对ADC的初始化。 
在ADC转换的过程中,ADSC将始终读出为“1”。当转换完成时,它将转变为“0”。强制写入“0”是无效的。

  1. 位5—ADATE:ADC自动转换触发允许

当该位被置为“1”时,允许ADC工作在自动转换触发工作模式下。在该模式下,在触发信号的上升沿时ADC将自动开始一次ADC转换过程。ADC的自动转换触发信号源由SFIOR寄存器中的ADTS位选择确定。

  1. 位4—ADIF:ADC中断标志位

当ADC转换完成并且ADC数据寄存器被更新后该位被置位。如果ADIE位(ADC转换结束中断允许)和SREG寄存器中的I位被置“1”,ADC中断服务程序将被执行。ADIF在执行相应的中断处理向量时被硬件自动清零。此外,ADIF位可以通过写入逻辑“1”来清零。

  1. 位3—ADIE:ADC中断允许

当该位和SREG寄存器中的I位同时被置位时,允许ADC转换完成中断。

  1. 位2,0—ADPS[2:0]:ADC预分频选择

这些位决定了XTAL时钟与输入到ADC的ADC时钟之间分频数,见表6.2.3。

表6.2.3   ADC时钟分频

ADPS[2:0]

分  频  系  数

000

2

001

2

010

4

011

8

100

16

101

32

110

64

111

128

本实例中我们需要使能ADC,即ADEN设置为1,我们不用自动转换,也不需要中断,所以,ADTE、ADIE位不需要设置。在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。本例中使用的时钟是12M的,所以要将时钟64分频,分频后ADC频率为188KHz,即时钟分频选择ADPS[2:0]=6。

3.ADC数据寄存器—ADCL和ADCH

  1. ADLAR = 0,ADC转换结果右对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第21张图片

  1. ADLAR = 1,ADC转换结果左对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第22张图片
当ADC转换完成后,可以读取ADC寄存器的ADC0-ADC9得到ADC的转换的结果。如果是差分输入,转换值为二进制的补码形式。一旦开始读取ADCL后,ADC数据寄存器就不能被ADC更新,直到ADCH寄存器被读取为止。因此,如果结果是左对齐(ADLAR=1),且不需要大于8位的精度的话,仅仅读取ADCH寄存器就足够了。否则,必须先读取ADCL寄存器,再读取ADCH寄存器。ADMUX寄存器中的ADLAR位决定了从ADC数据寄存器中读取结果的格式。如果ADLAR位为“1”,结果将是左对齐;如果ADLAR位为“0”(默认情况),结果将是右对齐。 
4.特殊功能I/O寄存器—SFIOR

  1. 位7..5—ADTS[2:0]:ADC自动转换触发源选择

当ADCSRA寄存器中的ADATE为“1”,允许ADC工作在自动转换触发工作模式时,这3位的设置用于选择ADC的自动转换触发源。如果禁止了ADC的自动转换触发(ADATE为“0”),这3个位的设置值将不起任何作用。

表10-6   ADC自动转换触发源的选择

ADTS[2:0]

触  发  源

000

连续自由转换

001

模拟比较器

010

外部中断0

011

T/C0比较匹配

100

T/C0溢出

101

T/C1比较匹配B

110

T/C1溢出

111

T/C1输入捕捉

本例中我们不使用自动转换功能,所以该寄存器可以不必设置。
6.2.3、电路 
本实例的电路包括232电平转换电路和电阻分压电路,这两种电路在前面的实例中均做过介绍,这里不再重复。
1、电路原理
在本实例中利用MAX3232芯片使单片机输出的TTL电平转换为标准的RS232电平,从而使计算机能够识别。同时将计算机输出的RS232电平转换为单片机可以识别的TTL电平。
利用电位器产生电阻分压电路,从而产生变化的模拟电压加到单片机的模拟信号采集端口,供单片机采集。
2、电路连接 
电路中MAX3232芯片的9、10引脚分别连接单片机的PD0、PD1端口,MAX3232的13、14引脚分别连接计算机串口线的3、2脚。
电位器RP2的动片引脚连接单片机的模拟信号采集通道PA0(ADC0)。
3、特别说明
本学习板采用的是串口插座是公头的,所以与计算机相连的串口连接线应该是交叉串口线,而不是串口延长线。
6.2.4、程序设计 
1、程序功能 

程序的功能是通过单片机的串行接口,将单片机采集到的模拟电压值发送到计算机中,通过计算机上的串口助手显示采集的电压值。    
● 单片机串行接收中断的编程
在本例中,我们用到了单片机的串行接收中断,需要编写串行接收中断服务程序,通过查询WINAVR(GCC)的中断库函数手册,可以查找到ATmega16单片机串行接收中断的中断向量为USART_RXC_vect。据此我们可以编写串行接收中断服务程序,如下:
//接收中断函数 
ISR(USART_RXC_vect )
{
unsigned char Rev;
Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 
Usart_PutChar(Rev);    //将接收到的数据发送 
}
在中断服务程序中,我们首先把单片机串口接收到的数据放入变量Rev中,然后调用上一实例中编写的串行接口字节发送函数将变量Rev中的数据发送到计算机。 
2、 单片机与计算机串行通信结果的观察 
在观察本例运行结果时,我们同样要用到串口助手,本例中,单片机发送串口数据采用的波特率是9600bps,数据格式是8位数据位,1位停止位,无奇偶校验。在串口助手里面,我们也要将波特率和数据格式设置成一样的。 
3、函数说明 
本实例用到了6个函数,分别是: 
void Port_Init(void);   //端口初始化配置 
void Usart_Init(void);  //USART寄存器设置 
void AD_Init(void);    //AD初始化 
void Usart_PutChar(unsigned char cTXData);  //字节发送函数 
void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 
unsigned int AD_GetData(void);    //AD转换函数   
4、使用WINAVR开发环境,在本例中我们使用的是外部12M的晶振,所以需要将MAKEFILE文件中的时钟频率修改为12M。另外在程序烧录到单片机的时候,熔丝位也要选择为外部12M晶振(注意是晶振,不是外部振荡器,一定不要选择错了,否则会导致单片机不能再烧写程序)。
5、程序代码

#include         #include #include   //中断函数头文件

//常量声明 #define BAUD 9600         //波特率设置值

//全局变量声明 unsigned int ADData;          //AD转换获得的数据

//函数声明 void Port_Init(void);   //端口初始化配置 void Usart_Init(void);  //USART寄存器设置 void AD_Init(void);    //AD初始化 void Usart_PutChar(unsigned char cTXData);  //字节发送函数 void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 unsigned int AD_GetData(void);    //AD转换函数

int main(void)             { unsigned char Delay3s;

      Port_Init(); Usart_Init(); AD_Init(); Usart_PutString("AD转换测试程序"); Usart_PutString("测得ADC0通道的电压值为:"); sei();          //使能全局中断   while(1) { ADData = (int)((long)AD_GetData() * 5010 / 1024);         //将获得的AD值转换为电压值 //单位为mv。 Usart_PutChar(ADData / 1000 + 0x30);      //得到电压值的千位并发送 Usart_PutChar('.');                          //发送小数点 Usart_PutChar(ADData % 1000 / 100 + 0x30);   //得到电压值的百位并发送 Usart_PutChar(ADData % 100 / 10 + 0x30);    //得到电压值的十位并发送 Usart_PutChar(ADData % 10 + 0x30);     //得到电压值的个位并发送 Usart_PutChar('V');                  //发送电压符号“V” Usart_PutChar(0x0d);     // Usart_PutChar(0x0a);     //  AD值发送结束,回车换行 for(Delay3s = 0;Delay3s < 30;Delay3s++)     //延时3S { _delay_ms(90); } } }

//端口状态初始化设置函数 void Port_Init() { PORTA = 0X00;         DDRA = 0x00;   //ADC通道设置为输入口,高阻态     }

//USART寄存器配置函数 void Usart_Init() { UCSRA = 0X00;     UCSRC |= (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0);  //异步,数据格式8,N,1 //UCSRC寄存器与UBRRH寄存器共用相同的I/O地址,写 UCSRC 时, URSEL 应设置为 1。 UBRRL = (F_CPU / BAUD / 16 - 1) % 256;    //波特率设置 UBRRH = (F_CPU / BAUD / 16 - 1) / 256;         UCSRB |= (1 << RXCIE) | (1 << RXEN) | (1 << TXEN);    //发送使能 }

//字节发送函数 void Usart_PutChar(unsigned char cTXData) { while( !(UCSRA & (1 << UDRE)) );  //只有数据寄存器为空时才能发送数据 UDR = cTXData;                   //发送数据送USART I/O数据寄存器-UDR }

//接收中断函数 ISR(USART_RXC_vect ) { unsigned char Rev; Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 Usart_PutChar(Rev);    //将接收到的数据发送 }

void Usart_PutString(unsigned char *pcString) { while (*pcString) { Usart_PutChar(*pcString++);   } Usart_PutChar(0x0D); Usart_PutChar(0x0A);  //结尾发送回车换行 }

//AD转换初始化函数 void AD_Init() { ADMUX |= (1 << REFS0);     //ADC参考电压为AVcc,ADC结果右对齐,选择通道ADC0 ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); //使能AD转换,ADC时钟64分频 }

//AD转换函数 unsigned int AD_GetData() { ADCSRA |= (1 << ADSC);     //开始AD转换 while(!(ADCSRA & (1 << ADIF)));    //等待转换完成 ADCSRA |= (1 << ADIF);              //清零ADC中断标志位 return ADC;        //返回ADC值 }

附录:ADC应用设计要点

1.预分频与转换时间 
在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。在要求转换精度低于10位的情况下,ADC的采样时钟可以高于200kHz,以获得更高的采样率。 
ADC模块中包含一个预分频器的ADC时钟源,它可以对大于100KHz的系统时钟进行分频,以获得合适的ADC时钟提供ADC使用。预分频器的分频系数由ADCSRA寄存器中的ADPS位设置的。一旦寄存器ADCSRA中的ADEN位置“1”(ADC开始工作),预分频器就启动开始计数。ADEN位为“1”时,预分频器将一直工作;ADEN位为“0”时,预分频器一直处在复位状态。 
AVR的ADC完成一次转换的时间见表6.2.5。从表中可以看出,完成一次ADC转换通常需要13-14个ADC时钟。而启动ADC开始第一次转换到完成的时间需要25个ADC时钟,这是因为要对ADC单元的模拟电路部分进行初始化。


表6.2.5  ADC转换和采样保持时间

转 换 形 式

采样保持时间

完 成 转 换 总 时 间

启动ADC后的第一次转换

13.5个ADC时钟

25个ADC时钟

正常转换,单端输入

1.5个ADC时钟

13个ADC时钟

自动触发方式

2个ADC时钟

13.5个ADC时钟

正常转换,差分输入

1.5/2.5个ADC时钟

13/14个ADC时钟

当ADCSRA寄存器中的ADSC位置位,启动ADC转换时,A/D转换将在随后ADC时钟的上升沿开始。一次正常的A/D转换开始时,需要1.5个ADC时钟周期的采样保持时间(ADC首次启动后需要13.5个ADC时钟周期的采样保持时间)。当一次A/D转换完成后,转换结果写入ADC数据寄存器,ADIF(ADC中断标志位)将被置位。在单次转换模式下,ADSC也同时被清零。用户程序可以再次置位ADSC位,新的一次转换将在下一个ADC时钟的上升沿开始。 
当ADC设置为自动触发方式时,触发信号的上升沿将启动一次ADC转换。转换完成的结果将一直保持到下一次触发信号的上升沿出现,然后开始新的一次ADC转换。这就保证了使ADC每隔一定的时间间隔进行一次转换。在这种方式下,ADC需要2个ADC时钟周期的采样保持时间。 
在自由连续转换模式下,一次转换完毕后马上开始一次新的转换,此时,ADSC位一直保持为“1”。

2.ADC输入通道和参考电源的选择 
寄存器ADMUX中的MUXn和REFS1、REFS0位实际上是一个缓冲器,该缓冲器与一个MCU可以随机读取的临时寄存器相连通。采用这种结构,保证了ADC输入通道和参考电源只能在ADC转换过程中的安全点被改变。在ADC转换开始前,通道和参考电源可以不断被更新,一旦转换开始,通道和参考电源将被锁定,并保持足够时间,以确保ADC转换的正常进行。在转换完成前的最后一个ADC时钟周期(ADCSRA的ADIF位置“1”时),通道和参考电源又开始重新更新。注意,由于A/D转换开始于置位ADSC后的第一个ADC时钟的上升沿,因此,在置位ADSC后的一个ADC时钟周期内不要将一个新的通道或参考电源写入到ADMUX寄存器中。 
改变差分输入通道时需特别当心。一旦确定了差分输入通道,增益放大器需要125µs的稳定时间。所以在选择了新的差分输入通道后的125µs内不要启动A/D转换,或将这段时间内转换结果丢弃。通过改变ADMUX中的REFS1、REFS0来更改参考电源后,第一次差分转换同样要遵循以上的时间处理过程。

  1. 当要改变ADC输入通道时,应该遵守以下方式,以保证能够选择到正确的通道:

在单次转换模式下,总是在开始转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到转换完成后,再改变通道选择。 
在连续转换模式下,总是在启动ADC开始第一次转换前改变通道设置。尽管输入通道改变发生在ADSC位被写入“1”后的1个ADC时钟周期内,然而,最简单的方法是等到第一次转换完成后再改变通道的设置。然而由于此时新一次的转换已经自动开始,所以,当前这次的转换结果仍反映前一通道的转换值,而下一次的转换结果将为新设置通道的值。

  1. ADC电压参考源

ADC的参考电压(VREF)决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果接近于0x3FF。ADC的参考电压VREF可以选择为AVCC或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。 
AVCC通过一个无源开关连接到ADC。内部2.56V参考源是由内部能隙参考源(VBC)通过内部的放大器产生的。注意,无论选用什么内部参考电源,外部AREF引脚都是直接与ADC相连的,因此,可以通过外部在AREF引脚和地之间并接一个电容,使内部参考电源更加稳定和抗噪。可以通过使用高阻电压表测量AREF引脚,来获得参考电源VREF的电压值。由于VREF是一个高阻源,因此,只有容性负载可以连接到该引脚。 
如果将一个外部固定的电压源连接到AREF引脚,那就不能使用任何的内部参考电源,否则就会使外部电压源短路。外部参考电源的范围应在2.0V到AVCC-0.2V之间。参考电源改变后的第一次ADC转换结果可能不太准确,建议抛弃该次转换结果。 
3.ADC转换结果 
A/D转换结束后(ADIF = 1),在ADC数据寄存器(ADCL和ADCH)中可以取得转换的结果。对于单端输入的A/D转换,其转换结果为: 
ADC =(VIN×1024)/ VREF 
其中VIN表示选定的输入引脚上的电压,VREF表示选定的参考电源的电压。0x000表示输入引脚的电压为模拟地,0x3FF表示输入引脚的电压为参考电压值减去一个LSB。 
对于差分转换,其结果为: 
ADC=(VPOS-VNEG) ×GAIN×512/VREF 
例:若差分输入通道选择为ADC3-ADC2,10倍增益,参考电压2.56V,左端对齐(ADMUX=0xED),ADC3引脚上电压300mV,ADC2引脚上电压500mV。 
则ADCR =(300-500)×10×512 / 2560 = -400 = 0x270
ADCL=0x00,ADCH=0x9C。 
若结果为右端对齐时(ADLAR=“0”),则ADCL=0x70,ADCH=0x02。

附录2、ADC应用设计的深入讨论 
尽管AVR内部集成了10位的ADC,但是在实际应用中,要想真正实现10位精度,比较稳定的ADC的话,并不象上一节中的例子那么简单。需要进一步从硬件、软件等方面进行综合的、细致的考虑。下面介绍一些在ADC设计应用中应该考虑的几个要点。 
1.AVcc的稳定性。 
AVcc是提供给ADC工作的电源,如果AVcc不稳定,就会影响ADC的转换精度。在图10-5中,系统电源通过一个LC滤波后接入AVcc,这样就能很好的抑制掉系统电源中的高频躁声,提高了AVcc的稳定性。另外在要求比较高的场合使用ADC时,PA口上的那些没被用做ADC输入的端口尽量不要作为数字I/O口使用。因为PA口的工作电源是由AVcc提供的,如果PA口上有比较大的电流波动,也会影响AVcc的稳定。 
2.参考电压VREF的选择确定 
在实际应用中,要根据输入测量电压的范围选择正确的参考电压VREF,以求得到比较好的转换精度。ADC的参考电压VREF还决定了A/D转换的范围。如果单端通道的输入电压超过VREF,将导致转换结果全部接近于0x3FF,因此ADC的参考电压应稍大于模拟输入电压的最高值。 
ADC的参考电压VREF可以选择为AVCC,或芯片内部的2.56V参考源,或者为外接在AREF引脚上的参考电压源。外接参考电压应该稳定,并大于2.0V(芯片的工作电压为1.8V时,外接参考电压应大于1.0V)。要求比较高的场合,建议在AREF引脚外接标准参考电压源来作为ADC的参考电源。 
3. ADC通道带宽和输入阻抗 
不管使用单端输入转换还是差分输入转换方式,所有模拟输入口的输入电压应在AVcc-GNG之间。 
在单端ADC转换方式时,ADC通道的输入频率带宽取决于ADC转换时钟频率。一次常规的ADC转换需要13个ADC时钟,当ADC转换时钟为1MHz时,一秒种内ADC采样转换的次数约77K。根据采样定理,此时ADC通道的带宽为38.5KHz。 
差分方式ADC转换的带宽是由芯片内部的差分放大器的带宽决定,为4KHz。 
AVR的ADC输入阻抗典型值为100MΩ,为保证测量的准确,被测信号源的输出阻抗要尽可能的低,应在10K以下。 
4. ADC采样时钟的选择 
通常条件下,AVR的ADC逐次比较电路要达到转换的最大精度,需要一个50K~200KHz的采样时钟。一次正常的ADC转换过程需要13个采样时钟,假定ADC采样时钟为200KHz,那么最高的采样速率为200K/13=15.384K。因此根据采样定理,理论上被测模拟信号的最高频率为7.7K! 
尽管可以设置ADC的采样时钟为1M,但并不能提高ADC转换精度,反而会降低转换精度(受逐次比较硬件电路的限制),因此AVR的ADC不能完成高速ADC的任务。如果所需的转换精度低于10位,那么采样时钟可以高于200KHz,以达到更高的采样频率。 
ADC采样时钟的选择方式为:给出或估计被测模拟信号的最高频率fs,取采样频率为fs的4-10倍,再乘上13为ADC采样时钟频率,该频率应在50K~200KHz之间。 如果该频率大于200KHz,则ADC的10位精度不能保证。如果该频率小于50Khz,则可选择50K~200KHz之间的数值。 
5.模拟噪声的抑制 
器件外部和内部的数字电路会产生电磁干扰,并会影响模拟测量的精度。如果ADC转换精度要求很高,可以采用以下的技术来降低噪声的影响: 
(1)使模拟信号的通路尽可能的短。模拟信号连线应从模拟地的布线盘上通过,并使它们尽可能远离高速开关数字信号线。 
(2)AVR的AVcc引脚应该通过LC网络与数字端电源Vcc相连。
(3)采用ADC噪声抑制器功能来降低来自MCU内部的噪声。 
(4)如果某些ADC引脚是作为通用数字输出口使用,那么在ADC转换过程中,不要改变这些引脚的状态。
6.ADC的校正 
由于AVD内部ADC部分的放大器非线性等客观原因,ADC的转换结果会有误差的。如果要获得高精度的ADC转换,还需要对ADC结果进行校正。具体的方法请参考AVR应用笔记AVR120(avr_app_120.pdf),在这篇应用设计参考中详细介绍了误差的种类,以及校正方案。 
7.ADC精度的提高 
在有了上述几点的保证后,通过软件的手段也能适当的提高ADC的精度。如采用多次测量取平均,软件滤波算法等。在AVR应用笔记AVR121(avr_app_121.pdf)中介绍了一种使用过采样算法的软件实现,可以将ADC的精度提高到11位或更高,当然这是在花费更多的时间基础上实现的。6.2 模数转换的ADC实验 

6.2.1、实例功能 
AVR的模数转换器ADC具有下列特点:

  1. 10位精度;
  2. 0.5LSB积分非线形误差
  3. ±2LSB的绝对精度;
  4. 13µs~260µs的转换时间;
  5. 在最大精度下可达到每秒15kSPS的采样速率;
  6. 8路可选的单端输入通道;
  7. 7路差分输入通道;
  8. 2路差分输入通道带有可选的10×和200×增益;
  9. ADC转换结果的读取可设置为左端对齐(LEFT ADJUSTMENT);
  10. ADC的电压输入范围0~Vcc;
  11. 可选择的内部2.56V的ADC参考电压源;
  12. 自由连续转换模式和单次转换模式;
  13. ADC自动转换触发模式选择;
  14. ADC转换完成中断;
  15. 休眠模式下的噪声抑制器(NOISE CANCELER)。

在本实例中,我们将编写程序实现将模数转换后获得的电压值通过单片机的串口发送到计算机,然后通过计算机上的串口助手显示测量的电压值。 
本实例共有3个功能模块,分别描述如下: 
● 单片机系统:使用单片机的串口实现将模数转换后获得的电压值通过串口发送到计算机。 
● 外围电路:RS232电平转换电路,DB9串行接口插座,模拟电压输入采集电路。
● 软件程序:进一步熟悉单片机的串行通信,并掌握单片机的模数转换的方法。
6.2.2、器件和原理 
关于串行接口的原理已接单片机与计算机的串口的连接在上一实例中进行了描述,在本实例中不再重复。
本实例只介绍ATmega16单片机如何通过内置的模数转换模块采集外界输入的模拟电压。
1、ATmega16单片机的模数转换器ADC介绍 
由于单片机只能处理数字信号,所以外部的模拟信号量需要转变成数字量才能进一步的由单片机进行处理。ATmega16内部集成有一个10位逐次比较(successive approximation)ADC电路。因此使用AVR可以非常方便的处理输入的模拟信号量。 
ATmega16的ADC与一个8通道的模拟多路选择器连接,能够对以PORTA作为ADC输入引脚的8路单端模拟输入电压进行采样,单端电压输入以0V(GND)为参考。另外还支持16种差分电压输入组合,其中2种差分输入方式(ADC1,ADC0和ACD3,ADC2)带有可编程增益放大器,能在A/D转换前对差分输入电压进行0dB(1×),20dB(10×)或46dB(200×)的放大。还有七种差分输入方式的模拟输入通道共用一个负极(ADC1),此时其它任意一个ADC引脚都可作为相应的正极。若增益为1×或10×,则可获得8位的精度。如果增益为200×,那么转换精度为7位。 
AVR的ADC功能单元由独立的专用模拟电源引脚AVcc供电。AVcc和Vcc的电压差别不能大于±0.3V。ADC转换的参考电源可采用芯片内部的2.56V参考电源,或采用AVcc,也可使用外部参考电源。使用外部参考电源时,外部参考电源由引脚ARFE接入。使用内部电压参考源时,可以通过在AREF引脚外部并接一个电容来提高ADC的抗噪性能。
ADC功能单元包括采样保持电路,以确保输入电压在ADC转换过程中保持恒定。ADC通过逐次比较(successive approximation)方式,将输入端的模拟电压转换成10位的数字量。最小值代表地,最大值为AREF引脚上的电压值减1个LSB。可以通过ADMUX寄存器中REFSn位的设置,选择将芯片内部参考电源(2.56V)或AVcc连接到AREF,作为A/D转换的参考电压。这时,内部电压参考源可以通过外接于AREF引脚的电容来稳定,以改进抗噪特性。 
模拟输入通道和差分增益的选择是通过ADMUX寄存器中的MUX位设定的。任何一个ADC的输入引脚,包括地(GND)以及内部的恒定能隙(fixed bandgap)电压参考源,都可以被选择用来作为ADC的单端输入信号。而ADC的某些输入引脚则可选择作为差分增益放大器的正、负极输入端。当选定了差分输入通道后,差分增益放大器将两输入通道上的电压差按选定增益系数放大,然后输入到ADC中。若选定使用单端输入通道,则增益放大器无效。 
通过设置ADCSRA寄存器中的ADC使能位ADEN来使能ADC。在ADEN没有置“1”前,参考电压源和输入通道的选定将不起作用。当ADEN位清“0”后,ADC将不消耗能量,因此建议在进入节电休眠模式前将ADC关掉。 
ADC将10位的转换结果放在ADC数据寄存器中(ADCH和ADCL)。默认情况下,转换结果为右端对齐(RIGHT ADJUSTED)的。但可以通过设置ADMUX寄存器中ADLAR位,调整为左端对齐(LEFT ADJUSTED)。如果转换结果是左端对齐,并且只需要8位的精度,那么只需读取ADCH寄存器的数据作为转换结果就达到要求了。否则,必须先读取ADCL寄存器,然后再读取ADCH寄存器,以保证数据寄存器中的内容是同一次转换的结果。因为一旦ADCL寄存器被读取,就阻断了ADC对ADC数据寄存器的操作。这就意味着,一旦指令读取了ADCL,那么必须紧接着读取一次ADCH;如果在读取ADCL和读取ADCH的过程中正好有一次ADC转换完成,ADC的2个数据寄存器的内容是不会被更新的,该次转换的结果将丢失。只有当ADCH寄存器被读取后,ADC才可以继续对ADCL和ADCH寄存器操作更新。 
ADC有自己的中断,当转换完成时中断将被触发。尽管在顺序读取ADCL和ADCH寄存器过程中,ADC对ADC数据寄存器的更新被禁止,转换的结果丢失,但仍会触发ADC中断。 
2、ATmwga16单片机的模数转换器ADC相关的I/O寄存器 
1.ADC多路复用器选择寄存器—ADMUX

  1. 位7,6—REFS[1:0]:ADC参考电源选择

REFS1、REFS2用于选择ADC的参考电压源,见表6.2.1。如果这些位在ADC转换过程中被改变,新的选择将在该次ADC转换完成后(ADCSRA中的ADIF被置位)才生效。一旦选择内部参考源(AVcc、2.56V)为ADC的参考电压后,AREF引脚上不得施加外部的参考电源,只能与GND之间并接抗干扰电容。


表6.2.1  ADC参考电源选择

REFS1

REFS0

ADC参考电源

0

0

外部引脚AREF,断开内部参考源连接

0

1

AVcc,AREF外部并接电容

1

0

保留

1

1

内部2.56V,AREF外部并接电容

  1. 位5—ADLAR:ADC结果左对齐选择

ADLAR位决定转换结果在ADC数据寄存器中的存放形式。写“1”到ADLAR位,将使转换结果左对齐(LEFT ADJUST);否则,转换结果为右对齐(RIGHT ADJUST)。无论ADC是否正在进行转换,改变ADLAR位都将会立即影响ADC数据寄存器。

  1. 位4..0—MUX4:0:模拟通道和增益选择

这5个位用于对连接到ADC的输入通道和差分通道的增益进行选择设置,详见表6.2.2。注意,只有转换结束后(ADCSRA的ADIF是“1”),改变这些位才会有效。


表6.2.2   ADC输入通道和增益选择

MUX[4:0]

单端输入

差分正极输入

差分负极输入

增益

00000

ADC0

N/A

00001

ADC1

00010

ADC2

00011

ADC3

00100

ADC4

00101

ADC5

00110

ADC6

00111

ADC7

01000

N/A

ADC0

ADC0

10×

01001

ADC1

ADC0

10×

01010

ADC0

ADC0

200×

01011

ADC1

ADC0

200×

01100

ADC2

ADC2

10×

01101

ADC3

ADC2

10×

01110

ADC2

ADC2

200×

01111

ADC3

ADC2

200×

10000

ADC0

ADC1

10001

ADC1

ADC1

10010

ADC2

ADC1

10011

ADC3

ADC1

10100

ADC4

ADC1

10101

ADC5

ADC1

10110

ADC6

ADC1

10111

ADC7

ADC1

11000

ADC0

ADC2

11001

ADC1

ADC2

11010

ADC2

ADC2

11011

ADC3

ADC2

11100

ADC4

ADC2

11101

ADC5

ADC2

11110

1.22V(VBG)

N/A

11111

0V(GND)

   本实例中我们需要设置ADC的参考电压源为AVcc,即REFS0设置为1,ADC默认转换结果为右对齐,我们不需要改变,模拟通道选择ADC0通道单端输入,即MUX4:0。 
2.ADC控制和状态寄存器A—ADCSRA

  1. 位7—ADEN:ADC使能

该位写入“1”时使能ADC,写入“0”关闭ADC。如在ADC转换过程中将ADC关闭,该次转换随即停止。

  1. 位6—ADSC:ADC转换开始

在单次转换模式下,置该位为“1”,将启动一次转换。在自由连续转换模式下,该位写入“1”将启动第一次转换。先置位ADEN位使能ADC,再置位ADSC;或置位ADSC的同时使能ADC,都会使能ADC开始进行第一次转换。第一次ADC转换将需要25个ADC时钟周期,而不是常规转换的13个ADC时钟周期,这是因为第一次转换需要完成对ADC的初始化。 
在ADC转换的过程中,ADSC将始终读出为“1”。当转换完成时,它将转变为“0”。强制写入“0”是无效的。

  1. 位5—ADATE:ADC自动转换触发允许

当该位被置为“1”时,允许ADC工作在自动转换触发工作模式下。在该模式下,在触发信号的上升沿时ADC将自动开始一次ADC转换过程。ADC的自动转换触发信号源由SFIOR寄存器中的ADTS位选择确定。

  1. 位4—ADIF:ADC中断标志位

当ADC转换完成并且ADC数据寄存器被更新后该位被置位。如果ADIE位(ADC转换结束中断允许)和SREG寄存器中的I位被置“1”,ADC中断服务程序将被执行。ADIF在执行相应的中断处理向量时被硬件自动清零。此外,ADIF位可以通过写入逻辑“1”来清零。

  1. 位3—ADIE:ADC中断允许

当该位和SREG寄存器中的I位同时被置位时,允许ADC转换完成中断。

  1. 位2,0—ADPS[2:0]:ADC预分频选择

这些位决定了XTAL时钟与输入到ADC的ADC时钟之间分频数,见表6.2.3。

表6.2.3   ADC时钟分频

ADPS[2:0]

分  频  系  数

000

2

001

2

010

4

011

8

100

16

101

32

110

64

111

128

本实例中我们需要使能ADC,即ADEN设置为1,我们不用自动转换,也不需要中断,所以,ADTE、ADIE位不需要设置。在通常情况下,ADC的逐次比较转换电路要达到最大精度时,需要50kHz~200kHz之间的采样时钟。本例中使用的时钟是12M的,所以要将时钟64分频,分频后ADC频率为188KHz,即时钟分频选择ADPS[2:0]=6。

3.ADC数据寄存器—ADCL和ADCH

  1. ADLAR = 0,ADC转换结果右对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第23张图片

  1. ADLAR = 1,ADC转换结果左对齐时,ADC结果的保存方式

AVR单片机模数转换的ADC实验 ATmega16_第24张图片
当ADC转换完成后,可以读取ADC寄存器的ADC0-ADC9得到ADC的转换的结果。如果是差分输入,转换值为二进制的补码形式。一旦开始读取ADCL后,ADC数据寄存器就不能被ADC更新,直到ADCH寄存器被读取为止。因此,如果结果是左对齐(ADLAR=1),且不需要大于8位的精度的话,仅仅读取ADCH寄存器就足够了。否则,必须先读取ADCL寄存器,再读取ADCH寄存器。ADMUX寄存器中的ADLAR位决定了从ADC数据寄存器中读取结果的格式。如果ADLAR位为“1”,结果将是左对齐;如果ADLAR位为“0”(默认情况),结果将是右对齐。 
4.特殊功能I/O寄存器—SFIOR

  1. 位7..5—ADTS[2:0]:ADC自动转换触发源选择

当ADCSRA寄存器中的ADATE为“1”,允许ADC工作在自动转换触发工作模式时,这3位的设置用于选择ADC的自动转换触发源。如果禁止了ADC的自动转换触发(ADATE为“0”),这3个位的设置值将不起任何作用。

表10-6   ADC自动转换触发源的选择

ADTS[2:0]

触  发  源

000

连续自由转换

001

模拟比较器

010

外部中断0

011

T/C0比较匹配

100

T/C0溢出

101

T/C1比较匹配B

110

T/C1溢出

111

T/C1输入捕捉

本例中我们不使用自动转换功能,所以该寄存器可以不必设置。
6.2.3、电路 
本实例的电路包括232电平转换电路和电阻分压电路,这两种电路在前面的实例中均做过介绍,这里不再重复。
1、电路原理
在本实例中利用MAX3232芯片使单片机输出的TTL电平转换为标准的RS232电平,从而使计算机能够识别。同时将计算机输出的RS232电平转换为单片机可以识别的TTL电平。
利用电位器产生电阻分压电路,从而产生变化的模拟电压加到单片机的模拟信号采集端口,供单片机采集。
2、电路连接 
电路中MAX3232芯片的9、10引脚分别连接单片机的PD0、PD1端口,MAX3232的13、14引脚分别连接计算机串口线的3、2脚。
电位器RP2的动片引脚连接单片机的模拟信号采集通道PA0(ADC0)。
3、特别说明
本学习板采用的是串口插座是公头的,所以与计算机相连的串口连接线应该是交叉串口线,而不是串口延长线。
6.2.4、程序设计 
1、程序功能 

程序的功能是通过单片机的串行接口,将单片机采集到的模拟电压值发送到计算机中,通过计算机上的串口助手显示采集的电压值。    
● 单片机串行接收中断的编程
在本例中,我们用到了单片机的串行接收中断,需要编写串行接收中断服务程序,通过查询WINAVR(GCC)的中断库函数手册,可以查找到ATmega16单片机串行接收中断的中断向量为USART_RXC_vect。据此我们可以编写串行接收中断服务程序,如下:
//接收中断函数 
ISR(USART_RXC_vect )
{
unsigned char Rev;
Rev = UDR;              //从USART I/O数据寄存器-UDR中读出数据 
Usart_PutChar(Rev);    //将接收到的数据发送 
}
在中断服务程序中,我们首先把单片机串口接收到的数据放入变量Rev中,然后调用上一实例中编写的串行接口字节发送函数将变量Rev中的数据发送到计算机。 
2、 单片机与计算机串行通信结果的观察 
在观察本例运行结果时,我们同样要用到串口助手,本例中,单片机发送串口数据采用的波特率是9600bps,数据格式是8位数据位,1位停止位,无奇偶校验。在串口助手里面,我们也要将波特率和数据格式设置成一样的。 
3、函数说明 
本实例用到了6个函数,分别是: 
void Port_Init(void);   //端口初始化配置 
void Usart_Init(void);  //USART寄存器设置 
void AD_Init(void);    //AD初始化 
void Usart_PutChar(unsigned char cTXData);  //字节发送函数 
void Usart_PutString(unsigned char *pcString);  // 字符串发送数据 
unsigned int AD_GetData(void);    //AD转换函数   
4、使用WINAVR开发环境,在本例中我们使用的是外部12M的晶振,所以需要将MAKEFILE文件中的时钟频率修改为12M。另外在程序烧录到单片机的时候,熔丝位也要选择为外部12M晶振(注意是晶振,不是外部振荡器,一定不要选择错了,否则会导致单片机不能再烧写程序)。
5、程序代码

#include         #include #include   //中断函数头文件

//常量声明 #define BAUD 9600         //波特率设置值

//全局变量声明 unsigned int ADData

你可能感兴趣的:(AVR)