
(rtc for real time clock)
(1)时间点和时间段的概念区分
(2)单片机为什么需要时间点【一定的时间点干什么事情】
(3)RTC如何存在于系统中(单片机内部集成 or 单片机外部扩展【DS1302】)

SPI通信协议【DS1302也使用这个协议】,两个芯片之间的通信


(1)3线(SCLK,RST,IO)或者4线(SCLK,RST,I,O)
(2)同步:SPI是同步通信(表示主机【产生CLK】和从机【接受CLK】使用同一个SCLK)【同步通信有SCLK,异步没有SCLK】
(3)主从:有主机和从机
(4)串行:数据都从一根线进出【数据都是从IO进出】
JP595断开,是为了让P3.4在控制DS1302的时候,不影响74HC595工作
JP1302接上,是为了让P3.4能控制到DS1302
J11断开,是为了让P3.5在控制DS1302的时候,不影响NE555模块工作

正常的产品一般不会这样设计,正常产品一般接线都是确定的,一般不会复用。
开发板来说,主要是为了学习,所以会放很多给模块,所以在这个时候GPIO就不够使用,这时候就需要复用设计。一个引脚接多个模块就会互相影响(有2种可能:一个是A模块工作时B模块莫名其妙的工作,二是有时候B模块会影响到A模块的正常工作)。对于复用引脚的情况,接线的关键是确认目标模块接线OK时还不会影响到其他模块。
https://www.dianyuan.com/upload/community/2014/02/22/1393058389-67878.pdf





RST:由低电平变为高电平表示要开始工作了





(1)横轴表示时间,纵轴表示同一个时间各个通信线的状态
(2)静态或动态2个角度去看
(3)主要SCLK的边缘--->会影响IO的电平状态【如果为上升沿,代表IO端口应该在快上升沿和结束上升沿时应该保持高电平】

- /*******************************************************************************
- * 函 数 名 : ds1302_write_byte
- * 函数功能 : DS1302写单字节
- * 输 入 : addr:地址/命令
- dat:数据
- * 输 出 : 无
- *******************************************************************************/
- void ds1302_write_byte(u8 addr,u8 dat)
- {
- u8 i=0;
- //出于安全期间,在进入之前要将SCLK和RST进行初始化为0
- DS1302_RST=0;
- _nop_();
- DS1302_CLK=0;//CLK低电平
- _nop_();
-
- DS1302_RST=1;//RST由低到高变化,表示要开始工作
- _nop_();
-
- //开始传送八位数据
- for(i=0;i<8;i++)
- {
- //将数据放入IO口中
- DS1302_IO=addr&0x01;//数据从低位开始传送
- addr>>=1;
- DS1302_CLK=1; //上升沿
- //因为读取数据需要一段时间
- _nop_();//delay()函数
- DS1302_CLK=0;//下降沿
- _nop_();
- }
- for(i=0;i<8;i++)//循环8次,每次写1位,先写低位再写高位
- {
- DS1302_IO=dat&0x01;
- dat>>=1;
- DS1302_CLK=1;
- _nop_();
- DS1302_CLK=0;
- _nop_();
- }
- //表示时序结束了
- DS1302_RST=0;//RST拉低
- DS1302_CLK=0;//为下一次循环做准备
- _nop_();
- }
- /*******************************************************************************
- * 函 数 名 : ds1302_read_byte
- * 函数功能 : DS1302读单字节
- * 输 入 : addr:地址/命令
- * 输 出 : 读取的数据
- *******************************************************************************/
- u8 ds1302_read_byte(u8 addr)
- {
- u8 i=0;
- u8 temp=0;
- u8 value=0;
-
- DS1302_RST=0;
- _nop_();
- DS1302_CLK=0;//CLK低电平
- _nop_();
- DS1302_RST=1;//RST由低到高变化
- _nop_();
-
- for(i=0;i<8;i++)//循环8次,每次写1位,先写低位再写高位
- {
- DS1302_IO=addr&0x01;
- addr>>=1;
- DS1302_CLK=1;
- _nop_();
- DS1302_CLK=0;//CLK由低到高产生一个上升沿,从而写入数据
- _nop_();
- }
- for(i=0;i<8;i++)//循环8次,每次读1位,先读低位再读高位
- {
- temp=DS1302_IO;
- value=(temp<<7)|(value>>1);//先将value右移1位,然后temp左移7位,最后或运算
- DS1302_CLK=1;
- _nop_();
- DS1302_CLK=0;
- _nop_();
- }
- DS1302_RST=0;//RST拉低
- _nop_();
- DS1302_CLK=1;//对于实物中,P3.4口没有外接上拉电阻的,此处代码需要添加,使数据口有一个上升沿脉冲。
- _nop_();
- DS1302_IO = 0;
- _nop_();
- DS1302_IO = 1;
- _nop_();
- return value;
- }
一个字节发出去,先发高位还是低位【IO=addr&0x01】表示先发低位
【IO=addr&0x80】先发高位

