今天有没有好好学习鸭?????!!!!
学到现在,基本上把底层过了一遍,,,DS1302不是很懂,今天又看了一遍,记录一下我的理解吧,,,之前学的底层这几天也打算再写一遍,加深一下理解。
<我的GitHub>:DS1302时钟显示
首先明确DS1302的通信时序是SPI的变种,所以我们自然要清楚SPI时序的特点。
上面是 CPOL = 1,CPHA = 1 时的SPI通信时序图
那么SPI与DS1302有什么关系呢?
DS1302内部有三根线:CE(使能线),I/O(数据线),SCLK(时钟线)是不是与SPI有点相像呢,下面给出对比图
图中可以看出:
寄存器在DS1302内部是非常重要,主要用来存储信息,一定要清楚每个寄存器的特点以及功能,这样才能更好的理解底层代码。
图 2 DS1302命令字节
DS1302内部有8个与时钟相关的寄存器,5位地址为0b00000-0b00111(这里是用二进制非圧缩型BCD码表示的)
明确了上面那些之后,我们就可以理解下面这段代码了!!!!!!
哦哦!!还要注意的一点是,DS1302在写入一个字节数据时,要先写一个字节指令,表明要写入的寄存器地址以及后续操作为写操作。
/*向某一寄存器中写入一个字节,reg表示寄存器地址,dat为待写入字节*/
void DS1302SingleWrite(u8 reg, u8 dat)
{
DS1302_CE = 1;
DS1302ByteWrite((reg<<1)|0x80);//指明要写入的寄存器地址以及后续操作为写
DS1302ByteWrite(dat);
DS1302_CE = 0;
}
现在再看这段程序就能理解为什么0x80表示写操作了吧,哈哈~
可以写出第7位到第0位上的值:1000 0000,然后再用16进制表示一下:0x80
同理,可以知道 0x81 表示读操作
当然我们在上面还要再定义一个函数,发送一个字节到DS1302的通信总线上去,也就是上面代码里的DS1302ByteWrite()这个函数。下面是代码:
/*发送一个字节到通信总线上*/
void DS1302ByteWrite(u8 dat)
{
u8 mask;
DS1302_IO = 1;//注意这里要把IO引脚拉高!!!!
for(mask = 0x01; mask != 0; mask <<= 1)
{
if((dat & mask) == 0)//注意这里!!!优先级的问题
DS1302_IO = 0;
else
DS1302_IO = 1;
DS1302_CK = 1;//拉高时钟
DS1302_CK = 0;//拉低时钟,完成一个位的操作
}
DS1302_IO = 1;//最后确保释放IO引脚?
}
有几个问题:
明白了写的过程后,我们再理解 读 的程序就比较容易啦~
下面贴出代码:
u8 DS1302ByteRead()
{
u8 mask;
u8 dat = 0;
for(mask = 0x01; mask != 0; mask <<= 1)
{
if(DS1302_IO)
dat |= mask;
DS1302_CK = 1;
DS1302_CK = 0;
}
return dat;
}
u8 DS1302SingleRead(u8 reg)
{
u8 dat;
DS1302_CE = 1;
DS1302ByteWrite((reg<<1)|0x81);
dat = DS1302ByteRead();
DS1302_CE = 0;
DS1302_IO = 0;//注意最后一定要将IO口拉低!!
return dat;
}
需要注意!!!
为了更好地理解下面的程序,我们需要知道DS1302内部有哪些时钟寄存器!!!
从上到下分别是 寄存器0到寄存器7 ,其中 寄存器0到寄存器6 分别用来存储“秒, 分, 时, 日,月, 周,年 ”(这个顺序一定要记清楚!!!)
寄存器7:最高位一个写保护位,如果这一位是 1,禁止给任何其它寄存器写数据的。因此在写数据之前,这一位必须先写成 0。
这里先大致了解一下这些寄存器,后面的程序里我会再一一说明。
在这里我们单独说一下BURST模式,也叫做突发模式,这里也只说明时钟突发模式。
先说明几个问题:
/*突发写*/
void DS1302BurstWrite(u8 *dat)
{
u8 i;
DS1302_CE = 1;
DS1302ByteWrite(0xBE);
for(i = 0; i<7; i++)//循环写入
{
DS1302ByteWrite(*dat++);
}
DS1302_CE = 0;
}
/*突发读*/
void DS1302BurstRead(u8 *dat)//指针相当于是定义了一个数组,突发读是无返回值的
{
u8 i;
DS1302_CE = 1;
DS1302ByteWrite(0xBF);
for(i = 0; i<7; i++)//循环读
{
dat[i] = DS1302ByteRead();
}
DS1302_CE = 0;
DS1302_IO = 0;//注意!!!!拉低IO引脚
}
struct sTime{/*年, 月, 日, 时, 分, 秒, 周*/
u16 year;
u8 mon;
u8 day;
u8 hour;
u8 min;
u8 sec;
u8 week;
};
/*获取实时时间,即读取DS1302当前时间并转换为时间结构体格式*/
void GetRealTime(struct sTime *time)
{
u8 buf[8];
DS1302BurstRead(buf);
time->year = buf[6] + 0x2000;
time->mon = buf[4];
time->day = buf[3];
time->hour = buf[2];
time->min = buf[1];
time->sec = buf[0];
time->week = buf[5];
}
/*设定实时时间,时间结构体格式的设定时间转换为数组并写入DS1302*/
void SetRealTime(struct sTime *time)
{
u8 buf[8];
buf[7] = 0;
buf[6] = time->year;
buf[4] = time->mon;
buf[3] = time->day;
buf[2] = time->hour;
buf[1] = time->min;
buf[0] = time->sec;
buf[5] = time->week;
DS1302BurstWrite(buf);
}
/*DS1302初始化,设置初始时间*/
void InitDS1302()
{
/*下面的结构体数组是用来存储初始化时间的*/
struct sTime InitTime[] = {/*年,月,日, 时, 分, 秒,显示初始化时间为
10时40分0秒*/
0x00, 0x00, 0x00, 0x10, 0x40, 0x00,0x00
};
DS1302_CE = 0;
DS1302_CK = 0;
DS1302SingleWrite(7, 0X00);//撤销写保护,可以看一下寄存器7的功能
SetRealTime(&InitTime);
}
几个问题:
(1). 关于初始化时间部分,可能会有疑问,为什么“struct sTime InitTime[] = {0x00, 0x00, 0x00, 0x10, 0x40, 0x00,0x00};”这样定义数组显示10时40分00秒呢?
这是因为我们在定义结构体时的变量顺序是“年,月,日, 时, 分, 秒”。
(2)关于设定以及获取实时时间部分: 还记得上面我强调一定要记清楚每个寄存器里储存什么内容吗?寄存器0-寄存器6 分别存储“秒,分,时,日,月,周,年”
那么我们自然就可以理解下面这段程序啦~
buf[6] = time->year;//寄存器6存储年
buf[4] = time->mon;//寄存器4存储月
buf[3] = time->day;
buf[2] = time->hour;
buf[1] = time->min;
buf[0] = time->sec;
buf[5] = time->week;
void ShowLedNumber(u8 index, u8 num)
{
if(num == 0xBF)//第二个及第五个数码管的‘-’处理
LedBuff[index] = 0xBF;
else
LedBuff[index] = LedChar[num];
}
void RefreshTime()
{
GetRealTime(&buffTime);//主函数里有定义buffTime变量
ShowLedNumber(7, buffTime.hour>>4);//高四位中存储有小时的十位
ShowLedNumber(6, buffTime.hour&0x0F);//低四位是小时的个位
ShowLedNumber(5, 0xBF);
ShowLedNumber(4, buffTime.min>>4);
ShowLedNumber(3, buffTime.min&0x0F);
ShowLedNumber(2, 0xBF);
ShowLedNumber(1, buffTime.sec>>4);
ShowLedNumber(0, buffTime.sec&0x0F);
}
关于刷新时间部分,可能也会不太理解,这里其实是用到了寄存器0(秒),寄存器1(分),寄存器2(时)的特点;
寄存器0:最高位 CH 是一个时钟停止标志位,剩下的7 位高 3 位是秒的十位,低 4 位是秒的个位;
寄存器1:最高位未使用,剩下的 7 位中高 3 位是分钟的十位,低 4 位是分钟的个位;
寄存器2:低 4 位代表的是小时的个位。
关于显示部分,当然还用到了Led扫描函数,等我再写数码管显示的底层的时候会再细讲哒~