12864与24C04的原理和使用方法——以电子密码锁为例(上篇)

最近在做有关51的电子密码锁实验,正巧用到了12864与24C04。想在网上找找相关资料,要么讲的含糊不清,看的一知半解;要么太过复杂,难以理解。花了几天搞明白之后,把自己的看法简单分享一下。下面使用了我自己的代码进行讲解,但是各位朋友要能够真正的明白原理,而不是只会用我的代码。文中可能会有认识不全面的地方,请大家多多指教。

12864原理

所谓12864,顾名思义,为128x64的点阵组。在实际使用时,通常会把它分为左半屏和右半屏,每个半屏为64x64点阵。这里先扔掉我们的固有思维,不要用平日里的阅读习惯(从左到右,从上到下)去理解其显示方法,否则只会越学越糊涂。
需要知道的是,12864中使用了页这个概念。什么是页呢?以右半屏为例,我们应该都知道行和列的概念,在我的代码中,从第一行开始,往下数8个行,这8个行所构成的8x64点阵就是一页。虽然不同的操作函数设置的页的大小会有差别,但总体构成原理是相似的。话不多说,直接上图:
12864与24C04的原理和使用方法——以电子密码锁为例(上篇)_第1张图片
可以看到,每个半屏由8页组成,从上至下编号为0~7,一块完整的屏幕由16个页组成。而这些页的容量便取决于你的汉字字符有多大。说到这里,需要先重复一个常识:汉字(包括中文输入法下的标点符号)所占用的空间是英文字母、数字的2倍。知道了这一点,下面就比较好理解了。
在我写的代码中,一个页最多可以排列4个汉字,或是8个数字、字母。也就是说,一个汉字要占用8x16大小的点阵,四个汉字便占满了8x64的点阵。注意,这里我没有说一个汉字的大小是8x16,而是说一个汉字需要8x16大小的点阵来表示,要注意这两种说法的区别。
看到这里大家应该就明白了,在12864上显示的内容实际上是由不同的页里面的内容组合、拼凑而成的。有些朋友可能会问,那如果在屏幕的中间位置显示字符的话,不就涉及到了左右两块半屏了吗?没错,如果想要在屏幕的中间位置显示的话,左右半屏就需要各自显示内容的左半部分和右半部分。虽然这样听起来有些别扭,但既然是这样设计的,我们就只能这样去使用。

12864使用方法

下面直接把我用的12864的C文件代码贴上来,如果你是认真读到这里的,那么12864的基本原理应该理解得差不多了,只要我对函数的参数稍加解释,很快就可以理解并上手。至于代码是如何实现显示功能的就不讨论了,毕竟对于大多数人而言,只要能够使用便足矣。

#include 
#include 
#include "12864header.h"
//直接往下翻到标有使用方法注释的操作函数部分,前面可以不用看
void busy()//忙检测函数,不用管
{
	P0=0X00; 
	RS=0;
 	RW=1;
 	EN=1;
 	while(P0&0X80);
 	EN=0;
}

void wcmd(uchar cmd)
{ 
 	busy(); 
 	RS=0;
 	RW=0;
 	P0=cmd;
 	EN=1;_nop_();_nop_();
 	EN=0;
}

void wdata(uchar dat) 
{ 
 	busy();
 	RS=1;
 	RW=0;
 	P0=dat;
 	EN=1;_nop_();_nop_();
 	EN=0;
}

void set_page(uchar page)
{ 
 	page=0xb8|page; 
 	wcmd(page);
}

void set_line(uchar line)
{ 
 	line=0xc0|line;
 	wcmd(line);
}

void set_column(uchar column)
{
 	column=0x40|column; 
 	wcmd(column); 
}

void set_onoff(uchar onoff)
{
 	onoff|=0x3e;
	wcmd(onoff);
}

void select_screen(uchar screen)
{	
	switch(screen)	
	{
  		case 0:CS1=0;CS2=0;break;
  		case 1:CS1=0;CS2=1;break;
  		case 2:CS1=1;CS2=0;break;
 		default:break;
 	}
}

