今天,又花了差不多一天时间,从手册看起,到写完代码,最后仿真。期间出现了一个细小的差错,折腾了约1个钟头才解决掉(所以,最怕底层细小之处出现错误)。
DS1302是达拉斯公司出品的一款实时时钟芯片。具体且详细的资料介绍在其芯片手册上面都有写着。说起来自己读DS1302芯片手册,也花了不少精力,主要是对一个问题一直没理解。
下图是DS1302时钟寄存器的结构。(注意左边READ与WRITE两列)
下图是DS1302的命令字节(就是跟它通信的格式)
其中A4到A0可以代表寄存器的地址。我想,5bit表示的地址,翻遍了手册,在上面也没有说明5bit的地址是怎么表示的(手册上只有读写地址,8bit)。就这样,我纳闷了,就去问问搜索引擎。网上挂出来最多的尽是些代码(说实话,驱动代码不难写,难的是对芯片的理解),最终还是找到了一篇博文(写了对DS1302的理解),而且我才焕然大悟,原来READ与WRITE两列是什么的。
举个例子:写秒寄存器的地址是0x80,展开成8bit的就是1000,0000(结合命令字节看看),你会发现其实就是命令字节。
现在来说说DS1302的SPI接口(其实比标准的SPI接口少了一根线),它包含RST线、SCLK线、IO线(双向传送数据用,标准的SPI则将其分成两根MISO与MOSI)3条接线。
上面这张图片就是其时序图,单字节读取和单字节写。相比1条线的单总线、2条线的IIC,这个SPI貌似是最简单的。每次传送时,需要先发送8bit命令字节,再发送/接收8bit数据。
再细小的看,CE(也就是RST)先拉至高电平,在IO线上事先要准备好数据,然后将SCLK拉高,一个上升沿,发完了1bit。如此往复,发完接下来的几位。如果是发送数据的话,注意每次都是上升沿发送1bit。如果是接收数据的话,注意,在传完最后命令字节的1(高电平)之后的第一个下降沿后DS1302发送数据。当然DS1302能够每次传送多个字节。
说完了通信机制,通信内容,基本上差不多了。值得提醒的是,DS1302内部有31字节RAM,可以用来保存数据。
今天写的有点少。下面贴上调好的代码:
头文件部分:
#ifndef __hal_ds1302_h__ #define __hal_ds1302_h__ #include#include"datatype.h" #include"delay.h" #include"hal.h" sbit rst=P3^2; sbit sclk=P3^3; sbit sda_ds1302=P1^7; #define RST rst #define SCLK sclk #define SIO sda_ds1302 #define WRITE_SECONDS 0x80//秒 #define READ_SECONDS 0x81 #define WRITE_MINUTES 0x82//分 #define READ_MINUTES 0x83 #define WRITE_HOUR 0x84//时 #define READ_HOUR 0x85 #define WRITE_DATE 0x86//日 #define READ_DATE 0x87 #define WRITE_MONTH 0x88//月 #define READ_MONTH 0x89 #define WRITE_DAY 0x8a//星期 #define READ_DAY 0x8b #define WRITE_YEAR 0x8c//年 #define READ_YEAR 0x8d #define WRITE_WP 0x8e//写保护bit7为高时,不允许写 #define READ_WP 0x8f//所以写之前需将其设为0 //ds1302时间结构体类型 struct ds1302_time { //秒0-59 uchar seconds; //分0-59 uchar minutes; //时0-23(24小时模式) //如果需要12小时模式,跟寄存器格式需要一致 uchar hour; //星期1-7 enum{Sunday=1,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday} day; //日1-31 uchar date; //月1-12 uchar month; //年0-99 uchar year; }; //参数1:写保护;参数0:取消写保护 #define HAL_DS1302_WRITE_PROTECT(wp) hal_ds1302_single_byte_write(WRITE_WP,wp?0x80:0x00) void hal_ds1302_init(); void hal_ds1302_single_byte_write(uchar comm,uchar val); uchar hal_ds1302_single_byte_read(uchar comm); void hal_ds1302_init_time(struct ds1302_time time); void hal_ds1302_get_time(struct ds1302_time * time); #endif
C文件部分:
1 #include"hal_ds1302.h" 2 3 void hal_ds1302_init() 4 { 5 RST=0; 6 SCLK=0; 7 SIO=1; 8 } 9 10 //实质上传送了16bit 11 void hal_ds1302_single_byte_write(uchar comm,uchar val) 12 { 13 //RST低电平 SCLK低电平 14 uchar i; 15 RST=1; 16 for(i=0;i<8;i++)//从低位开始传送 17 { 18 if(comm&(0x01<<i)) 19 SIO=1; 20 else 21 SIO=0; 22 SCLK=1;//SCLK一个上升沿,读取了SDA上的电平 23 //单片机时钟周期1us情况下,几乎不用考虑延时 24 SCLK=0; 25 } 26 for(i=0;i<8;i++) 27 { 28 if(val&(0x01<<i)) 29 SIO=1; 30 else 31 SIO=0; 32 SCLK=1;//上升沿读取 33 SCLK=0; 34 } 35 RST=0; 36 //RST低电平 SCLK低电平 SDA电平未知 37 } 38 39 //实质上传送了16bit 40 uchar hal_ds1302_single_byte_read(uchar comm) 41 { 42 //RST低电平 SCLK低电平 43 uchar i,tmp=0; 44 RST=1; 45 for(i=0;i<8;i++) 46 { 47 SCLK=0;//放在这儿是为了第8次SCLK依旧是高电平 48 if(comm&(0x01<<i)) 49 SIO=1; 50 else 51 SIO=0; 52 SCLK=1;//上升沿,DS1302读取值 53 } 54 //现在SCLK依旧是高电平 55 //而且达拉斯考虑到了总线释放问题,所以传送的最后一位都为1 56 for(i=0;i<8;i++) 57 { 58 SCLK=1;//放在此处是为了i=7时候,SCLK仍为低电平 59 SCLK=0;//下降沿,DS1302输出数据 60 if(SIO) 61 tmp=tmp|(0x01<<i); 62 } 63 RST=0; 64 return tmp; 65 //RST低电平 SCLK低电平 SDA电平状态未知 66 } 67 68 //参数char类型16进制 69 //传出2个4位BCD码组成的8bit数据 70 uchar hal_ds1302_uchar2BCD(uchar val) 71 { 72 uchar shi=0,ge=0; 73 shi=val/10%10; 74 ge=val%10; 75 return (shi<<4)|ge; 76 } 77 78 uchar hal_ds1302_BCD2uchar(uchar val) 79 { 80 uchar shi=0,ge=0; 81 shi=val>>4; 82 ge=val&0x0f;//小心,就是这儿0x0f写错了,不是0xf0 83 return shi*10+ge; 84 } 85 86 //1.设置写保护位为0 87 //2.设置秒字节,同时将bit7置1(暂停计时) 88 //3.设置年、月、日、星期、时、分 89 //4设置秒字节,晶振起震 90 //5.设置写保护字节bit7为1 91 //6.之后可以读取时间了 92 void hal_ds1302_init_time(struct ds1302_time time) 93 { 94 HAL_DS1302_WRITE_PROTECT(0); 95 delay_ms(1); 96 hal_ds1302_single_byte_write(WRITE_SECONDS,0x80); 97 delay_ms(1); 98 hal_ds1302_single_byte_write(WRITE_YEAR,hal_ds1302_uchar2BCD(time.year)); 99 delay_ms(1); 100 hal_ds1302_single_byte_write(WRITE_MONTH,hal_ds1302_uchar2BCD(time.month)); 101 delay_ms(1); 102 hal_ds1302_single_byte_write(WRITE_DATE,hal_ds1302_uchar2BCD(time.date)); 103 delay_ms(1); 104 hal_ds1302_single_byte_write(WRITE_DAY,hal_ds1302_uchar2BCD(time.day)); 105 delay_ms(1); 106 hal_ds1302_single_byte_write(WRITE_HOUR,hal_ds1302_uchar2BCD(time.hour)); 107 delay_ms(1); 108 hal_ds1302_single_byte_write(WRITE_MINUTES,hal_ds1302_uchar2BCD(time.minutes)); 109 delay_ms(1); 110 hal_ds1302_single_byte_write(WRITE_SECONDS,hal_ds1302_uchar2BCD(time.seconds)); 111 delay_ms(1); 112 HAL_DS1302_WRITE_PROTECT(1); 113 } 114 115 //注意对12小时模式没有进行编写,如有需要,需要额外修改 116 void hal_ds1302_get_time(struct ds1302_time * time) 117 { 118 time->year=hal_ds1302_BCD2uchar(hal_ds1302_single_byte_read(READ_YEAR)); 119 delay_ms(1); 120 time->month=hal_ds1302_BCD2uchar(hal_ds1302_single_byte_read(READ_MONTH)); 121 delay_ms(1); 122 time->date=hal_ds1302_BCD2uchar(hal_ds1302_single_byte_read(READ_DATE)); 123 delay_ms(1); 124 time->day=hal_ds1302_BCD2uchar(hal_ds1302_single_byte_read(READ_DAY)); 125 delay_ms(1); 126 time->hour=hal_ds1302_BCD2uchar(hal_ds1302_single_byte_read(READ_HOUR)); 127 delay_ms(1); 128 time->minutes=hal_ds1302_BCD2uchar(hal_ds1302_single_byte_read(READ_MINUTES)); 129 delay_ms(1); 130 time->seconds= hal_ds1302_BCD2uchar(hal_ds1302_single_byte_read(READ_SECONDS)); 131 }