void ds1302_write_byte(u8 addr,u8 dat)
addr:寄存器的地址
dat:寄存器数据

上升沿:CLK=0;CLK=1;
下降沿:CLK=1;CLK=0

延时长短,太短则单片机来不及读取
DS1302是在上升沿读取【表示我们如果要写入数据,则应该在上升沿写入】
1)先放入数据
2)SCLK=1
3)SCLK=0

- //****************************************************
- //向ds1302的内部寄存器addr写入一个值value
- /**
- addr:内部寄存器的地址
- value:内部寄存器的值
- */
- void ds1302_write_reg(unsigned char addr,unsigned char value){
-
- unsigned char i=0;
- unsigned char dat;
-
- //【第一步】起始部分 SCLK和RST为低电平,IO无所谓
- SCLK=0;
- delay();
- RST=0;
- delay();
-
- //此时开始工作
- RST=1; //SCLK为低时,RST由低变高,意味着一个大的周期的开始
- delay();
-
-
- //【第二步】写入第一个字节,addr
- for(i=0;i<8;i++){
- dat=addr&0x01; //SPI是从低位开始传输,此时取出最低位
-
- addr=addr>>1; //把addr右移一位,将原来的数值移回去
- delay();
- DSIO=dat; //表示将取出的二进制字符输入到IO口,把要发送的Bit数据丢到IO引脚上去准备
- //一个循环写入一个字节
- SCLK=1; //意味着有一个上升沿
- delay();
- SCLK=0; //读走之后,一个小周期就结束,把SCLK拉低,是为了下一个小周期做准备
- }
-
-
- //【第三步】写入第二个字节,value
- for(i=0;i<8;i++){
- dat=value&0x01; //SPI是从低位开始传输,此时取出最低位
-
- value=value>>1; //把addr右移一位,将原来的数值移回去
-
- DSIO=dat; //表示将取出的二进制字符输入到IO口,把要发送的Bit数据丢到IO引脚上去准备
- delay();
- //一个循环写入一个字节
- SCLK=1; //意味着有一个上升沿
- delay();
- SCLK=0; //读走之后,一个小周期就结束,把SCLK拉低,是为了下一个小周期做准备
- }
-
-
- //【第四步】时序结束,IO无所谓
- SCLK=0; //SCLK拉低是为了后面的周期时初始状态是正确的
- delay();
- RST=0;// 表示一个大周期的结束
- delay();
- }
下降沿写入【表示如果我们要读取数据,则应该在下降沿读取】
1)SCLK=1;
2)SCLC=0;
3)在将数据取出
- //****************************************************
- //向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部分: 写入要读取的寄存器地址,addr
- for (i=0; i<8; 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 (i=0; i<8; i++)
- {
- // 在前面向ds1302写入addr的最后一个bit后,ds1302就会将读取到的寄存器值
- // 的第一个bit放入到IO引脚上,所以我们应该先读取IO再制造下降沿然后继续
- // 读取下一个bit
- tmp = DSIO; //DSIO一次传输一个bit
- 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;
- }



如果要读取秒寄存器,地址是:0b 1000 0001(0x81)
如果要写入秒寄存器 ,地址是:0b 1000 0000(0x80)
-
- //**************************************
- //定义SPI的三个引脚
- sbit DSIO=P3^4;
- sbit RST=P3^5;
- sbit SCLK=P3^6;
-
-
-
- //加入delay,是为了防止速度过快,单片机感受不到
- void delay(){
-
- unsigned char i;
- for(i=0;i<3;i++){
- }
-
- }
-
- //********************************************************
- //因为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写入一个值value
- /**
- addr:内部寄存器的地址
- value:内部寄存器的值
- */
- void ds1302_write_reg(unsigned char addr,unsigned char value){
-
- unsigned char i=0;
- unsigned char dat;
-
- //【第一步】起始部分 SCLK和RST为低电平,IO无所谓
- SCLK=0;
- delay();
- RST=0;
- delay();
- RST=1; //SCLK为低时,RST由低变高,意味着一个大的周期的开始
- delay();
-
-
- //【第二步】写入第一个字节,addr
- for(i=0;i<8;i++){
- dat=addr&0x01; //SPI是从低位开始传输,此时取出最低位
-
- addr=addr>>1; //把addr右移一位,将原来的数值移回去
- delay();
- DSIO=dat; //表示将取出的二进制字符输入到IO口,把要发送的Bit数据丢到IO引脚上去准备
- //一个循环写入一个字节
- SCLK=1; //意味着有一个上升沿
- delay();
- SCLK=0; //读走之后,一个小周期就结束,把SCLK拉低,是为了下一个小周期做准备
- delay();
- }
-
-
- //【第三步】写入第二个字节,value
- for(i=0;i<8;i++){
- dat=value&0x01; //SPI是从低位开始传输,此时取出最低位
-
- value=value>>1; //把addr右移一位,将原来的数值移回去
-
- DSIO=dat; //表示将取出的二进制字符输入到IO口,把要发送的Bit数据丢到IO引脚上去准备
- delay();
- //一个循环写入一个字节
- SCLK=1; //意味着有一个上升沿
- delay();
- SCLK=0; //读走之后,一个小周期就结束,把SCLK拉低,是为了下一个小周期做准备
- }
-
-
- //【第四步】时序结束,IO无所谓
- SCLK=0; //SCLK拉低是为了后面的周期时初始状态是正确的
- delay();
- RST=0;// 表示一个大周期的结束
- delay();
- }
-
-
-
- //****************************************************
- //向ds1302的内部寄存器addr读入一个值,作为返回值
- /**
- addr:内部寄存器的地址
- value:内部寄存器的值
- */
- unsigned char ds1302_read_reg(unsigned char addr)
- {
- unsigned char i = 0;
- unsigned char dat = 0; // 用来存储读取到的一字节数据的
- unsigned char tmp = 0; //因为51单片机不允许DSIO进行移位,所以使用中间变量
-
- // 第1部分: 时序起始
- SCLK = 0;
- delay();
- RST = 0;
- delay();
- RST = 1; // SCLK为低时,RST由低变高,意味着一个大的周期的开始
- delay();
- // 第2部分: 写入要读取的寄存器地址,addr
- for (i=0; i<8; 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 (i=0; i<8; i++)
- {
- // 在前面向ds1302写入addr的最后一个bit后,ds1302就会将读取到的寄存器值
- // 的第一个bit放入到IO引脚上,所以我们应该先读取IO再制造下降沿然后继续
- // 读取下一个bit
- tmp = 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(i=0;i<7;i++){
- time[i]=ds1302_read_reg(READ_RTC_ADDR[i]);
- }
-
- }
-
-
-
- void main(){
- ds1302_read_time();
- }
1.注意波特率设置和晶振设置
2.注意串口相关的接线设置
3.测试串口输出效果
4.注意二进制显示和文本方式显示
5.注意串口助手打开时烧录软件是不能使用的
-
- //*************************************************************
- //通过串口将7个时间以二进制的方式输出到串口助手上
- void debug_print_time(void)
- {
- unsigned char i=0;
- while(1){
-
- //1.从ds1302读取时间
- ds1302_read_time();
-
- //2.for循环内打印一组7个时间
- for(i=0;i<7;i++){
- uart_send_byte(i);
- }
-
- //3.延时900ms后在继续下一个周期
- Delay900000us();
- }
- }
-
-
- //串口发送函数,发送一个字节【单个字节】
- void uart_send_byte(unsigned char c){
-
- //【第一步】发送一个字节
- SBUF=c;
- //【第二步】先确认串口发送部分没有在忙
- while(!TI);//TI=0,表示在忙
- //【第三步】软件复位TI标志位---数据手册要求的
- TI=0;
- }
-
-
- 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部分: 写入要读取的寄存器地址,addr
- for (i=0; i<8; 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 (i=0; i<8; i++)
- {
- // 在前面向ds1302写入addr的最后一个bit后,ds1302就会将读取到的寄存器值
- // 的第一个bit放入到IO引脚上,所以我们应该先读取IO再制造下降沿然后继续
- // 读取下一个bit
- tmp = 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;
- }

(1)代码确实得到了一系列的时间数据
(2)秒确实在变化,而且规律正确
(3)时间数据中有一些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部分: 写入要读取的寄存器地址,addr
- for (i=0; i<8; 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 (i=0; i<8; i++)
- {
- // 在前面向ds1302写入addr的最后一个bit后,ds1302就会将读取到的寄存器值
- // 的第一个bit放入到IO引脚上,所以我们应该先读取IO再制造下降沿然后继续
- // 读取下一个bit
- tmp = 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;
- }

上面显示的时间都是十六进制
(1)BCD码是一种数字编码,这种计数编码有个特点:很像十进制和十六进制的结合。看起来很像十进制(29下来是30而不是2A),BCD码实际是用十六进制来表示的。【BCD码的21其实在计算机中就是0x21】
BCD中只有0-9,而没有ABCDEF等字目。
综合来说:BCD码其实就是看起来很像十进制数的十六进制。
意思是:BCD码本质是十六进制数,但是因为它没有ABCDEF,所以看起来很像十进制数。
(2)BCD码的意义:十六进制适合计算机进行计算,十进制适合人看和理解

C语言:十进制、BCD码互换_51CTO博客_bcd码和十进制的互相转换
直接读出的数+2000就是当前的年份,比如读出的BCD码是16,对应0x16,其实就表示数字16,所以读出的是2016年。
- //********************************************************
- //因为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部分: 写入要读取的寄存器地址,addr
- for (i=0; i<8; 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 (i=0; i<8; i++)
- {
- // 在前面向ds1302写入addr的最后一个bit后,ds1302就会将读取到的寄存器值
- // 的第一个bit放入到IO引脚上,所以我们应该先读取IO再制造下降沿然后继续
- // 读取下一个bit
- tmp = 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 i=0;
- for(i=0;i<7;i++){
- time[i]=ds1302_read_reg(READ_RTC_ADDR[i]);
- }
-
- }

- //读取时间用到的数组:因为是【读】所以最后一位是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};

ds1302_write_reg(0x8E,0x00); //去掉写保护
ds1302_write_reg(0x8E,0x80);//打开写保护
- ds1302_write_reg(0x8E,0x00); //去掉写保护
- for(i=0;i<7;i++){
- ds1302_write_reg(WRITE_RTC_ADDR[i],time[i]);
- }
-
- ds1302_write_reg(0x8E,0x80);//打开写保护

- //******************************************************
- //写入时间
- void ds1302_write_time(void)
- {
- unsigned char i=0;
- //准备好要写入的时间
- time[0]=0x24; //对应24s
- time[1]=0x39;// 对应39m
- time[2]=0x11; //对应11h
- time[3]=0x30; //对应30日
- time[4]=0x11; //对应12月
- time[5]=0x02; //对应星期二
- time[6]=0x16; //对应2016年
-
- ds1302_write_reg(0x8E,0x00); //去掉写保护
- for(i=0;i<7;i++){
- ds1302_write_reg(WRITE_RTC_ADDR[i],time[i]);
- }
-
- ds1302_write_reg(0x8E,0x80);//打开写保护
- }
(1)多文件方式实现,意思是多个.c文件来实现
(2)多文件方式的目的是让各个功能模块分开实现,这样方便组织和查找
(1)c文件是c语言源文件,h文件是头文件
(2)源文件主要用来放:函数和全局变量的定义
(3)头文件主要用来存放:函数和全局变量的声明,宏定义,结构体共用体类型定义等
(4)一般是一个源文件就配一个头文件
(5)一般包含自己建立的头文件时用”“而不用<>
(6)头文件中还有固定格式
#ifndef __UART_H__
#define __UART_H__
#endif
- #ifndef __UART_H__
- #define __UART_H__
-
- #include
-
-
-
-
-
- void uart_init(void);
- void uart_send_byte(unsigned char c);
-
-
-
-
-
- #endif
-
-
-
- #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
-
-
-
-
-
- #include "uart.h"
- #include "ds1302.h"
-
-
- void main(void)
- {
- // unsigned char i = 0;
-
- uart_init();
-
- ds1302_write_time();
- /*
- // 测试串口工作
- for (i=0; i<255; i++)
- {
- uart_send_byte(i);
- delay1s();
- }
- while (1);
- */
- debug_print_time();
- }
- #include
- #include
- #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 (i=0; i<3; i++);
- }
- /*
- void delay1s(void) //误差 0us
- {
- unsigned char a,c;
- for(c=167;c>0;c--)
- for(a=16;a>0;a--);
- _nop_(); //if Keil,require use intrins.h
- }
- */
-
- void delay900ms(void) //误差 -0.000000000205us
- {
- unsigned char a,b,c;
- for(c=127;c>0;c--)
- for(b=128;b>0;b--)
- for(a=24;a>0;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字节,addr
- for (i=0; i<8; i++)
- {
- dat = addr & 0x01; // SPI是从低位开始传输的
- DSIO = dat; // 把要发送的bit数据丢到IO引脚上去准备好
- SCLK = 1; // 制造上升沿,让DS1302把IO上的值读走
- delay(); // 读走之后,一个小周期就完了
- SCLK = 0; // 把SCLK拉低,是为了给下一个小周期做准备
- delay();
- addr >>= 1; // 把addr右移一位
- }
- // 第3部分: 写入第2字节,value
- for (i=0; i<8; 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部分: 写入要读取的寄存器地址,addr
- for (i=0; i<8; 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 (i=0; i<8; i++)
- {
- // 在前面向ds1302写入addr的最后一个bit后,ds1302就会将读取到的寄存器值
- // 的第一个bit放入到IO引脚上,所以我们应该先读取IO再制造下降沿然后继续
- // 读取下一个bit
- tmp = 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 (i=0; i<7; i++)
- {
- time[i] = ds1302_read_reg(READ_RTC_ADDR[i]);
- }
- }
-
- void ds1302_write_time(void)
- {
- unsigned char i = 0;
-
- // 准备好要写入的时间
- time[0] = 0x24; // 对应 24s
- time[1] = 0x39; // 对应 39m
- time[2] = 0x11; // 对应 11h
- time[3] = 0x06; // 对应 6日
- time[4] = 0x12; // 对应 12月
- time[5] = 0x02; // 对应 星期2
- time[6] = 0x16; // 对应 2016年
-
- ds1302_write_reg(0x8E, 0x00); // 去掉写保护
- for (i=0; i<7; 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 (i=0; i<7; i++)
- {
- uart_send_byte(time[i]);
- }
-
- // 3 延时900ms后再继续下个周期
- delay900ms();
- }
- }
-
- #include "uart.h"
-
-
- // 串口设置为: 波特率9600、数据位8、停止位1、奇偶校验无
- // 使用的晶振是11.0592MHz的,注意12MHz和24MHz的不行
- void uart_init(void)
- {
- // 波特率9600
- SCON = 0x50; // 串口工作在模式1(8位串口)、允许接收
- PCON = 0x00; // 波特率不加倍
-
- // 通信波特率相关的设置
- TMOD = 0x20; // 设置T1为模式2
- TH1 = 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;
- }
-