jjzjj

基于51单片机+DS1302时钟模块+LCD1602显示

皮卡丘吉尔 2023-10-27 原文

DS1302时钟模块LCD1602显示

DS1302时钟模块相关介绍

基本介绍

概述

DS1302 可慢速充电实时时钟芯片包含实时时钟/日历和 31 字节的非易失性静态 RAM。它经过一个简
单的串行接口与微处理器通信。实时时钟/日历可对秒,分,时,日,周,月,和年进行计数,对于小于
31 天的月,月末的日期自动进行调整,还具有闰年校正的功能。时钟可以采用 24 小时格式或
带 AM(上
午)/PM(下午)的 12 小时格式31 字节的== RAM== 可以用来临时保存一些重要数据。使用同步串行通信,
简化了 DS1302 与微处理器的通信。与时钟/RAM 通信仅需3 根线(1)RST(复位),(2)I/O(数据线)
和(3)SCLK(串行时钟)
。数据可以以每次一个字节的单字节形式或多达 31 字节的多字节形式传输。DS1302
能在非常低的功耗下工作,消耗小于 1µW 的功率便能保存数据和时钟信息。

特点

各引脚功能


各引脚的功能:
Vcc1:主电源;Vcc2:备份电源。 当Vcc2>Vcc1+0.2V时,由Vcc2向DS1302供电,当Vcc2<Vcc1时,由Vcc1向DS1302供电。
SCLK: 串行时钟,输入,控制数据的输入与输出;
I/O: 三线接口时的双向数据线;
CE: 输入信号,在读和写的器件必须为高。该引脚有两个功能:1.开始控制字访问移位寄存器的控制逻辑;2.提供结束单字节或多字节数据传输的方法。

相关寄存器

有关日历、时间的寄存器共12个,其中有7个寄存器(读时81H——8DH,写时80H——8CH==)存放的数据格式是BCD码形式。如图所示

小时寄存器
位7用于定义DS1302是采用12小时模式还是24小时模式。高:12小时模式。在12小时模式时,位5 为1时,表示PM。在24小时模式时,为5是第二个10小时位。
秒寄存器
的就位7定义为时钟暂停标志(CH)。1:时钟振荡器停止,DS1302进入低功耗状态,当该位置为0时,时钟开始运行。
控制寄存器
的位7是写保护位(WP),其他7位均置0,对时钟和RAM进行写操作时,WP必须为0,也就是关闭写保护,当WP为1时,就是只读模式。
DS1302相关的RAM地址
DS1302中附加31字节静态RAM的地址如下图

DS1302的工作模式寄存器
所谓的突发模式(BURST模式)就是一次性传送多个字节的时钟信号和RAM数据。比如我可以一次性把时间和日期写入,也可以一次性的读出时间和日期。

通过对 31(十进制)位地址寻址(地址/命令位于 1 至 5=逻辑 1),可以把时钟/日历或 RAM 寄存器规定为多字节方式。如前所述,位 6 规定时钟或 RAM 而位 0 规定读或写。在时钟\日历寄存器中的地址 9 至 31或 RAM 寄存器中的地址 31 不能存储数据。在多字节方式中读或写从地址 0 的位 0 开始。当以多字节方式写时钟寄存器时必须按数据传送的次序写最先 8 个寄存器。意思就是写我们不是只有7个吗?时分秒、年月日、周一共七个 但是吧它得字节数为8 所有要写够8次。
但是,当以多字节方式写 RAM 时,为了传送数据不必写所有 31 个字节。不管是否写了全部 31 个字节,所写的每一个字节都将传送至 RAM。

DS1302充电寄存器
这个我没整过,全部复制手册的。哈哈

这个寄存器控制 DS1302 的慢速充电特性。图 4 的简化电路表示慢速充电器的基本组成。慢速充电选择(TCS)位(位 4-7)控制慢速充电器的选择。为了防止偶然的因素使之工作,只有 1010 模式才能使慢
速充电器工作,所有其它的模式将禁止慢速充电器。DS1302 上电时,慢速充电器被禁止。二极管选择(DS)位(位 2-3)选择是一个二极管还是两个二极管连接在 Vcc2 与 Vcc1 之间。如果 DS 为 01,那么选择一个二极管;如果 DS 为 10,则选择两个二极管。如果 DS 为 00 或 11,那么充电器被禁止,与 TCS 无关。RS 位(位0-1)选择连接在 Vcc2 与 Vcc1 之间的电阻。电阻选择(RS)位选择的电阻如下:

