/**********************************************************************
|
* Copyright: (C)2026 LingYun IoT System Studio
|
* Author: GuoWenxue<guowenxue@gmail.com>
|
*
|
* Description: ISL1208 RTC driver on ISKBoard (Unlocked Core Pattern)
|
*
|
* ChangeLog:
|
* Version Date Author Description
|
* V1.1.0 2026.07.08 GuoWenxue Refactor with unlocked core functions
|
*
|
***********************************************************************/
|
|
#include <stdio.h>
|
#include "isl1208.h"
|
|
/* 隐藏在私有源文件内部的 ISL1208 寄存器映射与参数 */
|
#define ISL1208_I2C_ADDR 0x6F
|
|
#define ISL1208_REG_SC 0x00 /* 秒 (00-59) */
|
#define ISL1208_REG_MN 0x01 /* 分 (00-59) */
|
#define ISL1208_REG_HR 0x02 /* 时 (24小时模式) */
|
#define ISL1208_REG_DT 0x03 /* 日 (01-31) */
|
#define ISL1208_REG_MO 0x04 /* 月 (01-12) */
|
#define ISL1208_REG_YR 0x05 /* 年 (00-99) */
|
#define ISL1208_REG_DW 0x06 /* 星期 (00-06) */
|
|
#define ISL1208_REG_SR 0x07 /* 状态寄存器 */
|
#define ISL1208_SR_RTCF 0x80 /* 掉电/晶振失效标志位 */
|
#define ISL1208_SR_WRTC 0x10 /* 写使能位 */
|
|
/* 前置私有静态辅助函数声明 (无锁底层业务函数) */
|
static int __isl1208_set_time_unlocked(const rtc_time_t *time);
|
static int __isl1208_get_time_unlocked(rtc_time_t *time);
|
|
/*
|
*+---------------------------------+
|
*| RTC 公共接口函数(带锁) |
|
*+---------------------------------+
|
*/
|
|
/* I2C 设备 RTC */
|
static i2c_dev_t rtc;
|
|
/**
|
* @brief ISL1208 硬件初始化与掉电自愈函数
|
* @note 只有检测到掉电(RTCF=1)或停振(ST=1)时,才强制清理控制位并激活时钟,平时不干扰原有走时
|
* @param bus: 绑定的 I2C 物理总线指针 (如 &i2c1_bus)
|
* @retval 0: 初始化成功; -1: 总线通信失败或芯片未响应
|
*/
|
int isl1208_init(i2c_bus_t *bus)
|
{
|
uint8_t sr_val;
|
uint8_t sc_val;
|
int rv = -1;
|
|
if (!bus)
|
return -1;
|
|
/* 绑定设备及其设备号到 I2C 总线上 */
|
i2c_bind_dev(&rtc, bus, ISL1208_I2C_ADDR);
|
|
/* 将I2C总线加锁让设备独占总线 */
|
i2c_lock_dev(&rtc);
|
|
/* 读取当前状态寄存器 (检查 RTCF) */
|
if (i2c_read(rtc.bus, rtc.dev_addr, ISL1208_REG_SR, &sr_val, 1) != I2C_OK)
|
{
|
goto cleanup;
|
}
|
|
/* 读取当前秒寄存器 (检查 ST) */
|
if (i2c_read(rtc.bus, rtc.dev_addr, ISL1208_REG_SC, &sc_val, 1) != I2C_OK)
|
{
|
goto cleanup;
|
}
|
|
/* 条件触发:只要 SBIB 位为 1 证明是断电冷启动,必须强制清除解冻 */
|
if (sr_val & 0x01)
|
{
|
printf("ISL1208 Power-Loss or Stop detected! Activating oscillator...\r\n");
|
|
/* 强开写保护 (WRTC=1),并抹除 SBIB 冻结位 */
|
sr_val = ISL1208_SR_WRTC;
|
if (i2c_write(rtc.bus, rtc.dev_addr, ISL1208_REG_SR, &sr_val, 1) != I2C_OK)
|
{
|
goto cleanup;
|
}
|
|
/* 写入完整的 2026-01-01 00:00:00 (星期四) 基准时间 */
|
uint8_t init_buf[7];
|
init_buf[0] = 0x00; /* 00秒,同时最高位 ST=0 激活晶振起振 */
|
init_buf[1] = 0x00; /* 00分 */
|
init_buf[2] = 0x80; /* 00时,Bit7=1 代表开启 24 小时制 */
|
init_buf[3] = 0x01; /* 01日 (BCD: 0x01) */
|
init_buf[4] = 0x01; /* 01月 (BCD: 0x01) */
|
init_buf[5] = 0x26; /* 26年 (BCD: 0x26 代表 2026年) */
|
init_buf[6] = 0x04; /* 星期四 (BCD: 0x04) */
|
|
/* 写入 7 个时间寄存器 */
|
if (i2c_write(rtc.bus, rtc.dev_addr, ISL1208_REG_SC, init_buf, 7) != I2C_OK)
|
{
|
goto cleanup;
|
}
|
|
/* 强制将秒寄存器的最高位 ST (Stop) 清零以激活晶振 */
|
sc_val &= 0x7F;
|
if (i2c_write(rtc.bus, rtc.dev_addr, ISL1208_REG_SC, &sc_val, 1) != I2C_OK)
|
{
|
goto cleanup;
|
}
|
|
/* 关闭写保护,并将状态寄存器洗干净 (SBIB 清零) */
|
sr_val = 0x00;
|
if (i2c_write(rtc.bus, rtc.dev_addr, ISL1208_REG_SR, &sr_val, 1) != I2C_OK)
|
{
|
goto cleanup;
|
}
|
}
|
|
rv = 0;
|
|
cleanup:
|
i2c_free_dev(&rtc);
|
return rv;
|
}
|
|
/**
|
* @brief 应用层设置 RTC 时间接口函数(rtc_time_t格式)
|
* @param time 指向待写入时间数据的 rtc_time_t 结构体常指针
|
* @retval 0:成功, -1:失败
|
*/
|
int isl1208_set_time(const rtc_time_t *time)
|
{
|
int rv = -1;
|
|
if (!rtc.bus || !time)
|
{
|
return -1;
|
}
|
|
i2c_lock_dev(&rtc);
|
|
/* 调用无锁底层核心实现写操作 */
|
rv = __isl1208_set_time_unlocked(time);
|
|
i2c_free_dev(&rtc);
|
|
return rv;
|
}
|
|
/**
|
* @brief 应用层设置 RTC 时间接口函数(字符串格式)
|
* @param time_str 输入的时间字符串指针,格式如 "2026-01-01 10:00:00"
|
* @retval 0:成功, -1:失败
|
*/
|
int isl1208_set_time_str(const char *time_str)
|
{
|
rtc_time_t tm;
|
int year, month, day, hour, minute, second;
|
|
if (!time_str)
|
{
|
return -1;
|
}
|
|
/* 1. 解析时间字符串 */
|
if (sscanf(time_str, "%d-%d-%d %d:%d:%d",
|
&year, &month, &day, &hour, &minute, &second) != 6)
|
{
|
return -1;
|
}
|
|
/* 2. 填充结构体(年份仅保留低两位) */
|
tm.year = year % 100;
|
tm.month = month;
|
tm.day = day;
|
tm.hour = hour;
|
tm.minute = minute;
|
tm.second = second;
|
tm.weekday = 0;
|
|
/* 3. 直接调用带锁的原本接口 */
|
return isl1208_set_time(&tm);
|
}
|
|
/**
|
* @brief 应用层获取 RTC 时间接口函数
|
* @param time 指向用于接收时间数据的 rtc_time_t 结构体指针
|
* @retval 0:成功, -1:失败
|
*/
|
int isl1208_get_time(rtc_time_t *time)
|
{
|
int rv = -1;
|
|
if (!rtc.bus || !time)
|
{
|
return -1;
|
}
|
|
i2c_lock_dev(&rtc);
|
|
/* 调用无锁底层核心实现读操作 */
|
rv = __isl1208_get_time_unlocked(time);
|
|
i2c_free_dev(&rtc);
|
return rv;
|
}
|
|
/**
|
* @brief 应用层获取 RTC 时间接口函数(字符串格式)
|
* @param buf 用于存储格式化时间字符串的缓冲区指针
|
* @param buf_len 接收缓冲区长度(建议至少20字节)
|
* @retval 0:成功, -1:失败
|
*/
|
int isl1208_get_time_str(char *buf, size_t buf_len)
|
{
|
rtc_time_t tm;
|
int rv = -1;
|
|
if (!buf || buf_len < 20)
|
{
|
return -1;
|
}
|
|
/* 直接调用带锁的原本接口获取时间结构体 */
|
rv = isl1208_get_time(&tm);
|
if (rv != 0)
|
{
|
return rv;
|
}
|
|
/* 格式化为字符串输出 */
|
snprintf(buf, buf_len, "20%02d-%02d-%02d %02d:%02d:%02d",
|
tm.year, tm.month, tm.day,
|
tm.hour, tm.minute, tm.second);
|
|
return 0;
|
}
|
|
/**
|
* @brief 获取并格式化打印当前 RTC 时间接口函数
|
* @param bus 指向已初始化的 I2C 总线结构体指针
|
* @retval 0:成功, -1:失败
|
*/
|
int isl1208_print_time(void)
|
{
|
char time_str[32];
|
|
/* 直接调用刚刚封装好的获取字符串接口 */
|
if (isl1208_get_time_str(time_str, sizeof(time_str)) != 0)
|
{
|
return -1;
|
}
|
|
/* 格式化输出 */
|
printf("ISL1208 Time: %s\r\n", time_str);
|
return 0;
|
}
|
|
/*
|
*+---------------------------------+
|
*| RTC 私有底层函数(无锁) |
|
*+---------------------------------+
|
*/
|
|
/**
|
* @brief 十进制转 BCD 码
|
*/
|
static inline uint8_t rtc_dec_to_bcd(uint8_t dec)
|
{
|
uint8_t high;
|
uint8_t low;
|
|
high = dec / 10;
|
low = dec % 10;
|
|
return (uint8_t)((high << 4) | low);
|
}
|
|
/**
|
* @brief BCD 码转十进制
|
*/
|
static inline uint8_t rtc_bcd_to_dec(uint8_t bcd)
|
{
|
uint8_t high;
|
uint8_t low;
|
|
high = bcd >> 4;
|
low = bcd & 0x0F;
|
|
return (uint8_t)((high * 10) + low);
|
}
|
|
/**
|
* @brief 向 RTC 芯片写入时间
|
*/
|
static int __isl1208_set_time_unlocked(const rtc_time_t *time)
|
{
|
uint8_t time_buf[7];
|
uint8_t sr_val;
|
|
if( !rtc.bus )
|
return -1;
|
|
/* 读取当前状态寄存器 */
|
if (i2c_read(rtc.bus, ISL1208_I2C_ADDR, ISL1208_REG_SR, &sr_val, 1) != I2C_OK)
|
{
|
return -1;
|
}
|
|
/* 解除 RTC 写保护 (WRTC 置 1) */
|
sr_val |= ISL1208_SR_WRTC;
|
if (i2c_write(rtc.bus, ISL1208_I2C_ADDR, ISL1208_REG_SR, &sr_val, 1) != I2C_OK)
|
{
|
return -1;
|
}
|
|
/* 将标准十进制转换为 BCD 码并打包 */
|
time_buf[0] = rtc_dec_to_bcd(time->second);
|
time_buf[1] = rtc_dec_to_bcd(time->minute);
|
time_buf[2] = (rtc_dec_to_bcd(time->hour) & 0x3F) | 0x80;
|
time_buf[3] = rtc_dec_to_bcd(time->day);
|
time_buf[4] = rtc_dec_to_bcd(time->month);
|
time_buf[5] = rtc_dec_to_bcd(time->year);
|
time_buf[6] = rtc_dec_to_bcd(time->weekday);
|
|
/* 写入 7 个时间寄存器 */
|
if (i2c_write(rtc.bus, ISL1208_I2C_ADDR, ISL1208_REG_SC, time_buf, 7) != I2C_OK)
|
{
|
return -1;
|
}
|
|
/* 恢复写保护并同时清除掉电失效位 (WRTC = 0, RTCF = 0) */
|
sr_val &= ~ISL1208_SR_WRTC;
|
sr_val &= ~ISL1208_SR_RTCF;
|
if (i2c_write(rtc.bus, ISL1208_I2C_ADDR, ISL1208_REG_SR, &sr_val, 1) != I2C_OK)
|
{
|
return -1;
|
}
|
|
return 0;
|
}
|
|
/**
|
* @brief 从 RTC 芯片读取时间
|
*/
|
static int __isl1208_get_time_unlocked(rtc_time_t *time)
|
{
|
uint8_t time_buf[7];
|
|
/* 连续读取 7 个寄存器的原始 BCD 数据 */
|
if (i2c_read(rtc.bus, ISL1208_I2C_ADDR, ISL1208_REG_SC, time_buf, 7) != I2C_OK)
|
{
|
return -1;
|
}
|
|
/* 解析 BCD 码并做去噪掩码隔离 */
|
time->second = rtc_bcd_to_dec(time_buf[0] & 0x7F);
|
time->minute = rtc_bcd_to_dec(time_buf[1] & 0x7F);
|
time->hour = rtc_bcd_to_dec(time_buf[2] & 0x3F);
|
time->day = rtc_bcd_to_dec(time_buf[3] & 0x3F);
|
time->month = rtc_bcd_to_dec(time_buf[4] & 0x1F);
|
time->year = rtc_bcd_to_dec(time_buf[5]);
|
time->weekday = rtc_bcd_to_dec(time_buf[6] & 0x07);
|
|
return 0;
|
}
|