esp32 操作DS1307时钟芯片

2024-01-27 17:36

本文主要是介绍esp32 操作DS1307时钟芯片,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

电气参数摘要

  • 有VCC供电,IIC活动状态是1.5mA,待机状态200μA,电池电流5nA(MAX=50nA)
  • 无VCC供电的时候,电池电流,300nA(时钟运行),10nA(时钟停止)
  • 供电电压是4.5-5.5V,不支持3.3V
  • IIC频率最大400kHZ。
  • 因为ESP32不支持5V电压,但是DS1307必须用5V供电,因此在IIC上拉电阻的位置,必须上拉到3.3V。否则直接给ESP32的IO灌5V可能会烧掉。可以使用ESP32内部的IO上拉,然后去掉外部的上拉就行。

IIC的通信

  • 访问是通过实施启动条件,并提供设备识别代码,后跟寄存器地址而获得的。可以按顺序访问后续寄存器,直到执行停止条件为止。
  • 芯片iic地址是0x68
  • 配置寄存器,先发送设备地址,发送寄存器地址(比如0),然后发送寄存器的值。可以一次性发送多个字节,芯片会自动偏移寄存器地址。
  • 读取寄存器,先发送寄存器地址,然后读取。支持连续读取多个寄存器。

寄存器

  • RTC寄存器位于地址00h至07h的位置。
  • RAM寄存器位于地址08h至3Fh的位置。

在多字节访问期间,当地址指针达到3Fh,即RAM空间的末尾时,它会环绕到位置00h,即时钟空间的开头。
在这里插入图片描述

  • 在初次上电的时候(指初次装上纽扣电池),CH位是置1的,表示振荡器停止,计时功能处于停止状态。这时候纽扣电池的耗电量位10nA。
  • 所以在初次使用的时候,需要对CH位清零,一般这个清零操作会在设置当前时间到DS1307时同时完成。
  • 当CH位置0后,断开主电,RTC也会继续运行,这时候纽扣电池的耗电量典型值为300nA(无方波输出时)。

计算

  1. 芯片将“年月日时分秒周”存放到地址00-06这7个寄存器里面。每个寄存器相应bit对应的含义,看寄存器列表。
  2. 读取秒的寄存器是00h。先要读取出10倍数的秒(也就是十位数),对应BIT4~bit6.
    然后读取个位数的秒数,BIT0~BIT3.
    在这里插入图片描述
    比如读取到寄存器值为0x59,则对应二进制为00101 1001b。则bit4~bit6=5 ,BIT0~BIT3= 9最终为5*10+9=59秒。
    这个寄存器的CH位是时钟启停控制bit。
    3.比如读小时,小时在芯片里面有12小时表示法和24小时表示法。具体看BIT6。
    当bit6=1,则剩余的bit按照12小时制展开。
    当bit6=0,则剩余的bit按照24小时制展开。
    在这里插入图片描述

DS1307上的RAM

这个RAM是留给用户使用的存储空间,RTC不会用到这部分RAM

DS1307上的RAM(随机存储器)用于存储临时数据,用户自定义的设置以及其他需要在断电时保持的信息。以下是DS1307上RAM的主要用途:

存储用户数据: 用户可以将自定义数据存储在DS1307的RAM中。这可以包括配置参数、计数器值、标志位等。因为这部分RAM是带电池支持的,所以即使在主电源断电时,这些数据也会被保持。保存配置信息: DS1307的RAM可以用于存储与实时时钟(RTC)和其他功能相关的配置信息。这些配置信息可能包括时钟格式(12小时或24小时制)、日期格式、方波输出频率等。临时存储: RAM还可用于存储临时性的运行时数据。例如,当进行时钟校准或其他运行时操作时,可能需要在RAM中存储一些中间数据。断电备份: 由于RAM是带电池支持的,所以它提供了断电备份的功能。即使主电源断电,RAM中的数据仍然会被保存,确保在电源重新连接时可以恢复先前的状态。

总的来说,DS1307上的RAM为用户提供了一块持久存储区域,允许在断电时保留关键的信息。这在需要在系统重新上电时恢复先前状态或保留特定配置和数据时非常有用。

操作时序

设置时钟,比如设置时钟为2024-01-26 10:37:10 周五
在这里插入图片描述发送的字节依次为:设备地址0x68,寄存器地址00,接下来7个字节是要设置进00h-06h这7个寄存器的值。

读取时钟
1.先发送一个数据00,将读取指针只想寄存器0.这个操作在数据手册有说。
在这里插入图片描述2.然后连续读取出数据

在这里插入图片描述## 赋上次芯片的driver代码

