Files
STM32F4-Base/Drivers/BSP/SD2506/sd2506.c
T
wandering1 0f7b5a87fb chore(编号): 存量编号全量转语义短语,门禁识别面与语料同批重打
登记簿、文档与代码注释里的存量字母加数字编号(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%。
2026-10-26 12:28:14 +08:00

574 lines
22 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称: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);
}