這個實驗的內容是:通過PCF8591芯片讀取滑動變阻器的電壓值,然後轉換成數字信號在數碼管上顯示。通過對PCF8591芯片進行寫入數字信號,然後轉換成模擬信號(即電壓值),來控制LED燈呼吸(由暗慢慢變亮,再由亮慢慢變暗,循環往復),並在Proteus上仿真。
Proteus圖:
I2C總線基本的時序信號圖:
具體實現方式請看下面的程序:
#include"dac.h"
#include"i2c.h"
#include"timer0.h"
unsigned char dat=0;
void main(){
timer0_init(); //定時器0初始化
while(1){
dat=PCF8591_Read(); //AD轉換,並通過數碼管顯示電壓值,後面的多次使用的原因是:當電壓變化時能及時地刷新
PCF8591_Write(255); //DA轉換,用來實現呼吸燈,寫入的值最大隻能是255
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(200);
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(150);
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(100);
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(50);
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(0);
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(50);
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(100);
delay(10000);
PCF8591_Write(150);
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(200);
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(255);
delay(10000);
}
}
void timer0() interrupt 1{ //定時器中斷函數
TH0=0xdc;//定時器延時10ms
TL0=0x00;
display(dat); //在數碼管上顯示電壓值
}
#include"i2c.h"
void delay5us(void) //5us延時函數
{
unsigned char a;
for(a=1;a>0;a--);
}
void I2cStart() //I2C總線的啓動函數
{
SDA=1;
SCL=1;
delay5us();//建立時間是SDA保持時間>4.7us
SDA=0;
delay5us();//保持時間是>4us
SCL=0;
delay5us();
}
void I2cStop() //I2C總線的停止函數
{
SDA=0;
SCL=1;
delay5us();//建立時間大於4.7us
SDA=1;
delay5us();
}
unsigned char I2cReadByte() //I2C總線讀取字節函數
{
unsigned char a=0,dat=0;
SDA=1; //起始和發送一個字節之後SCL都是0
delay5us();
for(a=0;a<8;a++)//接收8個字節
{
SCL=1; //保持數據穩定
delay5us();
dat<<=1;
dat|=SDA; //讀取SDA的一位數據
delay5us();
SCL=0; //數據改變
delay5us();
}
return dat;
}
unsigned char I2cSendByte(unsigned char dat) //I2C總線發送字節函數
{
unsigned char a=0,b=0;//最大255,一個機器週期爲1us,最大延時255us。
for(a=0;a<8;a++)//要發送8位,從最高位開始
{
SDA=dat>>7; //起始信號之後SCL=0,所以可以直接改變SDA信號
dat=dat<<1;
delay5us();
SCL=1;
delay5us();//建立時間>4.7us
SCL=0;
delay5us();//時間大於4us
}
//時I2C空閒
SDA=1;
delay5us();
SCL=1;
while(SDA)//等待應答,也就是等待從設備把SDA拉低
{
b++;
if(b>200) //如果超過2000us沒有應答發送失敗,或者爲非應答,表示接收結束
{
SCL=0;
delay5us();
return 0;
}
}
SCL=0;
delay5us();
return 1;
}
unsigned char PCF8591_Read(void) //AD轉換
{
unsigned char num;
I2cStart();
I2cSendByte(0x90); //發送寫器件地址,並聲明寫操作
I2cSendByte(0x00); //發送控制字節,採用通道0
I2cStart();
I2cSendByte(0x91); //發送讀器件地址,並聲明讀操作
num=I2cReadByte(); //讀取數據
I2cStop();
return num;
}
unsigned char PCF8591_Write(unsigned char val) //DA轉換
{
I2cStart();
I2cSendByte(0x90); //發送寫器件地址,並聲明寫操作
I2cSendByte(0x40); //發送控制字節,打開自動寫控制位
I2cSendByte(val);
I2cStop();
return 1;
}
#ifndef __I2C_H__
#define __I2C_H__
#include <reg52.h>
unsigned char PCF8591_Write(unsigned char val);
unsigned char PCF8591_Read(void);
sbit SDA = P2^0;
sbit SCL = P2^1;
#endif
#include"dac.h"
unsigned char code smgduan[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
unsigned char i;
unsigned char voltage[2];
void delay(unsigned int i){
while(i--);
}
void display(unsigned char dat){
/*數據的量化*/
float temp = FACTOR * dat;
if(dat == 255){
voltage[1]= 5;
voltage[0] = 0;
}
else{ // 分離整數和小數部分
voltage[1]= (unsigned char)temp;
voltage[0] = (unsigned char)((temp - voltage[1]) * 10);
}
for(i=0;i<2;i++)
{
switch(i){ //位選,選擇點亮的數碼管,
case(0):
LSA=0;LSB=0;LSC=1;LSD=0; break;//顯示第 1 位
case(1):
LSA=0;LSB=1;LSC=0;LSD=0; break;//顯示第 2 位
}
if(i==1)
P0=smgduan[voltage[i]]&0x7f;
else
P0=smgduan[voltage[i]];//發送數據
delay(100); //間隔一段時間掃描
P0=0xFF;//消隱
}
}
#ifndef __DAC_H__
#define __DAC_H__
#define FACTOR 0.01953125 // 量化單位(用一個字節來量化表示5V的電壓值,共爲256個量化等級; FACTOR = 5 / 256)
#include <reg52.h>
void display(unsigned char dat);
void delay(unsigned int i);
sbit LSA=P1^3;
sbit LSB=P1^2;
sbit LSC=P1^1;
sbit LSD=P1^0;
#endif
#include"timer0.h"
void timer0_init(){
EA=1;
ET0=1;
TR0=1;
TMOD=0x01;//定時器工作模式爲1
TH0=0xdc;//定時器延時10ms
TL0=0x00;
}
#ifndef __TIMER0_H__
#define __TIMER0_H__
#include <reg52.h>
void timer0_init();
#endif