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.hqwc.cn/news/58649.html

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

相关文章

安防监控视频汇聚平台EasyCVR分发的FLV视频流在VLC中无法播放是什么原因?

众所周知&#xff0c;TSINGSEE青犀视频汇聚平台EasyCVR可支持多协议方式接入&#xff0c;包括主流标准协议国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。在视频流的处理与分发上&#xff0c;视频监控…

安全防御(3)

1.总结当堂NAT与双机热备原理&#xff0c;形成思维导图 2.完成课堂nat与双机热备试验 引用IDS是指入侵检测系统&#xff0c;它可以在网络中检测和防御入侵行为。IDS的签名是指根据已知入侵行为的特征制定的规则&#xff0c;用于检测和警告可能存在的入侵行为。签名过滤器可以根…

人工智能编程软件 python,python人工智能编程入门

大家好&#xff0c;给大家分享一下人工智能python编程具体做什么&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 有不少同学学习 Python 的原因是对人工智能感兴趣&#xff0c;有志于从事相关行业。今天我们来聊聊这个方向所需要的一些技能…

8月8日上课内容 研究nginx组件rewrite

location 匹配uri location 匹配的规则和优先级。&#xff08;重点&#xff0c;面试会问&#xff0c;必须理解和掌握&#xff09; nginx常用的变量&#xff0c;这个要求掌握 rewrite&#xff1a;重定向功能。有需要掌握&#xff0c;有需要理解的。 location匹配&#xff1a;…

【单片机】51单片机串口的收发实验,串口程序

这段代码是使用C语言编写的用于8051单片机的串口通信程序。它实现了以下功能&#xff1a; 引入必要的头文件&#xff0c;包括reg52.h、intrins.h、string.h、stdio.h和stdlib.h。 定义了常量FSOC和BAUD&#xff0c;分别表示系统时钟频率和波特率。 定义了一个发送数据的函数…

BIO,NIO,AIO总结

文章目录 1. BIO (Blocking I/O)1.1 传统 BIO1.2 伪异步 IO1.3 代码示例 1.4 总结2. NIO (New I/O)2.1 NIO 简介2.2 NIO的特性/NIO与IO区别1)Non-blocking IO&#xff08;非阻塞IO&#xff09;2)Buffer(缓冲区)3)Channel (通道)4)Selector (选择器) 2.3 NIO 读数据和写数据方式…

1749. 任意子数组和的绝对值的最大值

诸神缄默不语-个人CSDN博文目录 力扣刷题笔记 文章目录 1. 暴力搜索2. 动态规划3. 前缀和 1. 暴力搜索 直接用2个指针从索引0开始找到最后一个索引&#xff0c;时间复杂度大概是 O ( n 2 ) O(n^2) O(n2)吧&#xff0c;总之这么搞不行&#xff0c;以下是我用Python写的一些典型…

Python web实战之Django 的 RESTful API 设计详解

关键词: Python, Web 开发, Django, RESTful API 1 API的一些事儿 1.1 什么是API&#xff1f; API是应用程序编程接口&#xff08;Application Programming Interface&#xff09;的缩写。它是一种定义了不同软件组件之间交互方式的规范。API允许不同的应用程序之间进行通信和…

04-4_Qt 5.9 C++开发指南_时间日期与定时器

文章目录 1. 时间日期相关的类2. 源码2.1 可视化UI设计2.2 dialog.h2.3 dialog.cpp 1. 时间日期相关的类 时间日期是经常遇到的数据类型&#xff0c;Qt 中时间日期类型的类如下。 QTime:时间数据类型&#xff0c;仅表示时间&#xff0c;如 15:23:13。 QDate:日期数据类型&…

【uniapp 小程序开发语法篇】资源引入 | 语法介绍 | UTS 语法支持(链接格式)

博主&#xff1a;_LJaXi Or 東方幻想郷 专栏&#xff1a; uni-app | 小程序开发 开发工具&#xff1a;HBuilderX 小程序开发语法篇 引用组件easycom Js文件引入NPM支持 Css文件引入静态资源引入css 引入静态资源如何引入字体图标&#xff1f;css 引入字体图标示例nvue 引入字体…

OceanBase 4.1.0 clog 目录探究

基于OceanBase 4.x 版本如何统计租户每日 clog 日志生成量的背景下&#xff0c;探究以及如何查看租户 clog 的使用情况。 作者&#xff1a;姜宇 爱可生 DBA 团队成员&#xff0c;擅长数据库故障排查和处理。对技术抱有热忱&#xff0c;实践是检验真理的唯一标准~ 本文来源&…