×
单片机 > 单片机程序设计 > 详情

AT89S51I2C控制PCF8576段码LCD模块

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

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

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

附硬件电路图:




『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
51单片机CO2检测显示程序解析