- #include "REG52.H"
- sbit LCDCS_dr = P1^6; //片选线
- sbit LCDSID_dr = P1^7; //串行数据线
- sbit LCDCLK_dr = P3^2; //串行时钟线
- sbit LCDRST_dr = P3^4; //复位线
- void SendByteToLcd(unsigned char ucData); //发送一个字节数据到液晶模块
- void SPIWrite(unsigned char ucWData, unsigned char ucWRS); //模拟SPI发送一个字节的命令或者数据给液晶模块的底层驱动
- void WriteCommand(unsigned char ucCommand); //发送一个字节的命令给液晶模块
- void LCDWriteData(unsigned char ucData); //发送一个字节的数据给液晶模块
- void LCDInit(void); //初始化 函数内部包括液晶模块的复位
- void display_clear(unsigned char ucFillDate); // 清屏 全部显示空填充0x00 全部显示点阵用0xff
- void insert_buffer_to_canvas(unsigned int x,unsigned int y,const unsigned char *ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount);//把字模插入画布.
- void display_lattice(unsigned int x,unsigned int y,const unsigned char *ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount,unsigned int uiOffSetAddr); //显示任意点阵函数
- unsigned char *number_to_matrix(unsigned char ucBitNumber); //把一位数字转换成字模首地址的函数
- void delay_short(unsigned int uiDelayshort); //延时
- void delay_long(unsigned int uiDelayLong);
- void initial_myself();
- void initial_peripheral();
- void lcd_display_service(void); //应用层面的液晶屏显示程序
- void clear_all_canvas(void); //把画布全部清零
- code unsigned char Zf816_0[]=
- {
- /*-- 文字: 0 --*/
- /*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
- 0x00,0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x24,0x18,0x00,0x00,
- };
- code unsigned char Zf816_1[]=
- {
- /*-- 文字: 1 --*/
- /*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
- 0x00,0x00,0x00,0x10,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x7C,0x00,0x00,
- };
- code unsigned char Zf816_2[]=
- {
- /*-- 文字: 2 --*/
- /*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
- 0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x04,0x04,0x08,0x10,0x20,0x42,0x7E,0x00,0x00,
- };
- code unsigned char Zf816_3[]=
- {
- /*-- 文字: 3 --*/
- /*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
- 0x00,0x00,0x00,0x3C,0x42,0x42,0x04,0x18,0x04,0x02,0x02,0x42,0x44,0x38,0x00,0x00,
- };
- code unsigned char Zf816_4[]=
- {
- /*-- 文字: 4 --*/
- /*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
- 0x00,0x00,0x00,0x04,0x0C,0x14,0x24,0x24,0x44,0x44,0x7E,0x04,0x04,0x1E,0x00,0x00,
- };
- code unsigned char Zf816_5[]=
- {
- /*-- 文字: 5 --*/
- /*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
- 0x00,0x00,0x00,0x7E,0x40,0x40,0x40,0x58,0x64,0x02,0x02,0x42,0x44,0x38,0x00,0x00,
- };
- code unsigned char Zf816_6[]=
- {
- /*-- 文字: 6 --*/
- /*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
- 0x00,0x00,0x00,0x1C,0x24,0x40,0x40,0x58,0x64,0x42,0x42,0x42,0x24,0x18,0x00,0x00,
- };
- code unsigned char Zf816_7[]=
- {
- /*-- 文字: 7 --*/
- /*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
- 0x00,0x00,0x00,0x7E,0x44,0x44,0x08,0x08,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,
- };
- code unsigned char Zf816_8[]=
- {
- /*-- 文字: 8 --*/
- /*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
- 0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x24,0x18,0x24,0x42,0x42,0x42,0x3C,0x00,0x00,
- };
- code unsigned char Zf816_9[]=
- {
- /*-- 文字: 9 --*/
- /*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
- 0x00,0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x26,0x1A,0x02,0x02,0x24,0x38,0x00,0x00,
- };
- code unsigned char Zf816_nc[]= //空字模
- {
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- };
- /* 注释一:
- * 为了实现跨区域无缝显示,就先在某个区域显示一块画布,我们只要在这块画布数组中插入字模数组,
- * 就可以达到跨区域无缝显示的目的。根据上几节的介绍,12864液晶屏由上下两半屏组成,以下这块画布
- * 显示在上半屏和下半屏之间。横向4个字节,纵向16行。其中上半屏显示8行,下半屏显示8行。注意,这个数组
- * 不带code关键字,是全局变量,这样可读可写。画布的横向x坐标范围是0至3,因为画布的横向只要4个字节。
- * 画布的纵向y坐标范围是0至15,因为画布的纵向只有16行。
- */
- unsigned char ucCanvasBuffer[]= //画布显示数组。注意,这里没有code关键字,是全局变量。初始化全部填充0x00
- {
- 0x00,0x00,0x00,0x00, //上半屏
- 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- //------------上半屏和下半屏的分割线-----------
- 0x00,0x00,0x00,0x00, //下半屏
- 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- };
- unsigned char ucDisplayUpdate=1; //更新显示变量
- /* 注释二:
- * 以下变量就是本程序的任意变量,网友可以自己更改它的大小来测试本程序,不要超过255.
- */
- unsigned char ucAnyNumber=218; //任意变量默认初始化为218。
- void main()
- {
- initial_myself(); //第一区,上电后马上初始化
- delay_long(100); //一线,延时线。延时一段时间
- initial_peripheral(); //第二区,上电后延时一段时间再初始化
- while(1) //第三区
- {
- lcd_display_service(); //应用层面的液晶屏显示程序
- }
- }
- void initial_myself() //第一区 上电后马上初始化
- {
- ;
- }
- void initial_peripheral() //第二区 上电后延时一段时间再初始化
- {
- LCDInit(); //初始化12864 内部包含液晶模块的复位
- display_clear(0xff); // 清屏 全部显示空填充0x00 全部显示点阵用0xff
- }
- /* 注释三:
- * 本程序的核心转换函数。
- * 是可以把一位任意数字变量的函数转换成对应的字模,由于字模是数组,所以返回的是指针,代表字模数组的首地址。
- */
- unsigned char *number_to_matrix(unsigned char ucBitNumber)
- {
- unsigned char *p_ucAnyNumber; //此指针根据ucBitNumber数值的大小,分别调用不同的字库。
- switch(ucBitNumber) //根据ucBitNumber数值的大小,分别调用不同的字库。
- {
- case 0:
- p_ucAnyNumber=Zf816_0;
- break;
- case 1:
- p_ucAnyNumber=Zf816_1;
- break;
- case 2:
- p_ucAnyNumber=Zf816_2;
- break;
- case 3:
- p_ucAnyNumber=Zf816_3;
- break;
- case 4:
- p_ucAnyNumber=Zf816_4;
- break;
- case 5:
- p_ucAnyNumber=Zf816_5;
- break;
- case 6:
- p_ucAnyNumber=Zf816_6;
- break;
- case 7:
- p_ucAnyNumber=Zf816_7;
- break;
- case 8:
- p_ucAnyNumber=Zf816_8;
- break;
- case 9:
- p_ucAnyNumber=Zf816_9;
- break;
- case 10:
- p_ucAnyNumber=Zf816_nc;
- break;
- default: //如果上面的条件都不符合,那么默认指向空字模
- p_ucAnyNumber=Zf816_nc;
- break;
- }
- return p_ucAnyNumber; //返回转换结束后的指针
- }
- void lcd_display_service(void) //应用层面的液晶屏显示程序
- {
- static unsigned char ucAnyNumber_1; //分解变量的个位
- static unsigned char ucAnyNumber_10; //分解变量的十位
- static unsigned char ucAnyNumber_100; //分解变量的百位
- static unsigned char *p_ucAnyNumber_1; //经过数字转换成字模后,分解变量的个位字模首地址
- static unsigned char *p_ucAnyNumber_10; //经过数字转换成字模后,分解变量的十位字模首地址
- static unsigned char *p_ucAnyNumber_100; //经过数字转换成字模后,分解变量的百位字模首地址
- if(ucDisplayUpdate==1) //需要更新显示
- {
- ucDisplayUpdate=0; //及时把标志清零,避免一直处于不断更新的状态。
- if(ucAnyNumber>=100) //有3位数以上
- {
- ucAnyNumber_100=ucAnyNumber/100; //百位
- }
- else //否则显示空
- {
- ucAnyNumber_100=10; //在下面的转换函数中,代码10表示空字模
- }
- if(ucAnyNumber>=10) //有2位数以上
- {
- ucAnyNumber_10=ucAnyNumber%100/10; //十位
- }
- else //否则显示空
- {
- ucAnyNumber_10=10; //在下面的转换函数中,代码10表示空字模
- }
- ucAnyNumber_1=ucAnyNumber%10/1; //个位
- p_ucAnyNumber_100=number_to_matrix(ucAnyNumber_100); //把数字转换成字模首地址
- p_ucAnyNumber_10=number_to_matrix(ucAnyNumber_10); //把数字转换成字模首地址
- p_ucAnyNumber_1=number_to_matrix(ucAnyNumber_1); //把数字转换成字模首地址
- clear_all_canvas(); //把画布全部清零
- insert_buffer_to_canvas(0,0,p_ucAnyNumber_100,0,1,16);//把百位的字模插入画布
- insert_buffer_to_canvas(1,0,p_ucAnyNumber_10,0,1,16);//把十的字模插入画布
- insert_buffer_to_canvas(2,0,p_ucAnyNumber_1,0,1,16);//把个的字模插入画布
- display_lattice(3,24,ucCanvasBuffer,0,4,8,0); //显示上半屏的画布,最后的参数0是偏移量
- display_lattice(11,0,ucCanvasBuffer,0,4,8,32); //显示下半屏的画布,最后的参数32是偏移量
- }
- }
- void clear_all_canvas(void) //把画布全部清零
- {
- unsigned int j=0;
- unsigned int i=0;
- for(j=0;j<16;j++) //这里的16表示画布有16行
- {
- for(i=0;i<4;i++) //这里的4表示画布每行有4个字节
- {
- ucCanvasBuffer[j*4+i]=0x00;
- }
- }
- }
- void display_clear(unsigned char ucFillDate) // 清屏 全部显示空填充0x00 全部显示点阵用0xff
- {
- unsigned char x,y;
- WriteCommand(0x34); //关显示缓冲指令
- WriteCommand(0x34); //关显示缓冲指令 故意写2次,怕1次关不了 这个是因为我参考到某厂家的驱动程序也是这样写的
- y=0;
- while(y<32) //y轴的范围0至31
- {
- WriteCommand(y+0x80); //垂直地址
- WriteCommand(0x80); //水平地址
- for(x=0;x<32;x++) //256个横向点,有32个字节
- {
- LCDWriteData(ucFillDate);
- }
- y++;
- }
- WriteCommand(0x36); //开显示缓冲指令
- }
- /* 注释四:
- * 把字模插入画布的函数.
- * 这是本节的核心函数,读者尤其要搞懂x_amount和y_amount对应的显示关系。
- * 第1,2个参数x,y是在画布中的坐标体系。
- * x的范围是0至3,因为画布的横向只要4个字节。y的范围是0至15,因为画布的纵向只有16行。
- * 第3个参数*ucArray是字模的数组。
- * 第4个参数ucFbFlag是反白显示标志。0代表正常显示,1代表反白显示。
- * 第5,6个参数x_amount,y_amount分别代表字模数组的横向有多少个字节,纵向有几横。
- */
- void insert_buffer_to_canvas(unsigned int x,unsigned int y,const unsigned char *ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount)
- {
- unsigned int j=0;
- unsigned int i=0;
- unsigned char ucTemp;
- for(j=0;j<y_amount;j++)
- {
- for(i=0;i<x_amount;i++)
- {
- ucTemp=ucArray[j*x_amount+i];
- if(ucFbFlag==0)
- {
- ucCanvasBuffer[(y+j)*4+x+i]=ucTemp; //这里的4代表画布每一行只有4个字节
- }
- else
- {
- ucCanvasBuffer[(y+j)*4+x+i]=~ucTemp; //这里的4代表画布每一行只有4个字节
- }
- }
- }
- }
- /* 注释五:
- * 显示任意点阵函数.
- * 注意,本函数在前几节的基础上多增加了第7个参数uiOffSetAddr,它是偏移地址。
- * 对于这个函数,读者尤其要搞懂x_amount和y_amount对应的显示关系。
- * 第1,2个参数x,y是坐标体系。x的范围是0至15,y的范围是0至31.
- * 第3个参数*ucArray是字模的数组。
- * 第4个参数ucFbFlag是反白显示标志。0代表正常显示,1代表反白显示。
- * 第5,6个参数x_amount,y_amount分别代表字模数组的横向有多少个字节,纵向有几横。
- * 第7个参数uiOffSetAddr是偏移地址,代表字模数组的从第几个数据开始显示。
- */
- void display_lattice(unsigned int x,unsigned int y,const unsigned char *ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount,unsigned int uiOffSetAddr)
- {
- unsigned int j=0;
- unsigned int i=0;
- unsigned char ucTemp;
- //注意,要把以下两行指令屏蔽,否则屏幕在更新显示时会整屏闪动
- // WriteCommand(0x34); //关显示缓冲指令
- // WriteCommand(0x34); //关显示缓冲指令 故意写2次,怕1次关不了 这个是因为我参考到某厂家的驱动程序也是这样写的
- for(j=0;j<y_amount;j++) //y_amount代表y轴有多少横
- {
- WriteCommand(y+j+0x80); //垂直地址
- WriteCommand(x+0x80); //水平地址
- for(i=0;i<x_amount;i++) //x_amount代表x轴有多少列
- {
- ucTemp=ucArray[j*x_amount+i+uiOffSetAddr]; //uiOffSetAddr是字模数组的偏移地址
- if(ucFbFlag==1) //反白显示
- {
- ucTemp=~ucTemp;
- }
- LCDWriteData(ucTemp);
- // delay_short(30000); //把上一节这个延时函数去掉,加快刷屏速度
- }
- }
- WriteCommand(0x36); //开显示缓冲指令
- }
- void SendByteToLcd(unsigned char ucData) //发送一个字节数据到液晶模块
- {
- unsigned char i;
- for ( i = 0; i < 8; i++ )
- {
- if ( (ucData << i) & 0x80 )
- {
- LCDSID_dr = 1;
- }
- else
- {
- LCDSID_dr = 0;
- }
- LCDCLK_dr = 0;
- LCDCLK_dr = 1;
- }
- }
- void SPIWrite(unsigned char ucWData, unsigned char ucWRS) //模拟SPI发送一个字节的命令或者数据给液晶模块的底层驱动
- {
- SendByteToLcd( 0xf8 + (ucWRS << 1) );
- SendByteToLcd( ucWData & 0xf0 );
- SendByteToLcd( (ucWData << 4) & 0xf0);
- }
- void WriteCommand(unsigned char ucCommand) //发送一个字节的命令给液晶模块
- {
- LCDCS_dr = 0;
- LCDCS_dr = 1;
- SPIWrite(ucCommand, 0);
- delay_short(90);
- }
- void LCDWriteData(unsigned char ucData) //发送一个字节的数据给液晶模块
- {
- LCDCS_dr = 0;
- LCDCS_dr = 1;
- SPIWrite(ucData, 1);
- }
- void LCDInit(void) //初始化 函数内部包括液晶模块的复位
- {
- LCDRST_dr = 1; //复位
- LCDRST_dr = 0;
- LCDRST_dr = 1;
- }
- void delay_short(unsigned int uiDelayShort) //延时函数
- {
- unsigned int i;
- for(i=0;i<uiDelayShort;i++)
- {
- ;
- }
- }
- void delay_long(unsigned int uiDelayLong)
- {
- unsigned int i;
- unsigned int j;
- for(i=0;i<uiDelayLong;i++)
- {
- for(j=0;j<500;j++) //内嵌循环的空指令数量
- {
- ; //一个分号相当于执行一条空语句
- }
- }
- }
总结陈词:
有了这一节的基础,我们继续循序渐进,下一节将会讲到液晶屏的菜单程序。欲知详情,请听下回分解-----在1个窗口里通过移动光标来设置不同参数的液晶屏菜单程序。
(未完待续,下节更精彩,不要走开哦)