最近因为课程需要,需要做一个基于PCF8591 A/D转换的数字直流电压表,精确到小数点后三位。本来一个很简单的题目,三下五下写完代码烧到开发板运行没多大问题。接下来需要画出相应的protues仿真电路图,画好电路图加载程序却出现数码管显示不出来的结果。
于是改代码,检查电路都没效果。最终发现是消影语句出的问题。 在开发板上演示时消影语句是:P0=0xff ; 而在protues 上仿真时消影要写成:P0=0x00 ; 只有这样程序才能运行正常。下面把整个题目分享出来,有兴趣的可以试一下,希望不要有人再掉入这个坑!!
源代码:
/**********************四位直流数字电压表************************
描述:通过调节可调电阻来模拟现实的电压输入量,采用PCF8091 A/D 转换芯片将电压采样, 量化后数码管显示出电压值
日期: 2016.10.27
******************************************************************/
/*-----------------------------------------------
名称:IIC协议 PCF8591ADDA转换 显示有4个,注意只有2个可以调节电压的
内容:此程序通过IIC协议对DAAD芯片操作,读取电位器的电压通过液晶显示,并输出模拟量,用LED亮度渐变指示
------------------------------------------------*/
#include //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
#include //包含NOP空指令函数_nop_();
#define AddWr 0x90 //写数据地址
#define AddRd 0x91 //读数据地址
#define uchar unsigned char
#define uint unsigned int
sbit RST=P2^4; //时钟 加上后可以关掉DS1302芯片输出
sbit Sda=P2^0; //定义总线连接端口
sbit Scl=P2^1;
sbit DU=P2^6;
sbit WE=P2^7;
bit ADFlag; //定义AD采样标志位
uchar code table[17]={0x3f , 0x06 , 0x5b ,0x4f , 0x66 , 0x6d ,0x7d ,
0x07 , 0x7f , 0x6f ,0x77 , 0x7c , 0x39 ,
0x5e , 0x79 , 0x71 , 0x80 }; //0-9&A-F&“不显示” 字型码?
void stopwatch(uchar ge, uchar z1,uchar z2, uchar z3) ;
/*------------------------------------------------
延时程序
------------------------------------------------*/
void mDelay(unsigned char j)
{
unsigned int i;
for(;j>0;j--)
{
for(i=0;i<125;i++)
{;}
}
}
/*------------------------------------------------
初始化定时器1
------------------------------------------------*/
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;
}
/*------------------------------------------------
启动IIC总线
------------------------------------------------*/
void Start(void)
{
Sda=1;
_nop_();
Scl=1;
_nop_();
Sda=0;
_nop_();
Scl=0;
}
/*------------------------------------------------
停止IIC总线
------------------------------------------------*/
void Stop(void)
{
Sda=0;
_nop_();
Scl=1;
_nop_();
Sda=1;
_nop_();
Scl=0;
}
/*------------------------------------------------
应答IIC总线
------------------------------------------------*/
void Ack(void)
{
Sda=0;
_nop_();
Scl=1;
_nop_();
Scl=0;
_nop_();
}
/*------------------------------------------------
非应答IIC总线
------------------------------------------------*/
void NoAck(void)
{
Sda=1;
_nop_();
Scl=1;
_nop_();
Scl=0;
_nop_();
}
/*------------------------------------------------
发送一个字节
------------------------------------------------*/
void Send(unsigned char Data)
{
unsigned char BitCounter=8;
unsigned char temp;
do
{
temp=Data;
Scl=0;
_nop_();
if((temp&0x80)==0x80)
Sda=1;
else
Sda=0;
Scl=1;
temp=Data<<1;
Data=temp;
BitCounter--;
}
while(BitCounter);
Scl=0;
}
/*------------------------------------------------
读入一个字节并返回
------------------------------------------------*/
unsigned char Read(void)
{
unsigned char temp=0;
unsigned char temp1=0;
unsigned char BitCounter=8;
Sda=1;
do
{
Scl=0;
_nop_();
Scl=1;
_nop_();
if(Sda)
temp=temp|0x01;
else
temp=temp&0xfe;
if(BitCounter-1)
{
temp1=temp<<1;
temp=temp1;
}
BitCounter--;
}
while(BitCounter);
return(temp);
}
/*------------------------------------------------
写入DA数模转换值
------------------------------------------------*/
void DAC(unsigned char Data)
{
Start();
Send(AddWr); //写入芯片地址
Ack();
Send(0x40); //写入控制位,使能DAC输出
Ack();
Send(Data); //写数据
Ack();
Stop();
}
/*------------------------------------------------
读取AD模数转换的值,有返回值
------------------------------------------------*/
unsigned char ReadADC(unsigned char Chl)
{
unsigned char Data;
Start(); //写入芯片地址
Send(AddWr);
Ack();
Send(0x40|Chl);//写入选择的通道,本程序只用单端输入,差分部分需要自行添加
//Chl的值分别为0、1、2、3,分别代表1-4通道
Ack();
Start();
Send(AddRd); //读入地址
Ack();
Data=Read(); //读数据
Scl=0;
NoAck();
Stop();
return Data; //返回值
}
/*------------------------------------------------
主程序
------------------------------------------------*/
void main()
{
int ge , z1, z2, z3 ; //DA数模输出变量
int ADtemp; //定义中间变量
mDelay(20);
Init_Timer1();
RST=0; // 关时钟DS1302
while(1)
{
if(ADFlag) //定时采集输入模拟量
{
ADFlag=0; // 清除标志位
ADtemp=(int)(ReadADC(3)*0.01961*1000); // 将八位量化值转化成十进制数
ge =ADtemp/1000;
z1 =ADtemp%1000/100;
z2 =ADtemp%1000%100/10;
z3=ADtemp%10;
stopwatch( ge, z1, z2, z3) ;
}
}
}
void stopwatch(uchar ge, uchar z1,uchar z2, uchar z3) // 数码管显示函数
{
DU=1;
P0=table[ ge]; // 各位数
DU=0;
WE=1;
P0=0xef;
WE=0;
mDelay(3) ;
P0=0x00;
//P0=0xff;
DU=1;
P0=table[ 16]; // 小数点
DU=0;
WE=1;
P0=0xef;
WE=0;
mDelay( 1) ;
P0=0x00;
// P0=0xff;
DU=1;
P0=table[ z1]; // 小数点后一位
DU=0;
WE=1;
P0=0xdf;
WE=0;
mDelay( 8) ;
P0=0x00;
// P0=0xff;
DU=1;
P0=table[ z2]; // 小数点后两位
DU=0;
WE=1;
P0=0xbf;
WE=0;
mDelay( 5) ;
P0=0x00;
// P0=0xff;
DU=1;
P0=table[ z3]; // 小数点后三位
DU=0;
WE=1;
P0=0x7f;
WE=0;
mDelay( 5) ;
P0=0x00;
// P0=0xff; // 使用开发板时用
}
/*------------------------------------------------
定时器中断程序
------------------------------------------------*/
void Timer1_isr(void) interrupt 3
{
static unsigned int j;
TH1=0xfb; //重新赋值
TL1=0x00;
j++;
if(j==10)
{j=0;ADFlag=1;} //定时置位AD采样标志位
}