void clear_screen(uchar screen)
{
	 uchar i,j;
	 select_screen(screen);
	 for(i=0;i<8;i++)?
	 {
		  set_page(i);   
		  set_column(0); 
		  for(j=0;j<64;j++)
 		  	wdata(0x00);  
	 }
}

void init() //这里是12864初始化,需要在使用前调用1次
{ 
	 busy(); 
	 select_screen(0);
	 set_onoff(0);
	 select_screen(0); 
	 set_onoff(1); 
	 select_screen(0);
	 clear_screen(0);
	 set_line(0);   
}

void Show(uchar screen,uchar page,uchar column,uchar *p)//显示汉字的函数,各参数的含义在下面讲解  
{
	 uchar i;
	 select_screen(screen); //选择要操作的是左半屏还是右半屏,1为左半屏,2为右半屏
	 set_page(page);    //选择要操作的页,范围为0~7
	 set_column(column);//选择从哪个地方开始写,这个在下文中解释
	 for(i=0;i<8;i++) 
	 	wdata(p[i]);//p为指针,指向待写入内容的起始位置,这里推荐用数组
	 set_page(page+1); 
	 set_column(column); 
 	 for(i=0;i<16;i++)
  		wdata(p[i+16]);
}

void Show1(uchar screen,uchar page,uchar column,uchar *p)//显示数字或字母的函数,参数含义与上一个函数相同,不做赘述
{
	 uchar i; 
	 select_screen(screen); 
	 set_page(page); 
	 set_column(column);
	 for(i=0;i<8;i++) 
	 	wdata(p[i]);  
	 set_page(page+1); 
	 set_column(column); 
	 for(i=0;i<8;i++) 
  	 	wdata(p[i+8]);  
}

那么,这个函数要怎么使用呢?举个例子,假设在一个名为start的数组中,有如下两个汉字:“通”,编码如下(利用字模软件生成):
0x40,0x42,0xCC,0x00,0x00,0xE2,0x22,0x2A,0x2A,0xF2,0x2A,0x26,0x22,0xE0,0x00,0x00,
0x80,0x40,0x3F,0x40,0x80,0xFF,0x89,0x89,0x89,0xBF,0x89,0xA9,0xC9,0xBF,0x80,0x00,
“信”,编码如下:
0x00,0x80,0x60,0xF8,0x07,0x00,0x04,0x24,0x24,0x25,0x26,0x24,0x24,0x24,0x04,0x00,
0x01,0x00,0x00,0xFF,0x00,0x00,0x00,0xF9,0x49,0x49,0x49,0x49,0x49,0xF9,0x00,0x00,
现在我的左半屏第1页中的第一个字为“通”,我想紧跟其后的显示“信”这个字,就可以这样来写:

Show(1,1,2*8,start+1*32);

(因为文本显示问题,下面都用乘来代替*)
这里面,第一个1表示在左半屏,第二个1表示在第1页,2乘8表示从第16位写起,因为第一个汉字“通”已经占据了8x16大小的点阵,也就是在横向的方向占据了2x8个点(纵向方向的不用在意,因为它们都占满了8个点),即0-15,那么下一个汉字就要从横向方向从左往右数的第16个点开始写起。而start+1乘32,则表示将指针指向数组中第二个字的起始位置,为什么是1乘32呢?观察一下上面每个字的编码,这里假设每个十六进制数占用一个数位,不难发现每个字的大小便是32个数位,那么就可以知道,第一个汉字“通”占用了数组的0-31数位,所以指针就需要指到第32个数位处,即1乘32。
12864就写到这里,过几天应该会把24C04的相关操作也写出来(最近陆陆续续的有考试,大概率会咕咕咕)。第一次写博客,不知道怎么写比较好,就随便写了一点,希望能够帮到大家,也欢迎大家来找我探讨交流电子类方面的问题。

你可能感兴趣的:(51及常用外设)