今天做了一个AD的实验,在此记下一点经验。
采用芯片:ADC0832,89S52
电路图如图:
今天第一次看ADC0832的数据手册,一头雾水啊,找了篇中文资料写的不错
其实在英文手册里的时序图写的也很明白了,可惜E文太差看的不懂。
这里无论是要用CH1,还是CH0都得把ADC0832的DI,DO搞好。DI决定了芯片工作的开始与CH0,CH1的选择,DO是输出数据的端口。DO,DI这两个端口其实不会同时工作,用一个IO口控制就够了,杜洋老师采用了如下电路,节省了一个IO口。如图:
在上面电路重要的是如何启动ADC0832和如何从ADC9832中读取数据,并在数码管上显示出来。
代码如下,是用keil uVesion编译过的,达到了想要的效果:
Code
#include<reg52.h>//包含相应的头文件
#include<stdio.h>
#include<intrins.h>
sbit CS=P1^6;//定义数模转换器硬件对应引脚
sbit CLK=P1^3;
sbit DO=P1^4;
sbit DI=P1^5;
bit ADFlag;
unsigned char code DIS[]={0xf7,0xfb,0xfd}; //定义数码管位码
unsigned char code Datatab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//7段数码管段码表
data unsigned char Display[3];//定义临时存放数码管数值
void Init_Timer1(void)
{
TMOD |= 0x10;
TH1=0xff; /* Init value */
TL1=0x00;
//PT1=1; /* 优先级 */
EA=1; /* interupt enable */
ET1=1; /* enable timer1 interrupt */
TR1=1;
}
unsigned char ReadADC(unsigned char channel)
{
unsigned char i,j;
unsigned char Temp=0,tmp;
CS =0; //按照资料进行时序操作
for(i=0;i<3;i++) //输入指令,包含通道选择
{
tmp=channel&0x01;
CLK=0;
_nop_(); //空操作,精确而又很短暂延时
DI=tmp;
channel>>=1;
_nop_();
CLK=1;
}
CLK=0;
_nop_();
CLK=1;
for(j=0;j<8;j++) //处理读入8位数据
{
CLK=0;
_nop_();
Temp=(Temp<<1)|DO;
_nop_();
CLK=1;
}
CS=1;
return(Temp); //返回转换值
}
void Timer1_isr(void) interrupt 3 using 1//定时器1执行数码管动态扫描
{
static unsigned char count,j;
TH1=0xfb; /* Init value */
TL1=0x00;
j++;
if(j==200) {j=0;ADFlag=1;}
P0=Display[count];
P2=DIS[count];
count++;
if(count==3)
count=0;
}
main()
{
unsigned char ADtemp;//定义中间变量
// unsigned char temp;
Init_Timer1();
while(1)
{
if(ADFlag) //定时采集输入模拟量
{
ADFlag=0;
ADtemp=ReadADC(5);
Display[0]=Datatab[((ADtemp-5)/50)]&0x7f;//处理电压显示
Display[1]=Datatab[((ADtemp-5)%50)/10];
Display[2]=Datatab[((ADtemp-5)%50)%10];
}
}
}
在ReadADC()中,对于ADC0832的启动一段代码具有通用性,可以按照得到的channel值启动CH0或者Ch1.这样我可以把键盘扩展进来用键盘来输入得到一个channel,来控制开启哪个CH。
顺便今天熟悉了一下c语言中的位操作:
在上面ReadADC()中tmp=channel&0x01;就用到与运算把最后一位提出来。
在reg51.h中P0,P1,P2,P3都被定义成sfr型,sfr是用于定义特殊功能寄存器的地址,sbit是用于定义一些特殊位的。因此程序里的CLK,DI,DO都是P1的某些个别端口,其实质是一个位而已,因此DI=tmp;这一句其实是把一个char赋给一个位,只要tmp不为0,那么DI就一直为1.因此必须把channel里的每一位取出来然后和DI进行赋值,才能得到我们想要的对channel的低三位每一位依次赋给DI的效果。
这个程序用到了intrins.h这个头文件,此头文件定义了Keil C中的内函数,在ReadADC()中,用了一个_nop_()这个空操作。
/*--------------------------------------------------------------------------
INTRINS.H
Intrinsic functions for C51.
Copyright (c) 1988-2004 Keil Elektronik GmbH and Keil Software, Inc.
All rights reserved.
--------------------------------------------------------------------------*/
#ifndef __INTRINS_H__
#define __INTRINS_H__
extern void _nop_ (void);
extern bit _testbit_ (bit);
extern unsigned char _cror_ (unsigned char, unsigned char);
extern unsigned int _iror_ (unsigned int, unsigned char);
extern unsigned long _lror_ (unsigned long, unsigned char);
extern unsigned char _crol_ (unsigned char, unsigned char);
extern unsigned int _irol_ (unsigned int, unsigned char);
extern unsigned long _lrol_ (unsigned long, unsigned char);
extern unsigned char _chkfloat_(float);
extern void _push_ (unsigned char _sfr);
extern void _pop_ (unsigned char _sfr);
#endif