厦门建设网站公司,惠州企业建站系统,设计网站源码,用python做网站我那些1.RTC的简介
1.什么是实时时钟#xff08;RTC#xff09; #xff08;rtc for real time clock) #xff08;1#xff09;时间点和时间段的概念区分 #xff08;2#xff09;单片机为什么需要时间点【一定的时间点干什么事情】 #xff08;3#xff09;RTC如何存在于…
1.RTC的简介
1.什么是实时时钟RTC rtc for real time clock) 1时间点和时间段的概念区分 2单片机为什么需要时间点【一定的时间点干什么事情】 3RTC如何存在于系统中单片机内部集成 or 单片机外部扩展【DS1302】 2.DS1302
1.数据手册
DS1302中文数据手册 - 豆丁网 2.SPI数字接口访问 SPI通信协议【DS1302也使用这个协议】两个芯片之间的通信 3.内部存着一个时间点年月日时分秒星期几信息可以读写上电自动走表
3.RTC学习关键点
1.SPI接口的特征 13线SCLKRSTIO或者4线SCLKRSTIO 2同步SPI是同步通信表示主机【产生CLK】和从机【接受CLK】使用同一个SCLK【同步通信有SCLK异步没有SCLK】 3主从有主机和从机 4串行数据都从一根线进出【数据都是从IO进出】 2.时序的理解
3.编程实现
2.原理图和接线
1.原理图分析
1DS1302引脚介绍 JP595断开是为了让P3.4在控制DS1302的时候不影响74HC595工作 JP1302接上是为了让P3.4能控制到DS1302 J11断开是为了让P3.5在控制DS1302的时候不影响NE555模块工作 2.接线 1详解接线设置的原理和必要性 正常的产品一般不会这样设计正常产品一般接线都是确定的一般不会复用。 开发板来说主要是为了学习所以会放很多给模块所以在这个时候GPIO就不够使用这时候就需要复用设计。一个引脚接多个模块就会互相影响有2种可能一个是A模块工作时B模块莫名其妙的工作二是有时候B模块会影响到A模块的正常工作。对于复用引脚的情况接线的关键是确认目标模块接线OK时还不会影响到其他模块。 3.数据手册带读
https://www.dianyuan.com/upload/community/2014/02/22/1393058389-67878.pdf
DS1302中文数据手册 - 豆丁网 3.时序图的读法
1.时序图的关键 1横轴表示时间纵轴表示同一个时间各个通信线的状态 2静态或动态2个角度去看 3主要SCLK的边缘---会影响IO的电平状态【如果为上升沿代表IO端口应该在快上升沿和结束上升沿时应该保持高电平】 2.结合时序图的代码来理解时序 写入数据
/*******************************************************************************
* 函 数 名 : ds1302_write_byte
* 函数功能 : DS1302写单字节
* 输 入 : addr地址/命令dat数据
* 输 出 : 无
*******************************************************************************/
void ds1302_write_byte(u8 addr,u8 dat)
{u8 i0;//出于安全期间在进入之前要将SCLK和RST进行初始化为0DS1302_RST0;_nop_(); DS1302_CLK0;//CLK低电平_nop_();DS1302_RST1;//RST由低到高变化,表示要开始工作_nop_();//开始传送八位数据for(i0;i8;i){//将数据放入IO口中DS1302_IOaddr0x01;//数据从低位开始传送addr1;DS1302_CLK1; //上升沿_nop_();//delay函数DS1302_CLK0;//下降沿_nop_(); }for(i0;i8;i)//循环8次每次写1位先写低位再写高位{DS1302_IOdat0x01;dat1;DS1302_CLK1;_nop_();DS1302_CLK0;_nop_(); }//表示时序结束了DS1302_RST0;//RST拉低_nop_();
}读数据
/*******************************************************************************
* 函 数 名 : ds1302_read_byte
* 函数功能 : DS1302读单字节
* 输 入 : addr地址/命令
* 输 出 : 读取的数据
*******************************************************************************/
u8 ds1302_read_byte(u8 addr)
{u8 i0;u8 temp0;u8 value0;DS1302_RST0;_nop_(); DS1302_CLK0;//CLK低电平_nop_();DS1302_RST1;//RST由低到高变化_nop_();for(i0;i8;i)//循环8次每次写1位先写低位再写高位{DS1302_IOaddr0x01;addr1; DS1302_CLK1;_nop_();DS1302_CLK0;//CLK由低到高产生一个上升沿从而写入数据_nop_(); }for(i0;i8;i)//循环8次每次读1位先读低位再读高位{tempDS1302_IO;value(temp7)|(value1);//先将value右移1位然后temp左移7位最后或运算DS1302_CLK1;_nop_();DS1302_CLK0;_nop_(); }DS1302_RST0;//RST拉低_nop_(); DS1302_CLK1;//对于实物中P3.4口没有外接上拉电阻的此处代码需要添加使数据口有一个上升沿脉冲。_nop_();DS1302_IO 0;_nop_();DS1302_IO 1;_nop_(); return value;
}
3.时序之上的东西
1.大小端 一个字节发出去先发高位还是低位【IOaddr0x10】表示先发低位 【IOaddr0x80】先发高位 2.如何读写寄存器 void ds1302_write_byte(u8 addr,u8 dat) addr寄存器的地址 dat:寄存器数据 4.SPI时序特征
1.低位在前 2.DS1302在上升沿读取下降沿写入 上升沿CLK0;CLK1; 下降沿CLK1CLK0 3.注意SCLK工作频率 延时长短太短则单片机来不及读取 4.编程实践
1.编写ds1302_write_reg //****************************************************
//向ds1302的内部寄存器addr写入一个值value
/**
addr:内部寄存器的地址
value:内部寄存器的值
*/
void ds1302_write_reg(unsigned char addr,unsigned char value){unsigned char i0;unsigned char dat;//【第一步】起始部分 SCLK和RST为低电平IO无所谓SCLK0;delay();RST0;delay();RST1; //SCLK为低时RST由低变高意味着一个大的周期的开始delay();//【第二步】写入第一个字节addrfor(i0;i8;i){dataddr0x01; //SPI是从低位开始传输此时取出最低位addraddr1; //把addr右移一位将原来的数值移回去delay();DSIOdat; //表示将取出的二进制字符输入到IO口把要发送的Bit数据丢到IO引脚上去准备//一个循环写入一个字节SCLK1; //意味着有一个上升沿delay();SCLK0; //读走之后一个小周期就结束把SCLK拉低是为了下一个小周期做准备}//【第三步】写入第二个字节valuefor(i0;i8;i){datvalue0x01; //SPI是从低位开始传输此时取出最低位valuevalue1; //把addr右移一位将原来的数值移回去DSIOdat; //表示将取出的二进制字符输入到IO口把要发送的Bit数据丢到IO引脚上去准备delay();//一个循环写入一个字节SCLK1; //意味着有一个上升沿delay();SCLK0; //读走之后一个小周期就结束把SCLK拉低是为了下一个小周期做准备}//【第四步】时序结束IO无所谓SCLK0; //SCLK拉低是为了后面的周期时初始状态是正确的delay();RST0;// 表示一个大周期的结束delay();
}2.编写ds1302_read_reg
//****************************************************
//向ds1302的内部寄存器addr读入一个值作为返回值
/**
addr:内部寄存器的地址
value:内部寄存器的值
*/
unsigned char ds1302_read_reg(unsigned char addr)
{unsigned char i 0;unsigned char dat 0; // 用来存储读取到的一字节数据的unsigned char tmp 0;// 第1部分: 时序起始SCLK 0;delay();RST 0;delay();RST 1; // SCLK为低时RST由低变高意味着一个大的周期的开始delay();// 第2部分: 写入要读取的寄存器地址addrfor (i0; i8; i){dat addr 0x01; // SPI是从低位开始传输的DSIO dat; // 把要发送的bit数据丢到IO引脚上去准备好SCLK 1; // 制造上升沿让DS1302把IO上的值读走delay(); // 读走之后一个小周期就完了SCLK 0; // 把SCLK拉低是为了给下一个小周期做准备delay();addr 1; // 把addr右移一位}// 第3部分: 读出一字节DS1302返回给我们的值【SPI下降沿才可以进行读取】dat 0;for (i0; i8; i){// 在前面向ds1302写入addr的最后一个bit后ds1302就会将读取到的寄存器值// 的第一个bit放入到IO引脚上所以我们应该先读取IO再制造下降沿然后继续// 读取下一个bittmp DSIO;dat | (tmp i); // 读出来的数值是低位在前的SCLK 1; // 由于上面SCLK是低所以要先拉到高delay();SCLK 0; // 拉低SCLK制造一个下降沿delay();}// 第4部分: 时序结束SCLK 0; // SCLK拉低为了后面的周期时初始状态是对的delay();RST 0; // RST拉低意味着一个大周期的结束delay();// 第5部分解决读取时间是ff的问题DSIO 0;return dat;
}
3.读取时间 1.DS1302的时间寄存器的地址 如果要读取秒寄存器地址是:0b 1000 0001(0x81) 如果要写入秒寄存器 地址是:0b 1000 0000(0x80) 2.移植串口输出代码将读取到的时间通过串口输出显示 //********************************************************
//因为51单片机的设计本身RAM比较少而Flash比较多像这里定义的数组内部
//的内容是不会变的常量数组我们就可以使用code关键字让编译器帮我们
//把这个数组放在flash中而不是RAM这样做可以省一些RAM
//判断要读取时分秒年月日星期几
unsigned char code READ_RTC_ADDR[7]{0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};//存储时间
unsigned char time[7];// 用来存储读取出来的时间格式是秒分时日月周年 //****************************************************
//向ds1302的内部寄存器addr读入一个值作为返回值
/**
addr:内部寄存器的地址
value:内部寄存器的值
*/
unsigned char ds1302_read_reg(unsigned char addr)
{unsigned char i 0;unsigned char dat 0; // 用来存储读取到的一字节数据的unsigned char tmp 0;// 第1部分: 时序起始SCLK 0;delay();RST 0;delay();RST 1; // SCLK为低时RST由低变高意味着一个大的周期的开始delay();// 第2部分: 写入要读取的寄存器地址addrfor (i0; i8; i){dat addr 0x01; // SPI是从低位开始传输的DSIO dat; // 把要发送的bit数据丢到IO引脚上去准备好SCLK 1; // 制造上升沿让DS1302把IO上的值读走delay(); // 读走之后一个小周期就完了SCLK 0; // 把SCLK拉低是为了给下一个小周期做准备delay();addr 1; // 把addr右移一位}// 第3部分: 读出一字节DS1302返回给我们的值【SPI下降沿才可以进行读取】dat 0;for (i0; i8; i){// 在前面向ds1302写入addr的最后一个bit后ds1302就会将读取到的寄存器值// 的第一个bit放入到IO引脚上所以我们应该先读取IO再制造下降沿然后继续// 读取下一个bittmp DSIO;dat | (tmp i); // 读出来的数值是低位在前的SCLK 1; // 由于上面SCLK是低所以要先拉到高delay();SCLK 0; // 拉低SCLK制造一个下降沿delay();}// 第4部分: 时序结束SCLK 0; // SCLK拉低为了后面的周期时初始状态是对的delay();RST 0; // RST拉低意味着一个大周期的结束delay();// 第5部分解决读取时间是ff的问题DSIO 0;return dat;
}//******************************************************
//读取时间
void ds1302_read_time(void){unsigned char i0;for(i0;i7;i){time[i]ds1302_read_reg(READ_RTC_ADDR[i]);}}void main(){ds1302_read_time();
} 5.使用串口进行调试 1.注意波特率设置和晶振设置 2.注意串口相关的接线设置 3.测试串口输出效果 4.注意二进制显示和文本方式显示 5.注意串口助手打开时烧录软件是不能使用的 1.将读取到的时间输出到串口上 //*************************************************************
//通过串口将7个时间以二进制的方式输出到串口助手上
void debug_print_time(void)
{unsigned char i0;while(1){//1.从ds1302读取时间ds1302_read_time();//2.for循环内打印一组7个时间for(i0;i7;i){uart_send_byte(i);}//3.延时900ms后在继续下一个周期Delay900000us();}
}//串口发送函数,发送一个字节【单个字节】
void uart_send_byte(unsigned char c){//【第一步】发送一个字节SBUFc;//【第二步】先确认串口发送部分没有在忙while(!TI);//TI0表示在忙//【第三步】软件复位TI标志位---数据手册要求的TI0;
}void Delay900000us() //12.000MHz
{unsigned char i, j, k;_nop_();_nop_();i 42;j 10;k 168;do{do{while (--k);} while (--j);} while (--i);
}//****************************************************
//向ds1302的内部寄存器addr读入一个值作为返回值
/**
addr:内部寄存器的地址
value:内部寄存器的值
*/
unsigned char ds1302_read_reg(unsigned char addr)
{unsigned char i 0;unsigned char dat 0; // 用来存储读取到的一字节数据的unsigned char tmp 0;// 第1部分: 时序起始SCLK 0;delay();RST 0;delay();RST 1; // SCLK为低时RST由低变高意味着一个大的周期的开始delay();// 第2部分: 写入要读取的寄存器地址addrfor (i0; i8; i){dat addr 0x01; // SPI是从低位开始传输的DSIO dat; // 把要发送的bit数据丢到IO引脚上去准备好SCLK 1; // 制造上升沿让DS1302把IO上的值读走delay(); // 读走之后一个小周期就完了SCLK 0; // 把SCLK拉低是为了给下一个小周期做准备delay();addr 1; // 把addr右移一位}// 第3部分: 读出一字节DS1302返回给我们的值【SPI下降沿才可以进行读取】dat 0;for (i0; i8; i){// 在前面向ds1302写入addr的最后一个bit后ds1302就会将读取到的寄存器值// 的第一个bit放入到IO引脚上所以我们应该先读取IO再制造下降沿然后继续// 读取下一个bittmp DSIO;dat | (tmp i); // 读出来的数值是低位在前的SCLK 1; // 由于上面SCLK是低所以要先拉到高delay();SCLK 0; // 拉低SCLK制造一个下降沿delay();}// 第4部分: 时序结束SCLK 0; // SCLK拉低为了后面的周期时初始状态是对的delay();RST 0; // RST拉低意味着一个大周期的结束delay();// 第5部分解决读取时间是ff的问题DSIO 0;return dat;
}
2.问题解决 状况 1代码确实得到了一系列的时间数据 2秒确实在变化而且规律正确 3时间数据中有一些FF是不合理的不应该出现的。 总结规律 FF总是出现在前一个周期数字是偶数时前一个如果是奇数则不会出现 解决方法解决读取时间为ff 1.硬件上在IO线上设置10k的电阻做弱上拉电阻处理 2.如果没有做弱上拉也有解决方法。在代码的读取寄存器时序之后加一个将IO置为低电平的代码进去就可以。 //****************************************************
//向ds1302的内部寄存器addr读入一个值作为返回值
/**
addr:内部寄存器的地址
value:内部寄存器的值
*/
unsigned char ds1302_read_reg(unsigned char addr)
{unsigned char i 0;unsigned char dat 0; // 用来存储读取到的一字节数据的unsigned char tmp 0;// 第1部分: 时序起始SCLK 0;delay();RST 0;delay();RST 1; // SCLK为低时RST由低变高意味着一个大的周期的开始delay();// 第2部分: 写入要读取的寄存器地址addrfor (i0; i8; i){dat addr 0x01; // SPI是从低位开始传输的DSIO dat; // 把要发送的bit数据丢到IO引脚上去准备好SCLK 1; // 制造上升沿让DS1302把IO上的值读走delay(); // 读走之后一个小周期就完了SCLK 0; // 把SCLK拉低是为了给下一个小周期做准备delay();addr 1; // 把addr右移一位}// 第3部分: 读出一字节DS1302返回给我们的值【SPI下降沿才可以进行读取】dat 0;for (i0; i8; i){// 在前面向ds1302写入addr的最后一个bit后ds1302就会将读取到的寄存器值// 的第一个bit放入到IO引脚上所以我们应该先读取IO再制造下降沿然后继续// 读取下一个bittmp DSIO;dat | (tmp i); // 读出来的数值是低位在前的SCLK 1; // 由于上面SCLK是低所以要先拉到高delay();SCLK 0; // 拉低SCLK制造一个下降沿delay();}// 第4部分: 时序结束SCLK 0; // SCLK拉低为了后面的周期时初始状态是对的delay();RST 0; // RST拉低意味着一个大周期的结束delay();// 第5部分解决读取时间是ff的问题DSIO 0;return dat;
}
6.DS1302的时间格式详解
1.BCD码 上面显示的时间都是十六进制 1.什么是BCD码 1BCD码是一种数字编码这种计数编码有个特点很像十进制和十六进制的结合。看起来很像十进制29下来是30而不是2ABCD码实际是用十六进制来表示的。【BCD码的21其实在计算机中就是0x21】 BCD中只有0-9而没有ABCDEF等字目。 综合来说BCD码其实就是看起来很像十进制数的十六进制。 意思是BCD码本质是十六进制数但是因为它没有ABCDEF所以看起来很像十进制数。 2BCD码的意义十六进制适合计算机进行计算十进制适合人看和理解 2.区别BCD码16进制10进制三种数
C语言十进制、BCD码互换_51CTO博客_bcd码和十进制的互相转换
2.年份从2000开始 直接读出的数2000就是当前的年份比如读出的BCD码是16对应0x16其实就表示数字16所以读出的是2016年。 7.向DS1302写入时间
1.读时间函数
//********************************************************
//因为51单片机的设计本身RAM比较少而Flash比较多像这里定义的数组内部
//的内容是不会变的常量数组我们就可以使用code关键字让编译器帮我们
//把这个数组放在flash中而不是RAM这样做可以省一些RAM
//判断要读取时分秒年月日星期几
unsigned char code READ_RTC_ADDR[7]{0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};//****************************************************
//向ds1302的内部寄存器addr读入一个值作为返回值
/**
addr:内部寄存器的地址
value:内部寄存器的值
*/
unsigned char ds1302_read_reg(unsigned char addr)
{unsigned char i 0;unsigned char dat 0; // 用来存储读取到的一字节数据的unsigned char tmp 0;// 第1部分: 时序起始SCLK 0;delay();RST 0;delay();RST 1; // SCLK为低时RST由低变高意味着一个大的周期的开始delay();// 第2部分: 写入要读取的寄存器地址addrfor (i0; i8; i){dat addr 0x01; // SPI是从低位开始传输的DSIO dat; // 把要发送的bit数据丢到IO引脚上去准备好SCLK 1; // 制造上升沿让DS1302把IO上的值读走delay(); // 读走之后一个小周期就完了SCLK 0; // 把SCLK拉低是为了给下一个小周期做准备delay();addr 1; // 把addr右移一位}// 第3部分: 读出一字节DS1302返回给我们的值【SPI下降沿才可以进行读取】dat 0;for (i0; i8; i){// 在前面向ds1302写入addr的最后一个bit后ds1302就会将读取到的寄存器值// 的第一个bit放入到IO引脚上所以我们应该先读取IO再制造下降沿然后继续// 读取下一个bittmp DSIO;dat | (tmp i); // 读出来的数值是低位在前的SCLK 1; // 由于上面SCLK是低所以要先拉到高delay();SCLK 0; // 拉低SCLK制造一个下降沿delay();}// 第4部分: 时序结束SCLK 0; // SCLK拉低为了后面的周期时初始状态是对的delay();RST 0; // RST拉低意味着一个大周期的结束delay();// 第5部分解决读取时间是ff的问题DSIO 0;return dat;
}//存储时间
unsigned char time[7];// 用来存储读取出来的时间格式是秒分时日月周年 //******************************************************
//读取时间
void ds1302_read_time(void){unsigned char i0;for(i0;i7;i){time[i]ds1302_read_reg(READ_RTC_ADDR[i]);}} 2.写时间函数
1.数组的设置
//读取时间用到的数组因为是【读】所以最后一位是1
unsigned char code READ_RTC_ADDR[7]{0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};
//写入时间用到的数组因为是【写】所以最后一位是0,所以比READ_RTC_ADDR中的地址分别少1
unsigned char code WRITE_RTC_ADDR[7]{0x80,0x82,0x84,0x86,0x88,0x8a,0x8c}; 2.“写保护”设置 ds1302_write_reg(0x8E0x00); //去掉写保护for(i0;i7;i){ds1302_write_reg(WRITE_RTC_ADDR[i],time[i]);}ds1302_write_reg(0x8E,0x80);//打开写保护3.注意写入地址和读出地址不同
//******************************************************
//写入时间
void ds1302_write_time(void)
{unsigned char i0;//准备好要写入的时间time[0]0x24; //对应24stime[1]0x39;// 对应39mtime[2]0x11; //对应11htime[3]0x30; //对应30日time[4]0x11; //对应12月time[5]0x02; //对应星期二time[6]0x16; //对应2016年ds1302_write_reg(0x8E0x00); //去掉写保护for(i0;i7;i){ds1302_write_reg(WRITE_RTC_ADDR[i],time[i]);}ds1302_write_reg(0x8E,0x80);//打开写保护
}
8.对程序进行规整
1.如何规整 1多文件方式实现意思是多个.c文件来实现 2多文件方式的目的是让各个功能模块分开实现这样方便组织和查找 2.c文件和头文件 1c文件是c语言源文件h文件是头文件 2源文件主要用来放函数和全局变量的定义 3头文件主要用来存放函数和全局变量的声明宏定义结构体共用体类型定义等 4一般是一个源文件就配一个头文件 5一般包含自己建立的头文件时用”“而不用 6头文件中还有固定格式 #ifndef __UART_H__ #define __UART_H__ #endif uart.h
#ifndef __UART_H__
#define __UART_H__#include reg51.hvoid uart_init(void);
void uart_send_byte(unsigned char c);#endif
ds1302.h
#ifndef __DS1302_H__
#define __DS1302_H__void delay(void);
//void delay1s(void);
void delay900ms(void);
void ds1302_write_reg(unsigned char addr, unsigned char value);
unsigned char ds1302_read_reg(unsigned char addr);
void ds1302_read_time(void);
void ds1302_write_time(void);
void debug_print_time(void);#endif
main.c
#include uart.h
#include ds1302.hvoid main(void)
{
// unsigned char i 0;uart_init();ds1302_write_time();
/*// 测试串口工作for (i0; i255; i){uart_send_byte(i);delay1s();}while (1);
*/debug_print_time();
}
ds1302.c
#include reg51.h
#include intrins.h
#include uart.h
#include ds1302.h/************** 全局变量定义 *************************************/// 定义SPI的三根引脚
sbit DSIO P3^4;
sbit RST P3^5;
sbit SCLK P3^6;// 因为51单片机的设计本身RAM比较少而Flash稍微多一些像这里定义的数组内部
// 的内容是不会变的常量数组我们就可以使用code关键字让编译器帮我们
// 把这个数组放在flash中而不是ram中这样做可以省一些ram。
unsigned char code READ_RTC_ADDR[7] {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
unsigned char code WRITE_RTC_ADDR[7] {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
unsigned char time[7]; // 用来存储读取的时间的格式是秒分时日月周年// 有用函数
void delay(void)
{unsigned char i;for (i0; i3; i);
}
/*
void delay1s(void) //误差 0us
{unsigned char a,c;for(c167;c0;c--)for(a16;a0;a--);_nop_(); //if Keil,require use intrins.h
}
*/void delay900ms(void) //误差 -0.000000000205us
{unsigned char a,b,c;for(c127;c0;c--)for(b128;b0;b--)for(a24;a0;a--);
}// 向ds1302的内部寄存器addr写入一个值value
void ds1302_write_reg(unsigned char addr, unsigned char value)
{unsigned char i 0;unsigned char dat 0;// 第1部分: 时序起始SCLK 0;delay();RST 0;delay();RST 1; // SCLK为低时RST由低变高意味着一个大的周期的开始delay();// 第2部分: 写入第1字节addrfor (i0; i8; i){dat addr 0x01; // SPI是从低位开始传输的DSIO dat; // 把要发送的bit数据丢到IO引脚上去准备好SCLK 1; // 制造上升沿让DS1302把IO上的值读走delay(); // 读走之后一个小周期就完了SCLK 0; // 把SCLK拉低是为了给下一个小周期做准备delay();addr 1; // 把addr右移一位}// 第3部分: 写入第2字节valuefor (i0; i8; i){dat value 0x01; // SPI是从低位开始传输的DSIO dat; // 把要发送的bit数据丢到IO引脚上去准备好SCLK 1; // 制造上升沿让DS1302把IO上的值读走delay(); // 读走之后一个小周期就完了SCLK 0; // 把SCLK拉低是为了给下一个小周期做准备delay();value value 1; // 把addr右移一位}// 第4部分: 时序结束SCLK 0; // SCLK拉低为了后面的周期时初始状态是对的delay();RST 0; // RST拉低意味着一个大周期的结束delay();
}// 从ds1302的内部寄存器addr读出一个值作为返回值
unsigned char ds1302_read_reg(unsigned char addr)
{unsigned char i 0;unsigned char dat 0; // 用来存储读取到的一字节数据的unsigned char tmp 0;// 第1部分: 时序起始SCLK 0;delay();RST 0;delay();RST 1; // SCLK为低时RST由低变高意味着一个大的周期的开始delay();// 第2部分: 写入要读取的寄存器地址addrfor (i0; i8; i){dat addr 0x01; // SPI是从低位开始传输的DSIO dat; // 把要发送的bit数据丢到IO引脚上去准备好SCLK 1; // 制造上升沿让DS1302把IO上的值读走delay(); // 读走之后一个小周期就完了SCLK 0; // 把SCLK拉低是为了给下一个小周期做准备delay();addr 1; // 把addr右移一位}// 第3部分: 读出一字节DS1302返回给我们的值dat 0;for (i0; i8; i){// 在前面向ds1302写入addr的最后一个bit后ds1302就会将读取到的寄存器值// 的第一个bit放入到IO引脚上所以我们应该先读取IO再制造下降沿然后继续// 读取下一个bittmp DSIO;dat | (tmp i); // 读出来的数值是低位在前的SCLK 1; // 由于上面SCLK是低所以要先拉到高delay();SCLK 0; // 拉低SCLK制造一个下降沿delay();}// 第4部分: 时序结束SCLK 0; // SCLK拉低为了后面的周期时初始状态是对的delay();RST 0; // RST拉低意味着一个大周期的结束delay();// 第5部分解决读取时间是ff的问题DSIO 0;return dat;
}void ds1302_read_time(void)
{unsigned char i 0;for (i0; i7; i){time[i] ds1302_read_reg(READ_RTC_ADDR[i]);}
}void ds1302_write_time(void)
{unsigned char i 0;// 准备好要写入的时间time[0] 0x24; // 对应 24stime[1] 0x39; // 对应 39mtime[2] 0x11; // 对应 11htime[3] 0x06; // 对应 6日time[4] 0x12; // 对应 12月time[5] 0x02; // 对应 星期2time[6] 0x16; // 对应 2016年ds1302_write_reg(0x8E, 0x00); // 去掉写保护for (i0; i7; i){ds1302_write_reg(WRITE_RTC_ADDR[i], time[i]);}ds1302_write_reg(0x8E, 0x80); // 打开写保护
}// 通过串口将7个时间以二进制方式输出在串口助手上
void debug_print_time(void)
{unsigned char i 0;while (1){// 1 从DS1302读取时间ds1302_read_time();// 2 for循环内打印一组7个时间for (i0; i7; i){uart_send_byte(time[i]); }// 3 延时900ms后再继续下个周期delay900ms();}
}
uart.c
#include uart.h// 串口设置为: 波特率9600、数据位8、停止位1、奇偶校验无
// 使用的晶振是11.0592MHz的注意12MHz和24MHz的不行
void uart_init(void)
{// 波特率9600SCON 0x50; // 串口工作在模式18位串口、允许接收PCON 0x00; // 波特率不加倍// 通信波特率相关的设置TMOD 0x20; // 设置T1为模式2TH1 253;TL1 253; // 8位自动重装意思就是TH1用完了之后下一个周期TL1会// 自动重装到TH1去TR1 1; // 开启T1让它开始工作ES 1;EA 1;
}// 通过串口发送1个字节出去
void uart_send_byte(unsigned char c)
{// 第1步发送一个字节SBUF c;// 第2步先确认串口发送部分没有在忙while (!TI);// 第3步软件复位TI标志位TI 0;
}