天天看點

第三講 單片機C語言之12864液晶顯示

上一講我們探索了12864的文本模式,這一講我們來看看繪圖模式。上一講中我們給出了繪圖模式的點陣圖(見下圖),是由512組每組16個點構成的點陣。螢幕分為上下兩半,其中每一半占256組;上半部分X軸方向(0-7),Y軸方向(0-31);下半部分X軸方向(8-15),Y軸方向(0-31)。如果一個點為高電平,則被點亮。12864隻能以組為機關控制,是以我們隻要給一個16位的二進制,就可以驅動一組的16個點。下面我們就通過兩個實驗來示範12864是如何顯示一幅圖形的。

第三講 單片機C語言之12864液晶顯示

實驗一 點亮點陣的四角的四個組,先上程式。

main.c

#include "reg51.h"
#include "12864.h"

void main()
{
	LCD_Init();
	Clean_Draw();
	Show_Group(0, 0, 0xaaaa);
	Show_Group(7, 0, 0xaaaa);
	Show_Group(8, 31, 0xaaaa);
	Show_Group(15, 31, 0xaaaa);

	while(1)
	{

	}
}
           

12864.c

#include "12864.h"
#include "stdio.h"

sbit LCDRS_CS=P3^5;
sbit LCDWR_SID=P3^6;
sbit LCDE_CLK=P3^7;

/*
 資料 0xfa
 指令 位址 0xf8
*/

void delay(u16 x)
{
	while(x--);
}

void LCD_Send8bit(u8 dat)	 //向LCD發送一個位元組
{
	u8 i;
	for(i=0;i<8;i++)
	{
		LCDE_CLK=0;
		LCDWR_SID=dat>>7;
		LCDE_CLK=1;	
		dat<<=1;

	}
}

void LCD_EntryText()  //LCD進入文本模式
{
	LCD_WriteCmd(0x30);	
}

void LCD_EntryDraw(u8 sw) //LCD進入繪圖模式
{
	if(sw==0)
	{
	   LCD_WriteCmd(0x34);
	}
	else
	{
	   LCD_WriteCmd(0x36);
	}
	
}

void LCD_SetAddText(u8 add)	//設定LCD文本模式顯示位址
{
	LCD_WriteCmd(0x30);
	LCDE_CLK=0;
	LCDRS_CS=1;
	LCD_Send8bit(0Xf8);
	LCD_Send8bit(add&0xf0);
	LCD_Send8bit(add<<4);
	LCDE_CLK=0;
	LCDRS_CS=0;
}

void LCD_SetAddDraw(u8 x, u8 y)	//設定LCD繪圖模式顯示位址
{	
	LCD_WriteCmd(y|0x80);
	LCD_WriteCmd(x|0x80);
}

void LCD_WriteDat(u8 dat)	//向LCD寫入顯示資料
{
	LCDE_CLK=0;
	LCDRS_CS=1;
	LCD_Send8bit(0Xfa);
	LCD_Send8bit(dat&0xf0);
	LCD_Send8bit(dat<<4);
	LCDE_CLK=0;
	LCDRS_CS=0;
}

void LCD_WriteCmd(u8 cmd) //向LCD發送一條指令
{
	LCDE_CLK=0;
	LCDRS_CS=1;
	LCD_Send8bit(0Xf8);
	LCD_Send8bit(cmd&0xf0);
	LCD_Send8bit(cmd<<4);
	LCDE_CLK=0;
	LCDRS_CS=0;
}

void Show_Chinese(u8 add, u8 mh, u8 ml)	//在LCD上顯示一個中文
{
	LCD_EntryText(); //lcd進入文本模式
	LCD_SetAddText(add);
	LCD_WriteDat(mh);
	LCD_WriteDat(ml);
}

void Show_String(u8 add, u8 *str)	//lcd顯示字元串
{
	LCD_SetAddText(add);
	while(*str)
	{
		LCD_WriteDat(*str++);	
	}
}

void Show_Number(u8 add, u16 n)  //lcd顯示一個數字
{
	char s[6];
	sprintf(s,"%05d",n);
	Show_String(add, s);	
}

void Show_Float(u8 add, float n)	 //lcd顯示一個浮點數
{
	char s[6];
	sprintf(s,"%03.2f",n);
	Show_String(add, s);	
}

