关键芯片都驱动,且测试成功
This commit is contained in:
381
Drivers/BSP/SD2506/sd2506.c
Normal file
381
Drivers/BSP/SD2506/sd2506.c
Normal file
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* 模块名称:SD2506API-G RTC 实时时钟驱动
|
||||
* 模块功能:SD2506API-G 高精度温补实时时钟模块 I2C 驱动
|
||||
* 适用平台:STM32F407ZGT6,I2C1 接口 (PB6-SCL, PB7-SDA)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-17
|
||||
* 修改记录:
|
||||
* 2026-07-17 王建锋 创建初始版本,参考 SD2506API-G Ver2.0 手册
|
||||
*/
|
||||
|
||||
#include "sd2506.h"
|
||||
#include "i2c.h"
|
||||
#include <string.h>
|
||||
|
||||
extern I2C_HandleTypeDef hi2c1;
|
||||
|
||||
/* ======================== 内部辅助函数 ======================== */
|
||||
|
||||
uint8_t sd2506_bcd_to_dec(uint8_t bcd)
|
||||
{
|
||||
return ((bcd >> 4) * 10) + (bcd & 0x0FU);
|
||||
}
|
||||
|
||||
uint8_t sd2506_dec_to_bcd(uint8_t dec)
|
||||
{
|
||||
return ((dec / 10) << 4) | (dec % 10);
|
||||
}
|
||||
|
||||
/*
|
||||
* 写单字节寄存器
|
||||
* reg: 寄存器地址 (00H~79H)
|
||||
* val: 写入值
|
||||
* 返回: 0=成功, -2=I2C错误
|
||||
*/
|
||||
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: 寄存器地址 (00H~79H)
|
||||
* val: 读取值指针
|
||||
* 返回: 0=成功, -2=I2C错误
|
||||
*/
|
||||
static int sd2506_read_reg(uint8_t reg, uint8_t *val)
|
||||
{
|
||||
if (HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
|
||||
I2C_MEMADD_SIZE_8BIT, val, 1,
|
||||
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
||||
return SD2506_I2C_ERROR;
|
||||
}
|
||||
return SD2506_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 写多字节寄存器 (从 reg 开始连续写入 len 字节)
|
||||
* reg: 起始寄存器地址
|
||||
* data: 数据缓冲区
|
||||
* len: 数据长度
|
||||
* 返回: 0=成功, -2=I2C错误
|
||||
*/
|
||||
static int sd2506_write_regs(uint8_t reg, const uint8_t *data, uint8_t len)
|
||||
{
|
||||
if (HAL_I2C_Mem_Write(&hi2c1, SD2506_I2C_ADDR_WRITE, reg,
|
||||
I2C_MEMADD_SIZE_8BIT, (uint8_t *)data, len,
|
||||
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
||||
return SD2506_I2C_ERROR;
|
||||
}
|
||||
return SD2506_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 读多字节寄存器 (从 reg 开始连续读取 len 字节)
|
||||
* reg: 起始寄存器地址
|
||||
* data: 数据缓冲区
|
||||
* len: 数据长度
|
||||
* 返回: 0=成功, -2=I2C错误
|
||||
*/
|
||||
static int sd2506_read_regs(uint8_t reg, uint8_t *data, uint8_t len)
|
||||
{
|
||||
if (HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
|
||||
I2C_MEMADD_SIZE_8BIT, data, len,
|
||||
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
||||
return SD2506_I2C_ERROR;
|
||||
}
|
||||
return SD2506_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 开启写保护 (允许写入 00H~71H 寄存器)
|
||||
* 顺序: 先写 WRTC1=1, 再写 WRTC2=1 + WRTC3=1
|
||||
*/
|
||||
static int sd2506_write_enable(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* 先置 WRTC1=1 (bit6=1), 其它位参考手册: 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 关闭写保护 (禁止写入 00H~71H 寄存器)
|
||||
* 顺序: 先写 WRTC2=0 + WRTC3=0, 再写 WRTC1=0
|
||||
*/
|
||||
static int sd2506_write_disable(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* 先清 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 实现 ======================== */
|
||||
|
||||
int sd2506_init(void)
|
||||
{
|
||||
int ret;
|
||||
uint8_t id[8];
|
||||
|
||||
/* 验证 I2C 通信: 尝试读取 8 字节 ID */
|
||||
ret = sd2506_read_regs(SD2506_REG_ID_START, id, SD2506_ID_SIZE);
|
||||
if (ret != SD2506_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 上电重置充电寄存器 18H = 82H (开启充电, 5K电阻)
|
||||
* 手册强烈建议每次上电时重置此值 */
|
||||
ret = sd2506_write_reg(SD2506_REG_CHARGE, 0x82U);
|
||||
if (ret != SD2506_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 配置: 24小时制, 自动复位使能
|
||||
* 0FH = 0x20 (bit5=ARST=1, 其它标志位清零)
|
||||
* 写入时需注意: 写允许状态下 0FH 的 WRTC 位必须为1
|
||||
* 此处直接写入 0x20 即可 (ARST=1, 无 WRTC 位) */
|
||||
ret = sd2506_write_reg(SD2506_REG_CTR1, SD2506_CTR1_WRITE_OFF);
|
||||
if (ret != SD2506_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return SD2506_OK;
|
||||
}
|
||||
|
||||
int sd2506_set_time(const sd2506_time_t *time)
|
||||
{
|
||||
int ret;
|
||||
uint8_t buf[7];
|
||||
|
||||
if (time == NULL) {
|
||||
return SD2506_ERROR;
|
||||
}
|
||||
|
||||
/* 组装 7 字节时间数据 (BCD 码) */
|
||||
buf[0] = sd2506_dec_to_bcd(time->second); /* 00H: 秒 */
|
||||
buf[1] = sd2506_dec_to_bcd(time->minute); /* 01H: 分 */
|
||||
buf[2] = sd2506_dec_to_bcd(time->hour) | 0x80U; /* 02H: 时 (bit7=1, 24小时制) */
|
||||
buf[3] = sd2506_dec_to_bcd(time->week); /* 03H: 星期 */
|
||||
buf[4] = sd2506_dec_to_bcd(time->day); /* 04H: 日 */
|
||||
buf[5] = sd2506_dec_to_bcd(time->month); /* 05H: 月 */
|
||||
buf[6] = sd2506_dec_to_bcd(time->year - 2000U); /* 06H: 年 */
|
||||
|
||||
/* 开启写保护 */
|
||||
ret = sd2506_write_enable();
|
||||
if (ret != SD2506_OK) return ret;
|
||||
|
||||
/* 一次性写入 7 字节时间数据 (00H~06H)
|
||||
* 手册要求: 不可单独写某一个时间寄存器,否则可能引起错误进位 */
|
||||
ret = sd2506_write_regs(SD2506_REG_SEC, buf, 7);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
|
||||
/* 关闭写保护 */
|
||||
ret = sd2506_write_disable();
|
||||
if (ret != SD2506_OK) return ret;
|
||||
|
||||
return SD2506_OK;
|
||||
}
|
||||
|
||||
int sd2506_get_time(sd2506_time_t *time)
|
||||
{
|
||||
int ret;
|
||||
uint8_t buf[7];
|
||||
|
||||
if (time == NULL) {
|
||||
return SD2506_ERROR;
|
||||
}
|
||||
|
||||
/* 一次读取 7 字节时间数据 (00H~06H)
|
||||
* 手册说明: 读取时所有实时数据被锁存,避免错读 */
|
||||
ret = sd2506_read_regs(SD2506_REG_SEC, buf, 7);
|
||||
if (ret != SD2506_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
time->second = sd2506_bcd_to_dec(buf[0] & 0x7FU); /* 00H: 秒 */
|
||||
time->minute = sd2506_bcd_to_dec(buf[1] & 0x7FU); /* 01H: 分 */
|
||||
time->hour = sd2506_bcd_to_dec(buf[2] & 0x7FU); /* 02H: 屏蔽 bit7 (12/24标志) */
|
||||
time->week = sd2506_bcd_to_dec(buf[3] & 0x07U); /* 03H: 星期 */
|
||||
time->day = sd2506_bcd_to_dec(buf[4] & 0x3FU); /* 04H: 日 */
|
||||
time->month = sd2506_bcd_to_dec(buf[5] & 0x1FU); /* 05H: 月 */
|
||||
time->year = sd2506_bcd_to_dec(buf[6]) + 2000U; /* 06H: 年 */
|
||||
|
||||
return SD2506_OK;
|
||||
}
|
||||
|
||||
int sd2506_get_temperature(int8_t *temp)
|
||||
{
|
||||
int ret;
|
||||
uint8_t val;
|
||||
|
||||
if (temp == NULL) {
|
||||
return SD2506_ERROR;
|
||||
}
|
||||
|
||||
ret = sd2506_read_reg(SD2506_REG_TEMP, &val);
|
||||
if (ret != SD2506_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* bit7 为符号位, 其余为温度值 */
|
||||
if (val & 0x80U) {
|
||||
/* 负温度: 取补码 */
|
||||
*temp = (int8_t)(val | 0xF0U);
|
||||
} else {
|
||||
/* 正温度 */
|
||||
*temp = (int8_t)(val & 0x7FU);
|
||||
}
|
||||
|
||||
return SD2506_OK;
|
||||
}
|
||||
|
||||
int sd2506_get_battery_voltage(uint16_t *voltage)
|
||||
{
|
||||
int ret;
|
||||
uint8_t val_high, val_low;
|
||||
|
||||
if (voltage == NULL) {
|
||||
return SD2506_ERROR;
|
||||
}
|
||||
|
||||
/* 读取 1AH bit7 (BAT8_VAL) 和 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 位: 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;
|
||||
*voltage = raw * 10; /* 转换为毫伏 (raw 单位 0.01V) */
|
||||
|
||||
return SD2506_OK;
|
||||
}
|
||||
|
||||
int sd2506_get_id(uint8_t id[8])
|
||||
{
|
||||
if (id == NULL) {
|
||||
return SD2506_ERROR;
|
||||
}
|
||||
|
||||
return sd2506_read_regs(SD2506_REG_ID_START, id, SD2506_ID_SIZE);
|
||||
}
|
||||
|
||||
int sd2506_read_sram(uint8_t addr, uint8_t *buf, uint8_t len)
|
||||
{
|
||||
if (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, buf, len);
|
||||
}
|
||||
|
||||
int sd2506_write_sram(uint8_t addr, const uint8_t *buf, uint8_t len)
|
||||
{
|
||||
if (buf == NULL || len == 0 || addr >= SD2506_SRAM_SIZE) {
|
||||
return SD2506_ERROR;
|
||||
}
|
||||
|
||||
if (addr + len > SD2506_SRAM_SIZE) {
|
||||
return SD2506_ERROR;
|
||||
}
|
||||
|
||||
/* SRAM 无需开写保护即可写入 (写保护仅对 00H~71H 有效, SRAM 是 2CH~71H)
|
||||
* 但为安全起见, SRAM 写入也走写保护流程 */
|
||||
int ret;
|
||||
ret = sd2506_write_enable();
|
||||
if (ret != SD2506_OK) return ret;
|
||||
|
||||
ret = sd2506_write_regs(SD2506_REG_SRAM_START + addr, buf, len);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
|
||||
ret = sd2506_write_disable();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sd2506_set_alarm(const sd2506_time_t *time, uint8_t mask)
|
||||
{
|
||||
int ret;
|
||||
uint8_t buf[8];
|
||||
|
||||
if (time == NULL) {
|
||||
return SD2506_ERROR;
|
||||
}
|
||||
|
||||
/* 组装 8 字节报警数据 (07H~0EH) */
|
||||
buf[0] = sd2506_dec_to_bcd(time->second) & 0x7FU; /* 07H: 秒报警 */
|
||||
buf[1] = sd2506_dec_to_bcd(time->minute) & 0x7FU; /* 08H: 分报警 */
|
||||
buf[2] = sd2506_dec_to_bcd(time->hour) & 0x3FU; /* 09H: 时报警 (最高位始终为0) */
|
||||
buf[3] = 0x00U; /* 0AH: 星期报警 */
|
||||
buf[4] = sd2506_dec_to_bcd(time->day) & 0x3FU; /* 0BH: 日报警 */
|
||||
buf[5] = sd2506_dec_to_bcd(time->month) & 0x1FU; /* 0CH: 月报警 */
|
||||
buf[6] = sd2506_dec_to_bcd(time->year - 2000U); /* 0DH: 年报警 */
|
||||
buf[7] = mask; /* 0EH: 报警允许 */
|
||||
|
||||
/* 开启写保护 */
|
||||
ret = sd2506_write_enable();
|
||||
if (ret != SD2506_OK) return ret;
|
||||
|
||||
/* 写入报警寄存器 (07H~0EH) */
|
||||
ret = sd2506_write_regs(SD2506_REG_AL_SEC, buf, 8);
|
||||
if (ret != SD2506_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 != SD2506_OK) return ret;
|
||||
|
||||
/* 关闭写保护 */
|
||||
ret = sd2506_write_disable();
|
||||
if (ret != SD2506_OK) return ret;
|
||||
|
||||
return SD2506_OK;
|
||||
}
|
||||
|
||||
int sd2506_clear_alarm(void)
|
||||
{
|
||||
int ret;
|
||||
uint8_t val;
|
||||
|
||||
/* 读取 CTR1, ARST=1 时自动清除 INTAF */
|
||||
ret = sd2506_read_reg(SD2506_REG_CTR1, &val);
|
||||
if (ret != SD2506_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return SD2506_OK;
|
||||
}
|
||||
|
||||
int sd2506_read_ctr1(uint8_t *val)
|
||||
{
|
||||
if (val == NULL) {
|
||||
return SD2506_ERROR;
|
||||
}
|
||||
|
||||
return sd2506_read_reg(SD2506_REG_CTR1, val);
|
||||
}
|
||||
Reference in New Issue
Block a user