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

蓝桥杯_DS1302时钟

一 DS13302时钟简介

DS1302是时钟的芯片,可以显示对应的时钟和日期,这里有一个前提,需要连接着电源,如果把程序在今天烧录到板子里,然后不连电,第二天的数据并不准确,所以说,如果想要准确的日期,那么需要一直给板子提供电源。

这里所说的数据,使用841BCD码来保存并传输,传输八位数据,这八位数据的最后一位,决定着是读还是写;1表示读,0表示写,如果想要真正运行DS1302,需要先写再读。

二 原理

上面这个是板子提供的原理图中的电路图,在这里,我们只需要关注三条线:RST、I/O、SCLK,这三根线决定着数据的传输,接下来详细的介绍一下:

  • RST:是控制着整体写的过程,并且只有把RST的电压拉高才能往里面写数据
  • SCLK:这是一个时钟,他一直以某一种速度发送频率,低电平高电平一直发送,他的作用是控制着节奏,并且是有一个上升沿读取一个。(RST必须是高电平)
  • I/O:他的作用主要是发送地址 + 数据,并且他遵循先写低位,再写高位

三 写的过程

微观

现在,假设,我要写86,换成BCD码,1000 0110,先从最低位,从右边第一位的0开始写起,向右走,现在把1000 0110中的0拿出来,然后再将1000 0110向右移一位,(最后一位去掉,最前面加个零)变成了0100 0011,再重复循环,取八位值

宏观

先将RST拉低,等一会,SCLK拉低,等一会,再将RST拉高,等一会,把数据的地址+data写进去,最后再关闭RST

可视化:

四 写和读的区别

在读的时候,和写的过程有不同的地方,在写的时候,我们当SCLK有上升沿的时候,进行一次数据的写,但是在读的时候,读地址:SCLK是上升沿;读数据:SCLK是下降沿

可视化:

五 代码层面

不用慌张要背这么多代码,因为在比赛的时候,会有范例代码给到我们,我们只需要到对应的文件夹中赋值即可,接下来说一下详细过程:

在比赛提供的文件夹中,将ds1302.c和.h的两个文件放到我们的代码中,然后打开ds1302.c文件进行代码的编辑。

DS1302.c

写函数(set_RTC)

在该文件中,我们需要将想要设置的时分秒(假设是22点59分55秒),转化成8421BCD码,转化成计算机所能看得懂的数字,随后,再将计算机看的懂得数字再转成最后实现的时间。

这里的设置的时分秒它属于是一个数组,ucRTC[3]

在写的过程中,要先把高位赋值成0,因为高位(WP)决定着这个写的功能,WP的位置是在8Eh,随后我们看到write一列,我们想要设置时分秒,我们需要找到对应的寄存器的位置,84h、82h、80h。最后一定要记得把WP高位再给关闭了。

相应的写函数的代码为:

//设置1302时分秒函数
//入口参数是数组,包含时、分、秒数据
void Set_RTC(unsigned char *ucRTC)	//这里的*,待定
{
	unsigned char temp;	//中间局部变量,存放时分秒
	
	Write_Ds1302_Byte(0x8e,0);	//WP=0 允许写write操作
	
	
	//这里需要记忆:
	//0x8e WP=0,允许写的操作;0x80不允许
	//0x84 小时
	//0x82 分钟
	//0x80 秒
	
	
	temp = ((ucRTC[0] / 10 ) << 4) + (ucRTC[0] % 10) ;	//数组的第零个数据,是小时,23 
																											// 2(0010)对应的是高位,3(0011)对应的是低位
	Write_Ds1302_Byte(0x84,temp);	
	
	
	temp = ((ucRTC[1] / 10 ) << 4) + (ucRTC[1] % 10) ;	//数组的第一个数据,是分钟,59 
	Write_Ds1302_Byte(0x82,temp);	

	temp = ((ucRTC[2] / 10 ) << 4) + (ucRTC[2] % 10) ;	//数组的第一个数据,是秒,59 
	Write_Ds1302_Byte(0x80,temp);	
	
	Write_Ds1302_Byte(0x8e,0x80);	//WP=1 禁止写write操作
	
}

