PCF8591的软件编程

来源:本站
导读:目前正在解读《PCF8591的软件编程》的相关信息,《PCF8591的软件编程》是由用户自行发布的知识型内容!下面请观看由(电工学习网 - www.9pbb.com)用户发布《PCF8591的软件编程》的详细说明。

  PCF8591的通信接口是I2C,那么编程肯定是符合这个协议的。单片机对PCF8591进行初始化,一共发送三个字节即可。第一个字节,和EEPROM类似,第一个字节是地址字节,其中7位代表地址,1位代表读写方向。地址高4位固定是1001,低三位是A2,A1,A0,这三位我们电路上都接了GND,因此也就是000,如图1所示。

PCF8591的软件编程

图1PCF8591地址字节

   发送到PCF8591的第二个字节将被存储在控制寄存器,用于控制PCF8591的功能。其中第3位和第7位是固定的0,另外6位各自有各自的作用,如图2所示,我逐一介绍。

PCF8591的软件编程

图2PCF8591控制字节

  控制字节的第6位是DA使能位,这一位置1表示DA输出引脚使能,会产生模拟电压输出功能。第4位和第5位可以实现把PCF8591的4路模拟输入配置成单端模式和差分模式,单端模式和差分模式的区别,我们17.4章节有介绍,这里大家只需要知道这两位是配置AD输入方式的控制位即可,如图3所示。

PCF8591的软件编程

图3PCF8591模拟输入配置方式

  控制字节的第2位是自动增量控制位,自动增量的意思就是,比如我们一共有4个通道,当我们全部使用的时候,读完了通道0,下一次再读,会自动进入通道1进行读取,不需要我们指定下一个通道,由于A/D每次读到的数据,都是上一次的转换结果,所以同学们在使用自动增量功能的时候,要特别注意,当前读到的是上一个通道的值。为了保持程序的通用性,我们的代码没有使用这个功能,直接做了一个通用的程序。

  控制字节的第0位和第1位就是通道选择位了,00、01、10、11代表了从0到3的一共4个通道选择。

  发送给PCF8591的第三个字节D/A数据寄存器,表示D/A模拟输出的电压值。D/A模拟我们一会介绍,大家知道这个字节的作用即可。我们如果仅仅使用A/D功能的话,就可以不发送第三个字节。

  下面我们用一个程序,把AIN0、AIN1、AIN3测到的电压值显示在液晶上,同时大家可以转动电位器,会发现AIN0的值发生变化。

/***********************lcd1602.c文件程序源代码*************************/

#include<reg52.h>

#defineLCD1602_DBP0

sbitLCD1602_RS=P1^0;

sbitLCD1602_RW=P1^1;

sbitLCD1602_E=P1^5;

voidLcdWaitReady()//等待液晶准备好

{

unsignedcharsta;

LCD1602_DB=0xFF;

LCD1602_RS=0;

LCD1602_RW=1;

do

{

LCD1602_E=1;

sta=LCD1602_DB;//读取状态字

LCD1602_E=0;

}while(sta&0x80);//bit7等于1表示液晶正忙,重复检测直到其等于0为止

}

voidLcdWriteCmd(unsignedcharcmd)//写入命令函数

{

LcdWaitReady();

LCD1602_RS=0;

LCD1602_RW=0;

LCD1602_DB=cmd;

LCD1602_E=1;

LCD1602_E=0;

}

voidLcdWriteDat(unsignedchardat)//写入数据函数

{

LcdWaitReady();

LCD1602_RS=1;

LCD1602_RW=0;

LCD1602_DB=dat;

LCD1602_E=1;

LCD1602_E=0;

}

voidLcdShowStr(unsignedcharx,unsignedchary,constunsignedchar*str)//显示字符串,屏幕起始坐标(x,y),字符串指针str

{

unsignedcharaddr;

//由输入的显示坐标计算显示RAM的地址

if(y==0)

addr=0x00+x;//第一行字符地址从0x00起始

else

addr=0x40+x;//第二行字符地址从0x40起始

//由起始显示RAM地址连续写入字符串

LcdWriteCmd(addr|0x80);//写入起始地址

while(*str!='')//连续写入字符串数据,直到检测到结束符

{

LcdWriteDat(*str);

str++;

}

}

voidLcdInit()//液晶初始化函数

