STC89C52RC 操作LCD1602技巧
- 1 代碼工程
- 2 LCD1602使用
- 2.1 LCD1602字庫
- 2.2 巧妙使用sprintf
- 2.3 光標顯示
- 2.4 寫固定長度的字符
- 2.5 所以引入固定長度寫入方式:
- 3 LCD1602操作總結
1 代碼工程
承接上文,在原有工程基礎上,新建關于lcd1602的c和h文件,并在includes.h中引用,并添加頭文件:
#include<intrins.h> //匯編指令
#include<stdio.h> //標準輸入輸出
整體工程如下:
各個文件代碼如下:
//main.c文件
#include "includes.h"/******************************************************************/
/* 微秒延時函數 */
/******************************************************************/
void delay_us(unsigned int us)//delay us
{while(us--){_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();}
}/******************************************************************/
/* 微秒延時函數 */
/******************************************************************/
void delay_ms(unsigned int Ms)//delay us
{while(--Ms){delay_us(1000);}
}/*------------------------------------------------延時子程序
------------------------------------------------*/
void delay(unsigned int cnt)
{while(--cnt);
}/*------------------------------------------------主函數
------------------------------------------------*/
void main (void)
{EA=0;//初始化定時器0sys_timer0_init();//初始化定時器1sys_timer1_init();//初始化定時器2sys_timer2_init();sys_wdog_init();//外部中斷初始化sys_exit_init();LCD_init();//8個指示燈的操作sys_led();sys_led_test();sys_led_test1();sys_ledtube_on1();sys_ledtube_on2();//主循環中添加其他需要一直工作的程序sys_keynum_ledon(sys_key_single());sys_keynum_ledon(sys_key_board());EA=1;P1=0x55; //P1口初始值,預設指示燈狀態,測試引腳sys_lcd1602_test1();sys_lcd1602_test2();sys_lcd1602_test3();while (1){clr_wdg();}
}
//c51_gpio.c文件
#include "includes.h"void sys_led(void)
{P1 = 0xFF; //P1口全部為高電平,對應的LED燈全滅掉,ff換算成二進制是 1111 1111P1 = 0x00; //P1口全部為低電平,對應的LED燈全亮起,ff換算成二進制是 0000 0000}/********************************************************
函數名稱:sys_led_test
函數功能:IO口高低電平測試
入口參數:
出口參數:
修 改:
內 容:
********************************************************/
void sys_led_test(void)
{ bit tmp = 0;//中間變量用于獲取io口狀態//控制4個引腳輸出P10 = 1;P11 = 0;P12 = 0;P13 = 1;//用另外四個IO口獲取狀態并測試(指示燈顯示)tmp = P10;P14 = tmp;tmp = P11;P15 = tmp;tmp = P12;P16 = tmp;tmp = P13;P17 = tmp;}/********************************************************
函數名稱:sys_led_test
函數功能:led流水燈
入口參數:
出口參數:
修 改:
內 容:
********************************************************/
void sys_led_test1(void)
{
// delay(30000);//延時程序P1<<=1; //左移一位 該語句等效于 P1=P1<<1P1|=0x01; //最后一位補1,該語句等效于 P1=P1|0x01 符號"|"表示"或"if(P1==0xFF) //檢測是否移到最左端?"=="表示檢測符號2端的值是否相等{
// delay(30000);P1=0xfe; //重新賦值}
}
//c51_ledtube.c文件
#include "includes.h"// 顯示段碼值01234567,可對應原理圖查看顯示不同圖形對應的引腳高點電平配置狀態
unsigned char const EL[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,\0x77,0x7c,0x39,0x5e,0x79,0x71};//0-F/********************************************************
函數名稱:sys_ledtube_on1
函數功能:點亮一個數碼管全為亮起來
入口參數:
出口參數:
修 改:
內 容:
********************************************************/
void sys_ledtube_on1(void)
{//根據原理圖,將P0口全部輸出高電平,P2選擇0號數碼管P0=0xFF;//取顯示數據,段碼P2=0; //取位碼
}/********************************************************
函數名稱:sys_ledtube_on2
函數功能:顯示一組數據
入口參數:
出口參數:
修 改:
內 容:
********************************************************/
static unsigned char ledtube_cnt = 0;
void sys_ledtube_on2(void)
{ledtube_cnt++;if(ledtube_cnt>7){ledtube_cnt = 0;}P0 = 0x00; //防止切換數碼管瞬間有虛影出現P2 = 0x00;P0 = EL[ledtube_cnt]; //取顯示數據,段碼P2 = ledtube_cnt; //取位碼//根據人眼適應虛影緩沖時間為50ms左右//我們調整delay在500以下可以看到明顯的看起來是一串數據一起顯示delay(100);
}/********************************************************
函數名稱:sys_keynum_ledon
函數功能:顯示按鍵數值
入口參數:按鍵數值
出口參數:
修 改:
內 容:
********************************************************/
void sys_keynum_ledon(unsigned char num)
{//根據原理圖,將P0口全部輸出高電平,P2選擇0號數碼管P0=EL[num];//取顯示數據,段碼P2=0; //取位碼
}
//c51_key.c文件
#include "includes.h"bit key1=0; //定義按鍵位置
bit key2=0;
bit key3=0;
bit key4=0;/********************************************************
函數名稱:sys_key_single
函數功能:按鍵檢測,帶有消抖策略
入口參數:
出口參數:按鍵數值
修 改:
內 容:
********************************************************/
static unsigned char key1_history = 0;//緩存上一次按鍵的結果
unsigned char sys_key_single(void)
{key1=P30; //定義按鍵位置key2=P31;key3=P32;key4=P33;if(!key1) //按下相應的按鍵,數碼管顯示相應的碼值{delay(1000);if(!key1){key1_history = 1;return 1;}else{return key1_history;}}else if(!key2){delay(1000);if(!key2){key1_history = 2;return 2;}else{return key1_history;}}else if(!key3){delay(1000);if(!key3){key1_history = 3;return 3;}else{return key1_history;}}else if(!key4){delay(1000);if(!key4){key1_history = 4;return 4;}else{return key1_history;}}else{return key1_history;}}unsigned char sys_key_board(void)
{unsigned char key = 0x00;unsigned char num = 0x00;key=keyscan(); //調用鍵盤掃描if(key == 0xFF){num = key1_history;}else{switch(key){case 0xee:num = 0x0;break;//0按下相應的鍵顯示相對應的碼值case 0xde:num = 0x1;break;//1 按下相應的鍵顯示相對應的碼值 case 0xbe:num = 0x2;break;//2case 0x7e:num = 0x3;break;//3case 0xed:num = 0x4;break;//4case 0xdd:num = 0x5;break;//5case 0xbd:num = 0x6;break;//6case 0x7d:num = 0x7;break;//7case 0xeb:num = 0x8;break;//8case 0xdb:num = 0x9;break;//9case 0xbb:num = 0xA;break;//acase 0x7b:num = 0xB;break;//bcase 0xe7:num = 0xC;break;//ccase 0xd7:num = 0xD;break;//dcase 0xb7:num = 0xE;break;//ecase 0x77:num = 0xF;break;//fdefault:num = key1_history; break;}key1_history = num;}return num;
}/*------------------------------------------------鍵盤掃描程序
------------------------------------------------*/
unsigned char keyscan(void) //鍵盤掃描函數,使用行列反轉掃描法
{unsigned char cord_h,cord_l;//行列值中間變量P3=0x0f; //行線輸出全為0cord_h=P3&0x0f; //讀入列線值if(cord_h!=0x0f) //先檢測有無按鍵按下{delay(100); //去抖if(cord_h!=0x0f){cord_h=P3&0x0f; //讀入列線值P3=cord_h|0xf0; //輸出當前列線值cord_l=P3&0xf0; //讀入行線值return(cord_h+cord_l);//鍵盤最后組合碼值}}return(0xff); //返回該值
}
//c51_timer.c文件
#include "includes.h"/*------------------------------------------------定時器初始化子程序
------------------------------------------------*/
void sys_timer0_init(void)
{TMOD |= 0x01; //使用模式1,16位定時器,使用"|"符號可以在使用多個定時器時不受影響 TH0=0x00; //給定初值,這里使用定時器最大值從0開始計數一直到65535溢出TL0=0x00;//EA=1; //總中斷打開 等最后一個中斷打開//ET0=1; //定時器中斷打開TR0=1; //定時器開關打開
}/*------------------------------------------------定時器初始化子程序
------------------------------------------------*/
void sys_timer1_init(void)
{TMOD |= 0x20; //使用模式2, TH1=0x05; //給定初值,這里使用定時器最大值從5開始計數一直到255溢出TL1=0x00;//EA=1; //總中斷打開//ET1=1; //定時器中斷打開TR1=1; //定時器開關打開
}/*------------------------------------------------定時器初始化子程序
------------------------------------------------*/
void sys_timer2_init(void)
{RCAP2H = 0/256;//RCAP2L = 0/256;//ET2=1; //打開定時器中斷//EA=1; //打開總中斷TR2=1; //打開定時器開關
}void sys_wdog_init(void)
{ //WDT_CONTR = 0x35;
}void clr_wdg(void)
{//WDT_CONTR = 0x35;
}/*------------------------------------------------定時器中斷子程序
------------------------------------------------*/
void Timer0_isr(void) interrupt 1
{TH0=0x00; //重新賦值TL0=0x00;//sys_led_test1(); //流水燈操作
}/*------------------------------------------------定時器中斷子程序
------------------------------------------------*/
void Timer1_isr(void) interrupt 3
{//sys_led_test1(); //流水燈操作} /*------------------------------------------------定時器中斷子程序
------------------------------------------------*/
void Timer2_isr(void) interrupt 5//定時器2中斷
{TF2=0;//sys_led_test1(); //流水燈操作
}
//c51_exit.c文件
#include "includes.h"void sys_exit_init(void)
{EX0=1; //外部中斷0開//IT0=0; //電平觸發IT0=1; //邊沿觸發EX1=1; //外部中斷1開//IT1=0; //電平觸發IT1=1; //邊沿觸發,IT1=0表示電平觸發
}/*------------------------------------------------外部中斷程序
------------------------------------------------*/
void Exit0_isr(void) interrupt 0
{//在此處可以添加去抖動程序,防止按鍵抖動造成錯誤//P1=~P1;
}/*------------------------------------------------外部中斷程序
------------------------------------------------*/
void Exit1_isr(void) interrupt 2
{//在此處可以添加去抖動程序,防止按鍵抖動造成錯誤//P1=~P1;
}
//c51_lcd1602.c文件
#include "includes.h"#include <string.h>/******************************************************************/
/* 寫入命令函數 */
/******************************************************************/
void LCD_write_com(unsigned char com)
{ RS_CLR; RW_CLR; EN_SET; P0 = com; delay_us(5); EN_CLR;
}/******************************************************************/
/* 寫入數據函數 */
/******************************************************************/
void LCD_write_Data(unsigned char Data)
{ RS_SET; RW_CLR; EN_SET; P0 = Data; delay_us(5); EN_CLR;
}
/******************************************************************/
/* 清屏函數 */
/******************************************************************/
void LCD_clear(void)
{ LCD_write_com(0x01); delay_ms(5);
}/******************************************************************/
/* 初始化函數 */
/******************************************************************/
void LCD_init(void)
{LCD_write_com(0x38); /*顯示模式設置*/ delay_ms(5); LCD_write_com(0x38); delay_ms(5); LCD_write_com(0x38); delay_ms(5); LCD_write_com(0x38); LCD_write_com(0x08); /*顯示關閉*/ LCD_write_com(0x01); /*顯示清屏*/ LCD_write_com(0x06); /*顯示光標移動設置*/ delay_ms(5); LCD_write_com(0x0C); /*顯示開及光標設置*/
}/******************************************************************/
/* 寫入字符串函數 */
/* X:第幾列 Y:第幾行 *s字符串為空時跳出 */
/******************************************************************/
void LCD_write_str(unsigned char x,unsigned char y,unsigned char *s)
{ if (y == 0) { LCD_write_com(0x80 + x); }else { LCD_write_com(0xC0 + x); } while (*s) { LCD_write_Data( *s); s ++; }
}/******************************************************************/
/* 寫入字符串函數 */
/* X:第幾列 Y:第幾行 *s字符串為空時跳出 */
/******************************************************************/
void LCD_write_strl(unsigned char x,unsigned char y,unsigned char *s,unsigned length)
{ unsigned char cnt = 0;if (y == 0) { LCD_write_com(0x80 + x); }else { LCD_write_com(0xC0 + x); }while(length--){ LCD_write_Data( *s); s ++;cnt++;}while(cnt<5){LCD_write_Data(' ');cnt++;}
}/******************************************************************/
/* 寫入字節函數 */
/* X:第幾列 Y:第幾行 Data字符 */
/******************************************************************/
void LCD_write_char(unsigned char x,unsigned char y,unsigned char Data)
{ if (y == 0) { LCD_write_com(0x80 + x); } else { LCD_write_com(0xC0 + x); } LCD_write_Data( Data);
}void sys_lcd1602_test1(void)
{LCD_clear();LCD_write_char(1,0,'E');LCD_write_str(2,0,"mail:");LCD_write_str(1,1,"xxxshow@163.com");}void sys_lcd1602_test2(void)
{char Test2[16]="";int i=8;float j=3.5;delay_ms(100);//LCD_clear();sprintf(Test2,"N:%d,float:%.2f ",i,j);LCD_write_str(1,1,Test2);delay_ms(100);j = 3.61;sprintf(Test2,"%.2f",j);LCD_write_str(12,1,Test2);}void sys_lcd1602_test3(void)
{unsigned char Test2[5]="";LCD_clear();sprintf(Test2,"A:");LCD_write_str(0,0,Test2);sprintf(Test2,"B:");LCD_write_str(8,0,Test2);sprintf(Test2,"C:");LCD_write_str(0,1,Test2);sprintf(Test2,"D:");LCD_write_str(8,1,Test2);delay_ms(100);sprintf(Test2,"12345");LCD_write_strl(2,0,Test2,strlen(Test2));sprintf(Test2,"54321");LCD_write_strl(10,0,Test2,strlen(Test2));sprintf(Test2,"QWERT");LCD_write_strl(2,1,Test2,strlen(Test2));sprintf(Test2,"TREWQ");LCD_write_strl(10,1,Test2,strlen(Test2));delay_ms(100);sprintf(Test2,"111");LCD_write_strl(2,0,Test2,strlen(Test2));sprintf(Test2,"55");LCD_write_strl(10,0,Test2,strlen(Test2));sprintf(Test2,"QW");LCD_write_strl(2,1,Test2,strlen(Test2));sprintf(Test2,"T");LCD_write_strl(10,1,Test2,strlen(Test2));delay_ms(100);
}
//includes.h文件
#ifndef __INCLUDES_H__
#define __INCLUDES_H__//#include<reg52.h> #include<intrins.h> //匯編指令
#include<stdio.h> //標準輸入輸出//包含頭文件,一般情況不需要改動,頭文件包含特殊功能寄存器的定義
#include "STC89C5xRC_RDP.h"//應用層頭文件
#include "c51_gpio.h"
#include "c51_ledtube.h"
#include "c51_key.h"
#include "c51_timer.h"
#include "c51_exit.h"
#include "c51_lcd1602.h"extern void delay(unsigned int cnt);
extern void delay_us(unsigned int us);//delay us;
extern void delay_ms(unsigned int Ms);//delay Ms;#endif
//c51_ledtube.h文件
#ifndef __C51_LEDTUBE_H__
#define __C51_LEDTUBE_H__extern unsigned char const EL[];extern void sys_ledtube_on1(void);
extern void sys_ledtube_on2(void);extern void sys_keynum_ledon(unsigned char num);#endif
//c51_gpio.h文件
#ifndef __C51_GPIO_H__
#define __C51_GPIO_H__extern void sys_led(void);
extern void sys_led_test(void);
extern void sys_led_test1(void);#endif
//c51_key.h文件
#ifndef __C51_KEY_H__
#define __C51_KEY_H__extern bit key1; //定義按鍵位置
extern bit key2;
extern bit key3;
extern bit key4;extern unsigned char sys_key_single(void);extern unsigned char sys_key_board(void);
extern unsigned char keyscan(void); //鍵盤掃描函數,使用行列反轉掃描法#endif
//c51_timer.h文件
#ifndef __C51_TIMER_H__
#define __C51_TIMER_H__extern void sys_timer0_init(void);
extern void Timer0_isr(void);
extern void sys_timer1_init(void);
extern void Timer1_isr(void);
extern void sys_timer2_init(void);
extern void Timer2_isr(void);extern void sys_wdog_init(void);
extern void clr_wdg(void);#endif
//c51_exit.h文件
#ifndef __C51_EXIT_H__
#define __C51_EXIT_H__extern void sys_exit_init(void);
extern void Exit0_isr(void);
extern void Exit1_isr(void);#endif
//c51_lcd1602.h文件
#ifndef __C51_LCD1602_H__
#define __C51_LCD1602_H__#include "STC89C5xRC_RDP.h"//定義宏定義sbit RS = P2^4; //定義端口
sbit RW = P2^5;
sbit EN = P2^6;#define RS_CLR RS=0
#define RS_SET RS=1
#define RW_CLR RW=0
#define RW_SET RW=1
#define EN_CLR EN=0
#define EN_SET EN=1extern void LCD_write_com(unsigned char com) ;
extern void LCD_write_Data(unsigned char Data) ;
extern void LCD_clear(void) ;
extern void LCD_init(void) ;
extern void LCD_write_str(unsigned char x,unsigned char y,unsigned char *s) ;
extern void LCD_write_strl(unsigned char x,unsigned char y,unsigned char *s,unsigned length) ;extern void LCD_write_char(unsigned char x,unsigned char y,unsigned char Data) ;
extern void sys_lcd1602_test1(void);
extern void sys_lcd1602_test2(void);
extern void sys_lcd1602_test3(void);#endif
2 LCD1602使用
2.1 LCD1602字庫
表所示是字符庫的內容、字符碼和字形的對應關系。例如“A”的字符碼 為 41(HEX),“B”的字符碼這 42(HEX)。列項是高四位,橫向是低四位。根據表中可知,前125個字符為ASCII碼表中的字符。剩余的其他字符我們不經常使用。
在使用125個ASCII碼時,直接寫入字符即可。
2.2 巧妙使用sprintf
sprintf 是一個格式化字符串的函數,它可以將格式化的數據輸出到一個字符串中。它的用法類似于其他編程語言中的 printf 函數。
以下是 sprintf 的基本用法:
c
復制
int sprintf(char *str, const char *format, ...);
? str 是一個字符數組(字符串),用于接收格式化后的輸出結果。
? format 是一個格式化字符串,用于指定輸出的格式。
? … 是可變參數,用于提供要格式化的數據。根據 format 中的格式說明符,將可變參數的值格式化并插入到輸出字符串中。
下面是一個簡單的示例,演示了 sprintf 的用法:
c
復制
#include <stdio.h>int main() {char str[100];int num = 10;float f = 3.14;sprintf(str, "Number: %d, Float: %.2f", num, f);printf("Formatted string: %s\n", str);return 0;
}
輸出結果:
復制
Formatted string: Number: 10, Float: 3.14
在上面的示例中,我們將整數 num 和浮點數 f 格式化并插入到字符串 str 中,使用 %d 和 %.2f 分別表示整數和帶兩位小數的浮點數。
sprintf 還支持其他格式說明符,例如 %s 表示字符串,%c 表示字符, %x 表示十六進制數等。你可以根據需要使用適當的格式說明符進行格式化。請注意,在使用 sprintf 時要確保目標字符串 str 能夠容納足夠的空間,以避免溢出。
2.3 光標顯示
在使用光標閃爍顯示時,我們將
LCD_write_com(0x0C); /*顯示開及光標設置*/
改為LCD_write_com(0x0); /*顯示開及光標設置*/
注意事項,不能寫滿16個字符,因為,LCD1602最寬是16個字符,光標配置成自動加1的方式,無法在屏幕上顯示出來光標,但是一般也不用光標進行顯示;
2.4 寫固定長度的字符
因為,LCD1602寫入相同的位置可以覆蓋歷史,但是如果上一次數據與本次數據占用屏幕上的位置不同,就會導致上一次的顯示無法覆蓋,結果就會顯示混亂的情況。
第一次顯示:
void sys_lcd1602_test1(void)
{LCD_clear();LCD_write_char(1,0,'E');LCD_write_str(2,0,"mail:");LCD_write_str(1,1,"xxxshow@163.com");}
第二次顯示:
void sys_lcd1602_test2(void)
{char Test2[16]="";int i=8;float j=3.5;delay_ms(100);//LCD_clear();sprintf(Test2,"N:%d,%.2f",i,j);LCD_write_str(1,1,Test2);
}
顯示結果
2.5 所以引入固定長度寫入方式:
(1)注意代碼陷阱:
/******************************************************************/
/* 寫入字符串函數 */
/* X:第幾列 Y:第幾行 *s字符串為空時跳出 */
/******************************************************************/
void LCD_write_str(unsigned char x,unsigned char y,unsigned char *s)
{ if (y == 0) { LCD_write_com(0x80 + x); }else { LCD_write_com(0xC0 + x); } while (*s) //注意:會打印出來最后一個空字符,導致后邊的數據有問題分塊的時候,注意此處。{ LCD_write_Data( *s); s ++; }
}
(2)固定長度寫入方式代碼:
/******************************************************************/
/* 寫入字符串函數 */
/* X:第幾列 Y:第幾行 *s字符串為空時跳出 */
/******************************************************************/
void LCD_write_strl(unsigned char x,unsigned char y,unsigned char *s,unsigned length)
{ unsigned char cnt = 0;if (y == 0) { LCD_write_com(0x80 + x); }else { LCD_write_com(0xC0 + x); }while(length--){ LCD_write_Data( *s); s ++;cnt++;}while(cnt<5){LCD_write_Data(' ');cnt++;}
}
使用情況:
void sys_lcd1602_test3(void)
{unsigned char Test2[5]="";LCD_clear();sprintf(Test2,"A:");LCD_write_str(0,0,Test2);sprintf(Test2,"B:");LCD_write_str(8,0,Test2);sprintf(Test2,"C:");LCD_write_str(0,1,Test2);sprintf(Test2,"D:");LCD_write_str(8,1,Test2);delay_ms(100);sprintf(Test2,"12345");LCD_write_strl(2,0,Test2,strlen(Test2));sprintf(Test2,"54321");LCD_write_strl(10,0,Test2,strlen(Test2));sprintf(Test2,"QWERT");LCD_write_strl(2,1,Test2,strlen(Test2));sprintf(Test2,"TREWQ");LCD_write_strl(10,1,Test2,strlen(Test2));delay_ms(100);sprintf(Test2,"111");LCD_write_strl(2,0,Test2,strlen(Test2));sprintf(Test2,"55");LCD_write_strl(10,0,Test2,strlen(Test2));sprintf(Test2,"QW");LCD_write_strl(2,1,Test2,strlen(Test2));sprintf(Test2,"T");LCD_write_strl(10,1,Test2,strlen(Test2));delay_ms(100);
}
最終顯示結果如下,空白處不會被覆蓋。
3 LCD1602操作總結
這些是 LCD1602 操作的一些常見技巧。具體的操作步驟和函數會因你使用的具體驅動程序、編程語言和平臺而有所不同。因此,在使用 LCD1602 時,請參考相關文檔或教程,了解你所使用的具體庫和函數的使用方法。