如果 RS 为 00,充电器被禁止,与 TCS 无关。
二极管和电阻的选择用户根据电池和超容量电容充电所需的最大电流决定。最大充电电流可以如下列
所说明的那样进行计算。假定 5V 系统电源加到 Vcc2 而超容量电容接至 Vcc1。再假设慢速充电器工作时在Vcc2 和 Vcc1 之间接有一个二极管和电阻 R1。因而最大电流可计算如下:
Imax =(5.0V-二极管压降)/R1
=(5.0V-0.7V)/2kΩ
= 2.2mA
显而易见,当超容量电容充电时,Vcc2 和 Vcc1 之间的电压减少,因而充电电流将会减小。

时序图

我们首先得给DS1302时钟模块写入时间和日期,然后呢这个时钟会自己走,我们之后只需要读取时间和日期即可。我们直接看时序图 编写代码

单字节写时序

数据输入
跟随在输入写命令字节的 8 个 SCLK 周期之后,在下 8 个 SCLK 周期的上升沿输入数据。如果有额外的SCLK 周期,它们将被忽略。输入从位 0 开始。

上面时序图是前8位是地址,后8位是数据。
我们根据上面的时序图 来编写 单字节写的一个函数
发现上面的

/*DS1302写单字节函数*/
void ds1302_write_byte(unsigned char addrOrData)
{
	unsigned char i = 0;
	for(i=0;i<8;i++)   
	{
		DAT = addrOrData & 0x01;	//开始传输低位 
		addrOrData = addrOrData >> 1;//右移一位
		CLK = 1;					 //拉高时钟表示已经发送
		CLK = 0;					 //拉低时钟准备继续放新的数据位
	}	
}

/*DS1302写函数*/
void ds1302_write(unsigned char addr,unsigned char data)
{
	RST = 0; //RST拉低
	SCLK = 0; //时钟SCLK也拉低
	RST = 1; //准备开始写	
	ds1302_writeByte(addr);	 //写入要写的地址
	ds1302_writeByte(dat);	 //写入地址的数据
	RST = 0; //关闭 表示结束
}


看上面的图,比如我想写入时 为18点 的写寄存器是84H 数据就是18点 但是DS1302存储格式为 BCD 码那就是 0x18。
那写入 时 为18点 的代码则是:

ds1302_write(0x84,0x18); //写入时  为 18点

单字节读时序


/*DS1302读单字节函数*/
unsigned char ds1302_read_byte(unsigned char data)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		dat = dat >> 1; //右移一位 数据是先从低位开始
		if(IO == 1)
			dat = dat | 0x80;	//1000 0000
		else
			dat = dat & 0x7f;	//0111 1111
		SCLK = 1; //时钟拉高表示这位已经结束
		SCLK = 0; //时钟拉低开始接收新的数据位
	}
	return data;
}

/*DS1302读取数据函数*/
unsigned char ds1302_read_data(unsigned char addr)
{
	unsigned char data; //用于存放接收的数据
	RST = 0; //RST拉低
	SCLK = 0; //时钟SCLK也拉低
	RST = 1; //准备开始写	
	ds1302_write_byte(addr); 	//写入要读取的地址
	data = ds1302_read_byte(data); //读取地址的数据
	RST = 0;//关闭 表示结束
	
	return data;	//返回读取到的数据
}

我们想读取时 里面时间 看看现在是几点 时 读寄存器是85H

time = ds1302_read_data(0x85);

写保护寄存器
写保护寄存器的位 7 是写保护位。开始 7 位(位 0-6)置为零,在读操作时总是读出零。在对时钟或
功能 字节数 脉冲数
CLOCK 8 72
RAM 31 256
DS1302
RAM 进行写操作之前,位 7 必须为零。当它为高电平时,写保护位禁止对任何其它寄存器进行写操作。

时钟/日历多字节(Burst)方式

所谓的突发模式(BURST模式)就是一次性传送多个字节的时钟信号和RAM数据。比如我可以一次性把时间和日期写入,也可以一次性的读出时间和日期。

代码

部分代码呈现

main.c

#include <reg52.h>
#include "lcd1602.h"
#include "ds1302.h"

void main()
{
	lcd_init();   //初始化LCD1602 
	ds1302_init();//初始化DS1302
	while(1)
	{
		ds1302_burst_read(DS1302_READ_TIME); //读取日期和时间
		lcd_display_dateAndTime(); //1602显示日期和时间	
	}
}

lcd1602.c

