Files
STM32F4-Base/Drivers/BSP/SD2506/sd2506.c
2026-07-19 15:27:49 +08:00

455 lines
14 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 实时时钟驱动
* 模块功能SD2506API-G 高精度温补实时时钟模<E9929F>?I2C 驱动
* 适用平台STM32F407ZGT6I2C1 接口 (PB6-SCL, PB7-SDA)
* 作者王建<E78E8B>? * 创建日期<E697A5>?026-07-17
* 修改记录<E8AEB0>? * 2026-07-17 王建<E78E8B>? 创建初始版本<EFBC8C>?SD2506API-G Ver2.0 手册
*/
#include <string.h>
#include "sd2506.h"
#include "i2c.h"
extern I2C_HandleTypeDef hi2c1;
/* ======================== 内部辅助函数 ======================== */
/*
* 函数功能BCD 码转十进<E58D81>? * 入口参数bcd - BCD 编码<E7BC96>? uint8_t 0x00 - 0x99
* <20>?<3F>?值十进制数<E588B6>? * 限定条件:输入为合法 BCD <20>? * 函数说明:高 4 位为十位,低 4 位为个位
*/
uint8_t sd2506_bcd_to_dec(uint8_t bcd) {
return ((bcd >> 4) * 10) + (bcd & 0x0FU);
}
/*
* 函数功能十进制<E8BF9B>?BCD <20>? * 入口参数dec - 十进制数<E588B6>? uint8_t 0 - 99
* <20>?<3F>?值BCD 编码<E7BC96>? * 限定条件:输入值在 0 - 99 范围<E88C83>? * 函数说明:十位存入高 4 位个位存入<E5AD98>?4 <20>? */
uint8_t sd2506_dec_to_bcd(uint8_t dec) {
return ((dec / 10) << 4) | (dec % 10);
}
/*
* 函数功能:写单字节寄存器
* 入口参数reg - 寄存器地址 uint8_t 00H - 79H
* val - 写入<E58699>? uint8_t 0x00 - 0xFF
* <20>?<3F>?值0 - 成功<E68890>?2 - I2C 错误
* 限定条件I2C 外设已初始化
* 函数说明:通过 I2C 向指定地址写入 1 字节
*/
static int sd2506_write_reg(uint8_t reg, uint8_t val) {
if (HAL_I2C_Mem_Write(&hi2c1, SD2506_I2C_ADDR_WRITE, reg,
I2C_MEMADD_SIZE_8BIT, &val, 1,
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
return SD2506_I2C_ERROR;
}
return SD2506_OK;
}
/*
* 函数功能:读单字节寄存器
* 入口参数reg - 寄存器地址 uint8_t 00H - 79H
* val - 读取值指<E580BC>? uint8_t*
* <20>?<3F>?值0 - 成功<E68890>?2 - I2C 错误
* 限定条件I2C 外设已初始化val 为非空指<E7A9BA>? * 函数说明:通过 I2C 从指定地址读取 1 字节
*/
static int sd2506_read_reg(uint8_t reg, uint8_t *p_val) {
if (HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
I2C_MEMADD_SIZE_8BIT, p_val, 1,
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
return SD2506_I2C_ERROR;
}
return SD2506_OK;
}
/*
* 函数功能:写多字节寄存器
* 入口参数reg - 起始寄存器地址 uint8_t 00H - 79H
* data - 数据缓冲区指<E58CBA>? uint8_t*
* len - 数据长度 uint8_t 1 - N
* <20>?<3F>?值0 - 成功<E68890>?2 - I2C 错误
* 限定条件I2C 外设已初始化data 为非空指<E7A9BA>? * 函数说明:从 reg 开始连续写<E7BBAD>?len 字节
*/
static int sd2506_write_regs(uint8_t reg, const uint8_t *p_data, uint8_t len) {
if (HAL_I2C_Mem_Write(&hi2c1, SD2506_I2C_ADDR_WRITE, reg,
I2C_MEMADD_SIZE_8BIT, (uint8_t *)p_data, len,
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
return SD2506_I2C_ERROR;
}
return SD2506_OK;
}
/*
* 函数功能:读多字节寄存器
* 入口参数reg - 起始寄存器地址 uint8_t 00H - 79H
* data - 数据缓冲区指<E58CBA>? uint8_t*
* len - 数据长度 uint8_t 1 - N
* <20>?<3F>?值0 - 成功<E68890>?2 - I2C 错误
* 限定条件I2C 外设已初始化data 为非空指<E7A9BA>? * 函数说明:从 reg 开始连续读<E7BBAD>?len 字节
*/
static int sd2506_read_regs(uint8_t reg, uint8_t *p_data, uint8_t len) {
if (HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
I2C_MEMADD_SIZE_8BIT, p_data, len,
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
return SD2506_I2C_ERROR;
}
return SD2506_OK;
}
/*
* 函数功能开启写保护允许写<E8AEB8>?00H~71H 寄存器)
* 入口参数:无
* 返回值0 - 成功<E68890>?2 - I2C 错误
* 限定条件I2C 外设已初始化
* 函数说明:顺序:先写 WRTC1=1, 再写 WRTC2=1 + WRTC3=1
*/
static int sd2506_write_enable(void) {
int ret = 0;
/* 先置 WRTC1=1 (bit6=1), 其它位参考手<E88083>? 0x84 */
ret = sd2506_write_reg(SD2506_REG_CTR1, 0x84U);
if (ret != SD2506_OK) return ret;
/* 再置 WRTC2=1(bit5) + WRTC3=1(bit4): 0x8C */
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x8CU);
if (ret != SD2506_OK) return ret;
return SD2506_OK;
}
/*
* 函数功能关闭写保护禁止写<E6ADA2>?00H~71H 寄存器)
* 入口参数:无
* 返回值0 - 成功<E68890>?2 - I2C 错误
* 限定条件I2C 外设已初始化
* 函数说明:顺序:先写 WRTC2=0 + WRTC3=0, 再写 WRTC1=0
*/
static int sd2506_write_disable(void) {
int ret = 0;
/* 先清 WRTC2=0, WRTC3=0: 0x00 */
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x00U);
if (ret != SD2506_OK) return ret;
/* 再清 WRTC1=0, 同时 ARST=1 使能自动复位: 0x20 */
ret = sd2506_write_reg(SD2506_REG_CTR1, SD2506_CTR1_WRITE_OFF);
if (ret != SD2506_OK) return ret;
return SD2506_OK;
}
/* ======================== 公共 API 实现 ======================== */
/*
* 函数功能:初始化 SD2506API-G RTC
* 入口参数:无
* 返回值0 - 成功<E68890>?2 - I2C 错误
* 限定条件CubeMX 已完<E5B7B2>?I2C1 初始<E5889D>?
* 函数说明<E8AFB4>?. 读取芯片 ID 验证通信
* 2. 上电重置充电寄存<E5AF84>?18H=82H
* 3. 配置 24 小时制、开自动复位
*/
int sd2506_init(void) {
int ret = 0;
uint8_t a_id[8] = {0};
/* 验证 I2C 通信: 尝试读取 8 字节 ID */
ret = sd2506_read_regs(SD2506_REG_ID_START, a_id, SD2506_ID_SIZE);
if (ret != SD2506_OK) {
return ret;
}
/* 上电重置充电寄存<E5AF84>?18H = 82H (开启充<E590AF>? 5K电阻)
* 手册强烈建议每次上电时重置此<E7BDAE>?*/
ret = sd2506_write_reg(SD2506_REG_CHARGE, 0x82U);
if (ret != SD2506_OK) {
return ret;
}
/* 配置: 24小时<E5B08F>? 自动复位使能
* 0FH = 0x20 (bit5=ARST=1, 其它标志位清<E4BD8D>?
* 写入时需注意: 写允许状态下 0FH <20>?WRTC 位必须为1
* 此处直接写入 0x20 即可 (ARST=1, <20>?WRTC <20>? */
ret = sd2506_write_reg(SD2506_REG_CTR1, SD2506_CTR1_WRITE_OFF);
if (ret != SD2506_OK) {
return ret;
}
return SD2506_OK;
}
/*
* 函数功能<EFBC9A>?RTC 时间日期
* 入口参数p_time - 时间结构体指针,包含要设置的时间
* 返回值0 - 成功<E68890>?2 - I2C 错误
* 限定条件sd2506_init() 已调<E5B7B2>?
* 函数说明<E8AFB4>?. 先开写保<E58699>?
* 2. 一次性写<E680A7>?7 字节时间数据 (00H~06H)
* 3. 关闭写保<E58699>?
*/
int sd2506_set_time(const sd2506_time_t *p_time) {
int ret = 0;
uint8_t buf[7] = {0};
if (p_time == NULL) {
return SD2506_ERROR;
}
/* 组装 7 字节时间数据 (BCD <20>? */
buf[0] = sd2506_dec_to_bcd(p_time->second); /* 00H: <20>?*/
buf[1] = sd2506_dec_to_bcd(p_time->minute); /* 01H: <20>?*/
buf[2] = sd2506_dec_to_bcd(p_time->hour) | 0x80U; /* 02H: <20>?(bit7=1, 24小时<E5B08F>? */
buf[3] = sd2506_dec_to_bcd(p_time->week); /* 03H: 星期 */
buf[4] = sd2506_dec_to_bcd(p_time->day); /* 04H: <20>?*/
buf[5] = sd2506_dec_to_bcd(p_time->month); /* 05H: <20>?*/
buf[6] = sd2506_dec_to_bcd(p_time->year - 2000U); /* 06H: <20>?*/
/* 开启写保护 */
ret = sd2506_write_enable();
if (ret != SD2506_OK) return ret;
/* 一次性写<E680A7>?7 字节时间数据 (00H~06H)
* 手册要求: 不可单独写某一个时间寄存器否则可能引起错误进<E8AFAF>?*/
ret = sd2506_write_regs(SD2506_REG_SEC, buf, 7);
if (ret != SD2506_OK) return ret;
/* 关闭写保<E58699>?*/
ret = sd2506_write_disable();
if (ret != SD2506_OK) return ret;
return SD2506_OK;
}
/*
* function: read RTC time
* param: p_time - output time struct
* return: 0=OK, -2=I2C error
* note: sd2506_init() must be called first
*/
int sd2506_get_time(sd2506_time_t *p_time) {
int ret = 0;
uint8_t buf[7] = {0};
if (p_time == NULL) {
return SD2506_ERROR;
}
/* 一次读<E6ACA1>?7 字节时间数据 (00H~06H)
* 手册说明: 读取时所有实时数据被锁存避免错<E5858D>?*/
ret = sd2506_read_regs(SD2506_REG_SEC, buf, 7);
if (ret != SD2506_OK) {
return ret;
}
p_time->second = sd2506_bcd_to_dec(buf[0] & 0x7FU); /* 00H: <20>?*/
p_time->minute = sd2506_bcd_to_dec(buf[1] & 0x7FU); /* 01H: <20>?*/
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: <20>?*/
p_time->month = sd2506_bcd_to_dec(buf[5] & 0x1FU); /* 05H: <20>?*/
p_time->year = sd2506_bcd_to_dec(buf[6]) + 2000U; /* 06H: <20>?*/
return SD2506_OK;
}
/*
* function: read internal temperature
* param: p_temp - output temperature
* return: 0=OK, -2=I2C error
* note: reads register 0x16
*/
int sd2506_get_temperature(int8_t *p_temp) {
int ret = 0;
uint8_t val = 0;
if (p_temp == NULL) {
return SD2506_ERROR;
}
ret = sd2506_read_reg(SD2506_REG_TEMP, &val);
if (ret != SD2506_OK) {
return ret;
}
/* bit7 为符号位, 其余为温度<E6B8A9>?*/
if (val & 0x80U) {
/* 负温<E8B49F>? 取补<E58F96>?*/
*p_temp = (int8_t)(val | 0xF0U);
} else {
/* 正温<E6ADA3>?*/
*p_temp = (int8_t)(val & 0x7FU);
}
return SD2506_OK;
}
/*
* function: read battery voltage (mV)
* param: p_voltage - output voltage
* return: 0=OK, -2=I2C error
* note: combines 9-bit value
*/
int sd2506_get_battery_voltage(uint16_t *p_voltage) {
int ret = 0;
uint8_t val_high = 0;
uint8_t val_low = 0;
if (p_voltage == NULL) {
return SD2506_ERROR;
}
/* 读取 1AH bit7 (BAT8_VAL) <20>?1BH (BAT_VL) */
ret = sd2506_read_reg(SD2506_REG_CTR5, &val_high);
if (ret != SD2506_OK) return ret;
ret = sd2506_read_reg(SD2506_REG_BAT_VAL, &val_low);
if (ret != SD2506_OK) return ret;
/* 组合 9 <20>? bit8=BAT8_VAL(1AH bit7), bit7~0=BAT_VL(1BH)
* <20>?1AH=80H, 1BH=30H => 电压 = 0x130 = 304 => 3.04V => 3040mV */
uint16_t raw = ((val_high & 0x80U) << 1) | val_low;
*p_voltage = raw * 10; /* 转换为毫<E4B8BA>?(raw 单位 0.01V) */
return SD2506_OK;
}
/*
* function: read 8-byte chip ID
* param: p_id - 8-byte output buffer
* return: 0=OK, -2=I2C error
* note: reads registers 0x72-0x79
*/
int sd2506_get_id(uint8_t p_id[8]) {
if (p_id == NULL) {
return SD2506_ERROR;
}
return sd2506_read_regs(SD2506_REG_ID_START, p_id, SD2506_ID_SIZE);
}
/*
* function: read SRAM data
* param: addr - start address (0-69)
* p_buf - output buffer
* len - read length
* return: 0=OK, -1=param err, -2=I2C err
* note: SRAM range 0x2C-0x71
*/
int sd2506_read_sram(uint8_t addr, uint8_t *p_buf, uint8_t len) {
if (p_buf == NULL || len == 0 || addr >= SD2506_SRAM_SIZE) {
return SD2506_ERROR;
}
if (addr + len > SD2506_SRAM_SIZE) {
return SD2506_ERROR;
}
return sd2506_read_regs(SD2506_REG_SRAM_START + addr, p_buf, len);
}
/*
* function: write SRAM data
* param: addr - start address (0-69)
* p_buf - input data
* len - write length
* return: 0=OK, -1=param err, -2=I2C err
*/
int 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 SD2506_ERROR;
}
if (addr + len > SD2506_SRAM_SIZE) {
return SD2506_ERROR;
}
/* SRAM 无需开写保护即可写<E58FAF>?(写保护仅<E68AA4>?00H~71H 有效, SRAM <20>?2CH~71H)
* 但为安全起见, SRAM 写入也走写保护流<E68AA4>?*/
int ret = 0;
ret = sd2506_write_enable();
if (ret != SD2506_OK) return ret;
ret = sd2506_write_regs(SD2506_REG_SRAM_START + addr, p_buf, len);
if (ret != SD2506_OK) return ret;
ret = sd2506_write_disable();
return ret;
}
/*
* function: set alarm interrupt
* param: p_time - alarm time struct
* mask - alarm match mask
* return: 0=OK, -2=I2C error
*/
int sd2506_set_alarm(const sd2506_time_t *p_time, uint8_t mask) {
int ret = 0;
uint8_t buf[8] = {0};
if (p_time == NULL) {
return SD2506_ERROR;
}
/* 组装 8 字节报警数据 (07H~0EH) */
buf[0] = sd2506_dec_to_bcd(p_time->second) & 0x7FU; /* 07H: 秒报<E7A792>?*/
buf[1] = sd2506_dec_to_bcd(p_time->minute) & 0x7FU; /* 08H: 分报<E58886>?*/
buf[2] = sd2506_dec_to_bcd(p_time->hour) & 0x3FU; /* 09H: 时报<E697B6>?(最高位始终<E5A78B>?) */
buf[3] = 0x00U; /* 0AH: 星期报警 */
buf[4] = sd2506_dec_to_bcd(p_time->day) & 0x3FU; /* 0BH: 日报<E697A5>?*/
buf[5] = sd2506_dec_to_bcd(p_time->month) & 0x1FU; /* 0CH: 月报<E69C88>?*/
buf[6] = sd2506_dec_to_bcd(p_time->year - 2000U); /* 0DH: 年报<E5B9B4>?*/
buf[7] = mask; /* 0EH: 报警允许 */
/* 开启写保护 */
ret = sd2506_write_enable();
if (ret != SD2506_OK) return ret;
/* 写入报警寄存<E5AF84>?(07H~0EH) */
ret = sd2506_write_regs(SD2506_REG_AL_SEC, buf, 8);
if (ret != SD2506_OK) return ret;
/* 使能报警中断: CTR2 <20>?INTAE=1, INTS1=0, INTS0=1, IM=1 (周期<E591A8>? */
ret = sd2506_write_reg(SD2506_REG_CTR2,
SD2506_CTR2_INTAE | SD2506_CTR2_INTS0 | SD2506_CTR2_IM);
if (ret != SD2506_OK) return ret;
/* 关闭写保<E58699>?*/
ret = sd2506_write_disable();
if (ret != SD2506_OK) return ret;
return SD2506_OK;
}
/*
* function: clear alarm interrupt flag
* param: none
* return: 0=OK, -2=I2C error
* note: reading CTR1 clears INTAF
*/
int sd2506_clear_alarm(void) {
int ret = 0;
uint8_t val = 0;
/* 读取 CTR1, ARST=1 时自动清<E58AA8>?INTAF */
ret = sd2506_read_reg(SD2506_REG_CTR1, &val);
if (ret != SD2506_OK) {
return ret;
}
return SD2506_OK;
}
/*
* function: read control register 1
* param: p_val - output value
* return: 0=OK, -2=I2C error
*/
int sd2506_read_ctr1(uint8_t *p_val) {
if (p_val == NULL) {
return SD2506_ERROR;
}
return sd2506_read_reg(SD2506_REG_CTR1, p_val);
}