PCF8951在90c51上的使用

来源:互联网 发布:java文件下载前台代码 编辑:程序博客网 时间:2024/05/29 10:08

     PCF8591是一个单片集成、单独供电、低功耗、8-bit CMOS数据获取器件。PCF8591具有4个模拟输入、1个模拟输出和1个串行I²C总线接口。在90c51上PCF8591的4个引脚AIN0, AIN1,AIN2和AIN3可接注入光敏电阻,滑变电阻器之类的原件。在PCF8591器件上输入输出的地址、控制和数据信号都是通过双线双向I2C总线以串行的方式进行传输。2条双向串行线,一条数据线SDA,一条时钟线SCL。


A/D实现:诸如光敏电阻将模拟量转化为电压连在AIN1口,而PCF将电压转化为数字由数据线SDA输出。


工作原理:

首先说PCF使用的I2C总线协议

I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件.在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件.然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下.主机负责产生定时时钟和终止数据传送。 
数据的传输使用数据线SDA,一条时钟线SCL。SDA每次传输一字节。

以下来自一位大佬的http://blog.csdn.net/subkiller/article/details/6854910


 1. I2C开始和结束信号
   开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
   结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。

  I2C学习笔记 - dp - dp: 生活的脚步,进步的点滴...


c51代码:

忽略<>里的东西

void I2C_start(){SCL = 1;SDA = 1;delay5us();SDA = 0;delay5us();SCL = 0;}

2. I2C位传输
   数据传输:SCL为高电平时,SDA线若保持稳定,那么SDA上是在传输数据bit;
   若SDA发生跳变,则用来表示一个会话的开始或结束(后面讲)
   数据改变:SCL为低电平时,SDA线才能改变传输的bit

I2C学习笔记 - dp - dp: 生活的脚步,进步的点滴...

代码

void I2C_write(uchar dat){uchar i;for(i=0; i<8; i++){SCL = 0;delay5us();SDA = dat & 0x80;delay5us();SCL = 1;dat <<= 1;delay5us();}SCL = 0;}

传输数据是一位一位地传,每次SCL为0时,SDA赋值要被传输的数据,SCL拉高,SDA的数据传送。


3. I2C应答信号

   Master每发送完8bit数据后等待Slave的ACK。
   即在第9个clock,若从IC发ACK,SDA会被拉低。
   若没有ACK,SDA会被置高,这会引起Master发生RESTART或STOP流程,如下所示:
I2C学习笔记 - dp - dp: 生活的脚步,进步的点滴...
代码

uchar I2C_WaitAck()   {uint i = 0;SDA = 1;      delay5us();SCL = 1;delay5us();while(SDA == 1 && i < 300)i++;if(SDA){SCL = 0;I2C_stop();return 0;}else {SCL = 0;return 1;}}

while循环或者SDA为1没有回应或者继续延时

返回1有回应


接下来就是读取数据

代码

uchar I2C_read(){uchar i;uchar byte;SCL = 0;delay5us();SDA = 1;for(i=0; i<8; i++){SCL = 1;   byte <<= 1;delay5us();if(SDA)byte |= 0x01;SCL = 0;delay5us();}return byte;}

每次赋值在byte的第一位,之后byte位移。


关于PCF8591的读写

写入数据时:起始信号->器件地址->应答信号->控制字节->写入的数据->应答信号(从机)->写入的数据->应答信号(从机)。。。。->停止信号

读取数据时:起始信号->器件地址(写)->应答信号(从机)->控制字节->应答信号(从机)->停止信号(这个前提是设置读取的是哪一个通道,采用的哪种模                           式)    接着再是:起始信号->器件地址(读)->读取的字节->非应答信号(主机)->停止信号

下面来自另一位大佬 http://www.cnblogs.com/whik/p/6650955.html

PCF8591的操作和AT24C02非常类似,只不过AT24C02是写入或读出数据,而PCF8591是AIN端口输入模拟电压,然后PCF8591将转换后的数字量通过IIC总线发送给单片机,或是单片机通过IIC总线给一个数字量,然后PCF8591通过AOUT端口将模拟电压输出.

控制字格式

最高位默认为0

第6位是选择是否允许模拟电压输出,在DA转换时设置为1,AD转换时设置为0或1均可

第5/4位是选择模拟电压输出方式,一般选择00单端输入方式,其他的几种方式如下图所示

 

第3位默认为0

第2位是自动增量使能位,如果自动增量(auto-increment)标志置1,每次A/D 转换后通道号将自动增加。

第1/0为是在AD转换时选择哪一个通道输入的电压转换为数字量.

由之前的图可以得知,如果显示光敏电阻的变化,那么通道输入0000 0001即可,就是0x01


