进阶四 带记忆功能的000-255 计数器
一、实验目的
- 掌握89C51单片机的基本操作和编程方法
- 学习I2C总线协议及其在单片机中的应用
- 实现基于AT24C02 EEPROM的带记忆功能的计数器
- 掌握LCD1602液晶显示模块的使用方法
二、实验设备
- 89C51单片机开发板(板载)
- AT24C02 EEPROM芯片(板载)
- LCD1602液晶显示屏(板载)
- 按键开关(板载)
三、实验原理
1. 系统组成
本实验系统主要由以下几部分组成:
- 89C51单片机:作为主控制器,负责处理按键输入、I2C通信和LCD显示
- AT24C02 EEPROM:用于存储计数器的当前值,实现断电记忆功能
- LCD1602液晶显示屏:用于显示当前计数值
- 按键开关:用于增加计数值
2. I2C总线协议
I2C总线是一种简单的双向二线制同步串行总线,由数据线SDA和时钟线SCL组成。本实验使用89C51的P2.0和P2.1引脚模拟I2C总线与AT24C02通信。
3. AT24C02 EEPROM
AT24C02是一种2Kbit的串行EEPROM,采用I2C接口。其设备地址为0xA0(写)和0xA1(读)。本实验使用其第一个地址(0x00)存储计数值。
4. LCD1602液晶显示
LCD1602是一种字符型液晶显示模块,可以显示两行,每行16个字符。本实验使用其第一行显示当前计数值。
四、实验电路连接
- LCD1602连接:
- RS引脚连接P3.5
- RW引脚连接P3.6
- EN引脚连接P3.4
- 数据线D0-D7连接P0口
- AT24C02连接:
- SDA引脚连接P2.1
- SCL引脚连接P2.0
- 按键连接:
- 按键一端连接P3.2,另一端接地
五、实验代码分析
- 头文件和宏定义
#include <reg52.h>
#include <intrins.h>
#define unchar unsigned char
#define uint unsigned int
- 全局变量和常量定义
unchar code dis_table[] = "0123456789"; // 数字显示表
sbit button = P3^2; // 定义按键
sbit lcden = P3^4; // 定义LCD的E端口
sbit lcdrw = P3^6; // 定义LCD的RW端口
sbit lcdrs = P3^5; // 定义LCD的RS端口
sbit SCL = P2^0; // 定义SCL时钟线端口
sbit SDA = P2^1; // 定义SDA数据线端口
- 延时函数
void Delay() {
_nop_(); _nop_(); _nop_(); _nop_(); _nop_();
}
void DelayMs(uint ms) {
uint i;
while(ms--) {
for(i = 110; i > 0; i--) ;
}
}
- LCD1602操作函数
void Write_Com(unchar com) {
lcdrs = 0; // 选择指令寄存器
P0 = com;
DelayMs(5);
lcden = 1;
DelayMs(5);
lcden = 0;
}
void Write_data(unchar datas) {
lcdrs = 1; // 选择数据寄存器
P0 = datas;
DelayMs(5);
lcden = 1;
DelayMs(5);
lcden = 0;
}
void init() {
lcdrw = 0; // 写操作
lcden = 0;
Write_Com(0x38); // 8位数据接口,两行显示,5x7点阵
Write_Com(0x0c); // 显示开,光标关,闪烁关
Write_Com(0x06); // 写入新数据后光标右移,显示不移动
Write_Com(0x01); // 清屏
}
- I2C操作函数
void IIC_init() {
SCL = 1;
Delay();
SDA = 1;
Delay();
}
void IIC_start() {
SDA = 1;
Delay();
SCL = 1;
Delay();
SDA = 0;
Delay();
}
void IIC_respons() {
unchar i = 0;
SCL = 1;
Delay();
while(SDA == 1 && (i < 255)) {
i++;
}
SCL = 0;
Delay();
}
void IIC_stop() {
SDA = 0;
Delay();
SCL = 1;
Delay();
SDA = 1;
Delay();
}
void IIC_writebyte(unchar date) {
unchar i, temp;
temp = date;
for(i = 0; i < 8; i++) {
temp = temp << 1;
SCL = 0;
Delay();
SDA = CY;
Delay();
SCL = 1;
Delay();
}
SCL = 0;
Delay();
SCL = 1;
Delay();
}
unchar IIC_readbyte() {
unchar i, Data;
SCL = 0;
Delay();
SDA = 1;
for(i = 0; i < 8; i++) {
SCL = 1;
Delay();
SDA = 1;
Data = (Data << 1) | SDA;
SCL = 0;
Delay();
}
Delay();
return Data;
}
- EEPROM读写函数
void Write_add(unchar date, unchar address) {
IIC_start();
IIC_writebyte(0xa0); // 写入设备地址
IIC_respons();
IIC_writebyte(address); // 写入存储地址
IIC_respons();
IIC_writebyte(date); // 写入数据
IIC_respons();
IIC_stop();
}
unchar Read_add(unchar address) {
unchar datas;
IIC_start();
IIC_writebyte(0xa0); // 写入设备地址
IIC_respons();
IIC_writebyte(address); // 写入存储地址
IIC_respons();
IIC_start();
IIC_writebyte(0xa1); // 读取设备地址
IIC_respons();
date = IIC_readbyte(); // 读取数据
IIC_stop();
return datas;
}
- 显示函数
void display(unchar date) {
Write_Com(0x80); // 设置显示位置为第一行第一个字符
Write_data(dis_table[date/100]); // 显示百位
Write_data(dis_table[date%100/10]); // 显示十位
Write_data(dis_table[date%10]); // 显示个位
}
- 主函数
void main() {
unchar num, NUM;
init();
IIC_init();
while(1) {
if(button == 0) { // 检测按键按下
DelayMs(10); // 消抖
if(button == 0) {
num = Read_add(0x00); // 读取当前计数值
num++;
if(num > 255) num = 0; // 限制计数值在0-255范围内
Write_add(num, 0x00); // 将新值写入EEPROM
}
while(button == 0); // 等待按键释放
}
NUM = Read_add(0x00); // 读取当前计数值
display(NUM); // 显示计数值
}
}
六、实验步骤
- 按照电路连接图连接好硬件电路
- 将编译好的程序烧录到89C51单片机中
- 接通电源,观察LCD1602显示初始计数值(应为0)
- 按下按键,观察计数值增加
- 断电后重新上电,观察计数值是否保持断电前的值
七、实验结果与分析
- 实验结果
- 系统上电后,LCD1602显示初始计数值0
- 每次按下按键,计数值增加1
- 当计数值达到255后,再按一次按键,计数值变为0
- 断电后重新上电,计数值保持断电前的值
- 结果分析
- 系统能够正确读取和写入EEPROM中的数据,实现了断电记忆功能
- 按键消抖处理有效,避免了按键抖动导致的误计数
- LCD1602显示正常,能够实时显示当前计数值
- 计数值范围限制在0-255之间,符合设计要求
八、实验总结
通过本次实验,我掌握了以下知识和技能:
- 89C51单片机的基本编程方法
- I2C总线协议及其在单片机中的应用
- AT24C02 EEPROM的使用方法
- LCD1602液晶显示模块的编程和显示控制
- 按键消抖处理的方法
实验中遇到的问题及解决方法:
- 问题:初始上电时LCD显示乱码
解决:检查发现LCD初始化时序不正确,调整延时后解决 - 问题:按键按下时计数值多次增加
解决:增加按键消抖处理,并在按键按下后等待释放 - 问题:断电后重新上电计数值不记忆
解决:检查EEPROM读写函数,发现I2C时序不正确,调整后解决
九、实验要求
- 可以增加减计数功能,通过另一个按键实现
- 可以增加清零功能,通过长按按键实现
- 可以优化I2C通信的延时函数,提高通信效率
- 可以增加计数值范围限制的提示信息