{

LcdWriteCmd(0x38);//16*2显示,5*7点阵,8位数据接口

LcdWriteCmd(0x0C);//显示器开,光标关闭

LcdWriteCmd(0x06);//文字不动,地址自动+1

LcdWriteCmd(0x01);//清屏

}

/***********************I2C.c文件程序源代码*************************/

#include<reg52.h>

#include<intrins.h>

#defineI2CDelay(){_nop_();_nop_();_nop_();_nop_();}

sbitI2C_SCL=P3^7;

sbitI2C_SDA=P3^6;

voidI2CStart()//产生总线起始信号

{

I2C_SDA=1;//首先确保SDA、SCL都是高电平

I2C_SCL=1;

I2CDelay();

I2C_SDA=0;//先拉低SDA

I2CDelay();

I2C_SCL=0;//再拉低SCL

}

voidI2CStop()//产生总线停止信号

{

I2C_SCL=0;//首先确保SDA、SCL都是低电平

I2C_SDA=0;

I2CDelay();

I2C_SCL=1;//先拉高SCL

I2CDelay();

I2C_SDA=1;//再拉高SDA

I2CDelay();

}

bitI2CWrite(unsignedchardat)//I2C总线写操作,待写入字节dat,返回值为应答状态

{

bitack;//用于暂存应答位的值

unsignedcharmask;//用于探测字节内某一位值的掩码变量

for(mask=0x80;mask!=0;mask>>=1)//从高位到低位依次进行

{

if((mask&dat)==0)//该位的值输出到SDA上

I2C_SDA=0;

else

I2C_SDA=1;

I2CDelay();

I2C_SCL=1;//拉高SCL

I2CDelay();

I2C_SCL=0;//再拉低SCL,完成一个位周期

}

I2C_SDA=1;//8位数据发送完后,主机释放SDA,以检测从机应答

I2CDelay();

I2C_SCL=1;//拉高SCL

ack=I2C_SDA;//读取此时的SDA值,即为从机的应答值

I2CDelay();

I2C_SCL=0;//再拉低SCL完成应答位,并保持住总线

return(~ack);//应答值取反以符合通常的逻辑:0=不存在或忙或写入失败,1=存在且空闲或写入成功

}

unsignedcharI2CReadNAK()//I2C总线读操作,并发送非应答信号,返回值为读到的字节

{

unsignedcharmask;

unsignedchardat;

I2C_SDA=1;//首先确保主机释放SDA

for(mask=0x80;mask!=0;mask>>=1)//从高位到低位依次进行

{

I2CDelay();

I2C_SCL=1;//拉高SCL

if(I2C_SDA==0)//读取SDA的值

dat&=~mask;//为0时,dat中对应位清零

else

dat|=mask;//为1时,dat中对应位置1

I2CDelay();

I2C_SCL=0;//再拉低SCL,以使从机发送出下一位

}

I2C_SDA=1;//8位数据发送完后,拉高SDA,发送非应答信号

I2CDelay();

I2C_SCL=1;//拉高SCL

I2CDelay();

I2C_SCL=0;//再拉低SCL完成非应答位,并保持住总线

returndat;

}

unsignedcharI2CReadACK()//I2C总线读操作,并发送应答信号,返回值为读到的字节

{

unsignedcharmask;

unsignedchardat;

I2C_SDA=1;//首先确保主机释放SDA

for(mask=0x80;mask!=0;mask>>=1)//从高位到低位依次进行

{

I2CDelay();

I2C_SCL=1;//拉高SCL

if(I2C_SDA==0)//读取SDA的值

dat&=~mask;//为0时,dat中对应位清零

else

dat|=mask;//为1时,dat中对应位置1

I2CDelay();

I2C_SCL=0;//再拉低SCL,以使从机发送出下一位

}

I2C_SDA=0;//8位数据发送完后,拉低SDA,发送应答信号

I2CDelay();

I2C_SCL=1;//拉高SCL

I2CDelay();

I2C_SCL=0;//再拉低SCL完成应答位,并保持住总线

returndat;

}

/***********************main.c文件程序源代码*************************/

#include<reg52.h>

bitflag300ms=1;//300ms定时标志

unsignedcharT0RH=0;//T0重载值的高字节

unsignedcharT0RL=0;//T0重载值的低字节