#include <stdio.h>
#include "esp_log.h"
#include "ds1307_driver.h"
#include "driver/i2c.h"
#include <string.h>static const char *TAG = "ds1307-driver";#define I2C_MASTER_SCL_IO CONFIG_I2C_MASTER_SCL /*!< GPIO number used for I2C master clock */
#define I2C_MASTER_SDA_IO CONFIG_I2C_MASTER_SDA /*!< GPIO number used for I2C master data  */
#define I2C_MASTER_NUM 0                        /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
#define I2C_MASTER_FREQ_HZ 100000               /*!< I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE 0             /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0             /*!< I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS 1000#define DS1307_I2C_ADDR 0x68 /*!< Slave address of the MPU9250 sensor */RTC_time_typedef time;esp_err_t ds1307_init(void)
{int ret;// 初始化iicint i2c_master_port = I2C_MASTER_NUM;i2c_config_t conf = {.mode = I2C_MODE_MASTER,.sda_io_num = I2C_MASTER_SDA_IO,.scl_io_num = I2C_MASTER_SCL_IO,.sda_pullup_en = GPIO_PULLUP_ENABLE,.scl_pullup_en = GPIO_PULLUP_ENABLE,.master.clk_speed = I2C_MASTER_FREQ_HZ,};i2c_param_config(i2c_master_port, &conf);ret = i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);ESP_LOGI(TAG, "I2C initialized successfully");return ret;
}esp_err_t ds1307_read_time(date_time_t *p_time)
{int ret;uint8_t write_buf[8] = {0};uint8_t *p = (uint8_t *)p_time;// 1.先要发一个字节(write),值为0,write_buf[0] = 0;ret = i2c_master_write_to_device(I2C_MASTER_NUM, DS1307_I2C_ADDR, write_buf, 1, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);vTaskDelay(20 / portTICK_PERIOD_MS);// 开始读取数据ret = i2c_master_read_from_device(I2C_MASTER_NUM, DS1307_I2C_ADDR, (uint8_t *)&time, 7, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);ESP_LOGI(TAG, "get raw data:%x,%x,%x,%x,%x,%x,%x,", *(p + 0), *(p + 1), *(p + 2), *(p + 03), *(p + 04), *(p + 05), *(p + 06));p_time->second = time.seconds.ten_sec * 10 + time.seconds.seconds;p_time->minute = time.minutes.ten_min * 10 + time.minutes.minutes;p_time->hour = time.hours.ten_hour * 10 + time.hours.hours;p_time->week = time.weeks.week;p_time->day = time.dates.ten_date * 10 + time.dates.date;p_time->month = time.months.ten_month * 10 + time.months.month;p_time->year = time.years.ten_year * 10 + time.years.year;ESP_LOGI(TAG, "%d-%d-%d %d:%d:%d week=%d", p_time->year, p_time->month, p_time->day, p_time->hour, p_time->minute, p_time->second, p_time->week);return ret;
}/*** @brief 设置芯片的时间*/
esp_err_t ds1307_set_time(date_time_t datetime)
{int ret;RTC_time_typedef time;uint8_t write_buf[9] = {0};uint8_t *p = (uint8_t *)&time;time.years.ten_year = datetime.year / 10;time.years.year = datetime.year % 10;time.months.ten_month = datetime.month / 10;time.months.month = datetime.month % 10;time.dates.ten_date = datetime.day / 10;time.dates.date = datetime.day % 10;time.weeks.week = datetime.week;time.hours.ten_hour = datetime.hour / 10;time.hours.hours = datetime.hour % 10;time.minutes.ten_min = datetime.minute / 10;time.minutes.minutes = datetime.minute % 10;time.seconds.ten_sec = datetime.second / 10;time.seconds.seconds = datetime.second % 10;time.seconds.ch = 0;ESP_LOGI(TAG, "set raw data:%x,%x,%x,%x,%x,%x,%x,", *(p + 0), *(p + 1), *(p + 2), *(p + 03), *(p + 04), *(p + 05), *(p + 06));memcpy(write_buf + 1, p, 7);ret = i2c_master_write_to_device(I2C_MASTER_NUM, DS1307_I2C_ADDR, write_buf, 8, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);return ret;
}

头文件

#ifndef __DS1307_H_
#define __DS1307_H_#include "driver/i2c.h"typedef struct
{union{uint8_t value;struct{uint8_t seconds : 4;uint8_t ten_sec : 3;uint8_t ch : 1;};};
} second_data_TypeDef; // 秒typedef struct
{union{uint8_t value;struct{uint8_t minutes : 4;uint8_t ten_min : 3;uint8_t bit7 : 1;};};
} minutes_data_TypeDef; // 分typedef struct
{union{uint8_t value;struct{uint8_t hours : 4;uint8_t ten_hour : 2;uint8_t jinzhi : 1;uint8_t : 1;};};
} hours_data_TypeDef; // 时typedef struct
{union{uint8_t value;struct{uint8_t week : 3;uint8_t : 5;};};
} weeks_data_TypeDef; // 周typedef struct
{union{uint8_t value;struct{uint8_t date : 4;uint8_t ten_date : 2;uint8_t : 2;};};
} dates_data_TypeDef; // 日typedef struct
{union{uint8_t value;struct{uint8_t month : 4;uint8_t ten_month : 1;uint8_t : 3;};};
} months_data_TypeDef; // 月typedef struct
{union{uint8_t value;struct{uint8_t year : 4;uint8_t ten_year : 4;};};
} years_data_TypeDef; // 年typedef struct
{second_data_TypeDef seconds;minutes_data_TypeDef minutes;hours_data_TypeDef hours;weeks_data_TypeDef weeks;dates_data_TypeDef dates;months_data_TypeDef months;years_data_TypeDef years;
} RTC_time_typedef;typedef struct
{uint16_t year;uint16_t month;uint16_t day;uint8_t week;uint16_t hour;uint16_t minute;uint16_t second;
} date_time_t;esp_err_t ds1307_init(void);
esp_err_t ds1307_read_time(date_time_t *p_time);
esp_err_t ds1307_set_time(date_time_t datetime);#endif

