AT89S51I2C控制PCF8576段码LCD模块
MSP430F1121与液晶驱动芯片PCF8576的连接程序
本文引用地址:https://www.eepw.com.cn/article/201611/316552.htm#i nclude "msp430x11x1.h"
#define uint unsigned int
#define uchar unsigned char
//器件地址
uchar PCF8576=0x70;
//内存数据定义
uchar ByteCnt; //I2C 数据字节计数器
uchar SlvAdr; //被控器地址
uchar SubAdr; //被控器单元地址
uchar XmtDat[5]; //发送数据缓冲区
//uchar MODE1=0x45;
uchar MODE2=0xCD; //
uchar Bank_sel=0x78;
uchar flag;
uchar Digit[10];
#define SDA BIT3 // P2.3 controls SDA line (pull-up used for logic
1)
#define SCL BIT4 // P2.4 controls SCL line (pull-up used for logic
1)
/******************************************************************************
; 子程序
;名称:START
;描述:启动I2C 总线子程序--发送I2C 起始条件
;;*****************************************************************************/
void START(void)
{
P2OUT |= SDA; //SDA=1
_NOP();
P2OUT |= SCL; //SCL=1
_NOP();
P2OUT &= ~SDA; //SDA=0
_NOP();
P2OUT &= ~SCL; //SCL=0
}
/*--------------------------------------------------------------------------
;名称:STOP
;描述:停止I2C 总线子程序--发送I2C 总线停止条件
;-------------------------------------------------------------------------*/
void STOP(void)
{
P2OUT &= ~SDA; //SDA=0
_NOP();
P2OUT |= SCL;
_NOP();
P2OUT |= SDA;
_NOP();
P2OUT &= ~SCL;
}
void cack(void) /* 应答位检查 */
{
P2OUT |= SDA;
P2OUT |= SCL;
P2DIR &= ~SDA;
_NOP();
P2OUT &= ~SCL;
P2DIR |= SDA;
}
void delay(uchar time)
{
uchar i;
do{
for(i=100;i!=0;i--);
} while(--time!=0);
}
/*----------------------------------------------------------------------
;名称:SendByte
;描述:字节数据传送子程序发送一个字节数据或地址给被控器PCF8576
;要发送的数据在ACC 中
;发送数据正常返回标志F0=0 F0=1 表示被控器无应答或损坏
;------------------------------------------------------------------------*/
void SendByte(uchar Da
{
uchar i=8;
do
{
if((Da
P2OUT |= SDA;
else
P2OUT &= ~SDA;
P2OUT |= SCL;
_NOP ();
P2OUT &= ~SCL;
Da
} while(--i!=0);
cack();
}
/***********************************************************
;发送数据程序
;名称:SendData
;描述:发送ByteCnt 个字节给被控器PCF8576
;被控器地址在SlvAdr 中单元地址在SubAdr 中
;所发送数据的字节数ByteCnt 在中发送的数据在XmtDat 缓冲区中
;发送数据正常返回标志F0=0 F0=1 表示被控器无应答或损坏
;**********************************************************/
void Display_Da
{
uchar i=0;
uchar size=ByteCnt;
START(); //发送I2C 总线起始条件
SendByte(SlvAdr); //发送被控器总线地址
SendByte(SubAdr); //发送单元地址
// SendByte(0x73); //闪烁方式为正常,闪烁频率为0.5Hz 的命令字送缓冲区首址,
//如果不需要闪烁应将数#70H 送入缓冲区首址
SendByte(0x70); //不闪烁
do
{
SendByte(XmtDat[i]); //发送数据
i++;
} while(--size!=0);
STOP();
delay(100);
}
void ClearLcd(void)
{
uchar size=ByteCnt;
START(); //启动I2C 总线
SendByte(SlvAdr); //送器件地址
SendByte(SubAdr); //发送单元地址
SendByte(0x70);
do
{
SendByte(0x00); //发送数据
} while(--size!=0);
STOP();
delay(200);
}
void PCF8576SET(void)
{
START();
SendByte(SlvAdr); //送器件地址
SendByte(MODE2); //取方式命令字
SendByte(Bank_sel);
STOP();
}
void main(void)
{
uint out_da
uint tmp_da
uchar i;
uchar k;
uchar tmp[5];
P2DIR |= SDA; //SDA
P2DIR |= SCL; //SCL
P2OUT &= ~SDA;
P2OUT &= ~SCL;
WDTCTL = WDTPW+WDTHOLD;
Digit[0]=0x7E; //0
Digit[1]=0x18; //1
Digit[2]=0xB6; //2
Digit[3]=0xBC; //3
Digit[4]=0xD8; //4
Digit[5]=0xEC; //5
Digit[6]=0xEE; //6
Digit[7]=0x38; //7
Digit[8]=0xFE; //8
Digit[9]=0xFC; //9
SubAdr=0x80;
SlvAdr=PCF8576;
ByteCnt=4;
PCF8576SET();
ClearLcd();
out_da
while(1)
{
tmp_da
for(i=0;i {
k=tmp_da
tmp_da
XmtDat[i]=Digit[k];
tmp[i]=k;
}
XmtDat[2] |= 0x01;
for(i=ByteCnt-1;i!=0;i--)
{
if( tmp[i] == 0 )
XmtDat[i]=0x00;
else
break;
}
Display_Da
out_da
}
}
这段程序网上转载的最多,也不知道出处,好在其简单易懂,配合PCF8576的PDF资料,总算是弄懂了这种两线控制原件的控制方式,接下来,我剖拆了这个模块,并测绘了其硬件电路图,经与资料比照,因其只有不到40段驱动位,只有一个背极,且其SA0脚接电源正极,从而确认其为静态驱动方式,总线地址为0x72,工作指令字为0x49,不需闪烁时闪烁指令字为0x70,没有用到存储体选择指令,没有用到屏幕清零函数。由于这种I2C器件为单向器件,只送数据,不读数据,送入数据的原理就是,不论多少数据,都是利用“一位数据输出函数”在时钟脉冲的配合下,一位一位的送入器件,所以上述这段程序刚刚好用,比起哪些通用I2C程序简单的多。事情往往就是这样,一旦弄懂了原理,改编自己的应用程序就不是很难了。经过改编,得到了自己的程序,这是一段演示程序,由它控制段码屏在最右侧一位累计加1,直到显示99999时屏显清零并重复,我是想,只要做到想在那个位置显示,就能在那个位置显示,也就可以了,至于其他的应用方式也就能够实现了,以下是我改编并测试通过的程序:
/********************************************************************
AT89S51驱动PCF8576演示5位计数显示程序: WANNENGGONG改编 2010/4/25
AT89S51的P0.1口做I2C的数据输出口;P0.2做I2C的时钟输出口
显示效果为首先满屏清零而后自动累加计数至显示99999时清零后重复
*********************************************************************/
#include
#include
#define uchar unsigned char
#define uint unsigned int
#define PCF8576 0x72 //器件总线地址
sbit SDA=P0^1; //定义模拟I2C数据传送端口
sbit SCL=P0^2; //定义模拟I2C时钟控制端口
bit ack; //定义应答标志位
uchar ByteCnt; //I2C 数据字节计数器
uchar SlvAdr; //被控器地址
uchar SubAdr; //被控器单元地址
uchar cnt[5]; //显示数据寄存器
uchar tmp[5]; //显示数据缓冲区
uchar MODE2=0x49; //工作方式命令字
//uchar Bank_sel=0x7a; //存储体选择命令字(未定义)
uchar Digit[]={0xd7,0x06,0xe3,0xa7,0x36,0xb5,0xf5,0x17,0xf7,0xb7,0xd7};
//{0,1,2,3,4,5,6,7,8,9,0}字形码;其定义方法参见硬件接线图。
//延时程序======延时2*t机器周期=====
void delay(uchar t)
{
while(--t);
}
//延时程序======长延时=====
void delay_long(uint time)
{
uchar i;
do{
for(i=100;i!=0;i--);
} while(--time!=0);
}
/*********************************************************
启动总线函数
名称:START
描述:启动I2C 总线子程序--发送I2C 起始条件
*********************************************************/
void START(void)
{
SDA=1; SCL=1;
delay(2); SDA=0;
delay(2); SCL=0;
}
/*********************************************************
停止总线函数
名称:STOP
描述:停止I2C 总线子程序--发送I2C 总线停止条件
*********************************************************/
void STOP(void)
{
SDA=0; SCL=1;
delay(2); SDA=1;
delay(2); SCL=0;
}
void cack(void)
{
bit a;
if(a==0)SDA=0; //在此发出应答或非应答信号
else SDA=1;
delay(2);
SCL=1;
delay(2); //时钟低电平周期大于4μs
SCL=0;
delay(2); //清时钟线,钳住I2C总线以便继续接收
}
/*********************************************************
发送一个字节函数
名称:SendByte
描述:字节数据传送子程序发送一个字节数据或地址给被控器PCF8576
要发送的数据在ACC中;
发送数据正常返回标志F0=0 F0=1 表示被控器无应答或损坏
*********************************************************/
void SendByte(uchar Da
{
uchar i=8;
do
{
if((Da
SDA=1;
else
SDA=0; SCL=1;SCL=0;
Da
} while(--i!=0);
cack();
}
/***********************************************************
发送数据函数
名称:Display_Da
描述:发送ByteCnt 个字节给被控器PCF8576
被控器地址在SlvAdr 中单元地址在SubAdr 中
所发送数据的字节数ByteCnt 在中发送的数据在tmp[ ] 缓冲区中
发送数据正常返回标志F0=0 F0=1 表示被控器无应答或损坏
**********************************************************/
void Display_Da
{
uchar i=0;
uchar size=ByteCnt;
START(); //发送I2C 总线起始条件
SendByte(SlvAdr); //发送被控器总线地址
SendByte(SubAdr); //发送单元地址
SendByte(0x70); //不闪烁命令字
do
{
SendByte(tmp[i]); //发送数据
i++;
} while(--size!=0);
STOP();
delay(100);
}
/*********************************************************
清除屏显函数(未采用)
名称:ClearLcd
描述:在向显示屏送数据前清除原有显示内容
*********************************************************/
/*********************************************************
void ClearLcd(void)
{
uchar size=ByteCnt;
START(); //启动I2C 总线
SendByte(SlvAdr); //送器件地址
SendByte(SubAdr); //发送单元地址
SendByte(0x70); //不闪烁命令字
do
{
SendByte(0x00); //发送数据
} while(--size!=0);
STOP();
delay(200);
}
*********************************************************/
/*********************************************************
器件总线初始化函数
名称:PCF8576SET
描述:用于器件控制的重复操作的指令集和
*********************************************************/
void PCF8576SET(void)
{
START();
SendByte(SlvAdr); //送器件地址
SendByte(MODE2); //送方式命令字
// SendByte(Bank_sel); //送存储体选择命令字
STOP();
}
void main(void)
{
uchar i,k,m;
uchar cnt[5];
SubAdr=0x80; //为显示缓冲区赋首地址
SlvAdr=PCF8576; //赋器件总线地址
ByteCnt=5; //显示位数赋值
PCF8576SET(); //总线初始化
//ClearLcd(); //清除屏显
while(1)
{
for(m=1;m<11;m++) //0-9 10个数字计数
{
for(k=0;k<5;k++) //循环5次形成稳定显示
{
for(i=0;i<5;i++) //5位显示
{
/***************************************************************************************
以下的程序含义:
显示数据寄存器cnt[5]设定后,在没有装填前只是5个空位置,此时cnt[0]=0;cnt[1]=0---cnt[i]=0;
在第一次执行tmp[i]=Digit[cnt[i]];时,屏幕显示5位全0,接下来执行cnt[4]++;后cnt[4]=1
此后在下一次执行tmp[i]=Digit[cnt[i]];时tmp[0]=Digit[cnt[0]]=Digit[0]=0(0xd7)其它依次同上
直至tmp[4]=Digit[cnt[4]]=Digit[1]=1(0x06);此时屏显为00001;循环重复直至当第一字符位累计
为9时屏显为99999此后屏显清0如上重复。
****************************************************************************************/
tmp[i]=Digit[cnt[i]]; //显示数据装入显示缓存区
Display_Da
delay_long(50); //通过延时控制显示字符的变换速度
}
}
/*********************************************************************
以下的程序含义:
首先在第5位累加,满10进位1至99999时清0重复
能力所限、终未能将下面单调的重复语句变为一个算式模型。
*********************************************************************/
cnt[4]++;
if(cnt[4]>9)
{
cnt[4]=0;
cnt[3]++;
}
if(cnt[3]>9)
{
cnt[3]=0;
cnt[2]++;
}
if(cnt[2]>9)
{
cnt[2]=0;
cnt[1]++;
}
if(cnt[1]>9)
{
cnt[1]=0;
cnt[0]++;
}
if(cnt[0]>9)
break;
}
}
}
程序中0-9的数字编码是根据实际测绘的硬件电路图编制的,至于别人程序中或PDF资料中的编码方式,是绝不可以照搬的,因为每一种模块的内部接线都不尽相同,编码也就不同了,这种电路在控制时,最主要的一点就是,一定要根据段码位数先建立一个数组(显示数据存储区),在数据显示之前,先把显示数据装填到数组当中,再把它按位送入显示缓冲区就OK了,至于开总线,关总线,送指令,送数据等等都是模式化操作,只要按规定做好即可,控制起来也没什么太复杂的;因为只是玩玩,不求甚解,至此,这只模块也就玩完了,如果日后需要数字显示的话或许还能用到它,别的用途我就想不出来了,至于做时间显示,我总觉得它土不土、洋不洋没劲。这种器件已流传很久了,或许已经过时了,或许还有新出的我不知道,虽然我玩过了,但这些资料可能还会有人有用,所以就放在这里共享吧。还是老办法,程序是在μV2编译软件窗口中粘贴过来的,若有用的话反向操作即可;如果用到了就知会一声,可以共享可以转载,但不可做为网站的登陆下载资源,发现必究,仅此而已,别无它求。
附硬件电路图:
评论