在temp和ucRTC[]之间的转换我给大家举个例子,例如,我现在要写23这个数字,我们先把2这个数字提取出来,需要先让23除以10,得出来了一个2,但是这个2计算机是识别不了的,自动把2写成了8421BCD码,也就是0010,然后我们将0010向左移动四位,然后再加上“23除以10取余,也就得到了3,3转换为BCD码成了0011”,最后23这个人能看懂的数字,转换成了0010 0011,并赋值给temp。

读函数(Read_RTC)

在上面的写函数已经将23这个数字,转换成了0010 0011,我们现在又需要将这个8421BCD码转换成人能看得懂的形式,那么如何取高四位和第四位呢,先将这个BCD码向右移动四位,得到了0010,然后将0010如何将0010转换成十进制也就是我们能看得懂的代码呢,我们需要将他再乘以10,就成了十进制,随后将0010 0011和0x0F相与,就可以得到第四位的东西,也就是0011,把他们相加就是最后的呈现数字啦。当然,也要记得把对应的地址写出来哦,对应读的地址,时分秒分别在85h、83h、81h。

//读取DS1302时分秒的函数
//入口参数:将读取到的时分秒数据存放到数组指针当中
void Read_RTC(unsigned char *ucRTC)
{
	unsigned char temp;
	
	temp = Read_Ds1302_Byte ( 0x85);	//读取小时
	ucRTC[0] = ((temp >> 4)*10 )+( temp & 0x0F );	//读取的是23(BCD),但是转成十进制的数是35,不能直接赋值,要进行BCD到十进制的转换
	
	temp = Read_Ds1302_Byte ( 0x83);	//读取分钟
	ucRTC[1] = ((temp >> 4)*10 )+( temp & 0x0F );

	temp = Read_Ds1302_Byte ( 0x81);	//读取秒
	ucRTC[2] = ((temp >> 4)*10 )+( temp & 0x0F );	
	

}

main.c

在主函数中,我们想要实现的功能是按一下按键,变成计时器,再按一下按键,是时钟

要特别强调一点,这里虽然代码很长,但是这是基于上一篇文章“通过按键控制数码管显示数字”,如果大家有所看不懂的,请先看懂上一篇文章,再来本章学习。

#include <STC15F2K60S2.H>
#include "Timer.h"
#include "dsp_init.h"
#include "dsp_seg.h"
#include "stdio.h"
#include "key.h"
#include "ds1302.h"
#include "dsp_led.h"



//函数声明,三个东西主体循环
void Key_Process(void);		//按键处理,底层的数据
void Seg_Process(void);		//显示处理,显示信息的生成
void Led_Process(void);		//LED状态信息


//全局变量
//显示专用
unsigned char seg_buf[8];		//放置字符串转换后的段码到数组
unsigned char seg_string[10];	//放置字符串
unsigned char pos = 0;	//中断显示专用


//按键专用
unsigned char Key_Val;	//按键的数值存储变量
unsigned char Key_Down;	
unsigned char Key_Old;


//按键和显示函数减速专用,基本不变,我不写试一下
unsigned char Key_Slow_Down;
unsigned char Seg_Slow_Down;


//可能会改变
//unsigned_char Seg_Show_Num;	//准备显示出来的数值

//运行时间
unsigned int ms_count = 0;
unsigned char s_count = 0;
//运行状态
unsigned char Change_State;
unsigned char ucled;

//使用1302时,基本不变
unsigned char ucRTC[3] = {23,23,55};	//数组初始值,里面放的是时分秒


