/*
 * 模块名称：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"

extern I2C_HandleTypeDef hi2c1;

/* ======================== 内部辅助函数 ======================== */

/*
 * 函数功能：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
 * �?�?值：BCD 编码�? * 限定条件：输入值在 0 - 99 范围�? * 函数说明：十位存入高 4 位，个位存入�?4 �? */
uint8_t sd2506_dec_to_bcd(uint8_t dec) {
    return ((dec / 10) << 4) | (dec % 10);
}

/*
 * 函数功能：写单字节寄存器
 * 入口参数：reg - 寄存器地址                                                          uint8_t  0x00 - 0x79
 *           val - 写入数据                                                            uint8_t  0x00 - 0xFF
 * 返回值：0-成功，2-I2C 错误                                                          int
 * 限定条件：I2C 外设已初始化
 * 函数说明：通过 I2C 向指定地址写入 1 字节数据
 * 限定条件：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  0x00 - 0x79
 *           p_val - 读取值指针                                                        uint8_t*（非空）
 * 返回值：0-成功，2-I2C 错误                                                          int
 * 限定条件：I2C 外设已初始化，p_val 为非空指针
 * 函数说明：通过 I2C 从指定地址读取 1 字节数据
 * 限定条件：I2C 外设已初始化，val 为非空指针函数说明：通过 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  0x00 - 0x79
 *           p_data - 数据缓冲区指针                                                    uint8_t*（非空）
 *           len   - 数据长度                                                          uint8_t  1 - N
 * 返回值：0-成功，2-I2C 错误                                                          int
 * 限定条件：I2C 外设已初始化，p_data 为非空指针
 * 函数说明：从 reg 开始连续写入 len 字节数据
 * 限定条件：I2C 外设已初始化，data 为非空指针
 */
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  0x00 - 0x79
 *           p_data - 数据缓冲区指针                                                    uint8_t*（非空）
 *           len   - 数据长度                                                          uint8_t  1 - N
 * 返回值：0-成功，2-I2C 错误                                                          int
 * 限定条件：I2C 外设已初始化，p_data 为非空指针
 * 函数说明：从 reg 开始连续读取 len 字节数据
 * 限定条件：I2C 外设已初始化，data 为非空指针
 */
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;
}

/*
 * 函数功能：开启写保护（允许写入 00H~71H 寄存器）
 * 入口参数：无
 * 返回值：0 - 成功 2 - I2C 错误
 * 限定条件：I2C 外设已初始化
 * 函数说明：顺序：先写 WRTC1=1, 再写 WRTC2=1 + WRTC3=1
 */
static int sd2506_write_enable(void) {
    int ret = 0;

    /* 先置 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 寄存器）
 * 入口参数：无
 * 返回值：0 - 成功 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-成功，2-I2C 错误                                                          int
 * 限定条件：CubeMX 已完成 I2C1 初始化（PB6-SCL, PB7-SDA）
 * 函数说明：1. 读取芯片 ID 验证通信
 *           2. 上电重置充电寄存器 (0x18=0x82)
 *           3. 配置 24 小时制、开自动复位
 * 限定条件：CubeMX 已完成I2C1 初始化
 * 函数说明：读取芯片 ID 验证通信
 *           2. 上电重置充电寄存器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;
    }

    /* 上电重置充电寄存�?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;
}

/*
 * 函数功能：设�?RTC 时间日期
 * 入口参数：p_time - 时间结构体指针，包含要设置的时间
 * 返回值：0 - 成功�?2 - I2C 错误
 * 限定条件：sd2506_init() 已调�?
 * 函数说明�?. 先开写保�?
 *           2. 一次性写�?7 字节时间数据 (00H~06H)
 *           3. 关闭写保�?
 */
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 �? */
    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 != 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;
}

