/********************************************************************** * Copyright: (C)2026 LingYun IoT System Studio * Author: GuoWenxue * * 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 #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; }