/*---------------------------------------------------------------------*/
/* --- STC MCU Limited ------------------------------------------------*/
/* --- STC 1T Series MCU Demo Programme -------------------------------*/
/* --- Mobile: (86)13922805190 ----------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 -----------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966 ------------------------*/
/* --- Web: www.STCMCU.com --------------------------------------------*/
/* --- Web: www.STCMCUDATA.com  ---------------------------------------*/
/* --- QQ:  800003751 -------------------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序        */
/*---------------------------------------------------------------------*/


/*************  功能说明    **************

LCM接口驱动液晶屏程序

8bit M6800模式, P2口接D0~D7

sbit LCD_RS = P4^1;      //数据/命令切换
sbit LCD_RW = P3^6;      //读写控制
sbit LCD_E = P3^7;       //使能
sbit LCD_RESET = P3^5;   //复位 

LCM指令通过中断方式等待发送完成

设置时钟 2.4MHz (128*64屏, ST7920驱动, 速度太快显示会不正常).

******************************************/


#include    "reg51.h"
#include    "intrins.h"
#include    "LCD_IMG.h"

#define     MAIN_Fosc       24000000L   //定义计算时钟参数

typedef     unsigned char   u8;
typedef     unsigned int    u16;
typedef     unsigned long   u32;

sfr TH2  = 0xD6;
sfr TL2  = 0xD7;
sfr IE2   = 0xAF;
sfr INT_CLKO = 0x8F;
sfr AUXR = 0x8E;
sfr AUXR1 = 0xA2;
sfr P_SW1 = 0xA2;
sfr P_SW2 = 0xBA;
sfr S2CON = 0x9A;
sfr S2BUF = 0x9B;

sfr P4   = 0xC0;
sfr P5   = 0xC8;
sfr P6   = 0xE8;
sfr P7   = 0xF8;
sfr P1M1 = 0x91;    //PxM1.n,PxM0.n     =00--->Standard,    01--->push-pull
sfr P1M0 = 0x92;    //                  =10--->pure input,  11--->open drain
sfr P0M1 = 0x93;
sfr P0M0 = 0x94;
sfr P2M1 = 0x95;
sfr P2M0 = 0x96;
sfr P3M1 = 0xB1;
sfr P3M0 = 0xB2;
sfr P4M1 = 0xB3;
sfr P4M0 = 0xB4;
sfr P5M1 = 0xC9;
sfr P5M0 = 0xCA;
sfr P6M1 = 0xCB;
sfr P6M0 = 0xCC;
sfr P7M1 = 0xE1;
sfr P7M0 = 0xE2;

sbit P00 = P0^0;
sbit P01 = P0^1;
sbit P02 = P0^2;
sbit P03 = P0^3;
sbit P04 = P0^4;
sbit P05 = P0^5;
sbit P06 = P0^6;
sbit P07 = P0^7;
sbit P10 = P1^0;
sbit P11 = P1^1;
sbit P12 = P1^2;
sbit P13 = P1^3;
sbit P14 = P1^4;
sbit P15 = P1^5;
sbit P16 = P1^6;
sbit P17 = P1^7;
sbit P20 = P2^0;
sbit P21 = P2^1;
sbit P22 = P2^2;
sbit P23 = P2^3;
sbit P24 = P2^4;
sbit P25 = P2^5;
sbit P26 = P2^6;
sbit P27 = P2^7;
sbit P30 = P3^0;
sbit P31 = P3^1;
sbit P32 = P3^2;
sbit P33 = P3^3;
sbit P34 = P3^4;
sbit P35 = P3^5;
sbit P36 = P3^6;
sbit P37 = P3^7;
sbit P40 = P4^0;
sbit P41 = P4^1;
sbit P42 = P4^2;
sbit P43 = P4^3;
sbit P44 = P4^4;
sbit P45 = P4^5;
sbit P46 = P4^6;
sbit P47 = P4^7;
sbit P50 = P5^0;
sbit P51 = P5^1;
sbit P52 = P5^2;
sbit P53 = P5^3;
sbit P54 = P5^4;
sbit P55 = P5^5;
sbit P56 = P5^6;
sbit P57 = P5^7;

//LCM
#define LCMIFCFG          (*(unsigned char volatile xdata *)0xfe50)
#define LCMCFG2           (*(unsigned char volatile xdata *)0xfe51)
#define LCMIFSTA          (*(unsigned char volatile xdata *)0xfe53)
#define LCMIFCR           (*(unsigned char volatile xdata *)0xfe52)
#define LCMIFDATL         (*(unsigned char volatile xdata *)0xfe54)
#define LCMIFDATH         (*(unsigned char volatile xdata *)0xfe55)