void main()
{
	Cls_Peripheral();		//清除外设
	Timer1Init();		//定时器1,1ms进入一次
	
	Set_RTC(ucRTC);		//23.59.55
	
	
	while(1)
	{
		Key_Process();		//按键处理,底层的数据
		Seg_Process();		//显示处理,显示信息的生成
		Led_Process();

	}

}


//定时器初始化
//-----------------------------------------------

/* Timer1_interrupt routine */
void tm1_isr() interrupt 3 
{
	if(++Key_Slow_Down == 10) Key_Slow_Down = 0;
	if(++Seg_Slow_Down == 10) Seg_Slow_Down = 0;
	
	if(++ms_count == 1000)	//记录运行时间
	{
		s_count++;
		ms_count = 0;
	}
	
	
  Seg_Disp( seg_buf,pos);//数码管显示
	if(++pos == 8)
	{
		pos = 0;
	}
	Led_Disp(ucled);//ucled显示
	
}


//key_process的函数内部
void Key_Process(void)		//按键处理,底层的数据
{
	
	
	//读取按键按下的编号数值
	Key_Val = Key_Read();	
	
	Key_Down = Key_Val & (Key_Old ^ Key_Val);
	Key_Old = Key_Val;
	//以上这五行,就是检验下降沿
	
	
	//根据代码改变
	if(Key_Down)	//如果捕捉到下降沿跳变
	{
		//Seg_Show_Num = Key_Down;
		if(++Change_State == 2) Change_State = 0;	//保证Change_State在0、1之间翻滚

	}
	
}


//seg_process的函数内部
void Seg_Process(void)		//显示处理,显示信息的生成
{
	if(Seg_Slow_Down) return;
	Seg_Slow_Down = 1;
	
	
	switch(Change_State)
	{
		case 0: 	
			Read_RTC(ucRTC);	//读取1302内部时分秒的数据,放到预定义的数组空间中
	    sprintf(seg_string,"%02d-%02d-%02d",(unsigned int)ucRTC[0],(unsigned int)ucRTC[1],(unsigned int)ucRTC[2]);
			break;
		
		case 1:
			sprintf(seg_string,"%8d",(unsigned int)s_count);
			break;

			
	}
	
	//永远不变
  Seg_Tran(seg_string, seg_buf);

}


//Led_Process的函数内部
void Led_Process(void)		
{
		switch(Change_State)
	{
		case 0: 	
			ucled = 0xF0;
			break;
		
		case 1:
			ucled = 0x0F;
			break;

			
	}
	
	
	
}


http://www.kler.cn/a/599043.html

相关文章:

  • 游戏引擎学习第174天
  • 【C++复习】——类型转换
  • git,openpnp - 根据安装程序打包名称找到对应的源码版本
  • LeetCode 3038 相同分数的最大操作数目I
  • 基于单片机的农作物自动灌溉系统
  • 蓝桥杯第九天 2022 省赛 第 4 题 最少刷题数
  • nt!KeWaitForMultipleObjects函数分析之一个例子ExpWorkerThreadBalanceManager
  • 【玩转全栈】---- Django 基于 Websocket 实现群聊(解决channel连接不了)
  • 【QA】QT事件处理流程是怎么样的?
  • Linux内核Netfilter框架分析
  • 【CC2530 教程 二】CC2530定时器实现微秒、毫秒、秒延时函数
  • 【Vue3入门1】04-计算属性 + 侦听器
  • 01 Java微服务架构(SpringBootSpringCloudJDK)_企业级升级方案指导手册
  • 【计算机操作系统】深入剖析操作系统中的存储器管理:从基础到高级
  • LORA 中的 梯度外积是什么意思; 方差和协方差的实际含义:衡量变量的离散程度和变量间的线性相关性
  • 在linux上启动微服务
  • MySQL 的多版本并发控制
  • 【IntelliJ IDEA快速绑定Maven配置指南】
  • 奇安信2面面试题。。。
  • 基于Python的智慧金融风控系统的设计与实现