/*
 * 函数功能：读取 RTC 时间日期
 * 入口参数：p_time - 输出时间结构体指针                                                sd2506_time_t*（非空）
 * 返回值：0-成功，2-I2C 错误                                                          int
 * 限定条件：sd2506_init() 已调用成功
 * 函数说明：一次读取 7 字节时间数据 (00H~06H)，所有实时数据被锁存避免错误
 *  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;
    }

    /* 一次读�?7 字节时间数据 (00H~06H)
     * 手册说明: 读取时所有实时数据被锁存，避免错�?*/
    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: �?*/
    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 SD2506_OK;
}

/*
 * 函数功能：读取内部温度
 * 入口参数：p_temp - 输出温度值指针                                                    int8_t*（非空）
 * 返回值：0-成功，2-I2C 错误                                                          int
 * 限定条件：sd2506_init() 已调用成功
 * 函数说明：读取寄存器 0x16，bit7 为符号位
 *  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 为符号位, 其余为温度�?*/
    if (val & 0x80U) {
        /* 负温�? 取补�?*/
        *p_temp = (int8_t)(val | 0xF0U);
    } else {
        /* 正温�?*/
        *p_temp = (int8_t)(val & 0x7FU);
    }

    return SD2506_OK;
}

/*
 * 函数功能：读取电池电压（毫伏）
 * 入口参数：p_voltage - 输出电压指针                                                    uint16_t*（非空）
 * 返回值：0-成功，2-I2C 错误                                                          int
 * 限定条件：sd2506_init() 已调用成功
 * 函数说明：组合 9 位值 (bit8=BAT8_VAL, bit7~0=BAT_VL)，转换为毫伏
 *  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) �?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;
    *p_voltage = raw * 10;  /* 转换为毫�?(raw 单位 0.01V) */

    return SD2506_OK;
}

/*
 * 函数功能：读取 8 字节芯片 ID
 * 入口参数：p_id - 输出缓冲区指针                                                      uint8_t[8]（非空）
 * 返回值：0-成功，2-I2C 错误                                                          int
 * 限定条件：sd2506_init() 已调用成功
 * 函数说明：读取寄存器 0x72~0x79
 *  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);
}

/*
 * 函数功能：读取 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: 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);
}

/*
 * 函数功能：写入 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: 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 无需开写保护即可写�?(写保护仅�?00H~71H 有效, SRAM �?2CH~71H)
     * 但为安全起见, SRAM 写入也走写保护流�?*/
    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;
}

/*
 * 函数功能：设置闹钟中断
 * 入口参数：p_time - 报警时间结构体指针                                                sd2506_time_t*（非空）
 *           mask  - 报警匹配掩码                                                       uint8_t
 * 返回值：0-成功，-2=I2C 错误                                                          int
 * 限定条件：sd2506_init() 已调用成功
 * 函数说明：写入 8 字节报警数据 (07H~0EH)，使能 CTR2.INTAE+INTS0+IM
 *  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: 秒报�?*/
    buf[1] = sd2506_dec_to_bcd(p_time->minute) & 0x7FU;     /* 08H: 分报�?*/
    buf[2] = sd2506_dec_to_bcd(p_time->hour) & 0x3FU;       /* 09H: 时报�?(最高位始终�?) */
    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 != 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;
}

/*
 * 函数功能：清除闹钟中断标志
 * 入口参数：无
 * 返回值：0-成功，-2=I2C 错误                                                          int
 * 限定条件：sd2506_init() 已调用成功；ARST=1 时自动清除 INTAF
 * 函数说明：读取 CTR1 即可清除 INTAF 标志
 *  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 时自动清�?INTAF */
    ret = sd2506_read_reg(SD2506_REG_CTR1, &val);
    if (ret != SD2506_OK) {
        return ret;
    }

    return SD2506_OK;
}

/*
 * 函数功能：读取控制寄存器 1 (CTR1)
 * 入口参数：p_val - 输出值指针                                                        uint8_t*（非空）
 * 返回值：0-成功，-2=I2C 错误                                                          int
 * 限定条件：sd2506_init() 已调用成功
 *  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);
}

