0f7b5a87fb
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号) 一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、 采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。 门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏 undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8 reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF; id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。 test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字, 是本批唯一改变构建产物的文件集合。 为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged 部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、 不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。 实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01% (基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 / 告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。 基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处 引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使 识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。 ⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二 根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有), 今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。 固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0; App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889 断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿; check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带 BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。 .cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法: Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。 同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%, RW_CCM 57,376 B / 87.9%,阈值 92%。
574 lines
22 KiB
C
574 lines
22 KiB
C
/*
|
||
* 模块名称:SD2506API-G RTC Driver
|
||
* 模块功能:SD2506API-G 高精度温补实时时钟驱动,I2C 接口
|
||
* 适用平台:STM32F407ZGTx + SD2506API-G 实时时钟芯片(I2C1: PB6-SCL, PB7-SDA)
|
||
* 作者:王建锋
|
||
* 创建日期:2026-07-17
|
||
* 修改记录:
|
||
* 2026-07-17 王建锋 创建初始版本,参考 SD2506API-G Ver2.0 手册
|
||
*/
|
||
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
|
||
#include "sd2506.h"
|
||
#include "i2c.h"
|
||
|
||
#define DBG_TAG "[RTC]"
|
||
#include "dbg_log.h"
|
||
|
||
/* ======================== 内部辅助函数 ======================== */
|
||
|
||
/*
|
||
* 函数功能:BCD 码转十进制数
|
||
* 入口参数:bcd - BCD 编码值 uint8_t 0x00 - 0x99
|
||
* 返回值:十进制数值 uint8_t
|
||
* 限定条件:输入为合法 BCD 编码(高 4 位和低 4 位均 <= 9)
|
||
* 函数说明:高 4 位为十位,低 4 位为个位,分别乘 10 和加后返回
|
||
*/
|
||
uint8_t sd2506_bcd_to_dec(uint8_t bcd) {
|
||
return ((bcd >> 4) * 10) + (bcd & 0x0FU);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:十进制转 BCD 码
|
||
* 入口参数:dec - 十进制数 uint8_t 0 - 99 */
|
||
uint8_t sd2506_dec_to_bcd(uint8_t dec) {
|
||
return ((dec / 10) << 4) | (dec % 10);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:把 HAL 的 I2C 状态映射成统一错误码(2026-10-23)
|
||
* 入口参数:status - HAL_I2C_* 返回的状态
|
||
* 返回值:HAL_OK → BSP_OK;HAL_TIMEOUT → BSP_ERR_TIMEOUT(可重试);其余 → BSP_ERR_IO
|
||
* 限定条件:仅本文件内部使用
|
||
* 函数说明:**这一映射是本次迁移的实质收益**——旧码 SD2506_I2C_ERROR 把"总线没人应答"
|
||
* 和"时钟拉长/超时"合成同一个 -2,调用方想重试也不知道该不该重试;SD2506 又是启动链
|
||
* 第一站(I2C1 上电时序敏感),归出 BSP_ERR_TIMEOUT 之后,上层才谈得上"退避再读一次"。
|
||
*/
|
||
static bsp_err_t sd2506_err_from_hal(HAL_StatusTypeDef status) {
|
||
switch (status) {
|
||
case HAL_OK:
|
||
return BSP_OK;
|
||
case HAL_TIMEOUT:
|
||
return BSP_ERR_TIMEOUT;
|
||
case HAL_BUSY:
|
||
return BSP_ERR_BUSY;
|
||
default:
|
||
return BSP_ERR_IO;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:写单字节寄存器
|
||
* 入口参数:reg - 寄存器地址 uint8_t 0x00 - 0x79
|
||
* val - 写入数据 uint8_t 0x00 - 0xFF
|
||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||
* 限定条件:I2C 外设已初始化
|
||
* 函数说明:通过 I2C 向指定地址写入 1 字节数据
|
||
* 限定条件:I2C 外设已初始化
|
||
* 函数说明:通过 I2C 向指定地址写入 1 字节
|
||
*/
|
||
static bsp_err_t sd2506_write_reg(uint8_t reg, uint8_t val) {
|
||
HAL_StatusTypeDef status;
|
||
|
||
status = HAL_I2C_Mem_Write(&hi2c1, SD2506_I2C_ADDR_WRITE, reg,
|
||
I2C_MEMADD_SIZE_8BIT, &val, 1,
|
||
SD2506_I2C_TIMEOUT_MS);
|
||
if (status != HAL_OK) {
|
||
return sd2506_err_from_hal(status);
|
||
}
|
||
return BSP_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读单字节寄存器
|
||
* 入口参数:reg - 寄存器地址 uint8_t 0x00 - 0x79
|
||
* p_val - 读取值指针 uint8_t*(非空)
|
||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||
* 限定条件:I2C 外设已初始化,p_val 为非空指针
|
||
* 函数说明:通过 I2C 从指定地址读取 1 字节数据
|
||
* 限定条件:I2C 外设已初始化,val 为非空指针函数说明:通过 I2C 从指定地址读取 1 字节
|
||
*/
|
||
static bsp_err_t sd2506_read_reg(uint8_t reg, uint8_t *p_val) {
|
||
HAL_StatusTypeDef status;
|
||
|
||
status = HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
|
||
I2C_MEMADD_SIZE_8BIT, p_val, 1,
|
||
SD2506_I2C_TIMEOUT_MS);
|
||
if (status != HAL_OK) {
|
||
return sd2506_err_from_hal(status);
|
||
}
|
||
return BSP_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:写多字节寄存器(连续写)
|
||
* 入口参数:reg - 起始寄存器地址 uint8_t 0x00 - 0x79
|
||
* p_data - 数据缓冲区指针 uint8_t*(非空)
|
||
* len - 数据长度 uint8_t 1 - N
|
||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||
* 限定条件:I2C 外设已初始化,p_data 为非空指针
|
||
* 函数说明:从 reg 开始连续写入 len 字节数据
|
||
* 限定条件:I2C 外设已初始化,data 为非空指针
|
||
*/
|
||
static bsp_err_t sd2506_write_regs(uint8_t reg, const uint8_t *p_data, uint8_t len) {
|
||
HAL_StatusTypeDef status;
|
||
|
||
status = HAL_I2C_Mem_Write(&hi2c1, SD2506_I2C_ADDR_WRITE, reg,
|
||
I2C_MEMADD_SIZE_8BIT, (uint8_t *)p_data, len,
|
||
SD2506_I2C_TIMEOUT_MS);
|
||
if (status != HAL_OK) {
|
||
return sd2506_err_from_hal(status);
|
||
}
|
||
return BSP_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读多字节寄存器(连续读)
|
||
* 入口参数:reg - 起始寄存器地址 uint8_t 0x00 - 0x79
|
||
* p_data - 数据缓冲区指针 uint8_t*(非空)
|
||
* len - 数据长度 uint8_t 1 - N
|
||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||
* 限定条件:I2C 外设已初始化,p_data 为非空指针
|
||
* 函数说明:从 reg 开始连续读取 len 字节数据
|
||
* 限定条件:I2C 外设已初始化,data 为非空指针
|
||
*/
|
||
static bsp_err_t sd2506_read_regs(uint8_t reg, uint8_t *p_data, uint8_t len) {
|
||
HAL_StatusTypeDef status;
|
||
|
||
status = HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
|
||
I2C_MEMADD_SIZE_8BIT, p_data, len,
|
||
SD2506_I2C_TIMEOUT_MS);
|
||
if (status != HAL_OK) {
|
||
return sd2506_err_from_hal(status);
|
||
}
|
||
return BSP_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:开启写保护(允许写入 00H~71H 寄存器)
|
||
* 入口参数:无
|
||
* 返回值:BSP_OK - 成功;BSP_ERR_IO / BSP_ERR_TIMEOUT - I2C 失败
|
||
* 限定条件:I2C 外设已初始化
|
||
* 函数说明:顺序:先写 WRTC1=1, 再写 WRTC2=1 + WRTC3=1
|
||
*/
|
||
static bsp_err_t sd2506_write_enable(void) {
|
||
bsp_err_t ret = BSP_OK;
|
||
|
||
/* 写允许顺序(SD2506/SD24xx 手册要求):先置 WRTC1(CTR2 bit7)=1,
|
||
* 再置 WRTC2(CTR1 bit2) + WRTC3(CTR1 bit7)=1。顺序反了芯片不会解开写保护。 */
|
||
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x80U);
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
ret = sd2506_write_reg(SD2506_REG_CTR1, 0x84U);
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
return BSP_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:关闭写保护(禁止写入 00H~71H 寄存器)
|
||
* 入口参数:无
|
||
* 返回值:BSP_OK - 成功;BSP_ERR_IO / BSP_ERR_TIMEOUT - I2C 失败
|
||
* 限定条件:I2C 外设已初始化
|
||
* 函数说明:顺序:先写 WRTC2=0 + WRTC3=0, 再写 WRTC1=0
|
||
*/
|
||
static bsp_err_t sd2506_write_disable(void) {
|
||
bsp_err_t ret = BSP_OK;
|
||
|
||
/* 写禁止顺序(手册要求):先清 WRTC2/WRTC3(CTR1),再清 WRTC1(CTR2) */
|
||
ret = sd2506_write_reg(SD2506_REG_CTR1, 0x00U);
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x00U);
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
return BSP_OK;
|
||
}
|
||
|
||
/* ======================== 公共 API 实现 ======================== */
|
||
|
||
/*
|
||
* 函数功能:初始化 SD2506API-G RTC
|
||
* 入口参数:无
|
||
* 返回值:0-成功,-1-校验失败,-2-I2C 错误 int
|
||
* 限定条件:CubeMX 已完成 I2C1 初始化(PB6-SCL, PB7-SDA)
|
||
* 函数说明:1. 读取芯片 ID 验证通信
|
||
* 2. 充电寄存器 18H 回读校验(出厂 82H 非易失),值漂移时经写允许
|
||
* 窗口重置并回读验证(手册 §4.8:强烈建议每次上电重置 18H)
|
||
* 3. 上电即写禁止态(WRTC 全 0),无需再写 0FH
|
||
*/
|
||
bsp_err_t sd2506_init(void) {
|
||
bsp_err_t ret = BSP_OK;
|
||
uint8_t a_id[8] = {0};
|
||
|
||
/* 验证 I2C 通信: 尝试读取 8 字节 ID */
|
||
ret = sd2506_read_regs(SD2506_REG_ID_START, a_id, SD2506_ID_SIZE);
|
||
if (ret != BSP_OK) {
|
||
/* 启动段可观测性(2026-10-02 加):本函数原先所有失败路径均静默返回,
|
||
* 而它是启动死机最可疑的源头(I2C1 硬件不通即卡此步且无任何输出)。
|
||
* 日志字符串必须纯 ASCII(文件无 BOM,ARMCC 按 CP936 解码)。 */
|
||
DBG_ERROR("sd2506_init: ID read FAIL (I2C err=%d)", ret);
|
||
return ret;
|
||
}
|
||
|
||
/* 充电寄存器 18H:手册 §4.8"出厂均配置成 82H"(非易失),
|
||
* 先回读校验,正常路径(=82H)不再写;仅值漂移(长时掉电等)时
|
||
* 经写允许窗口重置并回读验证(手册强烈建议每次上电重置 18H)
|
||
* 0x82 = EN_Charge(Bit7) | Charge1(Bit1):开启充电,5K 电阻 */
|
||
uint8_t charge_val = 0;
|
||
|
||
ret = sd2506_read_reg(SD2506_REG_CHARGE, &charge_val);
|
||
if (ret != BSP_OK) {
|
||
DBG_ERROR("sd2506_init: charge reg read FAIL (err=%d)", ret);
|
||
return ret;
|
||
}
|
||
|
||
if (charge_val != SD2506_CHARGE_INIT_VALUE) {
|
||
bsp_err_t ret_read = BSP_OK;
|
||
|
||
ret = sd2506_write_enable();
|
||
if (ret != BSP_OK) {
|
||
DBG_ERROR("sd2506_init: write-enable FAIL (err=%d)", ret);
|
||
return ret;
|
||
}
|
||
|
||
ret = sd2506_write_reg(SD2506_REG_CHARGE, SD2506_CHARGE_INIT_VALUE);
|
||
if (ret != BSP_OK) {
|
||
(void)sd2506_write_disable();
|
||
DBG_ERROR("sd2506_init: charge reg write FAIL (err=%d)", ret);
|
||
return ret;
|
||
}
|
||
|
||
ret_read = sd2506_read_reg(SD2506_REG_CHARGE, &charge_val);
|
||
ret = sd2506_write_disable();
|
||
if (ret != BSP_OK) {
|
||
DBG_ERROR("sd2506_init: write-disable FAIL (err=%d)", ret);
|
||
return ret;
|
||
}
|
||
|
||
if (ret_read != BSP_OK || charge_val != SD2506_CHARGE_INIT_VALUE) {
|
||
DBG_ERROR("sd2506_init: charge reg verify FAIL (read=%d val=0x%02X)",
|
||
ret_read, charge_val);
|
||
/* 回读校验不符 = 通信/介质类故障,不是调用方的参数错(旧码把两者都写成 -1) */
|
||
return BSP_ERR_IO;
|
||
}
|
||
|
||
DBG_INFO("sd2506_init: charge reg 18H reset to 0x%02X (was drifted)",
|
||
SD2506_CHARGE_INIT_VALUE);
|
||
}
|
||
|
||
DBG_INFO("sd2506_init: OK");
|
||
return BSP_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设置 RTC 时间日期
|
||
* 入口参数:p_time - 时间结构体指针,包含要设置的时间
|
||
* 返回值:0 - 成功,2 - I2C 错误
|
||
* 限定条件:sd2506_init() 已调用成功
|
||
* 函数说明:1. 先开写保护
|
||
* 2. 一次性写入 7 字节时间数据 (00H~06H)
|
||
* 3. 关闭写保护
|
||
*/
|
||
bsp_err_t sd2506_set_time(const sd2506_time_t *p_time) {
|
||
bsp_err_t ret = BSP_OK;
|
||
uint8_t buf[7] = {0};
|
||
|
||
if (p_time == NULL) {
|
||
return BSP_ERR_PARAM;
|
||
}
|
||
|
||
/* 组装 7 字节时间数据 (BCD 码) */
|
||
buf[0] = sd2506_dec_to_bcd(p_time->second); /* 00H: 秒 */
|
||
buf[1] = sd2506_dec_to_bcd(p_time->minute); /* 01H: 分 */
|
||
buf[2] = sd2506_dec_to_bcd(p_time->hour) | 0x80U; /* 02H: 时(bit7=1, 24小时制) */
|
||
buf[3] = sd2506_dec_to_bcd(p_time->week); /* 03H: 星期 */
|
||
buf[4] = sd2506_dec_to_bcd(p_time->day); /* 04H: 日 */
|
||
buf[5] = sd2506_dec_to_bcd(p_time->month); /* 05H: 月 */
|
||
buf[6] = sd2506_dec_to_bcd(p_time->year - 2000U); /* 06H: 年 */
|
||
|
||
/* 开启写保护 */
|
||
ret = sd2506_write_enable();
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
/* 一次性写入 7 字节时间数据 (00H~06H)
|
||
* 手册要求: 不可单独写某一个时间寄存器,否则可能引起错误进入 */
|
||
ret = sd2506_write_regs(SD2506_REG_SEC, buf, 7);
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
/* 关闭写保护 */
|
||
ret = sd2506_write_disable();
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
return BSP_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读取 RTC 时间日期
|
||
* 入口参数:p_time - 输出时间结构体指针 sd2506_time_t*(非空)
|
||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||
* 限定条件:sd2506_init() 已调用成功
|
||
* 函数说明:一次读取 7 字节时间数据 (00H~06H),所有实时数据被锁存避免错误
|
||
* note: sd2506_init() must be called first
|
||
*/
|
||
bsp_err_t sd2506_get_time(sd2506_time_t *p_time) {
|
||
bsp_err_t ret = BSP_OK;
|
||
uint8_t buf[7] = {0};
|
||
|
||
if (p_time == NULL) {
|
||
return BSP_ERR_PARAM;
|
||
}
|
||
|
||
/* 一次读取 7 字节时间数据 (00H~06H)
|
||
* 手册说明: 读取时所有实时数据被锁存,避免错误 */
|
||
ret = sd2506_read_regs(SD2506_REG_SEC, buf, 7);
|
||
if (ret != BSP_OK) {
|
||
return ret;
|
||
}
|
||
|
||
p_time->second = sd2506_bcd_to_dec(buf[0] & 0x7FU); /* 00H: 秒 */
|
||
p_time->minute = sd2506_bcd_to_dec(buf[1] & 0x7FU); /* 01H: 分 */
|
||
p_time->hour = sd2506_bcd_to_dec(buf[2] & 0x7FU); /* 02H: 屏蔽 bit7 (12/24标志) */
|
||
p_time->week = sd2506_bcd_to_dec(buf[3] & 0x07U); /* 03H: 星期 */
|
||
p_time->day = sd2506_bcd_to_dec(buf[4] & 0x3FU); /* 04H: 日 */
|
||
p_time->month = sd2506_bcd_to_dec(buf[5] & 0x1FU); /* 05H: 月 */
|
||
p_time->year = sd2506_bcd_to_dec(buf[6]) + 2000U; /* 06H: 年 */
|
||
|
||
return BSP_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读取内部温度
|
||
* 入口参数:p_temp - 输出温度值指针 int8_t*(非空)
|
||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||
* 限定条件:sd2506_init() 已调用成功
|
||
* 函数说明:读取寄存器 0x16,bit7 为符号位
|
||
* return: BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT (retryable)
|
||
* note: reads register 0x16
|
||
*/
|
||
bsp_err_t sd2506_get_temperature(int8_t *p_temp) {
|
||
bsp_err_t ret = BSP_OK;
|
||
uint8_t val = 0;
|
||
|
||
if (p_temp == NULL) {
|
||
return BSP_ERR_PARAM;
|
||
}
|
||
|
||
ret = sd2506_read_reg(SD2506_REG_TEMP, &val);
|
||
if (ret != BSP_OK) {
|
||
return ret;
|
||
}
|
||
|
||
/* bit7 为符号位, 其余为温度值 */
|
||
if (val & 0x80U) {
|
||
/* 负温度 取补码 */
|
||
*p_temp = (int8_t)(val | 0xF0U);
|
||
} else {
|
||
/* 正温度 */
|
||
*p_temp = (int8_t)(val & 0x7FU);
|
||
}
|
||
|
||
return BSP_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读取电池电压(毫伏)
|
||
* 入口参数:p_voltage - 输出电压指针 uint16_t*(非空)
|
||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||
* 限定条件:sd2506_init() 已调用成功
|
||
* 函数说明:组合 9 位值 (bit8=BAT8_VAL, bit7~0=BAT_VL),转换为毫伏
|
||
* return: BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT (retryable)
|
||
* note: combines 9-bit value
|
||
*/
|
||
bsp_err_t sd2506_get_battery_voltage(uint16_t *p_voltage) {
|
||
bsp_err_t ret = BSP_OK;
|
||
uint8_t val_high = 0;
|
||
uint8_t val_low = 0;
|
||
|
||
if (p_voltage == NULL) {
|
||
return BSP_ERR_PARAM;
|
||
}
|
||
|
||
/* 读取 1AH bit7 (BAT8_VAL) 与 1BH (BAT_VL) */
|
||
ret = sd2506_read_reg(SD2506_REG_CTR5, &val_high);
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
ret = sd2506_read_reg(SD2506_REG_BAT_VAL, &val_low);
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
/* 组合 9 位值 bit8=BAT8_VAL(1AH bit7), bit7~0=BAT_VL(1BH)
|
||
* 例: 1AH=80H, 1BH=30H => 电压 = 0x130 = 304 => 3.04V => 3040mV */
|
||
uint16_t raw = ((val_high & 0x80U) << 1) | val_low;
|
||
*p_voltage = raw * 10; /* 转换为毫伏 (raw 单位 0.01V) */
|
||
|
||
return BSP_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读取 8 字节芯片 ID
|
||
* 入口参数:p_id - 输出缓冲区指针 uint8_t[8](非空)
|
||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||
* 限定条件:sd2506_init() 已调用成功
|
||
* 函数说明:读取寄存器 0x72~0x79
|
||
* return: BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT (retryable)
|
||
* note: reads registers 0x72-0x79
|
||
*/
|
||
bsp_err_t sd2506_get_id(uint8_t p_id[8]) {
|
||
if (p_id == NULL) {
|
||
return BSP_ERR_PARAM;
|
||
}
|
||
|
||
return sd2506_read_regs(SD2506_REG_ID_START, p_id, SD2506_ID_SIZE);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读取 SRAM 数据
|
||
* 入口参数:addr - 起始地址 uint8_t 0-69
|
||
* p_buf - 输出缓冲区指针 uint8_t*(非空)
|
||
* len - 读取长度 uint8_t 1-N
|
||
* 返回值:0-成功,-1=参数错误,-2=I2C 错误 int
|
||
* 限定条件:sd2506_init() 已调用成功;addr+len <= SD2506_SRAM_SIZE(70)
|
||
* 函数说明:SRAM 范围 0x2C~0x71
|
||
* return: BSP_OK / BSP_ERR_PARAM / BSP_ERR_IO / BSP_ERR_TIMEOUT
|
||
* note: SRAM range 0x2C-0x71
|
||
*/
|
||
bsp_err_t sd2506_read_sram(uint8_t addr, uint8_t *p_buf, uint8_t len) {
|
||
if (p_buf == NULL || len == 0 || addr >= SD2506_SRAM_SIZE) {
|
||
return BSP_ERR_PARAM;
|
||
}
|
||
|
||
if (addr + len > SD2506_SRAM_SIZE) {
|
||
return BSP_ERR_PARAM;
|
||
}
|
||
|
||
return sd2506_read_regs(SD2506_REG_SRAM_START + addr, p_buf, len);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:写入 SRAM 数据
|
||
* 入口参数:addr - 起始地址 uint8_t 0-69
|
||
* p_buf - 输入数据指针 uint8_t*(非空)
|
||
* len - 写入长度 uint8_t 1-N
|
||
* 返回值:0-成功,-1=参数错误,-2=I2C 错误 int
|
||
* 限定条件:sd2506_init() 已调用成功;addr+len <= SD2506_SRAM_SIZE(70)
|
||
* 函数说明:SRAM 写入需走写保护流程(虽 SRAM 本身无需)
|
||
* return: BSP_OK / BSP_ERR_PARAM / BSP_ERR_IO / BSP_ERR_TIMEOUT
|
||
*/
|
||
bsp_err_t sd2506_write_sram(uint8_t addr, const uint8_t *p_buf, uint8_t len) {
|
||
if (p_buf == NULL || len == 0 || addr >= SD2506_SRAM_SIZE) {
|
||
return BSP_ERR_PARAM;
|
||
}
|
||
|
||
if (addr + len > SD2506_SRAM_SIZE) {
|
||
return BSP_ERR_PARAM;
|
||
}
|
||
|
||
/* SRAM 无需开写保护即可写入 (写保护仅对 00H~71H 有效, SRAM 为 2CH~71H)
|
||
* 但为安全起见, SRAM 写入也走写保护流程 */
|
||
bsp_err_t ret = BSP_OK;
|
||
ret = sd2506_write_enable();
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
ret = sd2506_write_regs(SD2506_REG_SRAM_START + addr, p_buf, len);
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
ret = sd2506_write_disable();
|
||
return ret;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设置闹钟中断
|
||
* 入口参数:p_time - 报警时间结构体指针 sd2506_time_t*(非空)
|
||
* mask - 报警匹配掩码 uint8_t
|
||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||
* 限定条件:sd2506_init() 已调用成功
|
||
* 函数说明:写入 8 字节报警数据 (07H~0EH),使能 CTR2.INTAE+INTS0+IM
|
||
* return: BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT (retryable)
|
||
*/
|
||
bsp_err_t sd2506_set_alarm(const sd2506_time_t *p_time, uint8_t mask) {
|
||
bsp_err_t ret = BSP_OK;
|
||
uint8_t buf[8] = {0};
|
||
|
||
if (p_time == NULL) {
|
||
return BSP_ERR_PARAM;
|
||
}
|
||
|
||
/* 组装 8 字节报警数据 (07H~0EH) */
|
||
buf[0] = sd2506_dec_to_bcd(p_time->second) & 0x7FU; /* 07H: 秒报 */
|
||
buf[1] = sd2506_dec_to_bcd(p_time->minute) & 0x7FU; /* 08H: 分报 */
|
||
buf[2] = sd2506_dec_to_bcd(p_time->hour) & 0x3FU; /* 09H: 时报(最高位始终为0) */
|
||
buf[3] = 0x00U; /* 0AH: 星期报警 */
|
||
buf[4] = sd2506_dec_to_bcd(p_time->day) & 0x3FU; /* 0BH: 日报 */
|
||
buf[5] = sd2506_dec_to_bcd(p_time->month) & 0x1FU; /* 0CH: 月报 */
|
||
buf[6] = sd2506_dec_to_bcd(p_time->year - 2000U); /* 0DH: 年报 */
|
||
buf[7] = mask; /* 0EH: 报警允许 */
|
||
|
||
/* 开启写保护 */
|
||
ret = sd2506_write_enable();
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
/* 写入报警寄存器 (07H~0EH) */
|
||
ret = sd2506_write_regs(SD2506_REG_AL_SEC, buf, 8);
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
/* 使能报警中断: CTR2 中 INTAE=1, INTS1=0, INTS0=1, IM=1 (周期中断) */
|
||
ret = sd2506_write_reg(SD2506_REG_CTR2,
|
||
SD2506_CTR2_INTAE | SD2506_CTR2_INTS0 | SD2506_CTR2_IM);
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
/* 关闭写保护 */
|
||
ret = sd2506_write_disable();
|
||
if (ret != BSP_OK) return ret;
|
||
|
||
return BSP_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:清除闹钟中断标志
|
||
* 入口参数:无
|
||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||
* 限定条件:sd2506_init() 已调用成功
|
||
* 函数说明:ARST 位于 CTR3(11H) bit7,本驱动从未写 CTR3 → ARST=0,
|
||
* 故读 CTR1 不会自动清 INTAF(手册 L519/L523),须手动写 CTR1 将 INTAF 置 0;
|
||
* 写 0FH 须在写允许窗口内且保持 WRTC2/WRTC3=1(手册 L521),
|
||
* 故写入值取写允许态 0xFF 清 INTAF 位后的 0xDF。
|
||
* return: BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT (retryable)
|
||
*/
|
||
bsp_err_t sd2506_clear_alarm(void) {
|
||
bsp_err_t ret = BSP_OK;
|
||
bsp_err_t ret_off = BSP_OK;
|
||
|
||
/* 写 0FH 须在写允许窗口内(手册 L519),且 WRTC2/WRTC3 必须保持 1(L521) */
|
||
ret = sd2506_write_enable();
|
||
if (ret != BSP_OK) {
|
||
return ret;
|
||
}
|
||
|
||
ret = sd2506_write_reg(SD2506_REG_CTR1,
|
||
(uint8_t)(SD2506_CTR1_WRITE_ON & (uint8_t)~SD2506_CTR1_INTAF));
|
||
|
||
ret_off = sd2506_write_disable();
|
||
if (ret == BSP_OK) {
|
||
ret = ret_off;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读取控制寄存器 1 (CTR1)
|
||
* 入口参数:p_val - 输出值指针 uint8_t*(非空)
|
||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||
* 限定条件:sd2506_init() 已调用成功
|
||
* return: BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT (retryable)
|
||
*/
|
||
bsp_err_t sd2506_read_ctr1(uint8_t *p_val) {
|
||
if (p_val == NULL) {
|
||
return BSP_ERR_PARAM;
|
||
}
|
||
|
||
return sd2506_read_reg(SD2506_REG_CTR1, p_val);
|
||
}
|