void LCD_Init()
{
	LCD_EntryText();  //lcd進入文本模式
	delay(50);
	LCD_WriteCmd(0x01);	//清屏
	delay(5000);
	LCD_WriteCmd(0x0c);	 //開顯示
	delay(50);
	Clean_Draw();
}
//-----------------------------------------//
//-------------------繪圖模式--------------//
//-----------------------------------------//
void Show_Group(u8 x, u8 y,u16 dat)  //顯示LCD繪圖模式的一組點
{
	LCD_EntryDraw(0);	//	進入繪圖模式 關閉
	LCD_SetAddDraw(x,y);  //
	LCD_WriteDat(dat>>8); // 不能用等于号,否則改變dat
	LCD_WriteDat(dat);	 //
	LCD_EntryDraw(1);	 //	進入繪圖模式 打開
}


void Clean_Draw()
{
	u8 i;
	u8 j;
	for(i=0;i<16;i++)
	{
		for(j=0;j<32;j++)
		{
			Show_Group(i,j,0);	  //調用512次,每次都有關閉打開,會在下個實驗改進
		}	
	}
}
           

12864.h

#include "reg51.h"
#include "stdio.h"

 #define u8 unsigned char
 #define u16 unsigned int
 void delay(u16 x);
 void LCD_EntryText();
 void LCD_EntryDraw(u8 sw);
 void LCD_SetAddText(u8 add);
 void LCD_SetAddDraw(u8 x, u8 y);
 void LCD_WriteDat(u8 dat);
 void LCD_WriteCmd(u8 cmd);
 void LCD_Send8bit(u8 dat);
 void Show_Chinese(u8 add, u8 mh, u8 ml);
 void Show_String(u8 add, u8 *str);
 void Show_Number(u8 add, u16 n);
 void Show_Float(u8 add, float n);
 void LCD_Init();

 void Show_Group(u8 x, u8 y,u16 dat);  //顯示LCD繪圖模式的一組點
 void Clean_Draw();
 
 #endif
           

程式分析:整個程式的目标是要能控制一個組的16個點陣,那麼首先我們要進入繪圖模式,然後設定顯示位址(x,y), 再就是寫16位資料。先看進入繪圖模式,由12864資料手冊知道,繪圖模式進入後有繪圖顯示on與繪圖顯示off兩種操作,這個on打開與off關閉又是怎麼操作的?資料手冊規定如下。

第三講 單片機C語言之12864液晶顯示

是以我們首先要關閉繪圖顯示,也就是寫指令LCD_WriteCmd(0x34),我們把這兩個關閉與打開指令都放到了LCD_EntryDraw(u8 sw)函數,其中sw為1時打開,為0時關閉。接下來要設定位址,我們建立LCD_SetAddDraw(x,y)函數,分别寫入XY位址,注意最高位始終為1(如下圖)是以要并上0x80。

第三講 單片機C語言之12864液晶顯示

接下來便是寫入16位資料,分兩次,先寫高8位,是以先要将資料右移8位,再寫低8位,隻需整個資料寫入即可,因為writedat函數會自動截取低8位。最好再打開繪圖顯示,完成。整個過程我們放在Show_Group(u8 x, u8 y,u16 dat) 函數。

void Show_Group(u8 x, u8 y,u16 dat)  //顯示LCD繪圖模式的一組點
{
	LCD_EntryDraw(0);	//	進入繪圖模式 關閉
	LCD_SetAddDraw(x,y);  //
	LCD_WriteDat(dat>>8); // 不能用等于号,否則改變dat
	LCD_WriteDat(dat);	 //
	LCD_EntryDraw(1);	 //	進入繪圖模式 打開
}
           

最後還要注意建立一個清屏函數Clean_Draw(),在每次顯示之前清屏,其實就是令所有點為0,這樣可以保持整個螢幕不出現亂碼。

實驗二 顯示一幅圖檔

前面我們分析了LCD的點陣分布,分上下兩塊,而每一塊正好有128X32個點,對應一幅128X32的像素圖。是以如果我們有一幅128X64個像素點圖,那麼完全可以用12864來表達。是以首先要将這樣一幅圖導入到畫圖軟體,生成字尾名為.bmp的128X64黑白、單色檔案,再用PCtoLCD2002軟體生成一個二進制檔案,再将二進制數值複制到工程檔案picture.c中,并做成一個數組儲存到ROM中。如下:

#include "picture.h"