unsignedcharGetADCValue(unsignedcharchn);

voidValueToString(unsignedchar*str,unsignedcharval);

voidConfigTimer0(unsignedintms);

externvoidLcdInit();

externvoidLcdShowStr(unsignedcharx,unsignedchary,constunsignedchar*str);

externvoidI2CStart();

externvoidI2CStop();

externunsignedcharI2CReadACK();

externunsignedcharI2CReadNAK();

externbitI2CWrite(unsignedchardat);

voidmain()

{

unsignedcharval;

unsignedcharstr[10];

EA=1;//开总中断

ConfigTimer0(10);//配置T0定时10ms

LcdInit();//初始化液晶

LcdShowStr(0,0,"AIN0AIN1AIN3");//显示通道指示

while(1)

{

if(flag300ms)

{

flag300ms=0;

//显示通道0的电压

val=GetADCValue(0);//获取ADC通道0的转换值

ValueToString(str,val);//转为字符串格式的电压值

LcdShowStr(0,1,str);//显示到液晶上

//显示通道1的电压

val=GetADCValue(1);

ValueToString(str,val);

LcdShowStr(6,1,str);

//显示通道3的电压

val=GetADCValue(3);

ValueToString(str,val);

LcdShowStr(12,1,str);

}

}

}

unsignedcharGetADCValue(unsignedcharchn)//读取当前的ADC转换值,chn为ADC通道号0-3

{

unsignedcharval;

I2CStart();

if(!I2CWrite(0x48<<1))//寻址PCF8591,如未应答,则停止操作并返回0

{

I2CStop();

return0;

}

I2CWrite(0x40|chn);//写入控制字节,选择转换通道

I2CStart();

I2CWrite((0x48<<1)|0x01);//寻址PCF8591,指定后续为读操作

I2CReadACK();//先空读一个字节,提供采样转换时间

val=I2CReadNAK();//读取刚刚转换完的值

I2CStop();

returnval;

}

voidValueToString(unsignedchar*str,unsignedcharval)//ADC转换值转为实际电压值的字符串形式

{

val=(val*25)/255;//电压值=转换结果*2.5V/255,式中的25隐含了一位十进制小数

str[0]=(val/10)+'0';//整数位字符

str[1]='.';//小数点

str[2]=(val%10)+'0';//小数位字符

str[3]='V';//电压单位

str[4]='';//结束符

}

voidConfigTimer0(unsignedintms)//T0配置函数

{

unsignedlongtmp;

tmp=11059200/12;//定时器计数频率

tmp=(tmp*ms)/1000;//计算所需的计数值

tmp=65536-tmp;//计算定时器重载值

tmp=tmp+12;//修正中断响应延时造成的误差

T0RH=(unsignedchar)(tmp>>8);//定时器重载值拆分为高低字节

T0RL=(unsignedchar)tmp;

TMOD&=0xF0;//清零T0的控制位

TMOD|=0x01;//配置T0为模式1

TH0=T0RH;//加载T0重载值

TL0=T0RL;

ET0=1;//使能T0中断

TR0=1;//启动T0

}

voidInterruptTimer0()interrupt1//T0中断服务函数

{

staticunsignedchartmr300ms=0;

TH0=T0RH;//定时器重新加载重载值

TL0=T0RL;

tmr300ms++;

if(tmr300ms>=30)//定时300ms

{

tmr300ms=0;

flag300ms=1;

}

}

   细心阅读程序的同学会发现,我们程序在进行A/D读取数据的时候,共使用了两条程序去读了2个字节。I2CReadACK();val=I2CReadNAK();PCF8591的转换时钟是I2C的SCL,而A/D的特点是每次读到的都是上一次的转换结果,因此我们这里第一条语句的作用是产生一个整体的SCL时钟提供给PCF8591进行A/D转换,第二次是读取当前的转换结果。如果我们只使用第二条语句的话,每次读到的都是上一次的转换结果。

提醒:《PCF8591的软件编程》最后刷新时间 2023-07-10 03:59:39,本站为公益型个人网站,仅供个人学习和记录信息,不进行任何商业性质的盈利。如果内容、图片资源失效或内容涉及侵权,请反馈至,我们会及时处理。本站只保证内容的可读性,无法保证真实性,《PCF8591的软件编程》该内容的真实性请自行鉴别。