DS1307是一款低功耗,具有56字节非失性RAM的全BCD码时钟日历实时时钟芯片,地址和数据通过两线双向的串行总线的传输,芯片可以提供秒,分,小时等信息,每一个月的天数能自动调整。并且有闰年补偿功能。
Arduino上可以使用别人写好的库,但是我们也可以自己写一个简单的,正好拿来练练手。
观察DS1307芯片,不难发现有SDA和SCL两个引脚,那么通信总线应该是I2C。
第一部分 I2C通信
Arduino进行I2C总线通信时,通常需要写出以下关键程序。
//初始化
void ds1307_init()
{
pinMode(sda,OUTPUT);
digitalWrite(scl,HIGH);
delayMicroseconds(5);
digitalWrite(sda,HIGH);
delayMicroseconds(5);
}
//启动信号
void ds1307_start()
{
pinMode(sda,OUTPUT);
digitalWrite(sda,HIGH);
digitalWrite(scl,HIGH);
delayMicroseconds(5);
digitalWrite(sda,LOW);
delayMicroseconds(5);
}
//等待应答
void ds1307_waitack()
{
unsigned char i=255;
pinMode(sda,INPUT);
digitalWrite(scl,HIGH);
while(digitalRead(sda) && i)i--;
delayMicroseconds(5);
digitalWrite(scl,LOW);
}
//停止信号
void ds1307_stop()
{
pinMode(sda,OUTPUT);
delayMicroseconds(2);
digitalWrite(sda,LOW);
delayMicroseconds(5);
digitalWrite(scl,HIGH);
delayMicroseconds(5);
digitalWrite(sda,HIGH);
}
//写1字节
void ds1307_writeByte(unsigned char oneByte)
{
pinMode(sda,OUTPUT);
for(int i=0;i<8;i++){
digitalWrite(scl,LOW);
delayMicroseconds(1);
if(oneByte & 0x80){digitalWrite(sda,HIGH);}
else{digitalWrite(sda,LOW);}
delayMicroseconds(1);
oneByte=oneByte<<1;
digitalWrite(scl,HIGH);
delayMicroseconds(1);
}
digitalWrite(scl,LOW);
delayMicroseconds(1);
digitalWrite(sda,HIGH);
delayMicroseconds(1);
}
//读1字节
unsigned char ds1307_readByte()
{
pinMode(sda,INPUT);
unsigned char dat=0;
unsigned char tmp;
for(int i=0;i<8;i++){
digitalWrite(scl,LOW);
delayMicroseconds(5);
digitalWrite(scl,HIGH);
tmp=digitalRead(sda);
tmp=tmp<<7-i;
dat=dat|tmp;
delayMicroseconds(1);
digitalWrite(scl,LOW);
}
return dat;
}
第二部分 DS1307时序
一.DS1307的内部地址如下:
00H-06H是我们需要的数据。
二.写入的时序如下:
启动信号-写入0xD0-等待ack-写入地址-等待ack-写入数据-等待ack-结束信号
地址是每写一个自动加1,写到0x06以后跳回0x00
因此程序如下:
void write_time()
{
ds1307_init();
ds1307_start();
ds1307_writeByte(0xd0);
ds1307_waitack();
ds1307_writeByte(0x00);
ds1307_waitack();
for(int i=0;i<7;i++){
ds1307_writeByte(TIME[i]);
ds1307_waitack();
}
ds1307_stop();
}
三.读取时序
和写入差不多,但要注意2点:
1.读取的地址是跟着写入地址的,也就是说如果上面的写入地址是0x01,读取的时候也是从0x01开始读,因此可以在读取之前先进行定位。
2.应答和非应答信号是单片机发送给DS1307的。
void read_time()
{
ds1307_start();
ds1307_writeByte(0xd1);
ds1307_waitack();
TIME[0]=ds1307_readByte();
ds1307_sendack();
TIME[1]=ds1307_readByte();
ds1307_sendack();
TIME[2]=ds1307_readByte();
ds1307_sendack();
TIME[3]=ds1307_readByte();
ds1307_sendack();
TIME[4]=ds1307_readByte();
ds1307_sendack();
TIME[5]=ds1307_readByte();
ds1307_sendack();
TIME[6]=ds1307_readByte();
ds1307_noack();
ds1307_stop();
}
void ds1307_noack()
{
pinMode(sda,OUTPUT);
digitalWrite(scl,LOW);
delayMicroseconds(5);
digitalWrite(sda,HIGH);
delayMicroseconds(2);
digitalWrite(scl,HIGH);
delayMicroseconds(5);
digitalWrite(scl,LOW);
pinMode(sda,INPUT);
}
void ds1307_sendack()
{
pinMode(sda,OUTPUT);
digitalWrite(scl,LOW);
delayMicroseconds(5);
digitalWrite(sda,LOW);
delayMicroseconds(2);
digitalWrite(scl,HIGH);
delayMicroseconds(5);
digitalWrite(scl,LOW);
pinMode(sda,INPUT);
}
这样基本上就差不多了。
结尾
其实后面还有个坑,没注意到的话程序是读不出来的,卖个关子,呵呵