code unsigned char pic1[]={
0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,0x01,0xFC,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0xFC,0x00,0x00,0x00,0x00,0x00,0x07,0xFE,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x01,0xF8,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0x00,0x00,0x00,0x00,0x00,0x3C,0x00,0x07,0xE0,0x00,0x00,0x00,0x00,0x00,0x3F,0x87,0x80,0x00,0x00,0x00,0x00,0x1E,0x00,
0x1F,0xC0,0x00,0x00,0x00,0x00,0x00,0x7E,0x03,0xC0,0x00,0x00,0x00,0x00,0x0F,0x00,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x01,0xC0,0x00,0x00,0x00,0x00,0x07,0xC0,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x01,0xC0,0x00,0x00,0x00,0x00,0x03,0xE0,0xF8,0x00,0x00,0x00,0x00,0x00,0x01,0xE0,0x01,0xE0,0x00,0x00,0x00,0x00,0x01,0xF0,0xE0,0x00,0x00,0x00,0x00,0x00,0x01,0xE0,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x7C,
0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xC0,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xC0,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x80,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x01,
0x00,0x00,0x00,0x00,0x00,0x01,0xF0,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xF8,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xF8,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xFC,0x00,0x3F,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x07,0xC0,0x00,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x07,0xFF,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x0F,0xF0,0x01,0xC0,0x3F,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xFC,0x00,0x3F,0x80,0x01,0xC0,0x03,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xF8,0x00,0x7E,0x0F,0xFF,0xFC,0x00,0x70,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x07,0xC0,0x00,0x78,0x1F,0xFF,0xFE,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x00,0xF0,0x1F,0xFF,0xFF,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0xE0,0x1F,0x87,0x8F,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x00,0xE0,0x1F,0xC7,0x3F,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x00,0xE0,0x0F,0xC7,0xFF,0x00,0x1C,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x1C,0x00,0x00,0xFF,0xFF,0xFF,0xFC,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x00,0x7F,0xFF,0xFF,0xE0,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x00,0x3F,0xFF,0xFF,0x80,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x1E,0x00,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x00,0x00,0x00,0x1C,0x00,0x00,0x1C,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x07,0xE0,0x00,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xFC,0x00,0x00,0x00,0x38,0x00,0x01,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF8,0x00,0x00,0x78,0x00,0x0F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFF,0xFC,0x00,0x78,0x03,0xFF,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x07,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x0F,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x1F,0x00,0x00,0x01,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x0F,0x80,0x00,0x07,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xC0,0x00,0x00,0x07,0xC0,0x00,0x1F,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x01,0xF0,0x00,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xF8,0x00,0x00,0x00,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x7C,0x00,0x00,0x00,0x7F,0xFF,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x3E,0x00,0x00,0x00,0x1F,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,
0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x0F,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x07,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x01,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xF0,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xE0,
0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xC0,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00};
           

接下來我們要将數組中的每兩個資料(也就是16位)去驅動一個組,這樣數組的1024個資料便可覆寫LCD的512組。建立一個圖檔顯示函數到12864.c中,另外我們修改Show_Group函數成Show_GroupX函數,這樣做的好處不用頻繁地進行關閉和打開繪圖顯示。另外一個要注意的地方是Show-Picture函數中指針的使用。如何将數組中相鄰的兩個資料組合成一個16位資料呢?我們可以使用中間變量t,t=*p++; t<<=8;t|=*p++;,這樣完全行得通。但是使用指針的話,則避免了重複性的操作,減輕了系統負擔。

void Show_GroupX(u8 x, u8 y,u16 dat)  //顯示LCD繪圖模式的一組點
{
	LCD_SetAddDraw(x,y);  //
	LCD_WriteDat(dat>>8); // 不能用等于号,否則改變dat
	LCD_WriteDat(dat);	 //
}

void Show_Picture(u8 *p)//	顯示一幅128*64的圖檔
{
	u16 *u;
	u8 x,y;	
	u=(u16*) p;
	LCD_EntryDraw(0);
	for(y=0;y<32;y++)
	{
		for(x=0;x<8;x++)
		{
		
			Show_GroupX(x,y,*u++);	 
		}	
	}

	for(y=0;y<32;y++)
	{
		for(x=8;x<16;x++)
		{
		
			Show_GroupX(x,y,*u++);	 
		}	
	}
	LCD_EntryDraw(1);		
}
           

最後我們隻需要在main函數中顯示圖檔即可。

#include "12864.h"
#include "picture.h"

void main()
{

	LCD_Init();
	Clean_Draw();
	Show_Picture((u8*)pic1);

	while(1)
	{

	}
}
           

picture.h

#ifndef _picture_
#define _picture_

code unsigned char pic1[];

#endif
           

繼續閱讀