DS1302电子时钟模块——树莓派
DS1302电子时钟模块
结构
通俗地说,这就是一个电子表。时间存放在里面的寄存器中,我们需要读取或修改里面的内容。
具体应用如下图,右侧是DS1302模块,一共八个管腿儿。
- VCC1、VCC2都是供电电源,有一个是后备不隐藏电源(可充电纽扣电池),GND接地。
- X1、X2接一个32.768kHz的晶振。32768是215,分频15次后是1Hz,周期为1s。这个数也是工程师的实践经验。
- CE是一个输入信号,为高时才能进行读写操作。之前也被称作 RST ‾ \overline{\text{RST}} RST。
- I/O是数据输入输出线。
- SCLK是串行时钟线,为输入。
仔细分析这八个管脚,会发现我们真正需要操作的只有CE、I/O、SCLK三条线。
逻辑
CE拉高允许数据进行传输,拉低禁止数据传输。
DS1302在时钟上升沿写入数据,在时钟下降沿输出数据。传输开始的第一个字节是写入的,称为命令字节(command byte)。而且不管输入输出,数据都是先发送最低有效位,即最低位。
命令字节如下图:
最高位固定为1。
第6位没搞懂什么意思,后面暂时使用0。
5-1位是要操作的寄存器地址。DS1302共有31个寄存器,从0到30。如果地址写入31即全1,是突发传输模式,写入一个命令字后,可以连续对所有寄存器操作。
第0位,表示是要进行读操作还是写操作。
单字节输入输出
即一次只对一个寄存器进行操作,需要一个命令字节+数据字节
- 单字节写入
一个命令字节+一个准备写入的数据字节。每一位都是写入操作,所以需要16个上升沿(16个时钟周期)。在时钟上升前,准备好数据。
- 单字节读取
写入一个字节+读取一个字节。需要八个上升沿和八个下降沿,但是要注意中间是共用一个周期的,所以需要15个周期。在时钟沿下降后,进行数据的读取。
- 时序
在进行数据传输的时候,还需要参考官方手册的要求。一些信号的变化和保持,都需要符合要求。听一些课程讲单片机的指令是微秒级别的,这里不主动延时也符合要求。
- 寄存器
对于时间的读取和设置,实际上只需要操作八个寄存器,如下图,从上到下对应的0-7号寄存器。每个字段的含义也已经标出来了。7号寄存器的WP是写保护位,如果需要修改寄存器内容时,一定要把这个位设置为0。0号寄存器CH(bit 7)是暂停标志,设置之后就不走秒了。3号寄存器的最高位表示是使用的是12小时计时还是24小时计时。其它字段看单词含义就很清楚了。
另外,每个字段都是用BCD码表示的,实际效果相当于使用十六进制的字面值来表示10进制,所以使用时可以不用编写转换函数,按十六进制操作即可。
树莓派准备
软件基础
需要使用到的函数:
//初始化函数,必须调用
int wiringPiSetup(void);
//设置管脚输入输出模式
void pinMode(int pin,int mode);
//设置管脚输出高低电平
void digitalWrite(int pin,int value);
//读取管脚的高低电平
int digitalRead(int pin);
设计和实现思路
简单地实现字节写入和字节读取的功能,从而实现设置时间和读取时间的功能。
实现按照时序图进行操作即可。
实际遇到的问题
- 读取数据和预期不相符
接收字节的时候位操作不当,把最低位和最高位弄反了。 - 数据循环读取仅第一次值符合预期,其余值为0
这个错误找了好久,最后才发现是读取数据的时候将管脚设置为输入模式后,没有将其恢复成输出模式,影响下一次数据的写入。
所以要特别注意输入输出模式,最好在需要进行操作前对管脚进行一下设置。或者在操作后将管脚恢复成默认状态(这个容易忘…)。 - 时钟不走秒,停在一个数
CH标志位为0但时钟就是不走秒,百度发现大概率是晶振的问题,经维修(物理掰动晶振)后正常。
附测试代码:
#include <wiringPi.h>
#include <stdio.h>
#define CLK 1
#define DAT 28
#define CE 24
typedef unsigned char u8;
void init()
{
wiringPiSetup();
pinMode(CE,OUTPUT);
pinMode(CLK,OUTPUT);
pinMode(DAT,OUTPUT);
digitalWrite(CE,LOW);
digitalWrite(CLK,LOW);
}
u8 read_reg(u8 reg)
{
u8 cmd=(reg<<1)|0x81;
u8 temp;
digitalWrite(CE,HIGH);
for(int i=0;i<8;i++)
{
temp=cmd&0x1;
cmd=cmd>>1;
if(temp==1) digitalWrite(DAT,HIGH);
else digitalWrite(DAT,LOW);
digitalWrite(CLK,LOW);
digitalWrite(CLK,HIGH);
}
pinMode(DAT,INPUT);
for(int i=0;i<8;i++)
{
digitalWrite(CLK,LOW);
if(digitalRead(DAT)==HIGH) temp|=0x1<<7;
else temp|=0x0<<7;
if(i!=7) temp=temp>>1;
if(i!=7) digitalWrite(CLK,HIGH);
}
digitalWrite(CE,LOW);
pinMode(DAT,OUTPUT);
return temp;
}
u8 write_reg(u8 reg,u8 data)
{
u8 cmd=(reg<<1)|0x80;
u8 temp;
digitalWrite(CE,HIGH);
for(int i=0;i<8;i++)
{
temp=cmd&0x1;
cmd=cmd>>1;
if(temp==1) digitalWrite(DAT,HIGH);
else digitalWrite(DAT,LOW);
digitalWrite(CLK,LOW);
digitalWrite(CLK,HIGH);
}
for(int i=0;i<8;i++)
{
temp=data&0x1;
data=data>>1;
if(temp==1) digitalWrite(DAT,HIGH);
else digitalWrite(DAT,LOW);
digitalWrite(CLK,LOW);
digitalWrite(CLK,HIGH);
}
digitalWrite(CLK,LOW);
digitalWrite(CE,LOW);
return 0;
}
int main()
{
init();
int i;
write_reg(7,0);
write_reg(0,0x80);
write_reg(6,0x21);
write_reg(5,0x07);
write_reg(4,0x11);
write_reg(3,0x14);
write_reg(2,0x20);
write_reg(1,0x10);
write_reg(0,0x1);
while(1){
delay(1000);
for(int i=7;i>=0;i--)
{
printf("%x:",read_reg(i));
}
printf("\n");
}
return 0;
}