一、综述
DS1302是美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片,因为应用非常广泛,结果就导致了大量的国产仿制品,GC1302是一款国产DS1302仿制芯片,使用方法和DS1302完全相同。
二、应用
1、电路原理
上图是一个典型的应用图,MCU通过CE、I/O、SCLK三个引脚控制GC1302工作,GC1302有两种供电方式:备用电源Vcc1和主电源Vcc2,GC1302工作在两者较大者,Vcc2>Vcc1+0.2v,Vcc2供电,Vcc1>Vcc2,Vcc1供电。故当Vcc2失效时,由Vcc1接替工作。
一般情况下,Vcc2由MCU供电,Vcc1外接备用电源,通过设计的充电电路,可以为可充放纽扣电池、大电容等充电。当Vcc2供电时,可以控制是否对备用电源充电,但是使用不可充电的纽扣电池时,不要使用充电功能。
图中的备用电源是外接电容,但是没有设计充电电路。假设加入充电电路给电容充电,经实际测量,如果Vcc2是3.3V,电容充满时为3V左右,大概工作3天,降到1.6V左右,此时已经基本无法维持GC1302正常计时。
2、命令字格式
命令字用来启动数据传输,它的格式如下所示
第7位:必须是1,如果是0,禁止对GC1302写入。
第6位:0表示访问时钟/日历数据,1访问RAM数据。GC1320不仅可以保存时钟,可以RAM中保存一些用户数据。
第5~1位:GC1302寄存器地址。
第0位:0表示写操作,1表示读操作。
下图是读写RTC数据的具体命令字,每一行给出了读写某个寄存器的命令字以及寄存器的存储内容。
例如第三行,读命令字为0x85,写命令字为0x86,该寄存器保存小时信息,并且数据范围1-12或0-23。该命令字的二进制为0b1000 0101,则第7位1,使能操作,第6位0,RTC访问,第5~1位,寄存器地址0x02,第0位1,表示写操作。
注意,寄存器中保存的数据是BCD格式,例如要保存秒数是54s,实际保存时转化为16进制并不是0x36,而是0x54。
有几组寄存器需要特殊的说明:
0x81、0x80,当CH为1时,时钟暂停,为0时,时钟开启。
0x8F、0x8E,当WP为0时,允许修改时钟寄存器,为1时,禁止修改。
0x91、0x90,充电使能及充电参数配置,TCS,位4~位7是1010才使能充电,DS是二极管选择位,用来表示在Vcc2和Vcc1之间连接了1个还是2个二极管,RS是电阻选择,表示Vcc2和Vcc1之间的电阻是多大。具体的配置如下图所示
例如,充电电路为1二极管,2k欧电阻,如要开始充电,该寄存器需要配置为10100101,即0xA3。
3、通讯波形
数据传输需要三条线,CE、SCLK和IO,空闲时,CE,SCLK为低电平,IO随意,也可以为低电平。
CE为1时,才允许读写,否则不允许读写。同时在CE置1时,SCLK必须为0。
SCLK的频率根据Vcc电压决定。例如当Vcc=5V,最大频率可达2MHz。手动拉高拉低对应的GPIO,并在拉高拉低中间加入一定的延时,即可产生SCLK。
数据通讯时,掌握一个关键原则,RTC在SCLK上升沿采集IO数据,MCU在SCLK下降沿采集IO数据。
1、写字节
首先写待读取寄存器的地址,然后再写要写入的字节。
写寄存器地址时,把IO设置为输出模式,在SCLK低电平期间,把IO拉高或拉低,GC1302会在SCLK上升沿读取IO数据,即可读到发送的地址。MCU发送完地址后,还是同样的操作方式,继续写字节即可。
简单来讲,写操作时,MCU在SCLK低电平期间把IO数据准备好,只要保证在SCLK上升沿之前准备好即可,然后把SCLK拉高,RTC就会采集IO上的数据。
2、读字节
首先要写待读取寄存器的地址,然后再读RTC发送的字节。
写寄存器地址时,把IO设置为输出模式,在SCLK上升沿到来之前,准备好IO的数据,GC1302在SCLK上升沿读取地址。
时段1,这是一段SCLK的高电平,它是读字节的关键时段。在该期间,RTC已经在刚过去的上升沿读取完命令字的最后一位,会马上输出寄存器的数据,在紧接的下降沿,MCU需要采集RTC输出的数据。故在时段1内,MCU需要把IO由输出切换为输入。
后续RTC会在SCLK的下降沿到来之前,把IO输出数据准备好,等SCLK的下降沿到来时,MCU采集IO数据。
如果在时段1内,没有及时的切换IO为输入模式,很有可能因为GPIO的输出推挽模式,导致IO的电平无法被GC1302控制,从而丢失数据。
三、源码
如下代码基于STMF411芯片,仅供参考,主要是提供一种思路
#define DS1302_STOP 0x80 #define DS1302_READ_BURST 0xBF
#define DS1302_WRITE_BURST 0xBE
#define DS1302_WRITE_SEC 0x80
#define DS1302_READ_SEC 0x81
#define DS1302_WRITE_MINUTE 0x82
#define DS1302_READ_MINUTE 0x83
#define DS1302_WRITE_HOUR 0x84
#define DS1302_READ_HOUR 0x85
#define DS1302_WRITE_DATE 0x86
#define DS1302_READ_DATE 0x87#define DS1302_WRITE_CHARGE 0x90
#define DS1302_READ_CHARGE 0x91void delay_200us(void)
{uint16_t a = 0;for(uint16_t i=0; i<30; i++)a++;
}void delay_100us(void)
{uint16_t a = 0;for(uint16_t i=0; i<15; i++)a++;
}void DS1302_Start() // 传输开始,拉高CE
{HAL_GPIO_WritePin(DS1302_CE_PORT, DS1302_CE_PIN, GPIO_PIN_RESET);HAL_GPIO_WritePin(DS1302_SCLK_PORT, DS1302_SCLK_PIN, GPIO_PIN_RESET);delay_200us();HAL_GPIO_WritePin(DS1302_CE_PORT, DS1302_CE_PIN, GPIO_PIN_SET);delay_200us();
}void DS1302_Over() // 传输结束,拉低CE
{HAL_GPIO_WritePin(DS1302_CE_PORT, DS1302_CE_PIN, GPIO_PIN_RESET);HAL_GPIO_WritePin(DS1302_IO_PORT, DS1302_IO_PIN, GPIO_PIN_RESET);HAL_GPIO_WritePin(DS1302_SCLK_PORT, DS1302_SCLK_PIN, GPIO_PIN_RESET);
}void DS1302_ClearWriteProtection()// 清除写保护
{DS1302_Start();DS1302_WriteByte(0x8E);DS1302_WriteByte(0x00);DS1302_Over();
}void DS1302_SetWriteProtection() // 设置写保护
{DS1302_Start();DS1302_WriteByte(0x8E);DS1302_WriteByte(0x80);DS1302_Over();
}void set_out(void) // data IO设置为输出模式
{GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.Pin = DS1302_IO_PIN;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_PULLUP;GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;HAL_GPIO_Init(DS1302_IO_PORT, &GPIO_InitStruct);
}void set_in(void) // data IO设置为输入模式
{GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.Pin = DS1302_IO_PIN;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_PULLUP;GPIO_InitStruct.Speed = GPIO_SPEED_LOW;HAL_GPIO_Init(DS1302_IO_PORT, &GPIO_InitStruct);
}uint8_t gc1302_read_byte(uint8_t cmd) // 读相应的寄存器数据
{DS1302_Start();uint8_t i = 0;// write cmdfor(i=0;i<8;i++){HAL_GPIO_WritePin(DS1302_IO_PORT, DS1302_IO_PIN, (GPIO_PinState)(cmd&0x01));cmd >>= 1;delay_100us();HAL_GPIO_WritePin(DS1302_SCLK_PORT, DS1302_SCLK_PIN, GPIO_PIN_SET);if(i == 7) // 注意,当命令字的第7位在上升沿结束后,需要在下降沿之前马上把该IO设置为输入模式set_in();delay_200us(); else// 在上升沿和下降沿之间延时200us,即周期为400us,频率为2.5KHz左右// 该频率可以随意设置,只要不超过最大值即可delay_200us(); HAL_GPIO_WritePin(DS1302_SCLK_PORT, DS1302_SCLK_PIN, GPIO_PIN_RESET);delay_100us();}// read datauint8_t data = 0;for(i=0;i<8;i++){ data >>= 1;if(HAL_GPIO_ReadPin(DS1302_IO_PORT, DS1302_IO_PIN)){data |= 0x80;}delay_100us();HAL_GPIO_WritePin(DS1302_SCLK_PORT, DS1302_SCLK_PIN, GPIO_PIN_SET);delay_200us();HAL_GPIO_WritePin(DS1302_SCLK_PORT, DS1302_SCLK_PIN, GPIO_PIN_RESET);delay_100us();}set_out();DS1302_Over();return data;
}void gc1302_send_byte(uint8_t data_byte) // 写一个字节的时序
{for(uint8_t i=0;i<8;i++){HAL_GPIO_WritePin(DS1302_IO_PORT, DS1302_IO_PIN, (GPIO_PinState)(data_byte&0x01));data_byte >>= 1;delay_100us();HAL_GPIO_WritePin(DS1302_SCLK_PORT, DS1302_SCLK_PIN, GPIO_PIN_SET);delay_200us();HAL_GPIO_WritePin(DS1302_SCLK_PORT, DS1302_SCLK_PIN, GPIO_PIN_RESET);delay_100us();//delay_200us();}
}void gc1302_write_byte(uint8_t cmd, uint8_t data) // 写相应的寄存器数据
{DS1302_Start();gc1302_send_byte(cmd);gc1302_send_byte(data);DS1302_Over();
}void gc1302_test()
{uint8_t write_data[4] = {0x30,0x12,0x18,0x04}; //BCD格式,该时间为4号18时12分30秒uint8_t read_data[4] = {0};DS1302_ClearWriteProtection(); // 清除写保护gc1302_write_byte(DS1302_WRITE_SEC, DS1302_STOP); // 停止计时gc1302_write_byte(DS1302_WRITE_MINUTE, write_data[1]); // 设置分gc1302_write_byte(DS1302_WRITE_HOUR, write_data[2]); // 设置时gc1302_write_byte(DS1302_WRITE_DATE, write_data[3]); // 设置天gc1302_write_byte(DS1302_WRITE_SEC, write_data[0]); // 设置秒,并开始计时gc1302_write_byte(DS1302_WRITE_CHARGE, 0xA5); // 使能充电read_data[0] = gc1302_read_byte(DS1302_READ_SEC);read_data[1] = gc1302_read_byte(DS1302_READ_MINUTE);read_data[2] = gc1302_read_byte(DS1302_READ_HOUR);read_data[3] = gc1302_read_byte(DS1302_READ_DATE);}