新闻中心

EEPW首页>嵌入式系统>设计应用> AT89S51I2C控制PCF8576段码LCD模块

AT89S51I2C控制PCF8576段码LCD模块

作者: 时间:2016-11-13 来源:网络 收藏
很长的一段时间的捣鼓,手上的这块段码屏、总算是可以按照我的要求显示数字了.这块小数码屏在我手里已经很久了,记得还是刚刚玩单片机时买的,当时在地摊上花了3元钱买了一只LCD1601模块和这块屏,当时并不知道如何才能点亮它,只是因为其背面的控制电路是标准的集成块,型号齐全清晰,屏面成色尚可,当时地摊上也时常能见到一些小的显示屏,但多数都是黑色树脂封装的电路模块,我只选带有型号的标准集成电路的买,因为我觉得,只要有型号,就可以在网上查到资料,事实证明,我是对的;随着所学知识的增加,后来玩了一阵子LCD1601,玩明白以后就扔到一边了,而这只模块一直没弄,前些时,在网上找到了这只模块的集成电路资料,才知道这种PCF8576电路、竟然是自成系列的I2C总线控制电路,资料不算多,只有周立功的那两篇文章和应用程序,而找到的C源代码,也多为硬件I2C方式,而AT89S51模拟驱动的C程序也是常被一些网站封锁不能下载,偶尔有见,也弄不懂,至于自己编程,我是无能为力的。后来找到了如下这段程序:

MSP430F1121与液晶驱动芯片PCF8576的连接程序

本文引用地址://m.amcfsurvey.com/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 Data)
{
uchar i=8;

do
{
if((Data&0x80)==0x80)
P2OUT |= SDA;
else
P2OUT &= ~SDA;

P2OUT |= SCL;
_NOP ();
P2OUT &= ~SCL;

Data=Data<<1;
} while(--i!=0);

cack();

}

/***********************************************************
;发送数据程序
;名称:SendData
;描述:发送ByteCnt 个字节给被控器PCF8576
;被控器地址在SlvAdr 中单元地址在SubAdr 中
;所发送数据的字节数ByteCnt 在中发送的数据在XmtDat 缓冲区中
;发送数据正常返回标志F0=0 F0=1 表示被控器无应答或损坏
;**********************************************************/
void Display_Data(void)
{
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_data;
uint tmp_data;
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_data=0;

while(1)
{
tmp_data=out_data;

for(i=0;i {
k=tmp_data%10;
tmp_data=tmp_data/10;

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_Data();

out_data++;

}

}
这段程序网上转载的最多,也不知道出处,好在其简单易懂,配合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 ta)
{
uchar i=8;
do
{
if((Da ta&0x80)==0x80)
SDA=1;
else
SDA=0; SCL=1;SCL=0;
Da ta=Da ta<<1;
} while(--i!=0);
cack();
}
/***********************************************************
发送数据函数
名称:Display_Da ta
描述:发送ByteCnt 个字节给被控器PCF8576
被控器地址在SlvAdr 中单元地址在SubAdr 中
所发送数据的字节数ByteCnt 在中发送的数据在tmp[ ] 缓冲区中
发送数据正常返回标志F0=0 F0=1 表示被控器无应答或损坏
**********************************************************/
void Display_Da ta(void)
{
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_Data(); //显示
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编译软件窗口中粘贴过来的,若有用的话反向操作即可;如果用到了就知会一声,可以共享可以转载,但不可做为网站的登陆下载资源,发现必究,仅此而已,别无它求。

附硬件电路图:



评论


技术专区

关闭