1. 程式人生 > >第三講 微控制器C語言之12864液晶顯示

第三講 微控制器C語言之12864液晶顯示

上一講我們探索了12864的文字模式,這一講我們來看看繪圖模式。上一講中我們給出了繪圖模式的點陣圖(見下圖),是由512組每組16個點構成的點陣。螢幕分為上下兩半,其中每一半佔256組;上半部分X軸方向(0-7),Y軸方向(0-31);下半部分X軸方向(8-15),Y軸方向(0-31)。如果一個點為高電平,則被點亮。12864只能以組為單位控制,因此我們只要給一個16位的二進位制,就可以驅動一組的16個點。下面我們就通過兩個實驗來演示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關閉又是怎麼操作的?資料手冊規定如下。


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


接下來便是寫入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