sbit LCD_RS = P4^1;      //数据/命令切换
sbit LCD_RW = P3^6;      //读写控制
sbit LCD_E = P3^7;       //使能
//sbit LCD_CS = P3^4;      //片选	
sbit LCD_RESET = P3^5;   //复位 

sbit LCD_PSB = P3^3;     //PSB脚为12864的串、并通讯功能切换，我们使用8位并行接口，PSB=1

bit  LCD_Flag;

//IO连接
#define  LCD_DataPort P2     //8位数据口

#define  USR_LCM_IF     1			//1: use LCM Interface

u8  code uctech[] = {"南通国芯微电子  "};
u8  code net[]    = {" www.stcmcu.com "};
u8  code mcu[]    = {"专业制造51单片机"};
u8  code qq[]     = {" QQ: 800003751  "};

void delay_ms(u16 ms);
void GPIO_Init(void);
void LCM_Config(void);
void LCD_Init(void);
void DisplayImage (u8 code *DData);
void DisplayListChar(u8 X, u8 Y, u8 code *DData);
void Write_Cmd(unsigned char WCLCD,unsigned char BuysC);
void Write_Data(unsigned char WDLCD);
void LCDClear(void);

void main(void)
{
	P0M1 = 0;   P0M0 = 0;   //设置为准双向口
	P1M1 = 0;   P1M0 = 0;   //设置为准双向口
	P2M1 = 0;   P2M0 = 0;   //设置为准双向口
	P3M1 = 0;   P3M0 = 0;   //设置为准双向口
	P4M1 = 0;   P4M0 = 0;   //设置为准双向口
	P5M1 = 0;   P5M0 = 0;   //设置为准双向口
	P6M1 = 0;   P6M0 = 0;   //设置为准双向口
	P7M1 = 0;   P7M0 = 0;   //设置为准双向口

	GPIO_Init();
#if USR_LCM_IF == 1	
	LCM_Config();
	EA = 1;
#endif
	
//	delay_ms(100); //启动等待，等LCD进入工作状态
	LCD_Init(); //LCM初始化

	while(1)
	{
		LCDClear();
		DisplayImage(gImage_gxw);
		delay_ms(200);
		LCDClear();
		DisplayListChar(0,1,uctech);    //显示字库中的中文数字
		DisplayListChar(0,2,net);       //显示字库中的中文数字
		DisplayListChar(0,3,mcu);       //显示字库中的中文
		DisplayListChar(0,4,qq);        //显示字库中的中文数字
		delay_ms(200);
	}
}

//按指定位置显示一串字符
void DisplayListChar(u8 X, u8 Y, u8 code *DData)
{
    u8 ListLength,X2;
    ListLength = 0;
    X2 = X;
    if(Y < 1)   Y=1;
    if(Y > 4)   Y=4;
    X &= 0x0F; //限制X不能大于16，Y在1-4之内
    switch(Y)
    {
        case 1: X2 |= 0X80; break;  //根据行数来选择相应地址
        case 2: X2 |= 0X90; break;
        case 3: X2 |= 0X88; break;
        case 4: X2 |= 0X98; break;
    }
    Write_Cmd(X2, 1); //发送地址码
    while (DData[ListLength] >= 0x20) //若到达字串尾则退出
    {
        if (X <= 0x0F) //X坐标应小于0xF
        {
            Write_Data(DData[ListLength]); //
            ListLength++;
            X++;
        }
    }
}

//图形显示122*32
void DisplayImage (u8 code *DData)
{
    u8 x,y,i;
    unsigned int tmp=0;
    for(i=0;i<9;)       //分两屏，上半屏和下半屏，因为起始地址不同，需要分开
    {
        for(x=0;x<32;x++)   //32行
        {
            Write_Cmd(0x34,1);
            Write_Cmd((0x80+x),1);//列地址
            Write_Cmd((0x80+i),1);    //行地址，下半屏，即第三行地址0X88
            Write_Cmd(0x30,1);        
            for(y=0;y<16;y++)   
                Write_Data(DData[tmp+y]);//读取数据写入LCD
            tmp+=16;        
        }
        i+=8;
    }
    Write_Cmd(0x36,1);    //扩充功能设定
    Write_Cmd(0x30,1);
}

//========================================================================
// 函数: void delay_ms(u16 ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数, 自动适应主时钟.
// 返回: none.
// 版本: VER1.0
// 日期: 2013-4-1
// 备注: 
//========================================================================
void delay_ms(u16 ms)
{
	 u16 i;
	 do{
			i = MAIN_Fosc / 10000;
			while(--i)    ;   //10T per loop
	 }while(--ms);
}

void LCD_delay(void)
{
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
}