main文件

#include <stdio.h>
#include "esp_log.h"
#include "ds1307_driver.h"static const char *TAG = "i2c_DS1307_main";void app_main(void)
{date_time_t date_time={.year = 24,.month = 1,.day = 26,.week = 5,.hour = 10,.minute = 37,.second = 10,},date_time_now;ESP_ERROR_CHECK(ds1307_init());/* Read the MPU9250 WHO_AM_I register, on power up the register should have the value 0x71 */ESP_ERROR_CHECK(ds1307_set_time(date_time));ESP_LOGI(TAG, "set successfully");vTaskDelay(3000/portTICK_PERIOD_MS);while(1){ds1307_read_time(&date_time_now);vTaskDelay(1000/portTICK_PERIOD_MS);}}

程序小结:

  1. 这个芯片读取数据没有校验位,因此在操作的时候,如果太快,还是比较容易出现数据不对。因此要想办法校验数据正确性。
  2. 可以多次读取,然后判断值的有效性

这篇关于esp32 操作DS1307时钟芯片的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/650997

相关文章

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

动手学深度学习【数据操作+数据预处理】

import osos.makedirs(os.path.join('.', 'data'), exist_ok=True)data_file = os.path.join('.', 'data', 'house_tiny.csv')with open(data_file, 'w') as f:f.write('NumRooms,Alley,Price\n') # 列名f.write('NA

线程的四种操作

所属专栏:Java学习        1. 线程的开启 start和run的区别: run:描述了线程要执行的任务,也可以称为线程的入口 start:调用系统函数,真正的在系统内核中创建线程(创建PCB,加入到链表中),此处的start会根据不同的系统,分别调用不同的api,创建好之后的线程,再单独去执行run(所以说,start的本质是调用系统api,系统的api

Java IO 操作——个人理解

之前一直Java的IO操作一知半解。今天看到一个便文章觉得很有道理( 原文章),记录一下。 首先,理解Java的IO操作到底操作的什么内容,过程又是怎么样子。          数据来源的操作: 来源有文件,网络数据。使用File类和Sockets等。这里操作的是数据本身,1,0结构。    File file = new File("path");   字

MySQL——表操作

目录 一、创建表 二、查看表 2.1 查看表中某成员的数据 2.2 查看整个表中的表成员 2.3 查看创建表时的句柄 三、修改表 alter 3.1 重命名 rename 3.2 新增一列 add 3.3 更改列属性 modify 3.4 更改列名称 change 3.5 删除某列 上一篇博客介绍了库的操作,接下来来看一下表的相关操作。 一、创建表 create

封装MySQL操作时Where条件语句的组织

在对数据库进行封装的过程中,条件语句应该是相对难以处理的,毕竟条件语句太过于多样性。 条件语句大致分为以下几种: 1、单一条件,比如:where id = 1; 2、多个条件,相互间关系统一。比如:where id > 10 and age > 20 and score < 60; 3、多个条件,相互间关系不统一。比如:where (id > 10 OR age > 20) AND sco

PHP7扩展开发之流操作

前言 啥是流操作?简单来讲就是对一些文件,网络的IO操作。PHP已经把这些IO操作,封装成流操作。这节,我们将使用PHP扩展实现一个目录遍历的功能。PHP示例代码如下: <?phpfunction list_dir($dir) {if (is_dir($dir) === false) {return;} $dh = opendir($dir);if ($dh == false) {ret

浙大数据结构:树的定义与操作

四种遍历 #include<iostream>#include<queue>using namespace std;typedef struct treenode *BinTree;typedef BinTree position;typedef int ElementType;struct treenode{ElementType data;BinTree left;BinTre

浙大数据结构:04-树7 二叉搜索树的操作集

这道题答案都在PPT上,所以先学会再写的话并不难。 1、BinTree Insert( BinTree BST, ElementType X ) 递归实现,小就进左子树,大就进右子树。 为空就新建结点插入。 BinTree Insert( BinTree BST, ElementType X ){if(!BST){BST=(BinTree)malloc(sizeof(struct TNo

hibernate修改数据库已有的对象【简化操作】

陈科肇 直接上代码: /*** 更新新的数据并并未修改旧的数据* @param oldEntity 数据库存在的实体* @param newEntity 更改后的实体* @throws IllegalAccessException * @throws IllegalArgumentException */public void updateNew(T oldEntity,T newEntity