目录
将程序烧录到单片机中后,lcd1602显示屏将从预设时间开始进行时钟功能。
在lcd1602显示屏第一行分别显示年,月,日,星期;在第二行显示时,分,秒。


DS1302 是 DALLAS 公司推出的涓流充电时钟芯片,内含有一个实时时钟/日历和 31 字节静态 RAM,通过简单的串行接口与单片机进行通信。实时时钟/日历电路提供秒、分、时、日、周、月、年的信息,每月的天数和闰年的天数可自动调整。
DS1302 与单片机之间可以通过三根数据线进行同步串行的方式进行通信:RES,IO数据,SCLK串行时钟。实时时钟具有能计算 2100 年之前的秒、分、时、日、星期、月、年的能力,还有闰年调整的能力。
从工作流程可知要使ds1302运转,首先要了解ds1302中的寄存器,然后设计写入数据与读取数据函数,最后进行调用。
在对ds1302进行读写操作的时候,第一步要向ds1302发送控制命令,用于后续对应的操作。在ds1302中,有这么一种寄存器,用于存放控制命令,这样ds1302就可以根据我们发送的指令,进行对应的操作。
一个控制命令有8位,输入一个八位的命令就可以进行操作。例如想要读取秒寄存器,就需要向ds1302发送命令10000001,例如想要写秒寄存器,就需要向ds1302发送命令10000000。
详情见以下表格:
| 操作 | 命令 |
|---|---|
| 读秒寄存器 | 10000001 |
| 写秒寄存器 | 10000000 |
| 读分寄存器 | 10000011 |
| 写分寄存器 | 10000010 |
| 读小时寄存器 | 10000101 |
| 写小时寄存器 | 10000100 |
| 读日寄存器 | 10000111 |
| 写日寄存器 | 10000110 |
| 读月寄存器 | 10001001 |
| 写月寄存器 | 10001000 |
| 读星期寄存器 | 10001011 |
| 写星期寄存器 | 10001010 |
| 读年寄存器 | 10001101 |
| 写年寄存器 | 10001100 |
| 写写保护寄存器 | 10001111 |
在ds1302中有着这种寄存器,用于保存时间或者其他数据。在秒,分,时,日,月,星期,年寄存器中,用bcd码保存着数据,高四位用于保存十进制的十位,低四位用于保存十进制的个位。例如想保存12月21日,就分别向日寄存器写0x21(00100001,高四位2,低四位1),向月寄存器写0x12。
秒的范围是0-59,因此对于高四位,其实用三位就足够了(最大为5,101),最高的那一位是ds1302的开关,为0时才能工作。
小时有12与24两种。最高位为0时表示24进制,此时剩下的位全是表示时间的数据;最高位为1时,表示12进制,此时A/P表示上午或者下午。
写保存寄存器的最高位WP是写入寄存器的通路的开关。当WP为0时,才可以向ds1302中写入数据。
前面提到,ds1302使用三根通信线CE,IO,SCLK。如何使用这三根线写入芯片呢?
IO用于接收一位数据,低电平为0,高电平为1。要向ds1302写数据,要做以下操作:
与写不同,在输入了读的执行命令后,会在SCLK的下降沿将对应寄存器的值从低位开始传给IO,因此,读寄存器的操作是:
IO,CE,SCLK分别连接着P34,35,36接口,因此要操作IO,CE,SCLK,在代码层仅需要对P34,35,36进行操作就行。
涉及到的操作最核心的部分是对ds1302的读与写,思路已在上文提及,读函数与写函数代码如下:
#include <REGX52.H>
#include "intrins.h"
//封装GPIO
#define CE P3_5
#define IO P3_4
#define SCLK P3_6
/*
* @brief 向ds1302芯片的寄存器写一个字节数据
* @details 提供控制指令与数据,将数据按位赋给IO,在SCLK出现上升沿的时候,
IO的值会发送给ds1302,先将控制命令用八次循环按位写入IO,经历
SCLK八次上升沿后命令送达,再用同样的方式将数据送入DS1302。
在整个期间,CE(RST)置高电平
* @param addr 写入的控制指令
* @param dat 向对应的寄存器要写的数据
* @retval 无
*/
void ds1302_write_byte(unsigned char addr,unsigned char dat){
unsigned char i;
CE = 0;
_nop_();
CE = 1;//将CE从低电平改为高电平
_nop_();
SCLK = 0;//SCLK置低电平
_nop_();
for (i = 0;i<8;i++){//输入指令
IO = addr&0x01; //取出最低位
addr = addr>>1; //右移一位,次低位变最低位
SCLK = 1; //产生上升沿,数据传入后再置低电平
_nop_();
SCLK = 0;
_nop_();
}
for (i = 0;i<8;i++){//输入数据
IO = dat&0x01;
dat = dat>>1;
SCLK = 1;
_nop_();
SCLK = 0;
_nop_();
}
CE = 0;
_nop_();
}
/*
* @brief 读取ds1302寄存器数据.
* @details 提供控制指令选择要读的寄存器之后,先将控制命令写入芯片,
此时sclk会完成八个上升沿,七个下降沿。
读的数据会在第八个下降沿开始返回,因此在将最后一个控制命令
的位数据写入后,置低电平时就要从IO开始读。
读到的时间数据是BCD码
* @param addr 写入的控制指令
* @retval 读出的寄存器数据
*/
unsigned char ds1302_read_byte(unsigned char addr){
unsigned char i=0,temp=0,value=0;
CE = 0;
_nop_();
CE = 1;
_nop_();
SCLK = 0;
_nop_();
for (i = 0;i<8;i++){ //输入指令
IO = addr&0x01;
addr = addr>>1;
SCLK = 1;
_nop_();
SCLK = 0;
_nop_();
}
for (i = 0;i<8;i++){ //下降沿读取数据
temp = IO;
/*先将 value 右移 1 位,然后 temp 左移 7 位, 最后或运算
这样可以将按低位拿到的数据变成八位数据
例如寄存器里为10001000,那么value在循环中的值是00000000,
00000000,00000000,10000000,01000000......10001000*/
value=(temp<<7)|(value>>1);
SCLK=1;
_nop_();
SCLK=0;
_nop_();
}
CE = 0;
_nop_();
/*
下面的代码在写函数里面没有,看官方教程解释也没搞明白,
删掉运行测试也没有异常...
*/
SCLK=1;
_nop_();
IO = 0;
_nop_();
IO = 1;
_nop_();
return value;
}
不仅需要读写操作的函数,还需要有调用这些函数的调用者。上文说过最开始要初始化时间,初始化后再继续不断读取。初始化的时间是以bcd码的形式保存在数组中的,得到的数据也是bcd码的形式。因此,还要写一个初始化函数,将初始值数组的值依次写入秒,分,时,日,月,星期,年寄存器;还要一个读取函数,依次读取这七个寄存器的值到数组。
代码如下:
//---DS1302 写入和读取时分秒的地址命令
//---秒分时日月周年 最低位读写位;
unsigned char gREAD_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
unsigned char gWRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
//---DS1302 时钟初始化 2022 年 8 月 31 日星期三 17 点 22 分 25 秒。
//---存储顺序是秒分时日月周年,存储格式是用 BCD 码---//
unsigned char gDS1302_TIME[7] = {0x25, 0x22, 0x17, 0x31, 0x08, 0x03, 0x22};
/*
* @brief 初始化时钟时间
* @details 将时间数组保存的值用写函数写入ds1302
* @param 无
* @retval 无
*/
void ds1302_init(){
unsigned char i;
ds1302_write_byte(0x8E,0X00);//在写寄存器之前,关闭写寄存器保护
for (i = 0;i<7;i++){
ds1302_write_byte(gWRITE_RTC_ADDR[i],gDS1302_TIME[i]);
}
ds1302_write_byte(0x8E,0X80);//重新开启保护
}
/*
* @brief 将寄存器中的时间写入数组
* @details 将时间数组保存的值用写函数写入ds1302
* @param 无
* @retval 无
*/
void ds1302_read_time(void){
unsigned char i=0;
for(i=0;i<7;i++) {
gDS1302_TIME[i]=ds1302_read_byte(gREAD_RTC_ADDR[i]);
}
}
整个ds1302.c代码如下:
#include <REGX52.H>
#include "intrins.h"
//封装GPIO
#define CE P3_5
#define IO P3_4
#define SCLK P3_6
//---DS1302 写入和读取时分秒的地址命令
//---秒分时日月周年 最低位读写位;
unsigned char gREAD_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
unsigned char gWRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
//---DS1302 时钟初始化 2022 年 8 月 31 日星期三 17 点 22 分 25 秒。
//---存储顺序是秒分时日月周年,存储格式是用 BCD 码---//
unsigned char gDS1302_TIME[7] = {0x25, 0x22, 0x17, 0x31, 0x08, 0x03, 0x22};
/*
* @brief 向ds1302芯片的寄存器写一个字节数据
* @details 提供控制指令与数据,将数据按位赋给IO,在SCLK出现上升沿的时候,
IO的值会发送给ds1302,先将控制命令用八次循环按位写入IO,经历
SCLK八次上升沿后命令送达,再用同样的方式将数据送入DS1302。
在整个期间,CE(RST)置高电平
* @param addr 写入的控制指令
* @param dat 向对应的寄存器要写的数据
* @retval 无
*/
void ds1302_write_byte(unsigned char addr,unsigned char dat){
unsigned char i;
CE = 0;
_nop_();
CE = 1;//将CE从低电平改为高电平
_nop_();
SCLK = 0;//SCLK置低电平
_nop_();
for (i = 0;i<8;i++){//输入指令
IO = addr&0x01; //取出最低位
addr = addr>>1; //右移一位,次低位变最低位
SCLK = 1; //产生上升沿,数据传入后再置低电平
_nop_();
SCLK = 0;
_nop_();
}
for (i = 0;i<8;i++){//输入数据
IO = dat&0x01;
dat = dat>>1;
SCLK = 1;
_nop_();
SCLK = 0;
_nop_();
}
CE = 0;
_nop_();
}
/*
* @brief 读取ds1302寄存器数据.
* @details 提供控制指令选择要读的寄存器之后,先将控制命令写入芯片,
此时sclk会完成八个上升沿,七个下降沿。
读的数据会在第八个下降沿开始返回,因此在将最后一个控制命令
的位数据写入后,置低电平时就要从IO开始读。
读到的时间数据是BCD码
* @param addr 写入的控制指令
* @retval 读出的寄存器数据
*/
unsigned char ds1302_read_byte(unsigned char addr){
unsigned char i=0,temp=0,value=0;
CE = 0;
_nop_();
CE = 1;
_nop_();
SCLK = 0;
_nop_();
for (i = 0;i<8;i++){ //输入指令
IO = addr&0x01;
addr = addr>>1;
SCLK = 1;
_nop_();
SCLK = 0;
_nop_();
}
for (i = 0;i<8;i++){ //下降沿读取数据
temp = IO;
/*先将 value 右移 1 位,然后 temp 左移 7 位, 最后或运算
这样可以将按低位拿到的数据变成八位数据
例如寄存器里为10001000,那么value在循环中的值是00000000,
00000000,00000000,10000000,01000000......10001000*/
value=(temp<<7)|(value>>1);
SCLK=1;
_nop_();
SCLK=0;
_nop_();
}
CE = 0;
_nop_();
SCLK=1;
_nop_();
IO = 0;
_nop_();
IO = 1;
_nop_();
return value;
}
/*
* @brief 初始化时钟时间
* @details 将时间数组保存的值用写函数写入ds1302
* @param 无
* @retval 无
*/
void ds1302_init(){
unsigned char i;
ds1302_write_byte(0x8E,0X00);//在写寄存器之前,关闭写寄存器保护
for (i = 0;i<7;i++){
ds1302_write_byte(gWRITE_RTC_ADDR[i],gDS1302_TIME[i]);
}
ds1302_write_byte(0x8E,0X80);//重新开启保护
}
/*
* @brief 将寄存器中的时间写入数组
* @details 将时间数组保存的值用写函数写入ds1302
* @param 无
* @retval 无
*/
void ds1302_read_time(void){
unsigned char i=0;
for(i=0;i<7;i++) {
gDS1302_TIME[i]=ds1302_read_byte(gREAD_RTC_ADDR[i]);
}
}
main程序中要做的是初始化,不断读取得到bcd码形式的时间值,转换成十进制后打印在LCD1602上。代码如下:
/*
实验名称:ds1302时钟
实现现象:从起点时间开始进行实时时钟
首先将保存在数组中的bcd码形式的起始时间值发送到ds1302,然后再在主函数中不断读取
ds1302寄存器中的时间数值,ds1302会不断进行时间增长。主函数读取到实时的时间后,通过
将bcd转换成10进制后打印在lcd1602上。
*/
#include "ds1302.h"
#include "LCD1602.h"
extern unsigned char gDS1302_TIME[7];
void main(){
unsigned char time_buf[7];
unsigned char i;
ds1302_init();//初始化 DS1302
LCD_Init();//初始化1602
while(1){
ds1302_read_time();//读取时间到数组
for (i = 0 ; i< 7 ; i++){
time_buf[i]=(gDS1302_TIME[i]/16)*10+gDS1302_TIME[i]%16;//将数组中的bcd形式的值转换成10进制并保存在其他数组中
}
//打印时间
LCD_ShowNum(1,1,20,2);
LCD_ShowNum(1,3,time_buf[6],2);
LCD_ShowChar(1,5,'-');
LCD_ShowNum(1,6,time_buf[4],2);
LCD_ShowChar(1,8,'-');
LCD_ShowNum(1,9,time_buf[3],2);
LCD_ShowNum(1,15,time_buf[5],1);
LCD_ShowNum(2,1,time_buf[2],2);
LCD_ShowChar(2,3,':');
LCD_ShowNum(2,4,time_buf[1],2);
LCD_ShowChar(2,6,':');
LCD_ShowNum(2,7,time_buf[0],2);
}
}
其中lcd1602操作的相关代码并不是本人所打,是使用的网络资源因此不在此放出。
本人为51单片机学习者,本文是自己学习的总结。自己水平十分有限。若有错误望请大家多多指教!
本文的代码是跟着官方教程打的,经自己测试没有问题。
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
如何学习ruby的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/
深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG
文章目录1、自相关函数ACF2、偏自相关函数PACF3、ARIMA(p,d,q)的阶数判断4、代码实现1、引入所需依赖2、数据读取与处理3、一阶差分与绘图4、ACF5、PACF1、自相关函数ACF自相关函数反映了同一序列在不同时序的取值之间的相关性。公式:ACF(k)=ρk=Cov(yt,yt−k)Var(yt)ACF(k)=\rho_{k}=\frac{Cov(y_{t},y_{t-k})}{Var(y_{t})}ACF(k)=ρk=Var(yt)Cov(yt,yt−k)其中分子用于求协方差矩阵,分母用于计算样本方差。求出的ACF值为[-1,1]。但对于一个平稳的AR模型,求出其滞
写在之前Shader变体、Shader属性定义技巧、自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用,方便后续回顾查看,如有以偏概全、不祥不尽之处,还望海涵。1、Shader变体先看一段代码......Properties{ [KeywordEnum(on,off)]USL_USE_COL("IsUseColorMixTex?",int)=0 [Toggle(IS_RED_ON)]_IsRed("IsRed?",int)=0}......//中间省略,后续会有完整代码 #pragmamulti_c
参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍 介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。 内容有: ①:Hub模型的方法介绍 ②:服务器端代码介绍 ③:前端vue3安装并调用后端方法 ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke() 去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on