51单片机LCD1602+定时器+UART(使用串口替代按键功能,完成LCD1602秒表
时间: 2023-10-24 13:11:10 浏览: 110
这个项目可以分为以下几个部分:
1. LCD1602的初始化和显示设置;
2. 定时器的初始化和计时功能;
3. 串口的初始化和数据接收;
4. 秒表功能的实现。
以下是基本的代码框架和说明:
```c
#include <reg51.h>
#include <stdio.h>
#include <stdlib.h>
#define LCD_RS P1_0
#define LCD_RW P1_1
#define LCD_EN P1_2
#define LCD_DATA P2
// LCD1602的命令和数据定义
#define LCD_CMD_CLEAR 0x01
#define LCD_CMD_HOME 0x02
#define LCD_CMD_ENTRY_MODE 0x06
#define LCD_CMD_DISPLAY_ON_OFF 0x0C
#define LCD_CMD_FUNCTION_SET 0x28
// 定时器的计数值
#define TIMER_VALUE 65536-50000
// 串口的波特率
#define BAUD_RATE 9600
// 全局变量
volatile unsigned int count = 0;
// 函数声明
void lcd_init();
void lcd_cmd(unsigned char cmd);
void lcd_data(unsigned char data);
void lcd_display(unsigned char *str);
void timer_init();
void timer_isr() interrupt 1;
void uart_init();
void uart_isr() interrupt 4;
void main() {
unsigned char str[16] = {0};
unsigned char sec = 0;
unsigned char min = 0;
unsigned char hour = 0;
unsigned char start = 0;
lcd_init();
timer_init();
uart_init();
while (1) {
// 从串口接收数据,判断是否开始计时
if (start == 0) {
if (RI == 1) {
RI = 0;
if (SBUF == 's') {
start = 1;
}
}
} else {
// 开始计时
if (count >= 20) {
count = 0;
sec++;
if (sec == 60) {
sec = 0;
min++;
if (min == 60) {
min = 0;
hour++;
if (hour == 24) {
hour = 0;
}
}
}
sprintf(str, "%02d:%02d:%02d", hour, min, sec);
lcd_cmd(LCD_CMD_CLEAR);
lcd_display(str);
}
}
}
}
// LCD1602的初始化
void lcd_init() {
lcd_cmd(LCD_CMD_FUNCTION_SET);
lcd_cmd(LCD_CMD_DISPLAY_ON_OFF);
lcd_cmd(LCD_CMD_CLEAR);
lcd_cmd(LCD_CMD_ENTRY_MODE);
}
// 发送命令到LCD1602
void lcd_cmd(unsigned char cmd) {
LCD_RS = 0;
LCD_RW = 0;
LCD_DATA = cmd;
LCD_EN = 1;
LCD_EN = 0;
delay(1);
}
// 发送数据到LCD1602
void lcd_data(unsigned char data) {
LCD_RS = 1;
LCD_RW = 0;
LCD_DATA = data;
LCD_EN = 1;
LCD_EN = 0;
delay(1);
}
// 在LCD1602上显示字符串
void lcd_display(unsigned char *str) {
while (*str) {
lcd_data(*str++);
}
}
// 定时器的初始化
void timer_init() {
TMOD |= 0x10;
TH1 = TH0 = TIMER_VALUE / 256;
TL1 = TL0 = TIMER_VALUE % 256;
TR1 = TR0 = 1;
ET0 = 1;
EA = 1;
}
// 定时器的中断服务函数
void timer_isr() interrupt 1 {
TH0 = TH1 = TIMER_VALUE / 256;
TL0 = TL1 = TIMER_VALUE % 256;
count++;
}
// 串口的初始化
void uart_init() {
TMOD |= 0x20;
TH1 = 256 - (11059200 / 12 / 32 / BAUD_RATE);
TL1 = TH1;
TR1 = 1;
SM0 = 0;
SM1 = 1;
REN = 1;
ES = 1;
EA = 1;
}
// 串口的中断服务函数
void uart_isr() interrupt 4 {
if (RI == 1) {
RI = 0;
}
}
```
在上面的代码中,我们使用了定时器和串口两个中断服务函数,需要在主函数中开启总中断。同时,我们定义了一个全局变量 `count` 来计数,每次定时器中断服务函数被调用时,`count` 就会加1。
在主函数中,我们先初始化 LCD1602、定时器和串口,然后进入一个死循环。在循环中,我们从串口接收数据,如果接收到了 's' 字符,就开始计时,否则等待下一次接收。
当开始计时后,每次定时器中断服务函数被调用时,我们就将 `count` 加1,当 `count` 大于等于 20 时,就表示一秒钟过去了,我们将秒数加1,然后根据秒、分、时的值来更新 LCD1602 的显示。最后,我们使用 `lcd_cmd(LCD_CMD_CLEAR)` 来清屏,然后使用 `lcd_display(str)` 来显示时间字符串。
需要注意的是,定时器的计数值需要根据实际情况进行调整,以实现精确的计时功能。同时,由于串口的中断服务函数中没有处理接收到的数据,因此需要在主函数中进行处理。
阅读全文