51单片机学习-AT24C02数据存储秒表(定时器扫描按键数码管)

在这里插入图片描述
首先编写I2C模块,根据下面的原理图进行位声明:
在这里插入图片描述

sbit I2C_SCL = P2^1;
sbit I2C_SDA = P2^0;


再根据下面的时序结构图编写函数:
在这里插入图片描述

/*** @brief I2C开始* @param 无* @retval 无*/
void I2C_Start(void)
{I2C_SDA = 1; I2C_SCL = 1; I2C_SDA = 0;I2C_SCL = 0;
}/*** @brief I2C停止* @param 无* @retval 无*/
void I2C_Stop(void)
{I2C_SDA = 0;I2C_SCL = 1;I2C_SDA = 1;
}


在这里插入图片描述

/*** @brief I2C发送一个字节* @param 要发送的字节* @retval 无*/
void I2C_SendByte(unsigned char Byte)
{unsigned char i;for(i = 0; i < 8; i ++) {I2C_SDA = Byte & (0x80 >> i); //由高位到低位依次取出写入I2C_SCL = 1;I2C_SCL = 0;}
}


在这里插入图片描述

/*** @brief I2C接受一个字节* @param 无* @retval 接收到的字节*/
unsigned char I2C_ReceiveByte(void)
{unsigned char i, Byte = 0x00;I2C_SDA = 1;for(i = 0; i < 8; i ++) {I2C_SCL = 1;if(I2C_SDA) {Byte |= (0x80 >> i);} //由高位到低位把读到的 取出I2C_SCL = 0;}return Byte;
}


在这里插入图片描述

/*** @brief I2C发送应答* @param 应答位,0为应答,1为非应答* @retval 无*/
void I2C_SendAck(unsigned char AckBit)
{I2C_SDA = AckBit;I2C_SCL = 1;I2C_SCL = 0;
}/*** @brief I2C接收应答* @param 无* @retval 接收到的应答位,0为应答,1为非应答*/
unsigned char I2C_ReceiveAck(void)
{unsigned char AckBit;I2C_SDA = 1;I2C_SCL = 1;AckBit = I2C_SDA;I2C_SCL = 0;return AckBit;
}


接下来利用写好的I2C模块,创建AT24C02模块来凑成数据帧:

在这里插入图片描述
同样根据图中的时序模拟就可以了:

#include <REGX52.H>
#include "I2C.h"#define AT24C02_ADDRESS  0xA0 //写地址  /*** @brief AT24C02写入一个字节* @param 要写入字节的地址,0~255* @param 要写入的数据* @retval 无*/
void AT24C02_WriteByte(unsigned char WordAddress, Data)
{I2C_Start();I2C_SendByte(AT24C02_ADDRESS);I2C_ReceiveAck();I2C_SendByte(WordAddress);I2C_ReceiveAck();I2C_SendByte(Data);I2C_ReceiveAck();I2C_Stop();
}/*** @brief AT24C02读取一个字节* @param 要读出字节的地址 0~255* @retval 读出的数据*/
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{unsigned char Data;I2C_Start();I2C_SendByte(AT24C02_ADDRESS);I2C_ReceiveAck();I2C_SendByte(WordAddress);I2C_ReceiveAck();I2C_Start();I2C_SendByte(AT24C02_ADDRESS | 0x01);I2C_ReceiveAck();Data = I2C_ReceiveByte();I2C_SendAck(1); //非应答I2C_Stop();return Data;
}


在main中调用看一下效果如何(这种存储,写入之后是掉电不丢失的):

#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "LCD1602.h"
#include "AT24C02.h"unsigned char Data;void main()
{LCD_Init();LCD_ShowString(1, 1, "Hello");AT24C02_WriteByte(1, 66);Delay(5); //写完不要马上去读,写需要时间Data = AT24C02_ReadByte(1);LCD_ShowNum(2, 1, Data, 3);while(1){}
}

在这里插入图片描述



接下来配合上按键就可以做出一个按键存储器啦,有了之前编写的模块,只要适当地调用就可以了,以下是按键存储器的main.c文件代码:
按键1:增大数据
按键2:减小数据
按键3:写入
按键4:读出

#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "LCD1602.h"
#include "AT24C02.h"unsigned char KeyNum;
unsigned int Num;void main()
{LCD_Init();LCD_ShowNum(1, 1, Num, 5);while(1){KeyNum = Key();if(KeyNum == 1){Num ++;LCD_ShowNum(1, 1, Num, 5);}if(KeyNum == 2){Num --;LCD_ShowNum(1, 1, Num, 5);}if(KeyNum == 3) //写入{AT24C02_WriteByte(0, Num % 256); //Num十六位,先取低八位Delay(5);AT24C02_WriteByte(1, Num % 256); //存高八位Delay(5);LCD_ShowString(2, 1, "Write OK");Delay(1000);LCD_ShowString(2, 1, "        ");}if(KeyNum == 4) //读出{Num = AT24C02_ReadByte(0); //拿到低八位Num |= (AT24C02_ReadByte(1) << 8); //高八位左移8位或上去LCD_ShowNum(1, 1, Num, 5);LCD_ShowString(2, 1, "Read OK");Delay(1000);LCD_ShowString(2, 1, "       ");}}
}

接下来,利用定时器扫描按键和数码管,制作一个秒表:
由于按键和数码管都需要中断函数,所以就为Key和Nixie都定义一个中断的时候需要执行的操作,然后在中断函数中调用它们即可。。

先来看重新编写的Key模块,只要在中断函数中每隔20ms执行一次Key_Loop()就能保证读到每一次按键松开时的键码值,这样也达到了定时器扫描按键的目的

#include <REGX52.H>
#include "Delay.h"unsigned char Key_KeyNumber;/*** @brief 获取独立按键键码* @param 无* @retval 按下的按键的键码,范围0~4,无按键按下时返回0*/
unsigned char Key(void)
{unsigned char Temp;Temp = Key_KeyNumber;Key_KeyNumber = 0;return Temp;
}/*** @brief 获取按键实时状态,0为松开* @param 无* @retval 无*/
unsigned char Key_GetState()
{unsigned char KeyNumber = 0;if(P3_1 == 0){KeyNumber = 1;}if(P3_0 == 0){KeyNumber = 2;}if(P3_2 == 0){KeyNumber = 3;}if(P3_3 == 0){KeyNumber = 4;}return KeyNumber;
}void Key_Loop(void) //捕捉按键松开瞬间,并得到键码
{static unsigned char NowState, LastState;LastState = NowState;NowState = Key_GetState();if(LastState == 1 && NowState == 0) //此时按键从1松开{Key_KeyNumber = 1;}if(LastState == 2 && NowState == 0) //此时按键从2松开{Key_KeyNumber = 2;}if(LastState == 3 && NowState == 0) //此时按键从3松开{Key_KeyNumber = 3;}if(LastState == 4 && NowState == 0) //此时按键从4松开{Key_KeyNumber = 4;}
}


接下来要改写Nixie,使定时器扫描数码管,每隔2ms调用一次Nixie_Loop();

#include <REGX52.H>
#include "Delay.h"unsigned char NixieTable[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00};unsigned char Nixie_Buf[9] = {0,10,10,10,10,10,10,10,10};void Nixie_SetBuf(unsigned char Location, Number)
{Nixie_Buf[Location] = Number;
}/*** @brief 在对应位置显示对应数字* @param 位置,数字* @retval 无*/
void Nixie_Scan(unsigned char Location, Number)
{P0 = 0x00; //清零消影switch(Location){case 1: P2_4 = 1; P2_3 = 1; P2_2 = 1; break;case 2: P2_4 = 1; P2_3 = 1; P2_2 = 0; break;case 3: P2_4 = 1; P2_3 = 0; P2_2 = 1; break;case 4: P2_4 = 1; P2_3 = 0; P2_2 = 0; break;case 5: P2_4 = 0; P2_3 = 1; P2_2 = 1; break;case 6: P2_4 = 0; P2_3 = 1; P2_2 = 0; break;case 7: P2_4 = 0; P2_3 = 0; P2_2 = 1; break;case 8: P2_4 = 0; P2_3 = 0; P2_2 = 0; break;}P0 = NixieTable[Number];
}void Nixie_Loop(void) //每调用一次就向后显示一位
{static unsigned char i = 1;Nixie_Scan(i, Nixie_Buf[i]);i ++;if(i >= 9) i = 1;
}


因此,要同时维护这两个中断,中断函数需要这样写:

void Timer0_Routine() interrupt 1
{static unsigned int T0Count1, T0Count2;TL0 = 0x66;		//设置定时初值TH0 = 0xFC;		//设置定时初值T0Count1 ++;if(T0Count1 >= 20) //20ms扫描一次{T0Count1 = 0;Key_Loop();}T0Count2 ++; if(T0Count2 >= 2) //2ms扫描一次{T0Count2 = 0;Nixie_Loop();}
}

有了以上这些模块,就可以开始编写秒表了:

#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include "Nixie.h"
#include "Delay.h"unsigned char KeyNum;
unsigned char Min, Sec, MiniSec;
unsigned char RunFlag;void main()
{Timer0_Init();while(1){KeyNum = Key();if(KeyNum == 1) //启动/暂停{RunFlag = !RunFlag;}if(KeyNum == 2) //清零复位{Min = 0;Sec = 0;MiniSec = 0;}Nixie_SetBuf(1, Min / 10);Nixie_SetBuf(2, Min % 10);Nixie_SetBuf(3, 11);Nixie_SetBuf(4, Sec / 10);Nixie_SetBuf(5, Sec % 10);Nixie_SetBuf(6, 11);Nixie_SetBuf(7, MiniSec / 10);Nixie_SetBuf(8, MiniSec % 10);}
}void Sec_Loop(void) //每过一个MiniSec执行一次
{if(RunFlag == 0) return;//暂停中不执行MiniSec ++;if(MiniSec >= 100){MiniSec = 0;Sec ++;if(Sec >= 60){Sec = 0;Min ++;if(Min >= 60){Min = 0;}}}
}void Timer0_Routine() interrupt 1
{static unsigned int T0Count1, T0Count2, T0Count3;TL0 = 0x66;		//设置定时初值TH0 = 0xFC;		//设置定时初值T0Count1 ++;if(T0Count1 >= 20) //20ms扫描一次{T0Count1 = 0;Key_Loop();}T0Count2 ++; if(T0Count2 >= 2) //2ms扫描一次{T0Count2 = 0;Nixie_Loop();}T0Count3 ++;if(T0Count3 >= 10) //一个MiniSec扫描一次{T0Count3 = 0;Sec_Loop();}
}

如果要和之前的AT24C02结合起来,那就KeyNum为3的时候写入秒表的时间,KeyNum为4的时候读出时间,利用WriteByte和ReadByte即可
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/28005.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SDXL 1.0出图效果直逼Midjourney!手把手教你快速体验!

介绍 最近&#xff0c;Stability AI正式推出了全新的SDXL 1.0版本。经过我的实际测试&#xff0c;与之前的1.5版本相比&#xff0c;XL的效果有了巨大的提升&#xff0c;可以说是全方位的超越。不仅在理解提示词方面表现出色&#xff0c;而且图片的构图、颜色渲染和画面细腻程度…

高绩效项目管理助力企业数字化变革︱海克斯康数字智能大中华区PMO经理周游

海克斯康数字智能大中华区PMO经理周游先生受邀为由PMO评论主办的2023第十二届中国PMO大会演讲嘉宾&#xff0c;演讲议题&#xff1a;高绩效项目管理助力企业数字化变革。大会将于8月12-13日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&#xff1a; 在当今项目驱动的…

全网首档操作系统探访体验栏目“龙蜥+超级探访”震撼上线!看国产 OS 如何乘风破浪

编者按&#xff1a;随着龙蜥社区规模的迅速壮大&#xff0c;为了更好的服务于生态伙伴和广大用户&#xff0c;龙蜥社区推出“全网首档操作系统探访体验节目”《龙蜥超级探访》&#xff0c;该栏目将带领大家走进龙蜥的生态伙伴和用户现场&#xff0c;共同揭示龙蜥与生态伙伴如何…

Django实现音乐网站 ⑻

使用Python Django框架制作一个音乐网站&#xff0c; 本篇主要是后台对单曲原有功能的基础上进行部分功能实现和显示优化。 目录 新增编辑 歌手下拉显示修改 设置歌曲时长 安装eyed3库 获取mp3时长 歌曲时长字段修改 重写save方法 增加歌手单曲数量 查询歌手单曲数量 …

爬虫011_元组高级操作_以及字符串的切片操作---python工作笔记030

获取元组的下标对应的值 注意元组是不可以修改值的,只能获取不能修改 但是列表是可以修改值的对吧

GO学习之 网络通信(Net/Http)

GO系列 1、GO学习之Hello World 2、GO学习之入门语法 3、GO学习之切片操作 4、GO学习之 Map 操作 5、GO学习之 结构体 操作 6、GO学习之 通道(Channel) 7、GO学习之 多线程(goroutine) 8、GO学习之 函数(Function) 9、GO学习之 接口(Interface) 10、 文章目录 GO系列前言一、H…

手写SpringCloud系列-一分钟理解微服务注册中心(Nacos)原理。

手写SpringCLoud项目地址&#xff0c;求个star github:https://github.com/huangjianguo2000/spring-cloud-lightweight gitee:https://gitee.com/huangjianguo2000/spring-cloud-lightweigh 一&#xff1a;什么是注册中心 1. 总结服务注册中心 我们可以理解注册中心就是一个…

【100天精通python】Day28:文件与IO操作_JSON文件处理

目录 专栏导读 1. JSON数据格式简介 1.1 示例JSON数据 1.2 JSON文件的特点 2 json模块的常用操作 2.1 读写JSON文件的示例 2.2 解析JSON字符串 2.3 修改JSON数据 2.4 查询和操作嵌套数据 2.5 处理包含特殊字符的JSON文件 2.6 处理日期和时间 2.7 处理大型JSON文…

Ceph入门到精通-远程开发Windows下使用SSH密钥实现免密登陆Linux服务器

工具&#xff1a; win10、WinSCP 服务器生成ssh密钥&#xff1a; 打开终端&#xff0c;使账号密码登录&#xff0c;输入命令 ssh-keygen -t rsa Winscp下载 Downloading WinSCP-6.1.1-Setup.exe :: WinSCP window 生成密钥 打开powershell ssh-keygen -t rsa 注意路径 …

JVM-运行时数据区

目录 什么是运行时数据区&#xff1f; 方法区 堆 程序计数器 虚拟机栈 局部变量表 操作数栈 动态连接 运行时常量池 方法返回地址 附加信息 本地方法栈 总结&#xff1a; 什么是运行时数据区&#xff1f; Java虚拟机在执行Java程序时&#xff0c;将它管…

Excel表格(一)

1.单一栏的宽度和高度设置 2.大标题的跨栏居中 3.让单元格内的文字------自动适应 4.序号递增 5.货币符号 6.日期格式的选择 选到单元格&#xff0c;选中对应的日期格式 7.自动求和的计算 然后在按住回车键即可求出当前行的金额 点击自动求和 8.冻结表格栏 9.排序 1.单栏排序 …

拥抱AIGC浪潮,亚信科技将如何把握时代新增量?

去年底&#xff0c;由ChatGPT带起的AIGC浪潮以迅雷不及掩耳之势席卷全球。 当互联网技术的人口红利逐渐消退之际&#xff0c;AIGC就像打开通用人工智能大门的那把秘钥&#xff0c;加速开启数智化时代的到来。正如OpenAI CEO Sam Altman所言&#xff1a;一个全新的摩尔定律可能…

基于Spring Boot的医院预约挂号网站设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的医院预约挂号网站设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java spring…

k8s之Pod控制器

目录 一、Pod控制器及其功用二、pod控制器的多种类型2.1 pod容器中的有状态和无状态的区别 三、Deployment 控制器四、SatefulSet 控制器4.1 StatefulSet由以下几个部分组成4.2 为什么要有headless&#xff1f;4.3 为什么要有volumeClaimTemplate&#xff1f;4.4 滚动更新4.5 扩…

【LeetCode每日一题】——304.二维区域和检索-矩阵不可变

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 矩阵 二【题目难度】 中等 三【题目编号】 304.二维区域和检索-矩阵不可变 四【题目描述】 …

拦截器对接口细粒度权限校验

文章目录 一、逻辑分析二、校验规则1.规则类型2.规则划分3.规则配置信息4.规则案例说明5.规则加载 三、拦截器定义1.自定义拦截器2.注册拦截器 四、获取请求参数1.获取get提交方式参数2.获取post提交方式参数&#xff08;1&#xff09;定义RequestWrapper类&#xff08;2&#…

地图 SDK gitlab 测试代码环境配置

文章目录 1、Gradle 插件版本和 Gradle 版本2、NDK 路径3、JDK 版本4、修改变量5、重新 BuildQ&A&#xff1a; test 用例启动之后问题问题描述 拉下项目的 dev 分支&#xff0c;然后依赖的 mapsdk-base 也完成下载 &#xff0c;之后就是Android Studio 配置环境 1、Gradle …

JDBC(常用类与接口、实现数据库的增删查改)

目录 1.Connection接口常用方法 2.DriverMange类 3.Statement接口 4.实现表的数据更新&#xff08;增、改、删&#xff09; 5.实现数据查找&#xff08;ResultSet接口&#xff09; 6.PreparedStatement 数据更新 1.Connection接口常用方法 用来与数据库连接的对象&#xff…

【Spring】Spring AOP 初识及实现原理解析

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE进阶 目录 文章目录 一、初识AOP 1.1 什么是AOP&#xff1f; 1.2 AOP的组成 1.2.1 切面&#xff08;Aspect&#xff09; 1.2.2 切点&#xff08;Pointcut&#xff09; 1.2.3 连接点&…

blender基础认识(选项开关、工具栏、视图等)

文章目录 引言一、大纲选项开关和保存启动文件1. 大纲选项1. 禁用选中2. 视图影藏3. 视图禁用4. 渲染禁用 2. 保存启动文件 二、工具栏和侧边栏1. 左侧工具栏2. 右侧工具栏 三、视图1. 视角2. 缩放3. 拖拽4. 摄像机视角5. 切换正交视图6. 局部视图7. 显示隐藏 四、添加删除物体…