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;
//初始化定时器0
sys_timer0_init();
//初始化定时器1
sys_timer1_init();
//初始化定时器2
sys_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 1111
P1 = 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<<1
P1|=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;//2
case 0x7e:num = 0x3;break;//3
case 0xed:num = 0x4;break;//4
case 0xdd:num = 0x5;break;//5
case 0xbd:num = 0x6;break;//6
case 0x7d:num = 0x7;break;//7
case 0xeb:num = 0x8;break;//8
case 0xdb:num = 0x9;break;//9
case 0xbb:num = 0xA;break;//a
case 0x7b:num = 0xB;break;//b
case 0xe7:num = 0xC;break;//c
case 0xd7:num = 0xD;break;//d
case 0xb7:num = 0xE;break;//e
case 0x77:num = 0xF;break;//f
default:num = key1_history; break;
}
key1_history = num;
}
return num;
}
/*------------------------------------------------
键盘扫描程序
------------------------------------------------*/
unsigned char keyscan(void) //键盘扫描函数,使用行列反转扫描法
{
unsigned char cord_h,cord_l;//行列值中间变量
P3=0x0f; //行线输出全为0
cord_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=1
extern 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 时,请参考相关文档或教程,了解你所使用的具体库和函数的使用方法。