DS1307RTC时钟模块
DS1307是I2C实时时钟芯片(RTC),它是一款低功耗,具有56字节非失性RAM的全BCD码时钟日历实时时钟芯片,地址和数据通过两线双向的串行总线的传输,芯片可以提供秒,分,小时等信息,每一个月的天数能自动调整。并且有闰年补偿功能。AM/PM 标志位决定时钟工作于24小时或12小时模式,芯片有一个内置的电源感应电路,具有掉电检测和电池切换功能。
模块来源
采购链接:
https://item.taobao.com/item.htm?spm=a1z10.3-c-s.w4002-24706531953.10.2b4a6a4bH7IysA&id=522553351753
资料下载链接:
https://pan.baidu.com/s/1BcLbfV5bCjKdAS4PL3BRnA
提取码:6uq7
规格参数
工作电压:4.5-5.5V
工作电流:1.5mA
模块尺寸:27mm28mm8.4mm
控制方式:IIC
实际使用管脚数量:4 Pin(2.54mm间距排针)
说明:带电池,具有掉电检测和电池切换功能
查看资料
原理图说明
- 模块上带有两个芯片,分别是DS1307时钟芯片和AT24C32数据存储芯片,两者都是使用IIC通信,虽然使用的是同一个接口,但是其器件地址不同,因此可以同时操作。
- 模块上带有一个DS18B20数字温度传感器的接口,在模块上引出的DS引脚就是温度传感器的数据输出引脚,但是模块上并没有温度传感器,需要自行安装。
- 模块上的SQ引脚是是DS1307的方波输出引脚,通过内部寄存器控制,可以输出四个方波频率(1Hz, 4kHz, 8kHz, 32kHz)之一。需要注意的是 SQ引脚是开漏输出,需要一个外部上拉电阻。
相关读写步骤说明
我们主要实现的是时间的读取和时间的设置。关于IIC原理,这里不再进行说明,请自行查阅。
读时序:发送IIC起始信号->发送器件地址+0->等待模块应答->应答后发送寄存器地址->等待模块应答->应答后重新发送起始信号->发送器件地址+1->等待模块应答->读取数据->读取完毕主机发送非应答信号->发送IIC停止信号。
写时序:发送IIC起始信号->发送器件地址+0->等待模块应答->应答后发送寄存器地址->等待模块应答->应答后发送数据->发送完毕等待模块应答->发送IIC停止信号。
相关寄存器说明
通过读取适当的寄存器字节来获得时间和日历信息。下图显示了RTC寄存器。时间和日历通过写入适当的寄存器字节来设置或初始化。时间寄存器和日历寄存器的内容采用BCD格式。与星期几对应的值是用户自定义的,但必须是连续的(例如,如果1等于星期日,那么2等于星期一,等等)。不合逻辑的时间和日期条目将导致未定义的操作。寄存器00h的第7位(BIT 7)是时钟停止(CH)位。当这个位被设置为1时,振荡器被禁用。当清除到0时,振荡器被启用。
第一次给设备上电时,时间和日期寄存器通常重置为01/01/00 01 00:00:00 (MM/DD/YY DOW HH:MM:SS)。秒寄存器中的CH位将被设置为1。当不需要计时功能时,可以暂停时钟(给CH置1),从而最小化输出电流。
示例:
//add写入寄存器地址 dat写入数据
uint8_t Write1307(uint8_t add,
uint8_t dat)
{
unsigned char temp;
/* 10进制转BCD码 */
temp=dat/10;
temp<<=4;
temp=dat%10+temp;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
int err=0;
i2c_master_start(cmd);
err = i2c_master_write_byte(cmd, 0xD0, ACK_CHECK_EN);
i2c_master_write_byte(cmd, add, ACK_CHECK_EN);
i2c_master_write_byte(cmd, temp, ACK_CHECK_EN);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_1, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
return (0);
}
Write1307(0x00,0); //设置秒
Write1307(0x01,57); //设置分
Write1307(0x02,13); //设置时
Write1307(0x03,5); //设置周
Write1307(0x04,7); //设置日
Write1307(0x05,4); //设置月
Write1307(0x06,23); //设置年
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
移植过程
引脚选择
移植至工程
我们的目标是将例程移植至ESP32-S3开发板上。已经为大家提供了完整的驱动代码,按照以下步骤,即可完成移植。
具体新建文件夹和新建c和h文件在 【DHT11温湿度传感器】章节中的1.4.2小节中有详细的教学,这里就不再多说了。
只不过这里我们将文件名 bsp_dht11.c 和 bsp_dht11.h 换成 bsp_ds1307.c 和 bsp_ds1307.h,文件夹名字改为DS1307。
代码写入
在文件bsp_ds1307.c中,编写如下代码。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
* Change Logs:
* Date Author Notes
* 2024-01-12 LCKFB-lp first version
*/
#include "bsp_ds1307.h"
#include "stdio.h"
#include "sys/unistd.h"
//时间数据结构体
_time_struct_ RTC_Time = {0};
static void delay_ms(unsigned int ms)
{
vTaskDelay(ms / portTICK_PERIOD_MS);
}
static void delay_us(unsigned int us)
{
ets_delay_us(us);
}
static void delay_1ms(unsigned int ms)
{
vTaskDelay(ms / portTICK_PERIOD_MS);
}
static void delay_1us(unsigned int us)
{
ets_delay_us(us);
}
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
/******************************************************************
* 函 数 名 称:DS1307_GPIO_Init
* 函 数 说 明:对IIC引脚初始化
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void DS1307_GPIO_Init(void)
{
int i2c_master_port = I2C_NUM_1;//I2C_NUMBER(CONFIG_I2C_SLAVE_PORT_NUM);
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = GPIO_SDA,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = GPIO_SCL,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 100000,
// .clk_flags = 0, /*!< Optional, you can use I2C_SCLK_SRC_FLAG_* flags to choose i2c source clock here. */
};
esp_err_t err = i2c_param_config(i2c_master_port, &conf);
//注册I2C服务即使能
i2c_driver_install(I2C_NUM_1, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}
/******************************************************************
* 函 数 名 称:Write1307
* 函 数 说 明:向DS1307的add地址写入dat数据
* 函 数 形 参:add写入寄存器地址 dat写入数据
* 函 数 返 回:0写入成功 1写入器件地址无应答 2写入寄存器地址无应答
* 作 者:LC
* 备 注:器件地址=0xD0
******************************************************************/
unsigned char Write1307(unsigned char add,unsigned char dat)
{
unsigned char temp;
/* 10进制转BCD码 */
temp=dat/10;
temp<<=4;
temp=dat%10+temp;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
int err=0;
i2c_master_start(cmd);
err = i2c_master_write_byte(cmd, 0xD0, ACK_CHECK_EN);
i2c_master_write_byte(cmd, add, ACK_CHECK_EN);
i2c_master_write_byte(cmd, temp, ACK_CHECK_EN);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_1, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
return (0);
}
/******************************************************************
* 函 数 名 称:Read1307
* 函 数 说 明:读取DS1307的时间数据
* 函 数 形 参:add读取的寄存器地址
* 函 数 返 回:255-读取失败 其他-读取成功
* 作 者:LC
* 备 注:无
******************************************************************/
unsigned char Read1307(unsigned char add)
{
int i =0;
unsigned char temp;
unsigned char dat;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, 0xD0, ACK_CHECK_EN);
i2c_master_write_byte(cmd, add, ACK_CHECK_EN);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, 0xD1, ACK_CHECK_EN);
i2c_master_read_byte(cmd, &dat, ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_1, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
/* BCD码转19进制 */
temp=dat/16;
dat=dat%16;
dat=dat+temp*10;
return(dat);
}
/******************************************************************
* 函 数 名 称:get_RTC_time
* 函 数 说 明:获取DS1307的时间
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void Get_RTC_Time(void)
{
RTC_Time.sec = Read1307(0x00);
RTC_Time.min = Read1307(0x01);
RTC_Time.hour = Read1307(0x02);
RTC_Time.week = Read1307(0x03);
RTC_Time.date = Read1307(0x04);
RTC_Time.month = Read1307(0x05);
RTC_Time.year = Read1307(0x06);
}
/******************************************************************
* 函 数 名 称:set_RTC_time
* 函 数 说 明:设置RTC时间
* 函 数 形 参:year=年份 范围00-99(年数后两位)
* month=月份 范围01-12
* date=日期 范围01-31
* week=星期 范围01-07
* hour=小时 范围01-12 or 00-23
* min =分钟 范围00-59
* sec =秒数 范围00-59
* 函 数 返 回:
* 作 者:LC
* 备 注:例如设置时间 2023-4-7 - 星期5 -13:57:00
* set_RTC_time(23, 4, 7, 5, 13,57, 0)
******************************************************************/
void Set_RTC_Time(uint8_t year,uint8_t month,uint8_t date,uint8_t week,uint8_t hour,uint8_t min,uint8_t sec)
{
Write1307(0x00,sec); //设置秒
Write1307(0x01,min); //设置分
Write1307(0x02,hour); //设置时
Write1307(0x03,week); //设置周
Write1307(0x04,date); //设置日
Write1307(0x05,month); //设置月
Write1307(0x06,year); //设置年
}
/*******************************************************************************
* 函数名:AT24CXX_ReadByte
* 描述 :从串行EEPROM指定地址处开始读取一个字节数据
* 输入 :_usAddress地址
* 输出 :读取的数据dat
* 调用 :
* 备注 :
*******************************************************************************/
uint8_t AT24CXX_ReadByte(uint16_t addr)
{
uint8_t u8Data = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);//起始信号
i2c_master_write_byte(cmd, 0xA0 | 0, ACK_CHECK_EN);//器件寻址+读/写选择
i2c_master_write_byte(cmd, (uint8_t)((addr >> 8) & 0xFF), ACK_CHECK_EN);
i2c_master_write_byte(cmd, (uint8_t)(addr & 0xFF), ACK_CHECK_EN);
i2c_master_start(cmd);//起始信号
i2c_master_write_byte(cmd, 0xA0 | 1, ACK_CHECK_EN);//器件寻址+读
i2c_master_read_byte(cmd, &u8Data, ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_1, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
return u8Data;
}
/*******************************************************************************
* 函数名:AT24CXX_WriteByte
* 描述 :从串行EEPROM指定地址处开始写入一个字节数据
* 输入 :addr地址,data数据
* 输出 :void
* 调用 :
* 备注 :
*******************************************************************************/
void AT24CXX_WriteByte(uint16_t addr,uint8_t data)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
int err=0;
i2c_master_start(cmd);
i2c_master_write_byte(cmd, 0xA0 | 0, ACK_CHECK_EN);//器件寻址+读/写选择
i2c_master_write_byte(cmd, (uint8_t)((addr >> 8) & 0xFF), ACK_CHECK_EN);
i2c_master_write_byte(cmd, (uint8_t)(addr & 0xFF), ACK_CHECK_EN);
i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_1, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
在文件bsp_ds1307.h中,编写如下代码。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
* Change Logs:
* Date Author Notes
* 2024-01-12 LCKFB-lp first version
*/
#ifndef _BSP_DS1307_H_
#define _BSP_DS1307_H_
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "freertos/queue.h"
#include <inttypes.h>
#include "sdkconfig.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "rom/ets_sys.h"
#include "esp_system.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "sdkconfig.h"
#define GPIO_SCL 7
#define GPIO_SDA 8
typedef struct _RTC_TIME_STRUCT_ {
unsigned char sec;
unsigned char min;
unsigned char hour;
unsigned char week;
unsigned char date;
unsigned char month;
unsigned char year;
}_time_struct_;
extern _time_struct_ RTC_Time;
void DS1307_GPIO_Init(void);//引脚初始化
unsigned char Write1307(unsigned char add,unsigned char dat);//写入一个字节
unsigned char Read1307(unsigned char add);//读取一个字节
void Set_RTC_Time(uint8_t year,uint8_t month,uint8_t date,uint8_t week,uint8_t hour,uint8_t min,uint8_t sec);//设置初始时间
void Get_RTC_Time(void);//获取RTC时间
uint8_t AT24CXX_ReadByte(uint16_t addr);
void AT24CXX_WriteByte(uint16_t addr,uint8_t data);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
移植验证
在自己工程中的main主函数中,编写如下。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
* Change Logs:
* Date Author Notes
* 2024-01-12 LCKFB-lp first version
*/
#include <stdio.h>
#include "bsp_ds1307.h"
#include "string.h"
#include "esp_private/esp_task_wdt.h"
#include "esp_private/esp_task_wdt_impl.h"
int app_main(void)
{
esp_task_wdt_deinit();
DS1307_GPIO_Init();
/*
我们上电之后会对AT24C32数据存储芯片内0x10的地址进行读取。
如果读出的数据是0x77,那就说明我们进行过时间设定了,反之则没有设定,执行设定程序。
*/
if( AT24CXX_ReadByte(0x10) != 0x77)
{
AT24CXX_WriteByte(0x10,0x77);
Set_RTC_Time(24, 1, 12, 5, 15, 36, 0); //第一次上电设置时间才使用
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("RTC Demo Start....\r\n");
while(1)
{
Get_RTC_Time();//获取时间
printf("%d-%d-%d-星期%d\r\n", RTC_Time.year,RTC_Time.month,RTC_Time.date,RTC_Time.week);
printf("%d:%d:%d\r\n",RTC_Time.hour,RTC_Time.min,RTC_Time.sec);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
上电现象:
开发板断开连接,再次连接之后发现时间不连续,说明在开发板断开连接的时候RTC还在计数。
驱动代码: