版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、轻松学51系列(作者:就抽精品)阿拉教你轻松学 51- 液晶篇(1602)1602 字符液晶在实际的产品中运用的也比较多了,前几天留意了一下,发现宿舍门前的自动售水机就是采用的1602 液晶进行显示的。而且对于单片机的学习而言, 掌握 1602的用法是每一个学习者必然要经历的过程。在此,我将使用 1602 过程中遇到的问题以及感受记录下来,希望能够给初学者带来一点指导,少走一点弯路。所谓 1602 是指显示的内容为 16*2,即可以显示两行,每行 16 个字符。目前市面 上字符液晶绝大多数是基于HD44780 液晶芯片的,控制原理是完全相同的,因此基于HD44780 写的控制程序可以很方便地应
2、用于市面上大部分的字符型液晶。背光,黑色字体)液晶的正面(绿色1602 液晶背面(绿色背光,黑色字体)背光白色字体字符型 LCD1602 通常有 14 条引脚线或 16 条引脚线的 LCD,多出来的 2 条线是背光电源线 VCC(15 脚)和地线 GND(16 脚),其控制原理与 14 脚的 LCD 完全一样,弓 I 脚定义如下表所示:引脚号引醉电平输入/输岀作用1Uss电源地2Ucc电源(+5U)3对比调整电压kRS10/1输入錯入数据5R/W0/1输入向LCD写入垣今或数据从LCD涙取宿魚6E2 十输入使能信号,1吋谖取信息.1-0(下降沿)齡指令7DB0输入/输岀数据总线口呢肌最低位)D
3、B1r0/1输入/输岀数据总线Hne19tB20/1输入/输出数据总Une210DB30/1输入#输岀数据总线lines11DB40/1输入/输出数据总线12D跖|输入瀚岀数据总线lines13DB60/1输入f输岀数据总线lnefiDB70/1输入#输岀数据总线lne7(最高位)15AI+UCC- -L3背光电源正极16K接地ILCD背光电源负极HD44780 内置了 DDRAM、CGROM 和 CGRAM。DDRAM 就是显示数据 RAM ,用来寄存待显示的字符代码。共 80 个字节,其地址和屏幕的对应关系如下表:显示位置234567DDRAM第一行Q3HQ2H03H&SH27H地
4、址第二行40HU2H43H44HU5H67H也就是说想要在 LCD1602 屏幕的第一行第一列显示一个 A字,就要向 DDRAM 的 00H 地址写入“ A 字的代码就行了。但具体的写入是要按LCD 模块的指令格式来进行的,后面我会说到的。那么一行可有 40 个地址呀?是的,在 1602 中我们就用前 16 个就行了。 第二行也一样用前 16 个地址。对应如下:DDRAM 地址与显示位置的对应关系(1:O0H 01H 02H 03H 64H眄H06H67H 08H餌H 6AH朋H 9CH ODH 0EH BFH:却却42H 43H屮45H46H7H 48H埠卯4AH 4BH4CH耳DHMEtf
5、 4FH:IJ(事实上我们往 DDRAM 里的 00H 地址处送一个数据,譬如0 x31(数字 1 的代码)并不能显示 1 出来。这是一个令初学者很容易出错的地方,原因就是如果你要想在 DDRAM的 00H 地址处显示数据,则必 须将 00H 加上 80H,即 80H,若要在 DDRAM 的 01H 处显示数据,则必须将 01H 加上 80H 即 81H (从 80H 开始到 9FH 开始才是空余的自 己编写空间其余的空间都有液晶自己的库文件。在CGRAM 中)。依次类推。大家看一下控制指令的的 8 条:DDRAM 地址的设定,即可以明白是怎么样的一回事了)1602 液晶模块内部的字符发生存储
6、器( CGROM)已经存储了 160 个不同的点阵字 符图形,如下表所示,这些字符有:阿拉伯数字、英文字母的大小写、常用的符号、和日文 假名等,每一个字符都有一个固定的代码,比如大写的英文字母“A 的代码是01000001B“A”(41H ),显示时模块把地址 41H 中的点阵字符图形显示出来,我们就能看到字母上表中的字符代码与我们 PC 中的字符代码是基本一致的。因此我们在向DDRAM写 C51 字符代码程序时甚至可以直接用P1 = A这样的方法。PC 在编译时就把 “A先转为41H 代码了。字符代码 0 x00OxOF 为用户自定义的字符图形RAM(对于 5X8 点阵的字符,可以存放8 组
7、,5X10 点阵的字符,存放 4 组),就是 CGRAM 了。后面我会详细说的。0 x200 x7F 为标准的 ASCII 码,0 xA00 xFF 为日文字符和希腊文字符,其M)41MIOM111OO1C141100111100410011010T51!HOCilOi11101111oxxOCOO0 3 PTXIPrcitfQGOl1 ITJTOIaH.|7|6|aXXXMOOIO砧MOO】noocOlDIQCCOItQ pi |icouiOCO M)WM1001忖)JKXK1011KKXKIIQQI (5)wxxlTOl苗)JOU-itiC汗)i讪IT(?)ri|2BR|b|r#|3|C|
8、S|c|sl$|4|D|T|d|t|5i|5|E!U|e|ul&|6|F|U|fMOTNJSIWIl9IIIVIi|y*|s|J|Z|J|z|+I5IKIC |k|(l丄klL|1111-|=|M|J|n| .|INIA|nWZfflH曰啊j|O|T|fe=?inizi3l|3|h|kPT*d亦|涯耳 引创神两J| V|71B创余字符码(0 x100 x1F 及 0 x80 0 x9F) 没有定义。那么如何对 DDRAM 的内容和地址进行具体操作呢,下面先说说HD44780 的指令集及其设置说明,请浏览该指令集,并找出对DDRAM 的内容和地址进行操作的指令。共11 条指令:1.清屏指
9、令攜令功能指令编码执行:/RSRSDB7DB3D02DB1DUU清屏01 11e00011.64功能: 清除液晶显示器,即将DDRAM 的内容全部填入空白”的 ASCII 码 20H; 光标归位,即将光标撤回液晶显示屏的左上方; 将地址计数器(AC)的值设为 0。2.光标归位指令指令功能指令编码RSDB7*DB5DB3DB10B05 冲/H5尤标归位0I#I*90Ct4X1,6*功能: 把光标撤回到显示器的左上方 把地址计数器(AC)的值设置为 0; 保持 DDRAM 的内容不变3.进入模式设置指令位名设置I/D0=写入新数据后光标左移1=写入新数据后光标右移0=写入新数据后显示屏不移动1=写
10、入新数据后显示屏整体右移1 个字4.显示开关控制指令指令功能II令编玛/USRSR/WDB7DB4DB5D8UDB1业示开关控制000091CB功能:控制显示器开/关、光标显示/关闭以及光标是否闪烁。参数设定的情况如下:位名设置D0=显示功能关1=显示功能开C0=无光标1=有光标B0=光标闪烁1=光标不闪烁5.设定显示屏或光标移动方向指令设定情况光标左移 1 格,且 AC 值减 1光标右移 1 格,且 AC 值加 1显示器上字符全部左移一格,但光标不动显示器上字符全部右移一格,但光标不动6.功能设定指令S/CR/L0 0位名设置DL0=数据总线为 4 位1=数据总线为 8 位N0=显示 1 行
11、1=显示 2 行F0=5X点阵/每字符仁 510 点阵/每字符7.设定 CGRAM 地址指令指令功能指令編码执行/USB/WDBADB5D站DB3DB20B1设CGRfiM地址00D1地址(6)40功能:设定下一个要存入数据的CGRAM 的地址。8.设定 DDRAM 地址指令指令功能指令编码执行/U5RSR/WDB7DB6DB5DB4DEI30B1DB0|设定ADRfiH地址001CGRAMfit地址(7)U0功能:设定下一个要存入数据的CGRAM 的地址。(注意这里我们送地址的时候应该是0 x80+Address,这也是前面说到写地址命令的时候要加上 0 x80 的原因)9.读取忙信号或 A
12、C 地址指令1抱令功能指令编码时同/usFtSR/WDB7DB6DBS j D时DB3DB2 DB1Dbn疇雛号1M內容灯m功能: 读取忙碌信号 BF 的内容,BF=1 表示液晶显示器忙,暂时无法接收单片机送 来的数据或指令;当 BF=O 时,液晶显示器可以接收单片机送来的数据或指令 读取地址计数器(AC)的内容。10.数据写入 DDRAM 或 CGRAM 指令一览指令功能指令编码I/USh/wDH70B6 I D65DB4DB30B2I站写入到1a共写入的数据tGRAM功能: 将字符码写入 DDRAM,以使液晶显示屏显示出相对应的字符 将使用者自己设计的图形存入CGRAM。11.从 CGRA
13、M 或 DDRAM 读出数据的指令一览指令功能指令编码/USwsR/UDEI 7tB6DBSDH4DB30H2&B1DK0出数据11要读岀的数据功能:读取 DDRAM 或 CGRAM 中的内容。基本操作时序:读状态输入:RS=L,RW=H ,E=H输出:DB0DB7= 状态字写指令输入:RS=L , RW=L , E=下降沿脉冲,DB0DB7=指令码输出:无读数据输入:RS=H , RW=H , E=H输出:DB0DB7=数据写数据输入:RS=H , RW=L , E=下降沿脉冲,DBODB7=数据输出:无呵呵,看到这么多的控制指令希望你没有头晕。其实这么多的指令刚开始的时候没有必要全
14、部掌握,随着学习的深入可以再尝试去用更复杂的控制指令。下面让我们一起驱动1602 的液晶吧。下面是我的液晶的连接图,用的是那种蓝底白字的液晶,其实蓝底白字和 那种绿底黑字的液晶唯一的区别就是颜色的问题,至于用哪种液晶,就看各位自己的喜好 咯。这就是我做测试用的最小系统,单片机是STC89C516 ,晶振为 12M。液晶为蓝底白字的那种 1602。/ / JrH当我们硬件连接错误, 或者程序错误时就会出现下图这种情况,就是上排显示 16 的白色的块(蓝底黑字的液晶则显示的是16 个黑块)。P2=com;II 把命令字送入 P2下面我们来驱动 1602 吧在 1602 的上排显示 “ LCD160
15、2 check ok下排显示“ study up 程序中没有用到忙检测,而是用的是延时函数来替代忙检测#include/包含头文件,这个嘛,就不用多说了-#defi ne uint un sig ned int /预定义一下ui nt x,y;for(x=n ;x0;x-)for(y=110;y0;y-);void lcd_wcom(uchar com) II1602rs=0;II 选择指令寄存器rw=0;II 选择写sbit rs=P3A5;II1602 的数据 I 指令选择控制线sbit rw=P3A6;II1602的读写控制线sbit en=卩3人7;II1602的使能控制线I*P2 口
16、接 1602的 D0D7 ,注意不要接错了顺序,我以前可在这上面吃过亏*Iuchar code table=LCD1602 check ok; /uchar code table1=study up;void delay(uint n)/ 延时函数II要显示的内容 2 放入数组 table1写命令函数#defi ne uchar un sig nedchar要显示的内容 1 放入数组 tableldelay(5);/ 延时一小会儿,让 1602 准备接收数据en=1;/ 使能线电平变化,命令送入 1602 的 8 位数据口en=0;void lcd_wdat(uchar dat)/1602 写
17、数据函数rs=1;/选择数据寄存器rw=0;/选择写P2=dat;/ 把要显示的数据送入 P2delay(5);/ 延时一小会儿,让 1602准备接收数据en=1;/使能线电平变化,数据送入1602 的 8 位数据口en=0;/1602 初始化函数/8 位数据,双列, 5*7 字形/ 开启显示屏,关光标,光标不闪烁/ 显示地址递增,即写一个数据后,显示位置右移一位/ 清屏void main() / 主函数uchar n,m=0;lcd_init();/ 液晶初始化lcd_wcom(0 x80);/ 显示地址设为 80H (即 00H ,)上排第一位for(m=0;m16;m+)/ 将 tabl
18、e 中的数据依次写入 1602 显示lcd_wdat(tablem); delay(200);lcd_wcom(0 x80+0 x44); / 重新设定显示地址为 0 xc4, 即下排第 5 位/ 将 table1 中的数据依次写入 1602 显示void lcd_init()lcd_wcom(0 x38);lcd_wcom(0 x0c);lcd_wcom(0 x06);lcd_wcom(0 x01);for(n=0;n8;n+)Icd_wdat(table1 n);delay(200);while(1);/动态停机程序写好后烧写进单片机,现在让我们看看效果吧下面让我们来看看如何显示一个自定义
19、的字符吧我们从 CGROM 表上可以看到,在表的最左边是一列可以允许用户自定义的CGRAM,从上往下看着是 16 个,实际只有 8 个字节可用。它的字符码是00000000 00000111 这 8 个地址,表的下面还有 8 个字节,但因为这个 CGRAM 的字符码规定 0 2 位为地址,3位无效,4 7 全为零。因此 CGRAM 的字符码只有最后三位能用也就是8个字节了。等效为 0000X111, X 为无效位,最后三位为 000 111 共 8 个。如果我们要想显示这 8 个用户自定义的字符,操作方法和显示CGROM 的一样,先设置 DDRAM 位置,再向 DDRAM 写入字符码,例如“A
20、就是 41H。现在我们要显示 CGRAM 的第一个自定义字符,就向DDRAM 写入 00000000B(00H),如果要显示第 8 个就写入00000111(08H),简单吧!好!现在我们来看怎么向这八个自定义字符写入字模。有个设置CGRAM 地址的指这就是显示的效果,你做成功了吗?令大家还记得吗?赶快再找出来看看。拾令功能指令编码/USRSDO7DBADBSOB4DB3DB2- i-DB-fIBSWa1CRRftHffi地址W位)40地址从这个指令可以看出指令数据的高2 位已固定是 01,只有后面的 6 位是地址数据,而这 6 位中的高 3 位就表示这八个自定义字符,最后的3 位就是字模数据
21、的八个地址了。下面我们写一段程序让这8 个自定义字符显示出一个心的图案:(由于上面那个显示程序已经有很详细的注释了,因此这个程序只对与上个程序不同的地方写注释)#in clude#defi ne uint un sig ned int#defi ne uchar un sig ned charsbit rs=P3A5;sbit rw=P3A6;sbit en=卩3人7;uchar code table=0 x03,0 x07,0 x0f,0 x1f,0 x1f,0 x1f,0 x1f,0 x1f,0 x18,0 x1E,0 x1f,0 x1f,0 x1f,0 x1f,0 x1f,0 x1f,0
22、 x07,0 x1f,0 x1f,0 x1f,0 x1f,0 x1f,0 x1f,0 x1f,0 x10,0 x18,0 x1c,0 x1E,0 x1E,0 x1E,0 x1E,0 x1E,节写入字模数据,让它能显示出地址:01000000数据:00010000010000010000011001000010000010010100001100001000010001000000100001000101000010010100011000000110010001110000000001000111 八个地址。我们向这 8 个字图示:OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
23、OOOOOOOOOOOOOOOO例如第一个自定义字符的字模地址为010000000 x0f,0 x07,0 x03,0 x01,0 x00,0 x00,0 x00,0 x00,0 x1f,0 x1f,0 x1f,0 x1f,0 x1f,0 x0f,0 x07,0 x01,0 x1f,0 x1f,0 x1f,0 x1f,0 x1f,0 x1c,0 x18,0 x00,0 x1c,0 x18,0 x10,0 x00,0 x00,0 x00,0 x00,0 x00;/*uchar code table1=0 x10,0 x06,0 x09,0 x08,0 x08,0 x09,0 x06,0 x00;
24、/符c*/void delay(uint n)uint x,y;for(x=n;x0;x-)for(y=110;y0;y-);void lcd_wcom(uchar com)rs=0;rw=0;P2=com;delay(5);en=1;en=0;void lcd_wdat(uchar dat)rs=1;rw=0;P2=dat;delay(5);en=1;en=0;void lcd_init()心图案字lcd_wcom(0 x38);lcd_wcom(0 x0c);lcd_wcom(0 x06);lcd_wcom(0 x01);void main()char m=0;lcd_init();lcd
25、_wcom(0 x40);for(m=0;m64;m+)lcd_wdat(tablem);lcd_wcom(0 x85); /for(m=0;m4;m+)lcd_wdat(m);lcd_wcom(0 xc5); /for(m=4;m KeyOverTime )/按键时间要/不是第一KeyCount = 0 ;KeyOverTime = KEY_QUICK_TIME ;return NOKEY ;else /是第一次按下则保存键值,以便下次执行此函数时与读到的键值作比较LastKey = KeyTemp ; /保存第一次读到的键值KeyCount = 0 ; /延时计数器清零KeyOverTim
26、e = KEY_OVER_TIME ;return NOKEY ;下面是我测试用的主程序(相关头文件未列出,仅仅作测试演示用)void main(void)uint8 KeyValue ;int16 Count ;v_LcdInit_f() ;v_KeyInit_f() ;CLSLOCATE(3, 1)PRINT(Key Test)LOCATE(6, 2)SHOW_ICONwhile(1)KeyValue = u8_ReadKey_f() ;if( KeyValue != NOKEY )LOCATE(1, 2)if( KeyValue = 0 x0e )Count+ ;if( KeyValue
27、 = 0 x0d )Count- ;if( KeyValue = 0 x0b )Count = 0 ;if( KeyValue = 0 x07 )Count = 0 ;HIDE_ICONPRINTD(Count, 5)LOCATE(6, 2)else/SHOW_ICON每次执行读键盘函数时,只是对一些标志进行判断,然后退出。因此能够充分的利用CPU的资源。同时可 以处理连发按键。此按键扫描按键函数可以直接放在主函数中。如果感觉按键太过灵敏或者迟钝则改一下 相关消抖动的宏定义即可。此函数也可以通过中断标志位进行定时的扫描。此时,需要添加一个定时标志 位,并将相关消抖动的和连击时间的宏定义改小即可
28、。然后在主程序类似下面这样写即可if( KeyTime ) /定时扫描时间到KeyValue = u8_ReadKey_f() ;具体的工作就交给您去完成啦。看看效果:按键单击连发时候的截图至此,关于单个按键的学习就告一段落了,您是否已经明白了。如果您还不明白,那么把这个程序好好的 看看,并画下流程图,分析分析。估计您就会恍然大悟。关键是思路要转换过来。下面我们来看看多个按键的情况吧一般情况下,如果多个按键每个都直接接在单片机的I/O上的话会占用很多的I/O资源。比较合理的一种 做法是, 按照行列接成矩阵的形式。 按键接在每一个的行列的相交处。 这样对于m行n列的矩阵, 可以接 的按键总数是m
29、*n。这里我们以常见的4*4矩阵键盘来讲解矩阵键盘的编程。上图就是矩阵键盘的一般接法*此函数需要定时器的支持(去抖)*51单片机特性决定的)/* *模块内相关的宏定义及常数宏*/*此模块所需的全局或者外部变量*/bit bdata StartScan = 0 ;/此变量需放在定时中断中置位/*按键扫描函数,按下去后经去抖,确定按下*则返回键值015;无键按下则返回0 xff ;*这里我们要介绍一种快速的键盘扫描法:线反转法(或者称为行列翻转法)。具体流程如下。首先,让单片机的行全部输出0,列全部输出1,读取列的值(假设行接P3口的高四位,列接低四位)。即P3= OxOf ;此时读列的值,如果有
30、键按下,则相应的列读回来的值应该为低。譬如此时读回来的值为0 x0e ;即按键列的位置已经确定。这时反过来,把行作为输入,列作为输出,即P0 = 0 xf0 ;此时再读行的值,如果按键仍然被按下,则相应的行的值应该为低,如果此时读回来的值为0 xe0,则确定了行的位置 。说到这里,您应该笑了,知道了一个按键被按下的行和列的位置,那么就可以肯定确定它的位置了。我们把读回来的行值和列值进行或运算。即0 xe0 | 0 x 0e即0 xee。那么0 xee就是我们按下的按键的键值了。怎么样。只需几步就可以判断所有的键值,简单吧。下面再结合一个例子具体看看。/*此模块所需相关支持库*/#include
31、regx52.h#define uint8 unsigned char#define uint16 unsigned int/*与硬件连接相关的定义及宏定义和操作宏*/#define KEYBOARDP3/键盘连接到单片机上的端口位置#define READ_ROW_ENLABLEKEYBOARD = 0 x0f ;/读端口之前先把相应口置位(由基本#define READ_COL_ENLABLEKEYBOARD = 0 xf0 ;/根据实际硬件连接情况修改#define NOKEY0 xff /定义无键按下时的返回值#define DELAY_COUNT 2/消抖时间常数*uint8 u8_
32、KeyBoardScan_f()static uint8 DelayCount = 0 ;uint8 KeyValueRow = 0 ;uint8 KeyValueCol = 0 ;uint8 KeyValue = 0 ;if( StartScan ) /开始扫描,StartScan在定时中断中置位StartScan = 0 ;/清除开始扫描标志位,避免多次重复执行扫描程序/读入按键状态前先向相应端口写1(由基本51单片机硬件结构决定) READ_ROW_ENLABLEif( ( KEYBOARD & 0 x0f ) != 0 x0f ) /判断是否有键按下/有键按下则判断延时去抖的时
33、间是否达到/再次判断是否按键真的按下/确定按下后,延时去抖计时器清0KeyValueRow = KEYBOARD & 0 x0f ; /取得行码/准备读列,先向相应端口写1(由基本51单片机硬件结构决定)READ_COL_ENLABLEif ( (KEYBOARD & 0 xf0) != 0 xf0 ) /KeyValueCol = KEYBOARD & 0 xf0 ;/取得列码/合并取得的行码和列码,即是相应按键的键值switch( KeyValueCol | KeyValueRow)case 0 x77 : KeyValue = 0 ; break ;case 0
34、xb7 : KeyValue = 1 ; break ;case 0 xd7 : KeyValue = 2 ; break ;case 0 xe7 : KeyValue = 3 ; break ;case 0 x7b : KeyValue = 4 ; break ;case 0 xbb : KeyValue = 5 ; break ;DelayCount+;if( DelayCount KeyOverTime )KeyCount = 0 ;KeyOverTime = KEY_QUICK_TIME ;return NOKEY ;else /是第一次按下则保存键值,以便下次执行此函数时与读到的键值
35、作比较LastKey = KeyTemp ;/保存第一次读到的键值KeyCount = 0 ; /延时计数器清零KeyOverTime = KEY_OVER_TIME ;return NOKEY ;下面是我测试用的主程序(相关头文件未列出,仅仅作测试演示用) void main(void)uint8 KeyValue ;int16 Count ;v_LcdInit_f() ;v_KeyInit_f() ;CLSLOCATE(3, 1)PRINT(Key Test)LOCATE(6, 2)/不是第一SHOW_ICONwhile(1)KeyValue = u8_ReadKey_f() ;if( K
36、eyValue != NOKEY )LOCATE(1, 2)if( KeyValue = 0 x0e )Count+ ;if( KeyValue = 0 x0d )Count- ;if( KeyValue = 0 x0b )Count = 0 ; if( KeyValue = 0 x07 )Count = 0 ;HIDE_ICONPRINTD(Count, 5)LOCATE(6, 2)else/SHOW_ICON每次执行读键盘函数时,只是对一些标志进行判断,然后退出。因此能够充分的利用CPU的资源。同时可以处理连发按键。此按键扫描按键函数可以直接放在主函数中。如果感觉按键太过灵敏或者迟钝则改一
37、下相关消抖动的宏定义即可。此函数也可以通过中断标志位进行定时的扫描。此时,需要添加一个定时标志位,并将相关消抖动的和连击时间的宏定义改小即可。然后在主程序类似下面这样写即可KeyValue = u8_ReadKey_f();具体的工作就交给您去完成啦。看看效果:按键单击至此,关于单个按键的学习就告一段落了,您是否已经明白了。如果您还不明白,那么把这个程序好好的看看,并画下流程图,分析分析。估计您就会恍然大悟。关键是思路要转换过来。下面我们来看看多个按键的情况吧一般情况下,如果多个按键每个都直接接在单片机的I/O上的话会占用很多的I/O资源。比较合理的一种if( KeyTime )/定时扫描时间
38、到做法是,按照行列接成矩阵的形式。 按键接在每一个的行列的相交处。这样对于m行n列的矩阵,可以接的按键总数是m*n。这里我们以常见的4*4矩阵键盘来讲解矩阵键盘的编程。上图就是矩阵键盘的一般接法。这里我们要介绍一种快速的键盘扫描法:线反转法(或者称为行列翻转法)。具体流程如下。首先,让单片机的行全部输出0,列全部输出1,读取列的值(假设行接P3口的高四位,列接低四位)。即P3= OxOf ;此 时读列的值,如果有键按下,则相应的列读回来的值应该为低。譬如此时读回来的值为OxOe ;即按键列的位置已经确定。这时反过来,把行作为输入,列作为输出,即P0 = OxfO ;此时再读行的值,如果按键仍然
39、被按下,则相应的行的值应该为低,如果此时读回来的值为OxeO,则确定了行的位置。说到这里,您应该笑了,知道了一个按键被按下的行和列的位置,那么就可以肯定确定它的位置了。我们把读回来的行值和列值进行或运算。即OxeO | Ox Oe即Oxee。那么Oxee就是我们按下的按键的键值了。怎么样。只需几步就可以判断所有的键值,简单吧。下面再结合一个例子具体看看。/*此模块所需相关支持库*/#includeregx52.h#define uint8 unsigned char#define uint16 unsigned int/*与硬件连接相关的定义及宏定义和操作宏*/键盘连接到单片机上的端口位置KE
40、YBOARD = 0 x0f ;/读端口之前先把相应口置位(由基本KEYBOARD = 0 xf0 ;/根据实际硬件连接情况修改/*模块内相关的宏定义及常数宏*/#define NOKEY 0 xff /定义无键按下时的返回值#define DELAY_COUNT 2 /消抖时间常数*此模块所需的全局或者外部变量*/*按键扫描函数,按下去后经去抖,确定按下*则返回键值015;无键按下则返回0 xff ; *此函数需要定时器的支持(去抖 .) */uint8 u8_KeyBoardScan_f()static uint8 DelayCount = 0 ;uint8 KeyValueRow = 0
41、 ;uint8 KeyValueCol = 0 ;uint8 KeyValue = 0 ;if( StartScan ) /开始扫描,StartScan在定时中断中置位StartScan = 0 ;/清除开始扫描标志位,避免多次重复执行扫描程序/读入按键状态前先向相应端口写1(由基本51单片机硬件结构决定)READ_ROW_ENLABLEif( ( KEYBOARD & 0 x0f ) != 0 x0f ) /判断是否有键按下DelayCount+;if( DelayCount = DELAY_COUNT )/有键按下则判断延时去抖的时间是否达到returnNOKEY ;#define
42、 KEYBOARD P3#define READ_ROW_ENLABLE51单片机特性决定的)#define READ_COL_ENLABLEbit bdata StartScan = 0 ;/此变量需放在定时中断中置位else /消除了抖动if( ( KEYBOARD & 0 x0f ) != 0 x0f )/再次判断是否按键真的按下DelayCount = 0 ;/确定按下后,延时去抖计时器清0KeyValueRow = KEYBOARD & 0 x0f ; /取得行码/准备读列,先向相应端口写1(由基本51单片机硬件结构决定) READ_COL_ENLABL Eif (
43、(KEYBOARD & 0 xf0) != 0 xf0 ) /反转,读列码KeyValueCol = KEYBOARD & 0 xf0 ;/取得列码/合并取得的行码和列码,即是相应按键的键值switch( KeyValueCol | KeyValueRow)case 0 x77 : KeyValue = 0 ; break ;case 0 xb7 : KeyValue = 1 ; break ;case 0 xd7 : KeyValue = 2 ; break ;case 0 xe7 : KeyValue = 3 ; break ;case 0 x7b : KeyValue =
44、 4 ; break ;case 0 xbb : KeyValue = 5 ; break ;case 0 xdb : KeyValue = 6 ; break ;case 0 xeb : KeyValue = 7 ; break ;case 0 x7d : KeyValue = 8 ; break ;case 0 xbd : KeyValue = 9 ; break ;case 0 xdd : KeyValue = 10 ;break ;case 0 xed : KeyValue = 11 ;break ;case 0 x7e : KeyValue = 12 ;break ;case 0 x
45、be : KeyValue = 13 ;break ;case 0 xde : KeyValue = 14 ;break ;case 0 xee : KeyValue = 15 ;break ; default : return NOKEY ; return KeyValue ;else DelayCount = 0 ; return NOKEY ; elseDelayCount = 0 ; return NOKEY ;elseDelayCount = 0 ; return NOKEY ;void v_T0_Isr_f( void ) interrupt INTERRUPT_TIMER2_OV
46、ERFLOWStartScan = 1 ;*模块调试*/主函数仅作演示用,主函数除按键扫描外的函数并没在这里给出voidv_Init_T2_f( void )T2CON = 0 x04 ;T2MOD= 0 x00 ;TH2 = 0 xd8 ;RCAP2H= 0 xd8 ;TL2 = 0 xf0 ;RCAP2L = 0 xf0 ;ET2 = 1 ;TR2= 1 ;void main( void )uint8 readkey = 0 ; v_Init_T2_f( ) ; v_LcdInit_f( );LOCATE( 1, 1)PRINT(4*4KeyBoard Test)EA = 1 ;LOCAT
47、E( 3, 2)while( 1 )SHOW_ICONreadkey = u8_KeyBoardScan_f() if( readkey != NOKEY)PRINTN( readkey , 2)LOCATE( 3, 2)continue ;elsecontinue ;呵呵,按键扫描程序已经注释的很详细了。我就不多费嘴舌了。如果有不清楚的地方,欢迎跟帖讨论。 下面是按键测试的截图我的自己搭建的实验板OK,Enioy it !自此按键检测告一段落。下次如果再讲按键。将会讨论另外一种按键的写法:基于状态机 的按键程序设计。欢迎讨论。tiankai (2010-2-05 16:39:19)晨辉教你轻
48、松学51-外围芯片之ds1302和ds18b20对于市面上的大多数51单片机开发板来说。ds1302和ds18b20应该是比较常见的两种外围芯片。ds1302是具有SPI总线接口的时钟芯片。ds18b20则是具有单总线接口的数字温度传感器。下面让我们分别来认识并学会应用这两种芯片。首先依旧是看DS1302的datasheet中的相关介绍。DS1302是 HALLOS 公司推出怖灣熬充电时悴芽片.内含有一 6 实时时日历W 31 7 9 RAM,逮罐勵 单的串柝擅口与单片 HI进柑通 f 雷时时忡旧册电路提供 X、汁、时、& H 期、小年的佶超冃的天 貌側的的天熬可自动调烙 川吓卄1 A
49、MPM梧:心吃* I: 24 JSL 12小潸咯式 DS13O2 与单丿;机上 闾縫电空采用同步申行的冇式iStrjttft*仅需用到三个匚 I域;llRESfH 柱人(DSCLK 时丼 h 时神 RAM 的读/斗敌 Helf 字棊或莠达 31 牛字前的字符畑方成通DS1302 TfW 功輕根 低*风曲數据和时抻值息时功率小DSB02魁由 DS1302 进厨来.堆加了以卜的特性 t农电 F主电觀备愉电灌供卜心 Vecl AW 輦程涓逾克电电譚.IHM七牛爭首存绪宅它湮应用于电话、梶摘式 USU 及电池供电的快餐岐表甘 产呂務 4U下丽将主聲的性施指算件一尊含实耐时建貝”诞计悴川 00干 2 削
50、的秒,爪时、H.日辄用期、”、年的龍 b 还有国年锻 U的能”世哲存數撇存 6BRAM率抒 1门方成他妙胃躺纺常诚少嵐褲消丁作电用:20-5 WT#电逵:2 0V时.小于 30WRAMi育背禅传送方式*单字持 14和多了関传送 0 ; i- )if( Content & 0 x01 )DS1302_IO_HIGHelseDS1302_IO_LOWContent = 1 ;DS1302_SCLK_HIGHDS1302_SCLK_LOW* * Function: static uint8 v_DS1302Read_f( void )* Description:从DS1302当前设定的地址读
51、取一个字节的内容* Parameter: * Return:返回读出来的值(uint8) */static uint8 v_DS1302Read_f( void )uint8 i, ReadValue ;DS1302_IO_HIGHfor( i = 8 ; i 0 ; i- )ReadValue = 1 ;if( DS1302_IO_READ )ReadValue |= 0 x80 ;elseReadValue &= 0 x7f ;DS1302_SCLK_HIGHDS1302_SCLK_LOWreturn ReadValue ;* * Function: void v_DS1302Wr
52、iteByte_f( uint8 Address, uint8 Content )* Description:从DS1302指定的地址写入一个字节的内容* Parameter: Address:要写入数据的地址* Content:写入数据的具体值*Return:*/void v_DS1302WriteByte_f( uint8 Address, uint8 Content )DS1302_RST_LOWDS1302_SCLK_LOWDS1302_RST_HIGHv_DS1302Write_f( Address ) ; v_DS1302Write_f( Content ) ;DS1302_RST
53、_LOWDS1302_SCLK_HIGH* * Function: uint8 v_DS1302ReadByte_f( uint8 Address )* Description:从DS1302指定的地址读出一个字节的内容* Parameter:Address:要读出数据的地址* * Return:指定地址读出的值(uint8) */uint8 v_DS1302ReadByte_f( uint8 Address )uint8 ReadValue ;DS1302_RST_LOWDS1302_SCLK_LOWDS1302_RST_HIGHv_DS1302Write_f( Address ) ; Re
54、adValue = v_DS1302Read_f() ;DS1302_RST_LOWDS1302_SCLK_HIGHreturn ReadValue ;/* Function: void v_ClockInit_f( void ) * Description:初始化写入DS1302时钟寄存器的值(主程序中只需调用一次即可) *Parameter:* Return: */void v_ClockInit_f( void )if( v_DS1302ReadByte_f( 0 xc1) != 0 xf0 )v_DS1302WriteByte_f( 0 x8e, 0 x00 ) ;v_DS1302Wr
55、iteByte_f( DS1302_YEAR_WRITE, 0 x08 ) ;/年v_DS1302WriteByte_f( DS1302_WEEK_WRITE, 0 x04 ) ;/星期v_DS1302WriteByte_f( DS1302_MONTH_WRITE, 0 x12 ) ;/月v_DS1302WriteByte_f( DS1302_DAY_WRITE, 0 x11 ) ;/日/允许写操作v_DS1302WriteByte_f( DS1302_HOUR_WRITE, 0 x13 ) ;/小时v_DS1302WriteByte_f( DS1302_MINUTE_WRITE, 0 x06
56、 ) ;/分钟v_DS1302WriteByte_f( DS1302_SECOND_WRITE, 0 x40 ) ;/秒v_DS1302WriteByte_f( 0 x90, 0 xa5 ) ;/充电DS1302WriteByte_f( 0 xc0, 0 xf0 ) ;/判断是否初始化一次标识写入v_DS1302WriteByte_f( 0 x8e, 0 x80 ) ;/禁止写操作* Function: void v_ClockUpdata_f( void ) * Description:读取时间数据,并保存在结构体CurrentTime中Parameter:Return:*/void v_C
57、lockUpdata_f( void )CurrentTime.Second = v_DS1302ReadByte_f( DS1302_SECOND_READ ) ;CurrentTime.Minute = v_DS1302ReadByte_f( DS1302_MINUTE_READ ) ;CurrentTime.Hour = v_DS1302ReadByte_f( DS1302_HOUR_READ ) ; CurrentTime.Day =v_DS1302ReadByte_f( DS1302_DAY_READ ) ;CurrentTime.Month = v_DS1302ReadByte_f
58、( DS1302_MONTH_READ ) ;CurrentTime.Week= v_DS1302ReadByte_f( DS1302_WEEK_READ ) ;CurrentTime.Year= v_DS1302ReadByte_f( DS1302_YEAR_READ ) ;有了上面的这些函数我们就可以对DS1302进行操作了。当我们想要获取当前时间时,只需要调用v_ClockUpdata_f( void )这个函数即可。读取到的时间数据保存在CurrentTime这个结构体中。至于如何把时间数据在数码管或者是液晶屏上显示岀来我相信大家应该都会了吧A A看看显示效果如何下面再让我们看看DS1
59、8B20吧。DS18B20是单总线的数字温度传感器。其与单片机的接口只需要一根数据线即可。当然连线简单意味着 软件处理上可能要麻烦一点。下面来看看它的优点:全数字温度转换及输岀先进的单总线数据通信。最高12位分辨率.精度可达土0.5摄氏度一12位分藕率时的最大丄作周期为了50毫秒 可选择寄生工作方式。检测溫度范围为 -55 C -+125 C( - 67 F-+257 F)内置EEPR0M,限温报警功能。64位光刻ROM,内舊产品序列号*方便多机挂接 多样封装形式再适应不同硬件系统.看看它的靓照。外形和我们常用的三极管没有什么两样哦。DALLAS 18B20a:、;DS18B20的内部存储器分
60、为以下几部分ROM:存放该器件的编码。前8位为单线系列的编码(DS18B20的编码是19H)后面48位为芯片的唯一 序列号。在出场的时候就已经设置好,用户无法更改。最后8位是以上56位的CRC码。RAM:DS18B20的内部暂存器共9个字节。其中第一个和第二个字节存放转换后的温度值。第二个和第 三个字节分别存放高温和低温告警值。(可以用RAM指令将其拷贝到EEPROM中)第四个字节为配置寄存器。第57个字节保留。第9个字节为前8个字节的CRC码。2322212212-232-4MSb(milt = C)LSbSsSS2625LSBMSBDS18B20的温度存放如上图所示。其中S位符号位。当温度值为负值时,S = 1,反之则S = 0。我们把得到的温度数据乘上对应的分辨率即可以得到转换后的温度值。DS18B20的通讯
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 《电主轴开题报告》课件
- 2024年度外语教学实验班外教选拔与培养合同3篇
- 2025专业版合同补充协议
- 师徒合同范本技巧分享
- 建筑工程合同执行监督规定
- 婴儿用品公司合同安全标准
- 保险公司应届生招聘合同样本
- 电子信息产业园租赁合同
- 2025监理工程师合同管理考点索赔程序
- 房地产公司销售顾问聘用合同
- 船舶航海业务管理知识考试题库及答案(新版)
- 委托融资协议三篇
- 2024 餐饮行业研究系列报告二:茶饮出海篇国内竞争加剧海外市场成“第二增长曲线”
- 2024年合理化建议管理制度(三篇)
- 湖南省益阳市2023-2024学年高二上学期普通高中期末质量检测数学试题 含解析
- 动火作业安全指导手册
- 物业项目移交清单表
- LNG(天然气)供气站(气化站)安全应急救援预案
- 7.5 歌曲 《红河谷》课件(20张)
- 电商平台购销合同范本
- 2024年大学试题(艺术学)-艺考乐理考试近5年真题集锦(频考类试题)带答案
评论
0/150
提交评论