由于ATmega8515没有自带的ADC所以就换块单片机—Atmega16,这款单片机拥有8路10位的ADC,10位的精度应该可以满足日常的需求了。设计ADC硬件电路时一定要注意参考电压的设计,如果参考系都不准确,坐标怎么能准确。涉及到ADC的寄存器主要有ADC多工选择寄存器—ADMUX,它主要设置参考电压,转换结果对齐方式及通道选择;ADC控制和状态寄存器A—ADCSRA,它主要是设置ADC转换开始,ADC中断标志及预分频器的选择等等;ADC数据寄存器—ADCL及ADCH主要是存放转换结果的,因为它是10位的ADC所以一定要注意转换结果的对齐方式;还有一个寄存器就是特殊功能IO寄存器—SFIOR,它主要是设置ADC自动触发源的,懒猫用的是连续转换模式,所以就是不用管它了。
懒猫比较懒今天就搭建一个简单的电路来学一下单片机内部的ADC了,说一下这个测试电路,主要用到两路ADC0与ADC1,然后把两路转换的结果用数码管显示出来,本来在B口还接了两个灯,想着哪个通道转换就哪个灯亮,可以转换速度太快了(数据手册上说的是转换时间为65-260us),所以看起来两个灯一直都在亮,而懒猫也没有写什么控制条件,最后懒猫干脆就把两个灯阉割掉了。下面是整体电路图:(参看附件)
硬件说完了,说一下软件,软件不是太难,主要就是寄存器的配置,还有就是数码显示,用的是八位共阳极数码管,显示精度是两位。如果你好是刚学单片机,建议你一定要好好看看数据手册,不然再简单的东东也容易出错哦。好了,还是把源程序贴出来吧:
/**************************************************************************
//文件名称:ADC_TEST.c
//功 能:学习使用单片机自带的ADC
//作 者:懒猫爱飞
//建立日期:2010.11.03
//备 注:MCU-ATmega16 开发环境-winavr100110 Crystal-8MHz
**************************************************************************/
#include <avr/io.h>
#include <inttypes.h> //关于数据类型的一些定义
#include <stdint.h> //同样是一些数据类型的定义
#include <avr/interrupt.h>
#include <util/delay.h>
//#difine LED_ON(N) PORTB &= ~_BV(N)
//共阴极数码管显示码0-9黑屏
const uint8_t Neg_Code[] =
{
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00
};
//共阳极数码管显示码0-9黑屏
const uint8_t Pos_Code[] =
{
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff
};
//显示缓存,显示两位小数,每路有效数字三位 即X.XX X.XX
uint8_t Dis_Buf[] = {0,0,0,10,0,0,0,10};
/**************************************************************************
//函数名称:void port_init(void)
//功 能:端口初始化
//入口参数:NULL
//出口参数:NULL
//备 注:NULL
**************************************************************************/
void port_init(void)
{
DDRA = 0xfc; //ADC输入
PORTA = 0x00; //
DDRB = 0x03; //接两个LED灯
PORTB = 0x03; //初始化时关掉灯
DDRC = 0xff; //C口作输出口,向数码管中送数据
PORTC = 0x00;
DDRD = 0xff; //D口作输出口,控制数码管位数
PORTD = 0x00;
}
/**************************************************************************
//函数名称:void adc_convert(int8_t N)
//功 能:对N通道进行ADC转换
//入口参数:N
//出口参数:NULL
//备 注:NULL
**************************************************************************/
void adc_convert(uint8_t N)
{
uint16_t Result = 0;
//一定要在ADSC置位后,再选择通道,否则会出错的
ADCSRA = _BV(ADEN)|_BV(ADSC)|_BV(ADATE)|_BV(ADPS2)|_BV(ADPS1);
ADMUX = N; //ADC通道选择
//PORTB &= ~_BV(N);转换时间太快,通道切换时肉眼是分辨不出来,所以灯一直亮
//读取转换结果,并转换为电压值
Result = (uint16_t)((ADCL+(ADCH<<8))*500.0/1023.0);
//Result = (uint16_t)(ADC*500.0/1023.0); //这个表达式具有同样的效果
//ADC0结果放入数组0.1.2单元 ADC1的结果放入数组4.5.6中
Dis_Buf[N * 4] = Result/100;
Dis_Buf[N * 4+1] = Result/10%10;
Dis_Buf[N * 4+2] = Result%10;
ADCSRA = 0; //转换完完毕,关闭ADC
}
/**************************************************************************
//函数名称:int main(void)
//功 能:主程序
//入口参数:NULL
//出口参数:NULL
//备 注:NULL
**************************************************************************/
int main(void)
{
uint8_t i = 0;
port_init();
ADCSRA = _BV(ADEN)|_BV(ADSC)|_BV(ADATE)|_BV(ADPS2)|_BV(ADPS1);
//_delay_ms(1000); //这个可以不用要的
while(1)
{
ADCSRA = 0xe6;
adc_convert(0); //转换第一通道的数据
//ADCSRA = 0;
//ADCSRA = 0xe6;
adc_convert(1); //转换第二通道的数据
for(i=0; i<8; i++)
{
PORTC = 0xff; //先关闭一下
PORTD = _BV(i); //点亮某位数码管
PORTC = Pos_Code[Dis_Buf]; //发送段码
if((i==0)||(i==4))
{
PORTC &= 0x7f; //添加小数点
}
_delay_ms(5);
}
//adc_convert(1); //转换第二通道的数据
//ADCSRA = 0;
}
return 0;
}
程序不是太难,就是简单的测试一下内部自带的ADC转换器,当然ADC转换还有其它模式,如果你也是单片机初学者不妨试一下其它方式,比如中断方式,还有差分输入,可选增益差分输入等。实践出真知,只有多写多练多思考才会有更多的收获^_^
所有源代码与电路图在附件中,可以自己查看.
再喊一下口号,鼓励一下自己:
每天进步一点点,开心多一点^_^
用户431312 2012-12-18 22:15
用户1588142 2011-4-27 18:49