//读状态
void ReadStatusLCD(void)
{
//#if USR_LCM_IF == 1
//	
//	LCD_Flag = 1;
//	LCMIFCR = 0x86;		//Enable interface, Read command
//	while(LCD_Flag);
//	
//#else
	
	LCD_DataPort = 0xFF; 
  //P2口设置成输入口
	P2M0=0x00;
	P2M1=0xFF;

	LCD_RS = 0;
	LCD_RW = 1; 
	LCD_delay();
	LCD_E = 1;
	LCD_delay();
	while (LCD_DataPort & 0x80); //检测忙信号
	LCD_E = 0;

  //P2口设置成推挽输出
	P2M0=0xFF;
	P2M1=0x00;
	LCD_DataPort = 0xFF; //拉高P0
	
//#endif
}

void Write_Cmd(unsigned char WCLCD,unsigned char BuysC)
{
#if USR_LCM_IF == 1
	
	if (BuysC) ReadStatusLCD(); //根据需要检测忙 
	LCMIFDATL = WCLCD;
	LCD_Flag = 1;
	LCMIFCR = 0x84;		//Enable interface, write command out
	while(LCD_Flag);
	
#else
	
	if (BuysC) ReadStatusLCD(); //根据需要检测忙 
	LCD_RS = 0;
	LCD_RW = 0; 
	LCD_DataPort = WCLCD;
	LCD_delay();
	LCD_E = 1; 
	LCD_delay();
	LCD_E = 0;  
	
#endif
}

void Write_Data(unsigned char WDLCD)
{
#if USR_LCM_IF == 1
	
	LCMIFDATL = WDLCD;
	LCD_Flag = 1;
	LCMIFCR = 0x85;		//Enable interface, write data out
	while(LCD_Flag);
	
#else
	
	ReadStatusLCD(); //检测忙 
	LCD_RS = 1;
	LCD_RW = 0;
	LCD_DataPort = WDLCD;
	LCD_delay();
	LCD_E = 1;
	LCD_delay();
	LCD_E = 0;
	
#endif
}

/*****************************************************************************
 * @name       :void GPIO_Init(void)
 * @date       :2018-11-13 
 * @function   :Set the gpio to push-pull mode
 * @parameters :None
 * @retvalue   :None
******************************************************************************/	
void GPIO_Init(void)
{
  //P0口设置成推挽输出
//	P0M0=0xff;
//	P0M1=0x00;

	//P1口设置成准双向口
//	P1M0=0x00;
//	P1M1=0x00;

  //P2口设置成推挽输出
	P2M0=0xff;
	P2M1=0x00;

	//P3.3~P3.7口设置成推挽输出
	P3M0=0xf8;
	P3M1=0x00;

	//P4.1口设置成推挽输出
	P4M0=0x02;
	P4M1=0x00;
}

/*****************************************************************************
 * @name       :void LCM_Config(void)
 * @date       :2018-11-13 
 * @function   :Config LCM
 * @parameters :None
 * @retvalue   :None
******************************************************************************/	
void LCM_Config(void)
{
	P_SW2 = 0x80;
	LCMIFCFG = 0x81;	//bit7 1:Enable Interrupt, bit1 0:8bit mode; bit0 0:8080,1:6800
	LCMCFG2 = 0x3f;		//RS:P41,RD:P37,WR:P36; Setup Time,HOLD Time
	LCMIFSTA = 0x00;
}

/*****************************************************************************
 * @name       :void LCDReset(void)
 * @date       :2018-08-09 
 * @function   :Reset LCD screen
 * @parameters :None
 * @retvalue   :None
******************************************************************************/	
void LCDReset(void)
{
	LCD_PSB = 1;    //并口
	delay_ms(10);
	LCD_RESET = 0;
	delay_ms(10);
	LCD_RESET = 1;
	delay_ms(100);
}

/*****************************************************************************
 * @name       :void LCD_Init(void)
 * @date       :2018-08-09 
 * @function   :Initialization LCD screen
 * @parameters :None
 * @retvalue   :None
******************************************************************************/	 	 
void LCD_Init(void)
{
	LCDReset(); //初始化之前复位
//*************LCD 初始化**********//	
	Write_Cmd(0x30,1); //显示模式设置,开始要求每次检测忙信号
	Write_Cmd(0x01,1); //显示清屏
	Write_Cmd(0x06,1); // 显示光标移动设置
	Write_Cmd(0x0C,1); // 显示开及光标设置
}

void LCDClear(void) //清屏
{
	Write_Cmd(0x01,1); //显示清屏
	Write_Cmd(0x34,1); // 显示光标移动设置
	Write_Cmd(0x30,1); // 显示开及光标设置
}

/*****************************************************************************
 * @name       :void LCM_Interrupt(void)
 * @date       :2018-11-13 
 * @function   :None
 * @parameters :None
 * @retvalue   :
******************************************************************************/ 
void LCM_Interrupt(void) interrupt 13
{
	LCMIFSTA = 0x00;
	LCD_Flag = 0;
}
