最新12864LCD菜单程序
最新12864LCD与4*4键盘人机交互界面源程序 0.95版
[ 2006-12-18 15:36:00 | By: ZZM@HAS ]
有朋友问我要源程序,这个源程序是我最近修改过的最新版本的12864与4*4
键盘的人机交互界面,我只给出关键交互界面菜单的源程序,至于12864和4*4键盘的驱动,相信大家都有了。
最新版本的使用反白表示选择行,增加了详细的注释。给需要的朋友一个参考。希望朋友们能写出更好的菜单程序。
//----------------------------------------
// 12864M四行多级菜单
// 文件名称12864menu1。c
// 作者张子墨
// 版本 0.95
// 最后更新2006/12/16
//----------------------------------------
#i nclude
#i nclude "12864Driver.c"
#define MENULEVEL 7 //当前菜单表个数
#define OPTIONMETE 29 //当前选项个数,包括一个特别功能选项
//-------------------------------------
//
// 菜单结构定义
//
//-------------------------------------
struct Option{
unsigned char KeyLevel; //菜单选项所属菜单表号
unsigned char EnterIndex; //选项进入索引号
unsigned char CancelIndex; //选项退出索引号
unsigned char KeyWord[16]; //菜单选项文字描述数组
unsigned char WordMete; //菜单选项描述文字字节数
};
//-------------------------------------
//
// 具体菜单选项定义,定义一个结构数组
// 存储在编码区,节省内存RAM
//
//-------------------------------------
struct Option code sOption[OPTIONMETE] = {
{0,9,0,"文本功能",8}, //0
{0,16,1,"时间功能",8}, //1
{0,19,2,"温度功能",8}, //2
{0,OPTIONMETE-1,3,"系统检测",8}, //3
{0,OPTIONMETE-1,4,"修改密码",8}, //4
{0,OPTIONMETE-1,5,"帮助说明",8}, //5
{0,OPTIONMETE-1,6,"作者介绍",8}, //6
{0,OPTIONMETE-1,7,"系统版本",8}, //7
{0,OPTIONMETE-1,8,"退出系统",8}, //8
{1,OPTIONMETE-1,0,"输入显示向导",12}, //9
{1,OPTIONMETE-1,0,"新建显示文本",12}, //10
{1,OPTIONMETE-1,0,"文本发送显示",12}, //11
{1,OPTIONMETE-1,0,"删除已存文本",12}, //12
{1,14,0,"输入法设定",10}, //13
{2,OPTIONMETE-1,13,"输入法选择",10}, //14
{2,OPTIONMETE-1,13,"输入法排序",10}, //15
{3,OPTIONMETE-1,1,"设定本机时间",12}, //16
{3,OPTIONMETE-1,1,"发送本机时间",12}, //17
{3,OPTIONMETE-1,1,"与计算机同步时间",16}, //18
{4,23,2,"显示当前温度",12}, //19
{4,OPTIONMETE-1,2,"存储当前温度",12}, //20
{4,OPTIONMETE-1,2,"显示已存温度",12}, //21
{4,OPTIONMETE-1,2,"删除已存温度",12}, //22
{5,25,18,"本机显示",8}, //23
{5,OPTIONMETE-1,19,"子机显示",8}, //24
{6,OPTIONMETE-1,23,"LED 显示",8}, //25
{6,OPTIONMETE-1,23,"数码管显示",10}, //26
{6,OPTIONMETE-1,23,"同时显示",8}, //27
{0,0,0,"",0} //28
};
//--------------------------------------
//
// 菜单表分类数组
//
//--------------------------------------
unsigned char code Level[MENULEVEL][3] = {
{0,8,9}, //每层表单对应开始结束索引号以及选项数目 {9,13,5},
{14,15,2},
{16,18,3},
{19,22,4},
{23,24,2},
{25,27,3}
};
unsigned char FirstLineDisIndex = 0; //屏幕第一行显示的索引号unsigned char SelectLine = 1; //当前选择的行
unsigned char SelectIndex = 0; //当前选定行对应的索引号
unsigned char LastIndex = 0; //进入功能函数前的索引号,判断具体功能使用
//----------------------------------------
//
// 刷新菜单功能函数
// 函数原形:void DIS12864M (void);
// 没有参数,没有返回值
// 调用方式:void DIS12864M (void);
//
//----------------------------------------
void DIS12864M (void)
{
unsigned char i,LineMete =
Level[sOption[SelectIndex].KeyLevel][2]; //循环量,显示行数
SentAIns(0x30); //进入液晶普通指令模式
SentAIns(0x01); //清屏
SetALineReverse (SelectLine); //填充所需反白
do //分别显示各行菜单项
{
SentAIns(0x80);
for(i=0;i SentAData(sOption[FirstLineDisIndex].KeyWord[i]); if(--LineMete == 0) break; SentAIns(0x90); for(i=0;i SentAData(sOption[FirstLineDisIndex+1].KeyWord[i]); if(--LineMete == 0) break; SentAIns(0x88); for(i=0;i SentAData(sOption[FirstLineDisIndex+2].KeyWord[i]); if(--LineMete == 0) break; SentAIns(0x98); for(i=0;i SentAData(sOption[FirstLineDisIndex+3].KeyWord[i]); }while(0); } //---------------------------------------- // // 判断当前索引是否是表单第一项功能函数 // 函数原形:bit MenuLevelStart (void); // 没有参数,返回Bit标志,是第一项返回1,不是返回0 // 调用方式:bit MenuLevelStart (void); // //---------------------------------------- bit MenuLevelStart (void) //判断当前索引是否为当前层第一个选项{ unsigned char i = MENULEVEL; do { i--; if(SelectIndex == Level[i][0]) return 1; }while(i); return 0; } //---------------------------------------- // // 判断当前索引是否是表单最后一项功能函数 // 函数原形:bit MenuLevelEnd (void); // 没有参数,返回Bit标志,是最后一项返回1,不是返回0 // 调用方式:bit MenuLevelEnd (void); // //---------------------------------------- bit MenuLevelEnd (void) { unsigned char i = MENULEVEL; do { i--; if(SelectIndex == Level[i][1]) return 1; }while(i); return 0; } //---------------------------------------- // // 菜单上移一项函数 // 函数原形:void MenuUpOneOption (void); // 没有参数,没有返回值 // 调用方式:void MenuUpOneOption (void); // //---------------------------------------- void MenuUpOneOption (void) { if(MenuLevelStart ()) //如果当前为表单第一项 { if(Level[sOption[SelectIndex].KeyLevel][2]>=4) //并且表单中选项数目大于等于4个 { FirstLineDisIndex = Level[sOption[SelectIndex].KeyLevel][1]-3; //第一行显示索引号为倒数第四项 SelectIndex = Level[sOption[SelectIndex].KeyLevel][1]; //选择索引为表单最后一项 SelectLine = 4; //标记选择行为第四行 DIS12864M (); //刷新屏幕显示 } else //如果选项数目并不大于四个 { SelectIndex = Level[sOption[SelectIndex].KeyLevel][1]; //选择索引为当前表单最后一个 SelectLine = Level[sOption[SelectIndex].KeyLevel][2]; //显示行表单数目(最后一个) DIS12864M (); //刷新屏幕显示 } } else //如果当前不是开始索引 { if(SelectLine==1) //并且已经在屏幕最上边一行 { FirstLineDisIndex--; //显示索引上移 SelectIndex--; //选择索引自减 SelectLine = 1; //选择行还是第一行 DIS12864M (); //刷新屏幕 } else //如果不是第一行 { SelectLine--; //选择行自减 SelectIndex--; //选择索引自减 DIS12864M (); //刷新屏幕显示 } } } //---------------------------------------- // // 菜单下移一项函数 // 函数原形:void MenuDownOneOption (void); // 没有参数,没有返回值 // 调用方式:void MenuDownOneOption (void); // //---------------------------------------- void MenuDownOneOption (void) { if(MenuLevelEnd ()) //如果当前是表单最后一个索引 { FirstLineDisIndex = Level[sOption[SelectIndex].KeyLevel][0]; //第一行显示索引为表单第一个选项 SelectIndex = Level[sOption[SelectIndex].KeyLevel][0]; //选择索引为表单第一个选项索引 SelectLine = 1; //选择行为第一行 DIS12864M (); //刷新显示 } else //如果不是最后的索引 { if(SelectLine!=4) //如果当前不是屏幕最底行 { SelectIndex++; //选择索引自加 SelectLine++; //选择行下移 SetALineReverse (SelectLine); //刷新选择行 DIS12864M (); //刷新显示 } else //如果是屏幕最低行 { FirstLineDisIndex++; //第一行显示下移 SelectIndex++; //选择索引自加 DIS12864M(); //刷新显示 } } } //---------------------------------------- // // 某一项功能函数实际应该改为实际功能函数 // //---------------------------------------- void ShutDown (void) { unsigned char code font[] = {"系统关闭"}; unsigned char i; SentAIns(0x01); SentAIns(0x92); for(i=0;i<8;i++) SentAData(font[i]); } void MenuCancelOption(void); void NoThisFunction (void) { unsigned char code Font[] = {"没有这个功能"}; unsigned int i,j; SentAIns(0x01); SentAIns(0x91); for(i=0;i<12;i++) SentAData(Font[i]); i = 65535; j = 10; do { do { i--; }while(i); j--; }while(j); MenuCancelOption(); } void TemperatureShow(void) { unsigned char code Font[] = {"测试温度:"}; unsigned int i,j; TempConvert (); SentAIns(0x01); SentAIns(0x90); for(i=0;i<10;i++) SentAData(Font[i]); for(i=0;i<6;i++) SentAData(TempFont[i]); i = 65535; j = 10; do { { i--; }while(i); j--; }while(j); MenuCancelOption(); } //---------------------------------------- // // 具体功能散转函数 // 函数原形:void FunctionAction // 没有参数,没有返回值 // 调用方式:void FunctionAction (void); // //---------------------------------------- void FunctionAction (void) { switch (LastIndex) //根据进入前的索引判断具体函数 { case 8: ShutDown(); break; case 5: TemperatureShow(); break; default: NoThisFunction(); break; //如果没有具体操作,显示没有这个功能 } } //---------------------------------------- // // 进入某项功能函数 // 函数原形:void MenuEnterOption (void); // 没有参数,没有返回值 // 调用方式:void MenuEnterOption (void); // //---------------------------------------- void MenuEnterOption (void) { LastIndex = SelectIndex; //标记进入前的索引号(以便判断具体功能) SelectIndex = sOption[SelectIndex].EnterIndex; //更新选择索引为之前索引号对应进入索引 if(SelectIndex != OPTIONMETE-1) //如果当前索引不是功能选择索 { FirstLineDisIndex = Level[sOption[SelectIndex].KeyLevel][0]; //第一行显示为进入表单第一项 SelectLine = 1; //设定第一行为选择行 DIS12864M (); //刷新菜单 } else FunctionAction (); //如果是功能选择项,进入功能分支判断函数 } //---------------------------------------- // // 菜单退出功能函数 // 函数原形:void MenuCancelOption (void); // 没有参数,没有返回值 // 调用方式:void MenuCancelOption (void); // //---------------------------------------- void MenuCancelOption (void) { if(SelectIndex != OPTIONMETE-1) //如果不是从功能返回 SelectIndex = sOption[SelectIndex].CancelIndex; //选择索引为选项返回索引 else //如果是从功能返回 SelectIndex = LastIndex; //索引等于进入前保存索引 if(Level[sOption[SelectIndex].KeyLevel][2]>=4) //如果返回表单选项数目大于4个 { if(SelectIndex > Level[sOption[SelectIndex].KeyLevel][1]-3) //根据返回选项确定显示首项 { FirstLineDisIndex = Level[sOption[SelectIndex].KeyLevel][1]-3; SelectLine = 4-(Level[sOption[SelectIndex].KeyLevel][1]-SelectIndex); DIS12864M (); //刷新显示 } else //一般显示方式 { FirstLineDisIndex = SelectIndex; //第一行显示索引 SelectLine = 1; //选择第一行 DIS12864M (); //刷新菜单 } else //如果返回表单选项数目不足4个 { FirstLineDisIndex = Level[sOption[SelectIndex].KeyLevel][0]; //第一行显示索引为表单第一项 SelectLine = SelectIndex - Level[sOption[SelectIndex].KeyLevel][0]+1; //选择行标志为当前选择索引对应行 DIS12864M (); //刷新菜单 } } //---------------------------------------- // // 菜单操作按键处理散转函数 // 函数原形:void KeyCodeAction (unsigned char KeyCode); // 键码作为参数,没有返回值 // 调用方式:void KeyCodeAction (unsigned char KeyCode); // 实际使用应根据自己的按键重新编写case项 // //---------------------------------------- void KeyCodeAction (unsigned char KeyCode) { switch (KeyCode) { case 'U': MenuUpOneOption(); break; //如果是向上按键,则菜单向上,以下类似 case 'D': MenuDownOneOption(); break; case 'E': MenuEnterOption(); break; case 'C': MenuCancelOption(); break; default: break; } } //---------------------------------------- // // 菜单更新函数 // 函数原形:void MenuReflash (void); // 没有参数,没有返回值 // 调用方式:void MenuReflash (void); // 实际应配合自己的按键编码获取程序使用 // //---------------------------------------- void MenuReflash (void) { unsigned char Keytemp; //保存按键编码变量 Keytemp = GetKeyCode(); //保存按键编码 if(Keytemp) //如果按键编码有效 { Keytemp = KeyCodeConvert (Keytemp); //根据按键编码获取按键说明字母 KeyCodeAction (Keytemp); //根据按键说明字母散转 } } void main (void) { LcdInit (); Init18b20(); DIS12864M (); while (1) MenuReflash (); } /********************************************************************************* 函数名:Draw_at_Anywhere; 函数说明:在任意位置显示任意大小的图形 参数说明:start_line--起始行(0-63);start_word--起始字(此处是以1个汉字宽为单位0-7共8个字) line_W:行宽(可任意);word_W:传过来的是一个汉字的整数倍(最大:起始字为0时word_W可为8); ***************************************************************************/ void Draw_at_Anywhere(unsigned char start_line, unsigned char start_word, unsigned char line_W, unsigned char word_W,unsigned char sg,const unsigned char *ptr) { unsigned char flag = 0; unsigned char line=0, col=0; unsigned char line_B = 0; //行边界,用于控制跨上下屏 unsigned char col_B = 0; //列边界;输入参数时要注意,是否会超出这2个边界 //程序本身没有对超越下界和右界加以限定 if(start_line > 31) //可输入的起始行范围0-63,一开始start_line>31则跳到下半屏 { start_line -= (31+1); //起始行>31则要转到下半屏显示 start_word += 8; //转到下半屏时相应的起始字也要变(上屏:0-7;下屏8-15参照GDRAM排列) } if(start_line > 63) { start_line = 0; start_word = 0; } line_B = start_line + line_W; col_B = start_word+word_W; wait(); Lcd_WriteCmd(0x34); //绘图之前必须关绘图 Lcd_WriteCmd(0x36); for(line=start_line; line { if(line > 31) //若start_line开始<31,但在自加过程中会超过31到下半屏{ flag = 1; line = line - (31+1); line_B = (line_B- 31-1); col_B = (start_word+8)+word_W; } if((line<=31) && (flag==0) ) col = start_word; else { col = start_word+8; } for(; col { Lcd_WriteCmd(0x80 | line); //必须要加小延时,要么就忙检测 Lcd_WriteCmd(0x80 | col); switch (sg) { case 0: Lcd_Writedata(0x00); //高8位 Lcd_Writedata(0x00); //低8位 break;//清零 case 1: Lcd_Writedata(0x7F); //高8位 Lcd_Writedata(0x00); //低8位 break;//游标 case 2: Lcd_Writedata(0x7F); //高8位*ptr++ Lcd_Writedata(0x7F); //低8位*ptr++ break;//启动 case 3: Lcd_Writedata(0xFF); //高8位 Lcd_Writedata(0xFF); //低8位 break;//反白 case 4: Lcd_Writedata(*ptr++); //高8位*ptr++ Lcd_Writedata(*ptr++); //低8位*ptr++ break;//写图 default:; } } } asm("NOP"); asm("NOP"); Lcd_WriteCmd(0x30); //还完到基本指令集 } 由LCD12864初探嵌入式系统设计 --菜单设计 声明:本文来自互联网,由于年代久远未能找到出处,现整理如下希望大家喜欢,如有侵权请联系 真的好想你QQ: 1320249827 前言往往要解释写文章的动机和原因,同时给作者一个正题以外灌水的机会——本文也不例外。 1、为什么我要写这篇文章。 不可否认,我的确受到了Armok 的利诱影响,但是最近发生的一些事情却使我觉得写这篇文章是非常有必要的。在OurA VR 上看到很多版本的LCD 驱动程序,几乎每一个版本都只是简单的将全部或部分的显示数据Cover 到LCD 的显存上,完成一个字或者是图片的显示就等着大家喊“牛”了。其实要走的路还很远。对一个工程项目来说,增加n 多的成本来提供一个点阵屏作为用户接口,不是一两幅欢迎图片和Now Loading...Please Standy By 的提示能糊弄的过去的。用户希望你提供的是友好的图形界面GUI ,虽然比不过XP 和Apple 的华丽,但是由各种基本图形组成的窗口界面还是需要的。 当我们真的想实现一个图形界面的时候,很快就会发现,我们需要的不仅仅是一个被喊了“牛”的初级驱动,我们需要的是一个图形引擎——一个自定义的图形函数包,没有DirectX 的华丽,但是能绘制一个任意的直线或是矩形就够了——结果往往发现无所适从。这个时候,我们遇到的就是一个门槛,真正的嵌入式工程师和一个业余电子爱好者之间的门槛。 2、我如何写这篇文章 考虑到本人老王卖瓜的习惯,所以请大家一定无比在吃饭前看本人写的技术文章,同时保持耐心等待续集(绝对有续集)。本人现单身,个人问题众多,学习任务重,所以可能有时候写文章象羊拉屎,不对大家 胃口,请见谅。 硬件平台:A VR Mega8级 LCD : 不带字库的12864 软件平台:ICC 规范: 符合基本的C 编程规范 3、何时开始正文 其实,本文应该算是计算机图形学的一个具体分支,所以,计算机图形学的基本要求就是本文的基本要求,考虑到各位兄弟的胃口,我就多罗嗦下。 h t t p :// h i .b a i d u .c o m /b a l l 648500361 本例程为通过用A T89C52芯片操作LCD12864显示的程序,使用的晶振为12M。 /********************************************************** 程序说明:LCD12864显示主程序 程序调试员:莫剑辉 调试时间:2010-6-7 **********************************************************/ #include ;实验目的:熟悉12864LCD的使用 ;12864LCD带中文字库 ;编程让12864LCD显示公司名称“深圳乾龙盛电子”,公司电话“0975”,公司传真“6”;硬件设置: ;关断所有拨码开关。 #include<> ;__CONFIG _DEBUG_OFF&_CP_ALL&_WRT_HALF&_CPD_ON&_LVP_OFF&_BODEN_OFF&_PWRTE_ON&_WDT_OFF&_H S_OSC ;芯片配置字,看门狗关,上电延时开,掉电检测关,低压编程关,加密,4M晶体HS振荡 #define RS PORTA,5 ;命令/数据选择 #DEFINE RW PORTA,4 ;读/写选择 #DEFINE E PORTA,3 ;使能信号 #DEFINE PSB PORTA,2 ;并口/串口选择(H/L) #DEFINE RST PORTA,0 ;复位信号 ;----------------------------------------------- LCD_X EQU 30H ;页地址 LCD_Y EQU 31H ;Y地址 COUNT EQU 32H ;循环计数用 COUNT1 EQU 33H ;循环计数用 COUNT2 EQU 34H ;循环计数用 POINT EQU 35H ;查表偏移地址 POINT1 EQU 36H ;查表偏移地址 POINT2 EQU 37H ;查表偏移地址 TEMP EQU 38H ;临时寄存器 TEMP1 EQU 39H ;临时寄存器 ;----------------------------------------------- ORG 0000H ;复位地址 NOP ;ICD需要的空指令 GOTO MAIN ;跳转到主程序 ;**********************主程序************************ MAIN BANKSEL TRISA CLRF TRISA ;A口输出 CLRF TRISD ;D口输出 BANKSEL ADCON1 MOVLW 06H MOVWF ADCON1 ;A口全为数字口 CLRF STATUS 51单片机综合学习 12864液晶原理分析1 辛勤学习了好几天,终于对12864液晶有了些初步了解~没有视频教程学起来真有些累,基本上内部程序写入顺序都是根据程序自我变动,然后逆向反推出原理…… 芯片:YM12864R P-1 控制芯片:ST7920A带中文字库 初步小结: 1、控制芯片不同,寄存器定义会不同 2、显示方式有并行和串行,程序不同 3、含字库芯片显示字符时不必对字符取模了 4、对芯片的结构地址一定要理解清楚 5、显示汉字时液晶芯片写入数据的顺序(即显示的顺序)要清楚 6、显示图片时液晶芯片写入数据的顺序(即显示的顺序)要清楚 7、显示汉字时的二级单元(一级为八位数据写入单元)要清楚 8、显示图片时的二级单元(一级为八位数据写入单元)要清楚 12864点阵液晶显示模块(LCM)就是由128*64个液晶显示点组成的一个128列*64行的阵列。每个显示点对应一位二进制数,1表示亮,0表示灭。存储这些点阵信息的RAM称为显示数据存储器。要显示某个图形或汉字就是将相应的点阵信息写入 到相应的存储单元中。图形或汉字的点阵信息由自己设计,问题的关键就是显示点在液晶屏上的位置(行和列)与其在存储器中的地址之间的关系。由于多数液晶显示模块的驱动电路是由一片行驱动器和两片列驱动器构成,所以12864液晶屏实际上是由左右两块独立的64*64液晶屏拼接而成,每半屏有一个512*8 bits显示数据RAM。左右半屏驱动电路及存储器分别由片选信号CS1和CS2选择。显示点在64*64液晶屏上的位置由行号(line,0~63)与列号(column,0~63)确定。512*8 bits RAM中某个存储单元的地址由页地址(Xpage,0~7)和列地址(Yaddress,0~63)确定。每个存储单元存储8个液晶点的显示信息。 在我们常用的人机交互显示界面中,除了数码管,LED,以及我们之前已经提到的LCD1602之外,还有一种液晶屏用的比较多。相信接触过单片机的朋友都知道了,那就是12864液晶。顾名思义,12864表示其横向可以显示128个点,纵向可以显示64个点。我们常用的12864液晶模块中有带字库的,也有不带字库的,其控制芯片也有很多种,如KS0108 T6963,ST7920等等。在这里我们以ST7920为主控芯片的12864液晶屏来学习如何去驱动它。(液晶屏采用金鹏的OCMJ4X8C) 关于这个液晶屏的更多信息,请参考它的DATASHEET,附件中有下载。 我们先来了解一下它的并行连接情况。 下面是电路连接图 从上面的图可以看出,液晶模块和单片机的连接除了P0口的8位并行数据线之外,还有RS,RW,E等几根线。其中R/S是指令和数据寄存器的选择控制线(串行模式下为片选),R/W 是读写控制线(串行模式下是数据线),E是使能线(串行模式下为时钟线)。 通过这几根控制线和数据线,再结合它的时序图,我们就可以编写出相应的驱动程序啦。 看看并行模式下的写时序图: 根据这个时序图,我们就可以写出写数据或者写命令到LCD12864液晶的子程序。 读时序图如下: 根据这个时序图我们就可以从LCD12864液晶模块内部RAM中读出相应的数据,我们的忙检测函数就是根据这个时序图写出来的。以及后面章节中讲的画点函数等都要用到读时序。有了这两个时序图,然后我们再看看OCMJ4X8C的相关指令集,就可以编写出驱动程序了。这里要注意的是指令集分为基本指令集和扩充指令集,其中扩充指令集主要是与绘图相关,在此后的章节中会有相应的介绍。 下面让我们根据这些编写出它的驱动程序吧。 我的硬件测试条件为:STC89C516(11.0592MHz) + OCMJ4X8C 实际显示效果图片如下: 程序部分如下,请结合液晶模块的DATASHEET看程序,这样能够更加快速的弄懂程序的流程。大致有如下几个函数:写数据,写指令,忙检测,初始化,指定地址显示字符串等等。[p][/p] #include "reg52.h" #include "intrins.h" sbit io_LCD12864_RS = P1^0 ; /****************************** 2012年5月19日 调试成功 编辑环境:ICCAVR 功能:用LCD12864显示汉字 ********************************/ #include 本例程为通过用AT89C52芯片操作LCD12864显示的程序,使用的晶振为12M。 /********************************************************** 程序说明:LCD12864显示主程序 程序调试员:莫剑辉 调试时间:2010-6-7 **********************************************************/ #include #include "includes.h" u8 key=0,hua; extern u32 Second; extern u32 minite,hour,day,k,month,year,shan; u8 xingqi[]={"一二三四五六日"}; u8 ModeFlag=0,wei=0;//液晶界面选择 int main(void) { u8 PasswordIndex=0;//密码数组索引 u8 Password[4]={" "};//存储密码数组 BSP_Init();//初始化外部资源 ModeFlag=0; while(1) { //////////////////////////////////////// 界面一////////////////////////////////// if(ModeFlag==0) { LCD12864_Waddr(1,3); LCD12864_WPoss(0,"WELCOME!"); LCD12864_Waddr(2,2); LCD12864_WPoss(0,"--欢迎使用--"); Delay_1ms(8000); LCD12864_Clear(); } //////////////////////////////////////// 界面二////////////////////////////////// // ModeFlag=0; ///// 改 while(ModeFlag==0) //进入登录界面 { LCD12864_Waddr(1,1); LCD12864_WPoss(0,"请输入登陆密码:"); LCD12864_Waddr(2,3); //LCD12864_WPoss(0," "); LCD12864_Waddr(3,1); LCD12864_WPoss(0,"密码为四位数字"); LCD12864_Waddr(4,1); LCD12864_WPoss(0,"删除*"); AT89s52-LCD12864多页菜单按键选择操 作 这篇程序的代码还未理解清楚,再一次深刻发现自 己的算法水平不行。今天学到的东西还行,就是那些C 文件的一些知识。反正都是些优化的东西,以后肯定也 用的着,过几天有兴趣的话,再去学习一下AVR单片机。 程序代码:硬件上与前面一样,有点变化就是多了 两个翻页的按键。到现在一整天的饭都还没吃,该去吃了,干这个果然不会困。 #include #include sbit RS = P2^4; sbit RW = P2^5; sbit E = P2^6; sbit PSB= P2^1; //串并口选择端并高串低 #define DataPort P0 sbit KEY_ADD=P3^3; //按键 sbit KEY_DEC=P3^4; unsigned char curr,currold;//全局变量,当前箭头位置 unsigned char code user16x16[]={ //箭头图片 0x00,0x00,0x20,0x00,0x30,0x00,0x38,0x00,0x3C,0x00 ,0x3E,0x00,0x3F,0x00,0x3F,0x80, 0x3F,0xC0,0x3F,0x80,0x3F,0x00,0x3E,0x00,0x3C,0x00 ,0x38,0x00,0x30,0x00,0x20,0x00, }; unsigned char code *MainMenu[]= { {" 1.设置1"}, {" 2.设置2"}, {" 3.设置3"}, {" 4.设置4"}, {" 5.设置5"}, {" 6.设置6"}, {" 7.设置7"}, {" 8.设置8"}, {" 9.设置9"}, 看到工具箱旁边那个LCD12864很久没用了(当初买回来用的时候只是简单地测试了一下),于是萌生了重新写一次接口程序的想法(而且这次要给它加个图片显示的功能),好,说做就做,就用Atmega16和ICCAVR来做吧,最近这MCU和平台用得比较熟练。 马上从书堆里把当初打印出来的中文datasheet给翻了出来,依葫芦画瓢地写了个初始化程序。好,OK。编译通过。于是又写了一个可以自定义从XY坐标值开始输出显示的函数,再次编译,也通过,OK。于是呼马上写了四行简单的字符烧到单片机上试了一下,嘿嘿,一次通过。如下图: 后来在进一步测试的时候也出了点小问题。就是我是使用USBISP烧写器把程序烧写进AVR的(此时实验板由USBISP烧写器供电),想要实现从第一行的第一个字符开始连续显示"0123456789"。刚烧写完程序后能看到LCD12864上正常显示"0123456789",但是把烧写器从实验板上断开连接,单独用USB给实验板供电的时候,LCD的第一行只是显示"123456789",第一个字符消失了……,左思右想地弄了一个多小时后,终于把问题给解决了,就是把初始化程序的延时适当增加了些,真是奇怪。刚开始一直想不通为什么在烧写器供电的情况下就正常显示,而换到USB供电后就出了问题。后来再想想,估计是跟供电有关。在使用USBISP烧写器供电的时候,LCD的背光灯明显比用USB供电的时候来得亮,而且对比度也高很多,看来是因为换到USB供电后,供电不怎么充足,以至于LCD在上电初始化的时候花上了更多的时间去初始化(因为供电低了,功率小了,跑起来有点力不从心,用的时间就久了嘛……我是觉得可以这样去理解的 接下来呢,就到了有点难度的画图了。当初刚买到12864的时候只是简单测试了字符显示功能,除了因为画图还不需要用到,另外一个原因就是那datasheet上关于画图那部分的内容不怎么看得懂…。现在重新拿起来看,依然一头雾水……。马上上网百度了一下“12864 7920 显示图片”,看到了不少的例子程序,可是……就是没看到有关于这部分功能实现的详细思路和讲解……下载下来的那些程序,基本上没注释,不是说晦涩难懂,但是至少看起来一团糟,让人家不想继续看下去……于是还是硬着头皮去啃那datasheet。上面对于画图这部分的内容是这样讲解的: 以下是RT12864引脚功能定义 引脚号 管脚 说明 1 Vss 电源地(0V)。 2 VDD 电源正(+5V)。 3 V0 LCD 驱动电压,应用时在VEE 与V0之间加一20K 可调电阻。 4 D/I 数据\指令选择: 高电平:数据D0-D7将送入显示RAM 低电平:数据D0-D7将送入指令寄存器执行。 5 R/W 读\写选择:高电平:读数据;低电平:写数据。 6 E 读写使能,高电平有效,下降沿锁定数据。 7 DB0 数据输入输出。 8 DB1 9 DB2 10 DB3 11 DB4 12 DB5 13 DB6 14 DB7 15 CS1 片选择信号,低电平时选择前64列。 16 CS2 片选择信号,低电平时选择后64列。 17 RET 复位信号,低电平有效。 18 VOUT LCD 驱动电源(-10V )。 19 LED+ 背光电源,LED+(+5V)。 20 LED- 背光电源,LED-(0V)。 以下是RT12864引脚功能定义 引脚号 管脚 说明 1 Vss 电源地(0V)。 2 VDD 电源正(+5V)。 3 V0 LCD驱动电压,应用时在VEE与V0之间加一20K可调电阻。4 D/I 数据\指令选择: 高电平:数据D0-D7将送入显示RAM 低电平:数据D0-D7将送入指令寄存器执行。 5 R/W 读\写选择:高电平:读数据;低电平:写数据。6 E 读写使能,高电平有效,下降沿锁定数据。7 DB0 数据输入输出。 8 DB1 9 DB2 10 DB3 11 DB4 12 DB5 13 DB6 14 DB7 15 CS1 片选择信号,低电平时选择前64列。 16 CS2 片选择信号,低电平时选择后64列。 17 RET 复位信号,低电平有效。 18 VOUT LCD驱动电源(-10V )。 19 LED+ 背光电源,LED+(+5V)。 20 LED- 背光电源,LED-(0V)。 基于STM32--LCD12864驱动程序 STM32 LCD12864驱动程序(头文件)(2012-05-29 21:25:08)转载▼ 标签:杂谈 #ifndef LCD12864_H #define LCD12864_H #define LCD_CONTROL GPIOD //默认LCD12864的控制口在PD口 #define LCD_DATAPORT GPIOD //默认LCD12864的数据口在PD口 #define LCD_RESET_Pin GPIO_Pin_12 //默认LCD12864的复位引脚连接到PD.12 也可不用 #define LCD_RS_Pin GPIO_Pin_13 //默认LCD12864 RS -- PD.13 #define LCD_RW_Pin GPIO_Pin_14 //默认LCD12864 RW -- PD.14 #define LCD_EN_Pin GPIO_Pin_15 //默认LCD12864 E -- PD.15 #define LCD_CONTROL_CLOCK RCC_APB2Periph_GPIOD //默认LCD12864的控制口时钟 #define LCD_DATAPORT_CLOCK RCC_APB2Periph_GPIOD //默认LCD12864的数据口时钟 #define LCD_RS_1 LCD_CONTROL->BSRR &=~LCD_RS_Pin;LCD_CONTROL->BSRR |=LCD_RS_Pin //RS置高电平 #define LCD_RS_0 LCD_CONTROL->BRR &=~LCD_RS_Pin;LCD_CONTROL->BRR |=LCD_RS_Pin //RS置低电平 #define LCD_RW_1 LCD_CONTROL->BSRR &=~LCD_RW_Pin;LCD_CONTROL->BSRR |=LCD_RW_Pin //RW置高电平 #define LCD_RW_0 LCD_CONTROL->BRR &=~LCD_RW_Pin;LCD_CONTROL->BRR |=LCD_RW_Pin //RW置低电平 #define LCD_EN_1 LCD_CONTROL->BSRR &=~LCD_EN_Pin;LCD_CONTROL->BSRR |=LCD_EN_Pin //EN置高电平 #define LCD_EN_0 LCD_CONTROL->BRR &=~LCD_EN_Pin;LCD_CONTROL->BRR |=LCD_EN_Pin //EN置低电平 用12864显示单色图片 首先介绍本12864液晶显示器: 型号:QC12864B 因为单片机读取的是数据,而不是直接的图片。得将图片进行取模,图片应该是单色图片,像素128*64。 下面我为大家介绍个实例。 ①、在电脑附件画图,首先设置属性 开始画图 保存文件,注意格式: ②、然后进行取模。 ③、编程: #include 51单片机+带字库液晶12864+DS1302数字时钟C源程序(无按键修改功能)过两天的搜索与调试,在别人程序的基础上,不断修改,终于调试成功了这个程序。目前还不能修改时间与日期,只是以预定时间以始。 适用于开发板:51单片机(AT89S52)+带字库液晶12864(ST7920)+DS1302(实时时钟) 实现功能:简单,数字时钟+日期(以后会不断完美)。 C语言源程序如下: #include LCD12864原理与应用 1、LCD12864简介: LCD12864分为两种,带字库的和不带字库的,不带字库的液晶显示汉字的时候可以选择自己喜欢的字体。而带字库的液晶,只能显示GB2312字体,当然也可以显示其他的字体,不过是用图片的形式显示。 下面介绍不带字库的LCD12864,以Proteus中的AMPIRE128×64为例,如下图所示,它的液晶驱动器为KS0108。 与带字库的液晶不同,此块液晶含有两个液晶驱动器,每块驱动器都控制64*64个点,分为左右两个屏幕显示,总共为128*64个点(即有128×64个点)。这就是为什么AMPIRE128*64有CS1和CS2两个片选端的原因。此液晶有8页,一页有8行点阵点,左右各64列,共128列。如下图所示: 2、LCD12864中的几条重要指令 (一)行(line)设置命令: 由此可见显示的起始行地址为0XC0,共64行,有规律地改变起始行号,可以实现滚屏效果。(二)页(page)设置指令: 起始页地址为0XB8,因为液晶有64行点,分为8页,每页就有8行点。 (三)列(column)地址设置指令 每块驱动器的列地址都是从0X40到0X7F,共64列,所以此液晶共有128列点。 (四)读状态指令 3、用LCD12864显示汉字(一) 由于这块液晶不带字库,我们就要自己编写字库,编写字库所用的字模提取软件为Zimo21(软件下载地址https://www.360docs.net/doc/e71993681.html,/),LCD1602显示自定义字符的时候也是用它。在取模之前我们要进行一些设定,根据此液晶的显示原理,设置为“纵向取模,字节倒序”,如下图所示:(若不是这样,则取模得到的数据不是我们想要的,将会出现乱码,同样可以在https://www.360docs.net/doc/e71993681.html,/下载到关于字模提取原理文档) 字体选择默认的“宋体,常规,小四号”,小四号为16*16大小,如下图所示: LCD12864图形液晶并口显示 【教学引入】 液晶屏,在生活中很常见,我们常见的液晶显示器,如电脑的显示器,电视机,手机等等。 液晶屏在生活中已得到了普遍应用,它显示个各种各样的画面。 【教学目标】 1、掌握LCD12864液晶屏的用法; 2、编写LCD12864液晶屏的指令代码; 【知识目标】 1、掌握LCD12864液晶屏的用法; 2、掌握LCD12864液晶屏指令代码; 【教学准备】 电脑、Proteus、Keil 【教学方法】 教法:讲授法、讨论法 学法:练习法、探究法 【教学课时】 四课时 【教学过程】 一、12864液晶介绍 (1)12864是128*64点阵液晶模块的点阵数简称,业界约定俗成的简称。12864点阵的屏显成本相对较低,适用于各类仪器,小型设备的显示领域。12864M汉字图形点阵液晶显示模块,可显示汉字及图形,内置8192个中文汉字(16X16点阵)、128个字符(8X16点阵)及64X256点阵显示RAM(GDRAM)。 12864引脚说明 查阅“12864M.PDF”12864M液晶显示模块技术手册——四、用户指令集 1、指令表1:(RE=0:基本指令表),如下图,讲解了12864的基本指令集和扩充指令集。 当模块在接受指令前,微处理器必须先确认模块内部处于非忙碌状态,即读取BF标志时BF需为0。“RE”为基本指令集与扩充指令集的选择控制位元,往后的指令集将维持在最后的状态。 当选择G=0 :绘图显示OFF,汉字显示的时,12864屏只能显示8X4=32个汉字,下面是汉字显示的坐标 二、12864液晶屏驱动电路 原件名称所属类(Category) 所属子类(Sub-category) AT89C52 Microprocessor ICs 8051 Family POT-HG Resistors Variable RESPACK-8 Resistors Resistor Packs LCD12864A 自制- AT89C52的P0口连接12864的并行数据口,RP1为P0口的上拉排阻。 三、52代码编写 (1)打开keil uVision4,建立一个新的工程,工程名为"12864 graphic LCD parallel display",保存类型*.uvproj,单片机型号AT89C52。在工程中添加12864 graphic LCD parallel display.c文件,如下图 编写者:董新凯2011.7.20 仅供交流 #include #include 由LCD12864初探嵌入式菜单设计(完全资料)
LCD12864显示程序
LCD12864显示程序
12864液晶显示图片原理(完整版)
玩转12864液晶(1)--显示字符
LCD12864写字符串程序及其头文件
LCD显示程序
12864并行多级菜单程序
AT89s52LCD12864多页菜单按键选择操作
12864显示图形
最完整的12864测试程序
基于STM32--LCD12864驱动程序
在12864显示任意图片及参考程序
51单片机+带字库液晶12864+DS1302数字时钟C源程序(无按键修改功能)
LCD12864原理与应用(源程序+原理图+proteus仿真)
LCD12864图形液晶并口显示
基于12864的菜单设计
12864液晶显示程序(图案+文字)