/*初始化LCD1602函数*/
void lcd_init()
{
	unsigned char array1[] = "date:    -  -  ";
	unsigned char array2[] = "time:  :  :  ";	
	write_com(CURSOR_RIGHT); //写入数据后光标自动右移 整屏不移动。 0x40(光标左移 整屏不移动)0x05(左移 整屏右移)0x07(右移 整屏右移)
	write_com(CLOSE_DIS_CUR); //开显示功能 无光标 不闪烁
	write_com(DIS_5X7); //数据总线8位 16X2显示 5*7点阵
	write_com(CLEAR_SCREEN); //清屏 0000 0001

	lcd_display_str(0,0,array1);  //第一行显示	date:    -  -  
	lcd_display_str(0,1,array2);  //第二行显示 	time:  :  :  

}

/*LCD1602 显示DS1302日期和时间 函数*/
void lcd_display_dateAndTime()	  
{	
	lcd_display_byte(5,0,(time[7]>>4)+48); 	//显示年
	lcd_display_byte(6,0,(time[7]&0x0f)+48);

	lcd_display_byte(7,0,(time[6]>>4)+48); 	//显示年
	lcd_display_byte(8,0,(time[6]&0x0f)+48);

	lcd_display_byte(10,0,(time[4]>>4)+48);	//显示月
	lcd_display_byte(11,0,(time[4]&0x0f)+48);

	lcd_display_byte(13,0,(time[3]>>4)+48);	//显示日
	lcd_display_byte(14,0,(time[3]&0x0f)+48);

	lcd_display_byte(5,1,(time[2]>>4)+48);	//显示时
	lcd_display_byte(6,1,(time[2]&0x0f)+48);

	lcd_display_byte(8,1,(time[1]>>4)+48);	//显示分
	lcd_display_byte(9,1,(time[1]&0x0f)+48);

	lcd_display_byte(11,1,(time[0]>>4)+48);	//显示秒
	lcd_display_byte(12,1,(time[0]&0x0f)+48);

	lcd_display_byte(14,1,(time[5]&0x0f)+48);	//显示周	

}

ds1302.c

#include "ds1302.h"

unsigned char time[8] = {0x00,0x45,0x19,0x18,0x10,0x01,0x21,0x20};//秒分时 日月周 年

/*DS1302单字节读数据函数*/
unsigned char ds1302_readByte(unsigned char dat)
{
	unsigned char i = 0;
	for(i=0;i<8;i++)  
	{
		dat = dat >> 1;	 //左移一次 低位开始 共有效左移7次
		if(DAT == 1) 		
			dat = dat | 0x80;	 //高位置1
		else
			dat = dat & 0x7F;	 //高位置0	
		CLK = 1; //时钟拉高表示这位已经结束
		CLK = 0; //时钟拉低开始接收新的数据位
	}	
	return dat;

}

/*DS1302单字节写地址或者数据函数*/
void ds1302_writeByte(unsigned char addrOrData)
{
	unsigned char i = 0;
	for(i=0;i<8;i++)   
	{
		DAT = addrOrData & 0x01;	//开始传输低位 
		addrOrData = addrOrData >> 1;//右移一位
		CLK = 1;					 //拉高时钟表示已经发送
		CLK = 0;					 //拉低时钟准备继续放新的数据位
	}
					
}

/*DS1302时钟模块写命令函数*/
void ds1302_writeCom(unsigned char addr,unsigned char dat)
{
 	RST = 0; //拉低RST
	CLK = 0; //拉低时钟
	RST = 1; //使能打开
	ds1302_writeByte(addr);	 //写入要写的地址
	ds1302_writeByte(dat);	 //写入地址的数据
	RST = 0; //关闭 表示结束
}

/*DS1302时钟模块读数据函数*/
unsigned char ds1302_readData(unsigned char addr)
{
	unsigned char dat = 0;
	RST = 0; //拉低RST
	CLK = 0; //拉低时钟
	RST = 1; //打开使能
	ds1302_writeByte(addr);		//写入要读的地址
	dat = ds1302_readByte(dat);	//读取地址的数据
	RST = 0; //关闭 表示结束
	
	return dat; //返回读取到的数据
}

/*DS1302时钟模块读取日期和时间函数*/
void ds1302_read_DateAndTime()
{
	time[0] = ds1302_readData(DS1302_READ_YEAR);  //读取年
	time[1] = ds1302_readData(DS1302_READ_MONTH); //读取月
	time[2] = ds1302_readData(DS1302_READ_DAY);   //读取日
	time[3] = ds1302_readData(DS1302_READ_HOUR);  //读取时
	time[4] = ds1302_readData(DS1302_READ_MINUTE);//读取分
	time[5] = ds1302_readData(DS1302_READ_SECOND);//读取秒
	time[6] = ds1302_readData(DS1302_READ_WEEK);  //读取周		
}