下面说器件地址

PCF8591的器件地址

每一个IIC器件都有一个器件地址,来区分不同的IIC设备,下面是PCF8591的地址

 

 

它的地址是由1001和A2A1A0组成的,在原理图中可以看出,A2A1A0均为0,所以器件地址为0x90/0x91,最后一位是读写方向位,0表示下一个字节往总线上写数据,1表示下一个字节从总线上读取数据.


代码

完整的代码,烧录在51上后显示的是前三位为滑变电阻器变化值,后三位显示光敏电阻,数码管通过中断控制,尽管我将周期写得很短很短了,它还是闪烁的我眼都要瞎了,只能说可以看清数字

#include <reg52.h>#include "intrins.h"#define uint unsigned int#define uchar unsigned charsbit SCL = P2^0;sbit SDA = P2^1;void delay5us();void I2C_start();void I2C_write(uchar dat);uchar I2C_read();uchar I2C_WaitAck();void I2C_Ack(uchar ackbit);void I2C_stop();void PCF8591_init();uchar ADC_PCF8591();#define uint unsigned int #define uchar unsigned charuchar code tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff};uchar dspbuf[]={10,10,10,10,10,10,10,10};uchar dspcom = 0;sfr AUXR = 0x8E;void close_buzz(){P2 = (P2 & 0x1f)|0xa0; P0 &= 0xbf;P2 &= 0x1f;}void Timer1Init(void){AUXR |= 0x40;TMOD &= 0x0F;TH1=(65535-65000)/256;TL1=(65535-65000)%256;TF1 = 0;TR1 = 1;ET1 = 1;EA = 1;}void delay5us(){uchar i;_nop_();i = 11;while (--i);}void I2C_start(){SCL = 1;SDA = 1;delay5us();SDA = 0;delay5us();SCL = 0;}void I2C_write(uchar dat){uchar i;for(i=0; i<8; i++){SCL = 0;delay5us();SDA = dat & 0x80;delay5us();SCL = 1;dat <<= 1;delay5us();}SCL = 0;}uchar I2C_WaitAck()   {uint i = 0;SDA = 1;      delay5us();SCL = 1;delay5us();while(SDA == 1 && i < 300)i++;if(SDA){SCL = 0;I2C_stop();return 0;}else {SCL = 0;return 1;}}void I2C_Ack(uchar ackbit){SCL = 0;if(ackbit)SDA = 0;else    SDA = 1;delay5us();SCL = 1;delay5us();SCL = 0;SDA = 1;delay5us();}uchar I2C_read(){uchar i;uchar byte;SCL = 0;delay5us();SDA = 1;for(i=0; i<8; i++){SCL = 1;  byte <<= 1;delay5us();if(SDA)byte |= 0x01;SCL = 0;delay5us();}return byte;}void I2C_stop(){SCL = 0;SDA = 0;delay5us();SCL = 1;delay5us();SDA = 1;}uchar ADC_PCF8591_AIN3(){uchar val;I2C_start();I2C_write(0x90);I2C_WaitAck();I2C_write(0x03);I2C_WaitAck();I2C_start();I2C_write(0x91);I2C_WaitAck();val = I2C_read();I2C_Ack(0);I2C_stop();//val = (val*50)/255;return val;}uchar ADC_PCF8591_AIN1(){uchar val;I2C_start();I2C_write(0x90);I2C_WaitAck();I2C_write(0x01);I2C_WaitAck();I2C_start();I2C_write(0x91);I2C_WaitAck();val = I2C_read();I2C_Ack(0);I2C_stop();//val = (val*50)/255;return val;}void display(){P2 = (P2 & 0x1f)|0xE0;P0 = ~0xff;P2 &= 0x1f;P2 = (P2 & 0x1f)|0xC0;P0 = ~(1 << dspcom);P2 &= 0x1f;P2 = (P2 & 0x1f)|0xE0;P0 = ~tab[dspbuf[dspcom]];P2 &= 0x1f;if(++dspcom == 8)dspcom = 0;}void main(){uchar val_AIN1,val_AIN3;close_buzz();Timer1Init();while(1){val_AIN1 = ADC_PCF8591_AIN1();val_AIN3 = ADC_PCF8591_AIN3();dspbuf[0] = val_AIN1/100;dspbuf[1] = val_AIN1/10%10;dspbuf[2] = val_AIN1%10;dspbuf[5] = val_AIN3/100;dspbuf[6] = val_AIN3/10%10;dspbuf[7] = val_AIN3%10;}}void Timer1() interrupt 3{display();}




刚看完什么什么特工2,极限特工2?



原创粉丝点击