当前位置: 首页 > news >正文

51单片机学习--DS1302可调时钟

之前学习过用定时器做的时钟,但是那样不仅误差大还费CPU,接下来利用DS1302时钟模块做一个可调实时时钟



这一次直接编写DS1302模块,首先要在DS1392.c 中根据下面的模块原理图进行位声明:

sbit DS1302_SCLK = P3^6;
sbit DS1302_IO = P3^4;
sbit DS1302_CE = P3^5;

在这里插入图片描述
在这里插入图片描述



命令字: 命令字确定了是要写还是要读,以及操作的是时还是分还是秒
在这里插入图片描述
首先需要一个初始化函数:

void DS1302_Init(void)
{DS1302_CE = 0;DS1302_SCLK = 0;
}



在这里插入图片描述
工作时CE必须置1,上升沿的时候可以读, 下降沿的时候可以写
可以理解为0是写入模式,1是读取模式
IO口从左往右是由低位到高位
要注意时序图中Read比Write少一个脉冲因为它上升到1完成了最后一个命令行位的写入之后马上要回到0开始进行读取功能了
单字节写入函数:
按照时序图进行模拟,Command:命令行,Data:写入的数据

void DS1302_WriteByte(unsigned char Command, Data)
{unsigned char i;DS1302_CE = 1;for(i = 0; i < 8; i ++) {DS1302_IO = Command & (0x01 << i); //IO非0即1,从低到高每次取出一位DS1302_SCLK = 1;Delay(10);DS1302_SCLK = 0;}for(i = 0; i < 8; i ++) {DS1302_IO = Data & (0x01 << i); //IO非0即1,从低到高每次取出一位DS1302_SCLK = 1;Delay(10);DS1302_SCLK = 0;}DS1302_CE = 0;
}

在这里插入图片描述
单字节读出函数

unsigned char DS1302_ReadByte(unsigned char Command)
{unsigned char i, Data = 0x00;DS1302_CE = 1;for(i = 0; i < 8; i ++) {DS1302_IO = Command & (0x01 << i); //IO非0即1,从低到高每次取出一位DS1302_SCLK = 0; //写入Delay(10);DS1302_SCLK = 1; //这样在不改变写入时序的同时还能保证最后是1}Delay(10);//接下来要读的数据已经在IO口上了已经可以读了for(i = 0; i < 8; i ++) {DS1302_SCLK = 1; //读入Delay(10);DS1302_SCLK = 0;if(DS1302_IO) {Data |= (0x01 << i);} //把IO口的数据由低位到高位复现在Data上}DS1302_IO = 0;DS1302_CE = 0;return Data;
}

要注意在main.c中使用时需要在DS1302初始化后,调用:DS1302_WriteByte(0x8E, 0x00); //关闭写入保护
再进行正常的写入



但其实,在DS1302模块的寄存器存储的数据都是BCD码
所以时钟的秒会从1, 2, ····9然后直接跳到16
9 = 0000 1001
根据BCD的进位原则,四位二进制数达到10就要清零进位了,下一个BCD码是:
0001 0000 这个数以十进制显示在LCD上就是16
此时只要把ShowNum改成ShowHexNum即可正常显示10, 11, 12·····
在这里插入图片描述
也可以利用公式来用十进制显示:

LCD_ShowNum(2, 1, Sec / 16 * 10 + Sec % 16, 3 );


接下来就可以编写一个完整的时钟模块了

#include <REGX52.H>
#include "Delay.h"sbit DS1302_SCLK = P3^6;
sbit DS1302_IO = P3^4;
sbit DS1302_CE = P3^5;//其实写的地址 或上 0x01 就是读的地址了
//所以下面只要重定义写的地址就行了
#define DS1302_SECOND  0x80
#define DS1302_MINUTE  0x82
#define DS1302_HOUR    0x84
#define DS1302_DATE    0x86
#define DS1302_MONTH   0x88
#define DS1302_DAY     0x8A
#define DS1302_YEAR    0x8C
#define DS1302_WP      0x8E  //写入保护的地址unsigned char DS1302_Time[] = {23, 8, 2, 10, 28, 50, 3};void DS1302_Init(void)
{DS1302_CE = 0;DS1302_SCLK = 0;
}void DS1302_WriteByte(unsigned char Command, Data)
{unsigned char i;DS1302_CE = 1;for(i = 0; i < 8; i ++) {DS1302_IO = Command & (0x01 << i); //IO非0即1,从低到高每次取出一位DS1302_SCLK = 1;Delay(10);DS1302_SCLK = 0;}for(i = 0; i < 8; i ++) {DS1302_IO = Data & (0x01 << i); //IO非0即1,从低到高每次取出一位DS1302_SCLK = 1;Delay(10);DS1302_SCLK = 0;}DS1302_CE = 0;
}unsigned char DS1302_ReadByte(unsigned char Command)
{unsigned char i, Data = 0x00;DS1302_CE = 1;for(i = 0; i < 8; i ++) {DS1302_IO = Command & (0x01 << i); //IO非0即1,从低到高每次取出一位DS1302_SCLK = 0; //写入Delay(10);DS1302_SCLK = 1; //这样在不改变写入时序的同时还能保证最后是1}Delay(10);//接下来要读的数据已经在IO口上了已经可以读了for(i = 0; i < 8; i ++) {DS1302_SCLK = 1; //读入Delay(10);DS1302_SCLK = 0;if(DS1302_IO) {Data |= (0x01 << i);} //把IO口的数据由低位到高位复现在Data上}DS1302_IO = 0;DS1302_CE = 0;return Data;
}void DS1302_SetTime(void) //将数组中的时间写入芯片
{DS1302_WriteByte(DS1302_WP, 0x00); //关闭写保护DS1302_WriteByte(DS1302_YEAR, DS1302_Time[0]/10*16+DS1302_Time[0]%10);DS1302_WriteByte(DS1302_MONTH, DS1302_Time[1]/10*16+DS1302_Time[1]%10);DS1302_WriteByte(DS1302_DATE, DS1302_Time[2]/10*16+DS1302_Time[2]%10);DS1302_WriteByte(DS1302_HOUR, DS1302_Time[3]/10*16+DS1302_Time[3]%10);DS1302_WriteByte(DS1302_MINUTE, DS1302_Time[4]/10*16+DS1302_Time[4]%10);DS1302_WriteByte(DS1302_SECOND, DS1302_Time[5]/10*16+DS1302_Time[5]%10);DS1302_WriteByte(DS1302_DAY, DS1302_Time[6]/10*16+DS1302_Time[6]%10);DS1302_WriteByte(DS1302_WP, 0x80); //打开写保护
}void DS1302_ReadTime(void) //把芯片中的时间读到数组中
{unsigned char temp;temp = DS1302_ReadByte(DS1302_YEAR | 0x01); //写的地址或上0x01就是读的地址DS1302_Time[0] = temp/16*10+temp%16;temp = DS1302_ReadByte(DS1302_MONTH | 0x01);DS1302_Time[1] = temp/16*10+temp%16;temp = DS1302_ReadByte(DS1302_DATE | 0x01);DS1302_Time[2] = temp/16*10+temp%16;temp = DS1302_ReadByte(DS1302_HOUR | 0x01);DS1302_Time[3] = temp/16*10+temp%16;temp = DS1302_ReadByte(DS1302_MINUTE | 0x01);DS1302_Time[4] = temp/16*10+temp%16;temp = DS1302_ReadByte(DS1302_SECOND | 0x01);DS1302_Time[5] = temp/16*10+temp%16;temp = DS1302_ReadByte(DS1302_DAY | 0x01);DS1302_Time[6] = temp/16*10+temp%16;
}

其实如果把BCD码与十进制相互转化的部分写成函数来处理,会大大减少代码量
要注意,这个封装好的DS1302.c要拿到外部调用的话,其中的DS1302_Time数组也需要在头文件中声明,外部可调用的变量要加上关键字extern

#ifndef __DS1302_H__
#define __DS1302_H__extern unsigned char DS1302_Time[];  //外部可调用的变量也需要声明void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command, Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);#endif

最后给出main.c代码

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"unsigned char Sec;void main()
{LCD_Init();DS1302_Init();DS1302_WriteByte(0x8E, 0x00); //关闭写入保护LCD_ShowString(1, 1, "  -  -  ");LCD_ShowString(2, 1, "  :  :  ");DS1302_SetTime();while(1){DS1302_ReadTime();LCD_ShowNum(1, 1, DS1302_Time[0], 2);LCD_ShowNum(1, 4, DS1302_Time[1], 2);LCD_ShowNum(1, 7, DS1302_Time[2], 2);LCD_ShowNum(2, 1, DS1302_Time[3], 2);LCD_ShowNum(2, 4, DS1302_Time[4], 2);LCD_ShowNum(2, 7, DS1302_Time[5], 2);LCD_ShowNum(2, 10, DS1302_Time[6], 2);}
}

在这里插入图片描述



但是一个好的时钟远不止显示时间这么简单,还需要具有可调的功能。。
于是需要加入按键模块实现修改时间和定时器模块来实现光标闪烁效果

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Timer0.h"unsigned char MODE, KeyNum, TimeSetSelect, TimeFlash;void Time_Show(void) //在LCD显示数组时间
{DS1302_ReadTime();LCD_ShowNum(1, 1, DS1302_Time[0], 2);LCD_ShowNum(1, 4, DS1302_Time[1], 2);LCD_ShowNum(1, 7, DS1302_Time[2], 2);LCD_ShowNum(2, 1, DS1302_Time[3], 2);LCD_ShowNum(2, 4, DS1302_Time[4], 2);LCD_ShowNum(2, 7, DS1302_Time[5], 2);LCD_ShowNum(2, 10, DS1302_Time[6], 2);
}void Time_Set(void) //利用按键修改数组并重新读取数组显示在LCD
{if(KeyNum == 2) //选择修改的位置{TimeSetSelect ++;TimeSetSelect %= 7;}if(KeyNum == 3) //增加时间{DS1302_Time[TimeSetSelect] ++;}if(KeyNum == 4) //减少时间{DS1302_Time[TimeSetSelect] --;}//接下来更新显示	if(TimeFlash == 0 && TimeSetSelect == 0) LCD_ShowString(1, 1, "  ");//熄灭的时候用空格覆盖else LCD_ShowNum(1, 1, DS1302_Time[0], 2);if(TimeFlash == 0 && TimeSetSelect == 1) LCD_ShowString(1, 4, "  ");else LCD_ShowNum(1, 4, DS1302_Time[1], 2);if(TimeFlash == 0 && TimeSetSelect == 2) LCD_ShowString(1, 7, "  ");else LCD_ShowNum(1, 7, DS1302_Time[2], 2);if(TimeFlash == 0 && TimeSetSelect == 3) LCD_ShowString(2, 1, "  ");else LCD_ShowNum(2, 1, DS1302_Time[3], 2);if(TimeFlash == 0 && TimeSetSelect == 4) LCD_ShowString(2, 4, "  ");else LCD_ShowNum(2, 4, DS1302_Time[4], 2);if(TimeFlash == 0 && TimeSetSelect == 5) LCD_ShowString(2, 7, "  ");else LCD_ShowNum(2, 7, DS1302_Time[5], 2);if(TimeFlash == 0 && TimeSetSelect == 6) LCD_ShowString(2, 10, "  ");else LCD_ShowNum(2, 10, DS1302_Time[6], 2);
}void Timer0_Routine() interrupt 1
{static unsigned int T0Count;TL0 = 0x66;		//设置定时初值TH0 = 0xFC;		//设置定时初值T0Count ++;if(T0Count >= 1000) //1s执行一次{T0Count = 0;TimeFlash = !TimeFlash; //1的时候显示数字,0的时候熄灭,达成闪烁}
}void main()
{LCD_Init();DS1302_Init();Timer0_Init();DS1302_WriteByte(0x8E, 0x00); //关闭写入保护LCD_ShowString(1, 1, "  -  -  ");LCD_ShowString(2, 1, "  :  :  ");DS1302_SetTime(); //先从数组中读取时间到芯片里while(1){KeyNum = Key(); //读取按键if(KeyNum == 1) //按下按键1切换时钟模式{if(MODE == 1) {MODE = 0; DS1302_SetTime();} //回到显示模式要重新读取数组到芯片里else MODE = 1;}switch(MODE){case 0: Time_Show(); break;case 1: Time_Set(); break;}}
}

但是这个程序有个bug,就是修改时间的部分没有进行越界判断,可能会出现13月,32日这样的数据,这个修改起来就是逻辑上的事情,在Time++或者–的时候特判一下就行,比较容易,这里偷个懒就不改了

相关文章:

51单片机学习--DS1302可调时钟

之前学习过用定时器做的时钟&#xff0c;但是那样不仅误差大还费CPU&#xff0c;接下来利用DS1302时钟模块做一个可调实时时钟 这一次直接编写DS1302模块&#xff0c;首先要在DS1392.c 中根据下面的模块原理图进行位声明&#xff1a; sbit DS1302_SCLK P3^6; sbit DS1302_IO …...

Matlab统计字符串中共有多少种字符以及每种字符出现次数的功能实现(Matlab R2021a)

在做2023年深圳杯B题的时候&#xff0c;需要使用隐写技术&#xff08;将特定信息嵌入信息载体且不易被察觉&#xff0c;可被广泛地应用于著作权保护、数据附加等领域&#xff09;将《中华人民共和国著作权法》全篇10314个字符写入图片&#xff0c;首先我想到的是利用霍夫曼编码…...

HTTPS文件传输

目录 0.https概述1.单钥匙锁2.双钥匙锁 - 防篡改3.双钥匙锁 - 防泄漏4.单双钥匙锁相互配合 0.https概述 HTTPS其实就是HTTP协议加上TLS/SSL&#xff0c;SSL是个加密套件&#xff0c;负责对HTTP的数据进行加密&#xff0c;TLS是SSL的升级版&#xff0c;现在提到HTTPS&#xff0…...

LOL-v2数据集和VE-LOL数据集的区别

LOL-v2数据集和VE-LOL数据集的区别 LOL-v2 LOL-v2数据集[64]包括两个不同的子集&#xff0c;即LOL-v2-real和LOL-v2-synthetic。LOL-v2-real子集是通过改变ISO和曝光时间在真实场景中捕获的&#xff0c;包括689对用于训练和测试的图像。在LOL-v2-synthetic子集中&#xff0c;…...

RabbitMQ(一) - 基本结构、SpringBoot整合RabbitMQ、工作队列、发布订阅、直接、主题交换机模式

RabbitMQ结构 Publisher &#xff1a; 生产者 Queue: 存储消息的容器队列&#xff1b; Consumer:消费者 Connection&#xff1a;消费者与消息服务的TCP连接 Channel:信道&#xff0c;是TCP里面的虚拟连接。例如&#xff1a;电缆相当于TCP&#xff0c;信道是一条独立光纤束&…...

涉及IMU的专业术语

文章目录 零偏维纳过程/布朗运动随机游走航迹推算 零偏 IMU&#xff08;惯性测量单元&#xff09;是一种用于测量物体在空间中的加速度和角速度的装置。它通常由加速度计和陀螺仪组成&#xff0c;这些传感器可以帮助确定物体的运动状态和方向。 在IMU中&#xff0c;“零偏”&…...

二维数组对角线判断

二维数组对角线判断 对于两个点&#xff08;x1, y1)和(x2, y2)。如何判断二者是否在同一条正对角线&#xff0c;反对角线&#xff0c;或者正或反对角线上&#xff1f; 正对角线判断 x2-x1 y2 -y1 证明&#xff1a;任意一点&#xff08;x1k, y1k)&#xff0c;&#xff08;k…...

数据可视化(六)多个子图及seaborn使用

1.多个子图绘制 #绘制多个子图 #subplot&#xff08;*args&#xff0c;**kwargs&#xff09; 每个subplot函数只能绘制一个子图 #subplots&#xff08;nrows&#xff0c;ncols&#xff09; #fig_add_subplot(行&#xff0c;列&#xff0c;区域) #绘制子图第一种方式 plt.subp…...

opencv-34 图像平滑处理-双边滤波cv2.bilateralFilter()

双边滤波&#xff08;BilateralFiltering&#xff09;是一种图像处理滤波技术&#xff0c;用于平滑图像并同时保留边缘信息。与其他传统的线性滤波方法不同&#xff0c;双边滤波在考虑像素之间的空间距离之外&#xff0c;还考虑了像素之间的灰度值相似性。这使得双边滤波能够有…...

Leetcode 268. Missing Number

Problem Given an array nums containing n distinct numbers in the range [0, n], return the only number in the range that is missing from the array. Algorithm Sum all the numbers as x x x and use n ( n 1 ) 2 − x \frac{n(n1)}{2} - x 2n(n1)​−x. Code …...

MybatisPlus实战笔记

概述 Mybatis支持定制化SQL、存储过程以及高级映射&#xff0c;避免几乎所有的 JDBC 代码和手动设置参数以及获取结果集。可以使用简单的 XML 或注解来配置和映射原生信息&#xff0c;将接口和Java的POJO映射成数据库中的记录。 缺点&#xff1a; SQL工作量很大&#xff0c;尤…...

Android Studio 报错:Failed to create Jar file xxxxx.jar

通过分析&#xff0c;新下载的项目没有project/gradle目录&#xff0c;故通过其他项目复制到当前项目&#xff0c;就解决了该问题。 同时也出现了新的问题 Unable to start the daemon process.The project uses Gradle 4.1 which is incompatible with Java 11 or newer.原因…...

Django实现音乐网站 ⑸

使用Python Django框架制作一个音乐网站&#xff0c; 本篇主要是配置媒体资源设置。 目录 配置介绍 设置媒体资源 创建媒体资源目录 修改settings.py 注册媒体资源路由 总结 配置介绍 静态资源是指项目配置的js/css/image等系统常用文件。对于一些经常变动的资源&#x…...

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)七:工作台界面实现

一、本章内容 本章实现工作台界面相关内容,包括echart框架引入,mock框架引入等,实现工作台界面框架搭建,数据加载。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 基于VUE3+Layui从头搭建通用后台管理系统合集-工作台界面布局实现 五、…...

前端vue uni-app自定义精美海报生成组件

在当前技术飞速发展的时代&#xff0c;软件开发的复杂度也在不断提高。传统的开发方式往往将一个系统做成整块应用&#xff0c;一个小的改动或者一个小功能的增加都可能引起整体逻辑的修改&#xff0c;从而造成牵一发而动全身的情况。为了解决这个问题&#xff0c;组件化开发逐…...

高通滤波器,低通滤波器

1.高通滤波器是根据像素与邻近像素的亮度差值来提升该像素的亮度。 import cv2 import numpy as np from scipy import ndimagekernel_3_3 np.array([[-1,-1,-1],[-1,8,-1],[-1,-1,-1]]) print(kernel_3_3) kernel_5_5 np.array([[-1,-1,-1,-1,-1],[-1,1,2,1,-1],[-1,2,4,2,-…...

机器学习深度学习——卷积的多输入多输出通道

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——从全连接层到卷积 &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习 希望文章对你们有所帮…...

HTML5中Canvas学习笔记:Canvas

目录 一、HTML中Canvas画图strokeStyle 和 fillStyle 的区别是什么&#xff1f; 二、如何设置一幅canvas图中某个颜色透明&#xff1f; 三、H5 canvas中strokeRect参数如果是小数&#xff0c;如何处理&#xff1f; 四、H5 Canvas中如何画圆角矩形框&#xff1f; 一、HTML中…...

Windows安装子系统Linux

Windows安装子系统(Linux ubuntu&#xff09; 安装条件步骤1.安装WSL命令2.设置Linux用户名和密码3.写个简单的.c程序看看4.如何互传文件 安装条件 Windows 10版本2004及更高的版本才能安装。 步骤 1.安装WSL命令 我们可以使用WSL来安装子系统 Linux ubuntu(默认是这个)。 …...

C 语言的 pow() 函数

作用: Calculates x raised to the power of y. 函数原型: double pow( double x, double y ); Required Header: <math.h> Compatibility: ANSI Return Value pow returns the value of x y x^{y} xy. No error message is printed on overflow or underflow. Paramete…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

C++ 设计模式 《小明的奶茶加料风波》

&#x1f468;‍&#x1f393; 模式名称&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09; &#x1f466; 小明最近上线了校园奶茶配送功能&#xff0c;业务火爆&#xff0c;大家都在加料&#xff1a; 有的同学要加波霸 &#x1f7e4;&#xff0c;有的要加椰果…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

Oracle11g安装包

Oracle 11g安装包 适用于windows系统&#xff0c;64位 下载路径 oracle 11g 安装包...

【无标题】湖北理元理律师事务所:债务优化中的生活保障与法律平衡之道

文/法律实务观察组 在债务重组领域&#xff0c;专业机构的核心价值不仅在于减轻债务数字&#xff0c;更在于帮助债务人在履行义务的同时维持基本生活尊严。湖北理元理律师事务所的服务实践表明&#xff0c;合法债务优化需同步实现三重平衡&#xff1a; 法律刚性&#xff08;债…...

数据库正常,但后端收不到数据原因及解决

从代码和日志来看&#xff0c;后端SQL查询确实返回了数据&#xff0c;但最终user对象却为null。这表明查询结果没有正确映射到User对象上。 在前后端分离&#xff0c;并且ai辅助开发的时候&#xff0c;很容易出现前后端变量名不一致情况&#xff0c;还不报错&#xff0c;只是单…...

若依项目部署--传统架构--未完待续

若依项目介绍 项目源码获取 #Git工具下载 dnf -y install git #若依项目获取 git clone https://gitee.com/y_project/RuoYi-Vue.git项目背景 随着企业信息化需求的增加&#xff0c;传统开发模式存在效率低&#xff0c;重复劳动多等问题。若依项目通过整合主流技术框架&…...