/*DS1302时钟模块初始化函数*/
void ds1302_init()
{
	RST = 0; //关闭RST
	CLK = 0; //拉低时钟
	ds1302_writeCom(DS1302_WRITE_PROTECT,0x00); //关闭写保护 这样才能进行写操作
	ds1302_burst_write(DS1302_WRITE_TIME);	//初始化全部时间
	//ds1302_initTime(); //初始时间:2021年10月18日 19点45分00秒 周一	
	ds1302_writeCom(DS1302_WRITE_PROTECT,0x80); //开启写保护 只读模式
		
}

项目展示


如果觉得这篇文章对你有用。欢迎大家点赞、评论哈哈
需要整个工程代码,欢迎大家打赏,评论区留上你的邮箱 or vx or qq。o( ̄︶ ̄)o

继续加油!!!

有关基于51单片机+DS1302时钟模块+LCD1602显示的更多相关文章

  1. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  2. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  3. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  4. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  5. ruby-on-rails - (Ruby,Rails) 基于角色的身份验证和用户管理...? - 2

    我正在寻找用于Rails的优质管理插件。似乎大多数现有的插件/gem(例如“restful_authentication”、“acts_as_authenticated”)都围绕着self注册等展开。但是,我正在寻找一种功能齐全的基于管理/管理角色的解决方案——但不是简单地附加到另一个非基于角色的解决方案。如果我找不到,我想我会自己动手......只是不想重新发明轮子。 最佳答案 RyanBates最近做了两个关于授权的railscast(注意身份验证和授权之间的区别;身份验证检查用户是否如她所说的那样,授权检查用户是否有权访问资源

  6. ruby - 在 Rakefile 中动态生成 Rake 测试任务(基于现有的测试文件) - 2

    我正在根据Rakefile中的现有测试文件动态生成测试任务。假设您有各种以模式命名的单元测试文件test_.rb.所以我正在做的是创建一个以“测试”命名空间内的文件名命名的任务。使用下面的代码,我可以用raketest:调用所有测试require'rake/testtask'task:default=>'test:all'namespace:testdodesc"Runalltests"Rake::TestTask.new(:all)do|t|t.test_files=FileList['test_*.rb']endFileList['test_*.rb'].eachdo|task|n

  7. ruby - 如何使用 Ruby 基于字母数字字符串生成颜色? - 2

    我想要像“嘿那里”这样的东西变成,例如,#316583。我希望将任意长度的字符串“归结”为十六进制颜色。我不知道从哪里开始。我在想,每个字符串的MD5散列都是不同的-但如何将该散列转换为十六进制颜色数字? 最佳答案 你可以只取几位前几位:require'digest/md5'color=Digest::MD5.hexdigest('Mytext')[0..5] 关于ruby-如何使用Ruby基于字母数字字符串生成颜色?,我们在StackOverflow上找到一个类似的问题:

  8. 【自动驾驶环境感知项目】——基于Paddle3D的点云障碍物检测 - 2

    文章目录1.自动驾驶实战:基于Paddle3D的点云障碍物检测1.1环境信息1.2准备点云数据1.3安装Paddle3D1.4模型训练1.5模型评估1.6模型导出1.7模型部署效果附录show_lidar_pred_on_image.py1.自动驾驶实战:基于Paddle3D的点云障碍物检测项目地址——自动驾驶实战:基于Paddle3D的点云障碍物检测课程地址——自动驾驶感知系统揭秘1.1环境信息硬件信息CPU:2核AI加速卡:v100总显存:16GB总内存:16GB总硬盘:100GB环境配置Python:3.7.4框架信息框架版本:PaddlePaddle2.4.0(项目默认框架版本为2.3

  9. ruby - 规范测试基于 EventMachine 的(Reactor)代码 - 2

    我正在尝试整个BDD方法并想测试AMQP基于Vanilla的方面Ruby我正在写的应用程序。选择Minitest后作为与其他名副其实的蔬菜框架不同的平衡功能和表现力的测试框架,我着手编写此规范:#File./test/specs/services/my_service_spec.rb#Requirementsfortestrunningandconfigurationrequire"minitest/autorun"require"./test/specs/spec_helper"#Externalrequires#MinitestSpecsforEventMachinerequire

  10. ruby - JSON的基于流的解析和写入 - 2

    我分1,000个批处理从服务器获取大约20,000个数据集。每个数据集都是一个JSON对象。坚持这会产生大约350MB的未压缩明文。我的内存限制为1GB。因此,我以追加模式将每1,000个JSON对象作为一个数组写入到一个原始JSON文件中。结果是一个包含20个需要聚合的JSON数组的文件。无论如何我都需要触摸它们,因为我想添加元数据。一般RubyYajlParser使这成为可能:raw_file=File.new(path_to_raw_file,'r')json_file=File.new(path_to_json_file,'w')datasets=[]parser=Yajl::

随机推荐