STC89C52R控制LCD1602码表+数码管+后台数显(串口)
- 1 概述
- 1.1 项目概述
- 1.2 项目组成部分
- 1.3 功能描述
- 2 开发环境
- 2.1 支持设备
- 2.2 硬件电路
- 3 软件代码工程
- 4 演示
- 4.1 Proteus仿真
- 4.2 实物演示
- 5 总结
1 概述
1.1 项目概述
本项目旨在利用STC89C52R单片机实现一个多功能的显示系统,包括LCD1602码表、数码管和后台数码显示器。通过串口通信,单片机可以与后台数码显示器进行数据交换和控制,实现更加灵活和多样化的显示功能。
1.2 项目组成部分
(1)STC89C52R单片机:
(2)LCD1602码表:
(3)数码管:
(4)后台数码显示器(通过串口通信):
(5)按键:
(6)Proteus:
1.3 功能描述
(1)STC89C52R单片机计时:
单片机作为项目的核心控制单元,负责数据处理、显示控制以及与外部设备的通信,更重要的是实现数据计时功能。
(2)LCD1602:
LCD1602显示屏用于显示文字信息,可以显示日期、时间、温度等实时数据。
(3)数码管:
数码管用于显示数字信息,比如计数器数值、计时器时间等。
(4)后台数码显示器(通过串口通信):
通过串口通信,将单片机与后台数码显示器连接,实现双向数据传输和控制,扩展显示功能。
(5)按键:
通过按键截取码表计时时间,有2个按键。
按键1:准备,开始、停止,清零,根据按键的次数依次表示不同的功能;
按键2:截取时间;
(6)Proteus:
通过实物演示和Proteus方式进行仿真。
2 开发环境
2.1 支持设备
我们采用华晴51开发板、计算机Keil开发工具、PROTEUS仿真软件。结合华晴51开发板、Keil开发工具和PROTEUS仿真软件,开发人员可以进行完整的嵌入式系统开发流程。他们可以使用Keil开发工具编写、编译和调试8051单片机程序,然后通过PROTEUS仿真软件验证程序的正确性和系统的功能。一旦验证通过,程序可以下载到华晴51开发板中进行实际硬件调试和测试,从而完成嵌入式系统的开发。这些工具的结合使用使开发人员能够在软件和硬件层面对嵌入式系统进行全面的开发和验证,提高开发效率,降低开发成本,并确保系统的稳定性和可靠性。
(1)华晴51开发板:
华晴51开发板是一种基于8051系列单片机的开发板,通常用于学习、开发和测试嵌入式系统。该开发板具有丰富的外设接口,包括LED、按键、数码管、LCD显示屏、通信接口等,方便用户进行各种实验和项目开发。华晴51开发板通常支持多种编程方式,如汇编语言、C语言等,适合初学者和专业开发人员使用。
(2)Keil开发工具:
Keil开发工具是一套专业的嵌入式系统开发工具,主要用于针对ARM和8051等处理器的嵌入式软件开发。其中,Keil C51是用于8051系列单片机的C语言集成开发环境,提供了编译、调试、仿真等功能,使开发人员能够高效地进行嵌入式软件开发。Keil开发工具配备了强大的调试功能,能够帮助开发人员快速定位和解决程序中的问题,提高开发效率。
(3)PROTEUS仿真软件:
PROTEUS是一款广泛用于电子电路设计和仿真的软件。在嵌入式系统开发中,PROTEUS常被用于对电路设计和单片机程序进行仿真,以验证系统功能、调试程序和检测潜在问题。PROTEUS提供了丰富的元件库和仿真功能,用户可以方便地搭建电路原理图、加载单片机程序,并进行实时仿真。
2.2 硬件电路
(1)STC89C52R单片机:
晶振使用11.0592Mhz
(2)LCD1602码表:
(3)数码管:
(4)后台数码显示器(通过串口通信):
(5)按键:
按键使用独立按键形式,P32 P33引脚
(6)实物与Proteus:
3 软件代码工程
//main.c文件
#include "includes.h"
/******************************************************************/
/* 微秒延时函数 //10us */
/******************************************************************/
void delay_us(unsigned int us)//delay us
{
while(us--)
{
}
}
/******************************************************************/
/* 微秒延时函数 */
/******************************************************************/
void delay_ms(unsigned int Ms)//delay us
{
while(Ms--)
{
delay_us(100);
}
}
/*------------------------------------------------
主函数
------------------------------------------------*/
unsigned int pro_stopwatch_time = 0;
unsigned char pro_key_data = 0;
unsigned int pro_stopwatch_buf[10] = {0};
unsigned char pro_time_cnt = 0;
unsigned char pro_start_flag = 0;
void main (void)
{
char Test2[] = " ";
unsigned char databuf[4] = {0};
//串口外设初始化
sys_uart_init();
//定时器外设初始化
sys_timer_init();
//LCD1602显示屏初始化
LCD_init();
//数据清零
pro_stopwatch_time = 0;
//主程序
sprintf(Test2,"TIME: . ");
LCD_write_strl(0,0,Test2,16);
sprintf(Test2,"N: . time: . ");
LCD_write_strl(0,1,Test2,16);
while (1)
{
pro_key_data = sys_key_single();
switch(pro_key_data)
{
case 3:
//截取数据量是有限的10个,只能在运行时截取
/*
pro_start_flag = 0://码表准备
pro_start_flag = 1://码表开始
pro_start_flag = 2://码表停止
pro_start_flag = 3://码表清零
*/
pro_start_flag++;
if(pro_start_flag>3)
{
pro_start_flag = 0;
}
if(pro_start_flag==3)
{
pro_stopwatch_time = 0;
pro_time_cnt = 0;
pro_stopwatch_buf[0] = 0;
pro_stopwatch_buf[1] = 0;
pro_stopwatch_buf[2] = 0;
pro_stopwatch_buf[3] = 0;
pro_stopwatch_buf[4] = 0;
pro_stopwatch_buf[5] = 0;
pro_stopwatch_buf[6] = 0;
pro_stopwatch_buf[7] = 0;
pro_stopwatch_buf[8] = 0;
pro_stopwatch_buf[9] = 0;
}
break;//0按下相应的键显示相对应的码值
case 4:
if(pro_start_flag==1 && pro_time_cnt<10)
{
pro_stopwatch_buf[pro_time_cnt]=pro_stopwatch_time;
pro_time_cnt += 1;
if(pro_time_cnt>9)
{
pro_time_cnt = 10;
}
}
//printf("Number:%d ,Time:%d~~~",pro_time_cnt,pro_stopwatch_time);
Uart_SendChar(pro_time_cnt);
Uart_SendChar(pro_stopwatch_buf[pro_time_cnt-1]>>8);
Uart_SendChar(pro_stopwatch_buf[pro_time_cnt-1]&0xFF);
break;//1 按下相应的键显示相对应的码值
default:break;
}
//数码管显示
if(pro_time_cnt>0)
{
databuf[0] = pro_stopwatch_buf[pro_time_cnt-1]/1000;
databuf[1] = pro_stopwatch_buf[pro_time_cnt-1]%1000/100;
databuf[2] = pro_stopwatch_buf[pro_time_cnt-1]%100/10;
databuf[3] = pro_stopwatch_buf[pro_time_cnt-1]%10;
sys_keynum_ledon(pro_time_cnt,0);
sys_keynum_ledon(11,1);
sys_keynum_ledon(databuf[0],2);
sys_keynum_ledon(databuf[1],3);
sys_keynum_ledon(12,3);
sys_keynum_ledon(databuf[2],4);
sys_keynum_ledon(databuf[3],5);
sys_keynum_ledon(0,6);
//显示数据和刷新
LCD_write_com(0xC0+2);
lcd_number(pro_time_cnt/10);
LCD_write_com(0xC0+3);
lcd_number(pro_time_cnt%10);
LCD_write_com(0xC0+11);
lcd_number(databuf[0]);
LCD_write_com(0xC0+12);
lcd_number(databuf[1]);
LCD_write_com(0xC0+14);
lcd_number(databuf[2]);
LCD_write_com(0xC0+15);
lcd_number(databuf[3]);
}
else
{
sys_keynum_ledon(0,0);
sys_keynum_ledon(11,1);
sys_keynum_ledon(0,2);
sys_keynum_ledon(0,3);
sys_keynum_ledon(12,3);
sys_keynum_ledon(0,4);
sys_keynum_ledon(0,5);
sys_keynum_ledon(0,6);
LCD_write_com(0xC0+2);
lcd_number(0);
LCD_write_com(0xC0+3);
lcd_number(0);
LCD_write_com(0xC0+11);
lcd_number(0);
LCD_write_com(0xC0+12);
lcd_number(0);
LCD_write_com(0xC0+14);
lcd_number(0);
LCD_write_com(0xC0+15);
lcd_number(0);
}
LCD_write_com(0x80+5);
lcd_number(pro_stopwatch_time%10000/1000);
LCD_write_com(0x80+6);
lcd_number(pro_stopwatch_time%1000/100);
LCD_write_com(0x80+8);
lcd_number(pro_stopwatch_time%100/10);
LCD_write_com(0x80+9);
lcd_number(pro_stopwatch_time%10);
P1= pro_start_flag;
}
}
//includes.h文件
#ifndef __INCLUDES_H__
#define __INCLUDES_H__
//#include<reg52.h>
#include<intrins.h> //汇编指令_nop_
#include<stdio.h> //标准输入输出
//_nop_(); 产生一条NOP指令
//作用:对于延时很短的,要求在us级的,采用“_nop_”函数,这个函数相当汇编NOP指令,延时几微秒。
//NOP指令为单周期指令,可由晶振频率算出延时时间。
//8051 为每个机器周期 12 时钟
//对于12M晶振,延时1uS。
//11.0592M晶振,延时1.0851uS。
//对于延时比较长的,要求在大于10us,采用C51中的循环语句来实现。
//包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
#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"
//#include "c51_iic.h"
//#include "c51_tx1838.h"
#include "c51_uart.h"
//#include "c51_28byj48.h"
//#include "c51_ds1302.h"
extern unsigned int pro_stopwatch_time;
extern unsigned char pro_ledtube_data;
extern unsigned char pro_key_data;
extern unsigned int pro_stopwatch_buf[10];
extern unsigned char pro_cnt;
extern unsigned char pro_time_cnt;
extern unsigned char pro_start_flag;
extern void delay_us(unsigned int us);//delay us;
extern void delay_ms(unsigned int Ms);//delay Ms;
#endif
//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
code unsigned char ledmap[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x40,0x80};
///********************************************************
//函数名称: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 = ledmap[ledtube_cnt]; //取显示数据,段码
// P2 = ledtube_cnt; //取位码
//
// //根据人眼适应虚影缓冲时间为50ms左右
// //我们调整delay在500以下可以看到明显的看起来是一串数据一起显示
// delay(100);
//}
///********************************************************
//函数名称:sys_keynum_ledon
//函数功能:显示按键数值
//入口参数:按键数值
//出口参数:
//修 改:
//内 容:
//********************************************************/
void sys_keynum_ledon(unsigned char num,unsigned char pn)
{
//根据原理图,将P0口全部输出高电平,P2选择0号数码管
P0 = 0x00; //防止切换数码管瞬间有虚影出现
P2 = pn; //取位码
P0 = ledmap[num]; //取显示数据,段码
delay_ms(1);
}
//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,unsigned char pn);
#endif
//c51_timer.c文件
#include "includes.h"
void sys_timer_init(void)
{
TMOD |= 0x01; //定时器设置 10ms in 11.0592M crystal,工作在模式1,16位定时
TH0 = 0xDC;
TL0 = 0x00;
TR0 = 1; //打开定时开关
ET0 = 1;
EA = 1; //打开中断
}
/*------------------------------------------------
10ms定时器中断子程序
------------------------------------------------*/
void Timer0_isr(void) interrupt 1
{
TH0=0xd8; //重新赋值
TL0=0xf0;
if(pro_start_flag==1)
{
pro_stopwatch_time++;
if(pro_stopwatch_time>9999)
{
pro_stopwatch_time = 9999;
}
}
}
//c51_timer.h文件
#ifndef __C51_TIMER_H__
#define __C51_TIMER_H__
//extern void timer0_set(unsigned int Value);
//extern unsigned int timer0_get(void);
//extern void timer0_run(unsigned char Flag);
extern void sys_timer_init(void);
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_uart.c文件
#include "includes.h"
/*-----------------------------------------------
名称:串口通信
内容:连接好串口或者usb转串口至电脑,下载该程序,打开电源
打开串口调试程序,将波特率设置为9600,无奇偶校验
晶振11.0592MHz,发送和接收使用的格式相同,如都使用
字符型格式,在发送框输入 hello,I Love MCU ,在接
收框中同样可以看到相同字符,说明设置和通信正确
------------------------------------------------*/
/******************************************************************/
void sys_uart_init(void)
{
SCON = 0x50; /* SCON: 模式 1, 8-bit UART, 使能接收 */
TMOD |= 0x20; /* TMOD: timer 1, mode 2, 8-bit reload */
TH1 = 0xFD; /* TH1: reload value for 9600 baud @ 11.0592MHz */
TR1 = 1; /* TR1: timer 1 run */
EA = 1; /*打开总中断*/
//ES = 1; /*打开串口中断*///当使用串口协议通讯时可以使用此型号中断
}
void Uart_SendChar(unsigned char dat)
{
SBUF = dat;
while(!TI);
TI = 0;
}
char putchar(char c)//重定向
{
Uart_SendChar(c);
return c;
}
/******************************************************************/
/* 串口中断程序 */
/******************************************************************/
static unsigned char uart_temp = 0; //定义临时变量
static unsigned char uart_cnt = 0; //定义临时变量
void UART_isr(void) interrupt 4 //串行中断服务程序
{
if(RI) //判断是接收中断产生
{
RI=0; //标志位清零
uart_temp=SBUF; //读入缓冲区的值
if(uart_cnt==0)
{
if(0x02 == uart_temp)
{
uart_cnt = 1;
}
else
{
uart_cnt = 0;
}
}
else if(uart_cnt==1)
{
if(0x05 == uart_temp)
{
uart_cnt = 2;
}
else
{
uart_cnt = 0;
}
}
else if(uart_cnt==2)
{
uart_cnt = 0;
//P1=uart_temp; //把值输出到P1口,用于观察
SBUF=uart_temp; //把接收到的值再发回电脑端
}
else
{
uart_cnt = 0;
}
}
if(TI) //如果是发送标志位,清零
{
TI=0;
}
}
//c51_uart.h文件
#ifndef __C51_UART_H__
#define __C51_UART_H__
extern void Uart_SendChar(unsigned char dat);
extern char putchar(char c);//重定向
extern void sys_uart_init(void);
extern void UART_isr(void);
#endif
//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;
}
/****************************************
函数功能:LCD显示数字0-9
*****************************************/
void lcd_number(unsigned int number)
{
int num[10]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39};
switch(number)
{
case 0:LCD_write_Data(num[0]);
break;
case 1:LCD_write_Data(num[1]);
break;
case 2:LCD_write_Data(num[2]);
break;
case 3:LCD_write_Data(num[3]);
break;
case 4:LCD_write_Data(num[4]);
break;
case 5:LCD_write_Data(num[5]);
break;
case 6:LCD_write_Data(num[6]);
break;
case 7:LCD_write_Data(num[7]);
break;
case 8:LCD_write_Data(num[8]);
break;
case 9:LCD_write_Data(num[9]);
break;
}
}
/******************************************************************/
/* 初始化函数 */
/******************************************************************/
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_strl(unsigned char x,unsigned char y,unsigned char *s,unsigned length)
{
unsigned char cnt = 0;
//LCD_write_com(0x01); /*显示清屏*/
if (y == 0)
{
LCD_write_com(0x80 + x);
}
else
{
LCD_write_com(0xC0 + x);
}
while(length--)
{
LCD_write_Data( *s);
s ++;
cnt++;
}
}
//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_number(unsigned int number);
extern void LCD_init(void) ;
extern void LCD_write_strl(unsigned char x,unsigned char y,unsigned char *s,unsigned length) ;
#endif
//c51_key.c文件
#include "includes.h"
/********************************************************
函数名称:sys_key_single
函数功能:按键检测,带有消抖策略
入口参数:
出口参数:按键数值
修 改:
内 容:
********************************************************/
unsigned char sys_key_single(void)
{
if(!P32)
{
delay_ms(20);
if(!P32)
{
while(!P32);
return 3;
}
else
{
return 0;
}
}
else if(!P33)
{
delay_ms(20);
if(!P33)
{
while(!P33);
return 4;
}
else
{
return 0;
}
}
else
{
return 0;
}
}
//c51_key.h文件
#ifndef __C51_KEY_H__
#define __C51_KEY_H__
extern unsigned char sys_key_single(void); //键盘扫描函数,使用行列反转扫描法
#endif
4 演示
4.1 Proteus仿真
4.2 实物演示
5 总结
将STC89C52R与LCD1602码表、数码管以及后台数码显示器(通过串口)结合起来的应用具有以下价值和优势:
- 多功能显示系统:
通过整合LCD1602码表和数码管,您可以实现多种信息的显示,如时间、日期、计数器数值等。后台数码显示器通过串口通信扩展了显示功能,使系统更加灵活和多样化。 - 实时数据显示:
LCD1602码表和数码管可以显示实时数据,如温度、湿度、计时等信息,帮助用户实时监测系统状态。 - 数据交互与控制:
通过串口通信,后台数码显示器可以接收或发送数据,实现与系统的双向交互。这为用户提供了更多控制选项,使系统更具交互性。 - 灵活性和扩展性:
结合这些组件,系统具有较强的灵活性和扩展性。可以根据需求增加更多显示设备或扩展更多功能,满足不同应用场景的需求。 - 教学和学习用途:
这样的应用可以作为嵌入式系统开发的教学实践项目,帮助学生理解单片机控制、显示技术和串口通信的原理和应用。 - 嵌入式系统应用:
这种应用在嵌入式系统中具有广泛的应用前景,可用于智能家居、工业控制、自动化系统等领域,提供实时监控和控制功能。 - 技术学习和应用实践:
通过这种项目,开发人员可以学习嵌入式系统设计、硬件与软件的协同开发,串口通信协议等相关知识,并将其应用于实际项目中。
综上所述,STC89C52R控制LCD1602码表、数码管和后台数码显示器的应用具有丰富的功能、灵活性和教学学习的价值,同时具备广泛的应用前景,可在各种嵌入式系统和电子设备中发挥重要作用。