feat: 校准电池电压与ADC2充电检测,重构RGB为Bitbang翻转驱动,并修复RTC晶振起振问题
This commit is contained in:
171
Drivers/adc.c
Normal file
171
Drivers/adc.c
Normal file
@@ -0,0 +1,171 @@
|
||||
#include "adc.h"
|
||||
#include "../App/system.h"
|
||||
|
||||
/**
|
||||
* @brief 初始化 STC32G 硬件 ADC 模块
|
||||
* @details 配置 P0.3 (KEY_DOWN) 与 P1.3 (BATT_ADC) 为高阻输入模式,
|
||||
* 上电开启 ADC 内部电源并配置结果对齐格式为右对齐。
|
||||
*/
|
||||
void ADC_Init(void)
|
||||
{
|
||||
// 1. 配置 P0.3 和 P1.3 引脚模式为高阻输入 (M1=1, M0=0)
|
||||
P0M1 |= (1 << 3);
|
||||
P0M0 &= ~(1 << 3);
|
||||
|
||||
P1M1 |= (1 << 3);
|
||||
P1M0 &= ~(1 << 3);
|
||||
|
||||
// 配置 P1.2 (CHRG_DET / ADC2) 为高阻输入模式
|
||||
P1M1 |= (1 << 2);
|
||||
P1M0 &= ~(1 << 2);
|
||||
|
||||
// 2. 开启 ADC 模块主电源 (ADC_POWER = 1)
|
||||
ADC_POWER = 1;
|
||||
|
||||
// 3. 配置 ADCCFG 寄存器:
|
||||
// RESFMT (bit 5) = 1: 转换结果右对齐 (ADC_RES 存高4位, ADC_RESL 存低8位)
|
||||
// 底部 4 位 SPEED: 设置 ADC 转换工作时钟频率 (0x0F 表示最快速度)
|
||||
ADCCFG = 0x2F;
|
||||
|
||||
// 4. 软启动延迟,等待 ADC 模块内部电源稳定
|
||||
Delay_ms(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 单次读取指定 ADC 通道的原始 12 位转换结果
|
||||
* @param channel ADC 通道号 (0~15)
|
||||
* @return u16 12 位 ADC 采样原始值 (0 ~ 4095)
|
||||
*/
|
||||
u16 ADC_ReadRaw(u8 channel)
|
||||
{
|
||||
u16 adc_val = 0;
|
||||
|
||||
// 选择指定通道并保持电源开启
|
||||
ADC_CONTR = (ADC_CONTR & 0xF0) | (channel & 0x0F);
|
||||
|
||||
// 延时等待通道切换电容充放电稳定
|
||||
Delay_us(10);
|
||||
|
||||
// 启动 A/D 转换
|
||||
ADC_START = 1;
|
||||
|
||||
// 阻塞等待转换结束 (ADC_FLAG 置 1)
|
||||
while (!ADC_FLAG);
|
||||
|
||||
// 清除转换完成标志
|
||||
ADC_FLAG = 0;
|
||||
|
||||
// 读取 12 位右对齐结果 (ADC_RES 取低 4 位,结合 ADC_RESL 构成 12 位数据)
|
||||
adc_val = ((u16)(ADC_RES & 0x0F) << 8) | ADC_RESL;
|
||||
|
||||
return adc_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 5 次采样排序取中值滤波算法
|
||||
* @param channel ADC 通道号 (0~15)
|
||||
* @return u16 滤波后的 12 位 ADC 采样结果
|
||||
*/
|
||||
u16 ADC_ReadFiltered(u8 channel)
|
||||
{
|
||||
u16 buf[5];
|
||||
u8 i, j;
|
||||
u16 temp;
|
||||
|
||||
// 连续采集 5 次数据
|
||||
for (i = 0; i < 5; i++) {
|
||||
buf[i] = ADC_ReadRaw(channel);
|
||||
}
|
||||
|
||||
// 冒泡排序法进行升序排列
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 4 - i; j++) {
|
||||
if (buf[j] > buf[j + 1]) {
|
||||
temp = buf[j];
|
||||
buf[j] = buf[j + 1];
|
||||
buf[j + 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 取中间第 3 个值 (索引 2) 作为有效滤波输出,有效去除偶然性噪声尖峰
|
||||
return buf[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检测 USB 充电是否插入
|
||||
* @return bit 0=未充电, 1=充电中
|
||||
* @note TP4054 CHRG 开漏输出:
|
||||
* - 未充电/充满:CHRG 为高阻(被内部上拉拉高)
|
||||
* - 充电中:CHRG 为低电平
|
||||
* 返回值 1 表示正在充电,0 表示未充电或充满。
|
||||
*/
|
||||
bit Is_Charging(void)
|
||||
{
|
||||
u32 val = ADC_ReadFiltered(ADC_CHANNEL_CHRG_DET);
|
||||
u32 det_mv = (val * 3300UL) / 4095UL;
|
||||
|
||||
// 0.8V ~ 1.8V (800mV ~ 1800mV):正在充电
|
||||
if (det_mv >= 800 && det_mv <= 1800) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检测 KEY_DOWN (P0.3) 按键电平是否按下
|
||||
* @details KEY_DOWN 节点未按下时电压约 2.0V,按下后升至约 3.7V。
|
||||
* @return bit 1: 按下, 0: 常态未按下
|
||||
*/
|
||||
bit ADC_IsKeyDown(void)
|
||||
{
|
||||
u16 val = ADC_ReadFiltered(ADC_CHANNEL_KEY_DOWN);
|
||||
// 若采样值超过阈值 KEY_DOWN_ADC_THRESHOLD (3300),判定为按键被按下
|
||||
if (val > KEY_DOWN_ADC_THRESHOLD) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取 P1.3 电池分压计算得出的实际电池电压值 (单位: mV)
|
||||
* @details 硬件电阻分压电路: R15 (10kΩ) 上拉, R16 (22kΩ) 下拉。
|
||||
* 分压公式: V_node = V_bat * (22 / 32) => V_bat = V_node * (32 / 22)
|
||||
* 根据 ADC 采样: V_node(mV) = adc_val * 3300 / 4095
|
||||
* 真实电池电压 V_bat(mV) = (adc_val * 3300 * 32) / (4095 * 22)
|
||||
* @return u16 电池总电压 (mV, 例如 3850 代表 3.85V)
|
||||
*/
|
||||
u16 ADC_GetBatteryVoltage_mV(void)
|
||||
{
|
||||
u32 adc_val = ADC_ReadFiltered(ADC_CHANNEL_BATTERY);
|
||||
u32 vbat_mv;
|
||||
|
||||
// 经用户实测校准:
|
||||
// 当电池真实电压为 4.0V (4000mV) 时,10k/22k 分压点万用表实测为 2.7V,单片机读取的过滤 ADC 原值约为 2357。
|
||||
// 换算公式:V_bat(mV) = adc_val * 4000 / 2357
|
||||
vbat_mv = (adc_val * 4000UL) / 2357UL;
|
||||
|
||||
return (u16)vbat_mv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算电池剩余电量百分比 (0% ~ 100%)
|
||||
* @details 锂电池标准电压区间定义:
|
||||
* 4.20V (4200mV) 对应 100%
|
||||
* 3.30V (3300mV) 对应 0%
|
||||
* @return u8 电量百分比 (0 ~ 100)
|
||||
*/
|
||||
u8 ADC_GetBatteryPercent(void)
|
||||
{
|
||||
u16 vbat = ADC_GetBatteryVoltage_mV();
|
||||
|
||||
if (vbat >= 4200) {
|
||||
return 100;
|
||||
}
|
||||
if (vbat <= 3300) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 线性百分比换算: percent = (vbat - 3300) * 100 / (4200 - 3300)
|
||||
return (u8)(((u32)(vbat - 3300) * 100UL) / 900UL);
|
||||
}
|
||||
61
Drivers/adc.h
Normal file
61
Drivers/adc.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef __ADC_H__
|
||||
#define __ADC_H__
|
||||
|
||||
#include "../App/config.h"
|
||||
|
||||
/* ====== ADC 通道与硬件引脚定义 ====== */
|
||||
#define ADC_CHANNEL_KEY_DOWN 11 // KEY_DOWN (P0.3) 对应 STC32G 的 ADC 通道 11
|
||||
#define ADC_CHANNEL_BATTERY 3 // 电池检测 (P1.3) 对应 STC32G 的 ADC 通道 3
|
||||
#define ADC_CHANNEL_CHRG_DET 2 // 充电检测 (P1.2) 对应 STC32G 的 ADC 通道 2
|
||||
|
||||
/* ====== 判定阈值定义 (基于 12 位 ADC, 0~4095) ====== */
|
||||
// KEY_DOWN 2.2V 常态 (ADC~2700), 3.7V 按下 (ADC 饱和 4095)
|
||||
#define KEY_DOWN_ADC_THRESHOLD 3400 // ADC 采样值超过 3400 判定为按键按下
|
||||
|
||||
/**
|
||||
* @brief 初始化 STC32G 硬件 ADC 模块
|
||||
* @details 开启 ADC 电源、设置结果右对齐格式及采样时钟分频,并将 P0.3 和 P1.3 设置为高阻输入模式
|
||||
*/
|
||||
void ADC_Init(void);
|
||||
|
||||
/**
|
||||
* @brief 读取指定 ADC 通道的原始 12 位转换结果
|
||||
* @param channel ADC 通道号 (0~15)
|
||||
* @return u16 12 位 ADC 采样原始值 (0 ~ 4095)
|
||||
*/
|
||||
u16 ADC_ReadRaw(u8 channel);
|
||||
|
||||
/**
|
||||
* @brief 对指定 ADC 通道进行多重采样与中值滤波
|
||||
* @param channel ADC 通道号 (0~15)
|
||||
* @return u16 滤波后的 12 位 ADC 采样值
|
||||
*/
|
||||
u16 ADC_ReadFiltered(u8 channel);
|
||||
|
||||
/**
|
||||
* @brief 判断 KEY_DOWN (P0.3) 当前是否处于按下状态 (ADC 阈值判定)
|
||||
* @return bit 1: 按下 (电压 ~3.7V), 0: 松开/常态 (电压 ~2.0V)
|
||||
*/
|
||||
bit ADC_IsKeyDown(void);
|
||||
|
||||
/**
|
||||
* @brief 获取 P1.3 电池分压测得的实际电压 (毫伏 mV)
|
||||
* @return u16 电池电压,单位 mV (例如 3850 代表 3.85V)
|
||||
*/
|
||||
u16 ADC_GetBatteryVoltage_mV(void);
|
||||
|
||||
/**
|
||||
* @brief 获取电池电量百分比 (0% ~ 100%)
|
||||
* @return u8 电量百分比 (0 ~ 100)
|
||||
*/
|
||||
u8 ADC_GetBatteryPercent(void);
|
||||
|
||||
/**
|
||||
* @brief 检测 USB 充电是否插入
|
||||
* @return bit 0=未充电, 1=充电中
|
||||
* @note TP4054 CHRG 引脚开漏输出,低电平时表示正在充电。
|
||||
* 需要 MCU 内部上拉或外部上拉电阻。
|
||||
*/
|
||||
bit Is_Charging(void);
|
||||
|
||||
#endif // __ADC_H__
|
||||
434
Drivers/pcf8563.c
Normal file
434
Drivers/pcf8563.c
Normal file
@@ -0,0 +1,434 @@
|
||||
#include "pcf8563.h"
|
||||
#include "../App/system.h"
|
||||
|
||||
// 动态引脚对调全局标志 (0: 默认 SCL=P3.2, SDA=P3.3; 1: 对调 SCL=P3.3, SDA=P3.2)
|
||||
volatile u8 i2c_swapped = 0;
|
||||
|
||||
static void SCL_W(u8 val)
|
||||
{
|
||||
if (!i2c_swapped) P32 = val;
|
||||
else P33 = val;
|
||||
}
|
||||
|
||||
static void SDA_W(u8 val)
|
||||
{
|
||||
if (!i2c_swapped) P33 = val;
|
||||
else P32 = val;
|
||||
}
|
||||
|
||||
static u8 SDA_R(void)
|
||||
{
|
||||
if (!i2c_swapped) return P33;
|
||||
else return P32;
|
||||
}
|
||||
|
||||
// 动态延时参数 (单位: us)
|
||||
volatile u8 i2c_delay_us = 30;
|
||||
|
||||
/**
|
||||
* @brief I2C 产生起始信号 (START)
|
||||
*/
|
||||
static void I2C_Start(void)
|
||||
{
|
||||
SDA_W(1);
|
||||
SCL_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
SDA_W(0);
|
||||
Delay_us(i2c_delay_us);
|
||||
SCL_W(0);
|
||||
Delay_us(i2c_delay_us);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C 产生停止信号 (STOP)
|
||||
*/
|
||||
static void I2C_Stop(void)
|
||||
{
|
||||
SDA_W(0);
|
||||
Delay_us(i2c_delay_us);
|
||||
SCL_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
SDA_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C 发送应答/非应答信号
|
||||
* @param ack 1: ACK(0), 0: NACK(1)
|
||||
*/
|
||||
static void I2C_SendAck(bit ack)
|
||||
{
|
||||
SDA_W(!ack);
|
||||
Delay_us(i2c_delay_us);
|
||||
SCL_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
SCL_W(0);
|
||||
Delay_us(i2c_delay_us);
|
||||
SDA_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C 等待从机应答 (Wait ACK)
|
||||
* @return bit 1: 收到应答, 0: 未收到应答
|
||||
*/
|
||||
static bit I2C_WaitAck(void)
|
||||
{
|
||||
u8 retry = 0;
|
||||
SDA_W(1); // 释放 SDA 接收 ACK
|
||||
Delay_us(i2c_delay_us);
|
||||
SCL_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
while (SDA_R()) {
|
||||
retry++;
|
||||
Delay_us(5);
|
||||
if (retry > 250) {
|
||||
I2C_Stop();
|
||||
return 0; // 超时未收到 ACK 应答,立即优雅退出,防止死锁
|
||||
}
|
||||
}
|
||||
SCL_W(0);
|
||||
Delay_us(i2c_delay_us);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C 发送单字节数据
|
||||
* @param dat 待发送 of 字节数据
|
||||
*/
|
||||
static void I2C_SendByte(u8 dat)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
SDA_W((dat & 0x80) ? 1 : 0);
|
||||
Delay_us(i2c_delay_us);
|
||||
SCL_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
SCL_W(0);
|
||||
Delay_us(i2c_delay_us);
|
||||
dat <<= 1;
|
||||
}
|
||||
SDA_W(1); // 释放 SDA 接收 ACK
|
||||
Delay_us(i2c_delay_us);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C 读取单字节数据
|
||||
* @return u8 接收到的字节数据
|
||||
*/
|
||||
static u8 I2C_ReadByte(void)
|
||||
{
|
||||
u8 i, dat = 0;
|
||||
SDA_W(1); // 设为输入
|
||||
Delay_us(i2c_delay_us);
|
||||
for (i = 0; i < 8; i++) {
|
||||
dat <<= 1;
|
||||
SCL_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
if (SDA_R()) {
|
||||
dat |= 0x01;
|
||||
}
|
||||
SCL_W(0);
|
||||
Delay_us(i2c_delay_us);
|
||||
}
|
||||
return dat;
|
||||
}
|
||||
|
||||
/* ====== BCD 与二进制 10 进制互转辅助函数 ====== */
|
||||
|
||||
static u8 BCD2DEC(u8 bcd)
|
||||
{
|
||||
return ((bcd >> 4) * 10) + (bcd & 0x0F);
|
||||
}
|
||||
|
||||
static u8 DEC2BCD(u8 dec)
|
||||
{
|
||||
return (((dec / 10) << 4) | (dec % 10));
|
||||
}
|
||||
|
||||
static void I2C_ReleaseBus(void)
|
||||
{
|
||||
u8 i;
|
||||
SDA_W(1);
|
||||
SCL_W(1);
|
||||
Delay_us(30);
|
||||
|
||||
// 如果 SDA 被拉低,说明总线被死锁占用
|
||||
if (SDA_R() == 0) {
|
||||
for (i = 0; i < 9; i++) {
|
||||
SCL_W(0);
|
||||
Delay_us(30);
|
||||
SCL_W(1);
|
||||
Delay_us(30);
|
||||
if (SDA_R() == 1) {
|
||||
break; // 从机已释放总线,提前退出
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 强制产生停止条件恢复空闲
|
||||
SDA_W(0);
|
||||
Delay_us(30);
|
||||
SCL_W(1);
|
||||
Delay_us(30);
|
||||
SDA_W(1);
|
||||
Delay_us(30);
|
||||
}
|
||||
|
||||
/* ====== PCF8563 驱动核心接口 ====== */
|
||||
|
||||
bit PCF8563_Init(void)
|
||||
{
|
||||
// 1. 默认引脚尝试 (SCL=P3.2, SDA=P3.3)
|
||||
i2c_swapped = 0;
|
||||
I2C_ReleaseBus(); // 强力总线死锁释放
|
||||
|
||||
I2C_Start();
|
||||
I2C_SendByte(PCF8563_ADDR_WRITE);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[RTC] Detected Normal pin mapping (SCL=P32, SDA=P33)\r\n");
|
||||
} else {
|
||||
// 2. 尝试对调引脚 (SCL=P3.3, SDA=P3.2)
|
||||
I2C_Stop();
|
||||
i2c_swapped = 1;
|
||||
I2C_ReleaseBus(); // 强力总线死锁释放
|
||||
|
||||
I2C_Start();
|
||||
I2C_SendByte(PCF8563_ADDR_WRITE);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[RTC] Detected SWAPPED pin mapping (SCL=P33, SDA=P32)!\r\n");
|
||||
} else {
|
||||
// 恢复默认,防止干扰
|
||||
i2c_swapped = 0;
|
||||
Uart_SendString("[RTC-ERR] PCF8563 Offline on both normal/swapped pins!\r\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
I2C_SendByte(REG_CTRL_STATUS1);
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
I2C_SendByte(0x00); // 正常模式,不停止芯片计数
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
I2C_Stop();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PCF8563_ScanDiagnostic(void)
|
||||
{
|
||||
u16 addr;
|
||||
u8 found = 0;
|
||||
|
||||
Uart_SendString("[I2C-SCAN] --- 1. Scanning with default pins (SCL=P32, SDA=P33) ---\r\n");
|
||||
i2c_swapped = 0;
|
||||
for (addr = 0x02; addr < 0x100; addr += 2) {
|
||||
I2C_Start();
|
||||
I2C_SendByte((u8)addr);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[I2C-SCAN] Device Found: 0x");
|
||||
Uart_SendHex8((u8)addr);
|
||||
Uart_SendString("\r\n");
|
||||
found = 1;
|
||||
}
|
||||
I2C_Stop();
|
||||
Delay_ms(2);
|
||||
}
|
||||
|
||||
Uart_SendString("[I2C-SCAN] --- 2. Scanning with SWAPPED pins (SCL=P33, SDA=P32) ---\r\n");
|
||||
i2c_swapped = 1;
|
||||
for (addr = 0x02; addr < 0x100; addr += 2) {
|
||||
I2C_Start();
|
||||
I2C_SendByte((u8)addr);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[I2C-SCAN] Device Found: 0x");
|
||||
Uart_SendHex8((u8)addr);
|
||||
Uart_SendString("\r\n");
|
||||
found = 1;
|
||||
}
|
||||
I2C_Stop();
|
||||
Delay_ms(2);
|
||||
}
|
||||
|
||||
// 扫描结束后,根据 init 侦测结果重新绑定,此处临时归零
|
||||
i2c_swapped = 0;
|
||||
Uart_SendString("[I2C-SCAN] Dual-mode Address Scan Completed!\r\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从 PCF8563 读取当前时间
|
||||
*/
|
||||
bit PCF8563_ReadTime(u8 *hour, u8 *min, u8 *sec)
|
||||
{
|
||||
u8 raw_sec, raw_min, raw_hour;
|
||||
|
||||
I2C_Start();
|
||||
I2C_SendByte(PCF8563_ADDR_WRITE);
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
// 设置起始读取地址为 0x02 (REG_VL_SECONDS)
|
||||
I2C_SendByte(REG_VL_SECONDS);
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
// 重新产生 Start 读取数据
|
||||
I2C_Start();
|
||||
I2C_SendByte(PCF8563_ADDR_READ);
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
raw_sec = I2C_ReadByte();
|
||||
I2C_SendAck(1); // 发送 ACK
|
||||
|
||||
raw_min = I2C_ReadByte();
|
||||
I2C_SendAck(1); // 发送 ACK
|
||||
|
||||
raw_hour = I2C_ReadByte();
|
||||
I2C_SendAck(0); // 发送 NACK 结束连续读取
|
||||
|
||||
I2C_Stop();
|
||||
|
||||
// 解析 BCD 数据 (掩码屏蔽无效位)
|
||||
*sec = BCD2DEC(raw_sec & 0x7F);
|
||||
*min = BCD2DEC(raw_min & 0x7F);
|
||||
*hour = BCD2DEC(raw_hour & 0x3F);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将时间精准写入 PCF8563 RTC 芯片
|
||||
*/
|
||||
bit PCF8563_WriteTime(u8 hour, u8 min, u8 sec)
|
||||
{
|
||||
I2C_Start();
|
||||
I2C_SendByte(PCF8563_ADDR_WRITE);
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
// 定位写入起始寄存器 0x02
|
||||
I2C_SendByte(REG_VL_SECONDS);
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
// 写入秒
|
||||
I2C_SendByte(DEC2BCD(sec & 0x7F));
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
// 写入分
|
||||
I2C_SendByte(DEC2BCD(min & 0x7F));
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
// 写入时
|
||||
I2C_SendByte(DEC2BCD(hour & 0x3F));
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
I2C_Stop();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bit PCF8563_TryReadDiagnostic(void)
|
||||
{
|
||||
u8 val = 0;
|
||||
|
||||
Uart_SendString("[I2C-DIAG] --- Test 1: Trying 30us Delay (30kHz) ---\r\n");
|
||||
i2c_delay_us = 30;
|
||||
|
||||
i2c_swapped = 0;
|
||||
I2C_ReleaseBus();
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA2);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[I2C-DIAG] (swapped=0, 30us) 0xA2 ACK OK\r\n");
|
||||
I2C_SendByte(0x00);
|
||||
if (I2C_WaitAck()) {
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA3);
|
||||
if (I2C_WaitAck()) {
|
||||
val = I2C_ReadByte();
|
||||
I2C_SendAck(0);
|
||||
I2C_Stop();
|
||||
Uart_SendString("[I2C-DIAG] (swapped=0, 30us) Success! Reg 0x00=0x");
|
||||
Uart_SendHex8(val);
|
||||
Uart_SendString("\r\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
I2C_Stop();
|
||||
|
||||
i2c_swapped = 1;
|
||||
I2C_ReleaseBus();
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA2);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[I2C-DIAG] (swapped=1, 30us) 0xA2 ACK OK\r\n");
|
||||
I2C_SendByte(0x00);
|
||||
if (I2C_WaitAck()) {
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA3);
|
||||
if (I2C_WaitAck()) {
|
||||
val = I2C_ReadByte();
|
||||
I2C_SendAck(0);
|
||||
I2C_Stop();
|
||||
Uart_SendString("[I2C-DIAG] (swapped=1, 30us) Success! Reg 0x00=0x");
|
||||
Uart_SendHex8(val);
|
||||
Uart_SendString("\r\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
I2C_Stop();
|
||||
|
||||
Uart_SendString("[I2C-DIAG] --- Test 2: Retrying with ultra-slow 200us Delay (2.5kHz) ---\r\n");
|
||||
i2c_delay_us = 200;
|
||||
|
||||
i2c_swapped = 0;
|
||||
I2C_ReleaseBus();
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA2);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[I2C-DIAG] (swapped=0, 200us) 0xA2 ACK OK\r\n");
|
||||
I2C_SendByte(0x00);
|
||||
if (I2C_WaitAck()) {
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA3);
|
||||
if (I2C_WaitAck()) {
|
||||
val = I2C_ReadByte();
|
||||
I2C_SendAck(0);
|
||||
I2C_Stop();
|
||||
Uart_SendString("[I2C-DIAG] (swapped=0, 200us) Success! Reg 0x00=0x");
|
||||
Uart_SendHex8(val);
|
||||
Uart_SendString("\r\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
I2C_Stop();
|
||||
|
||||
i2c_swapped = 1;
|
||||
I2C_ReleaseBus();
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA2);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[I2C-DIAG] (swapped=1, 200us) 0xA2 ACK OK\r\n");
|
||||
I2C_SendByte(0x00);
|
||||
if (I2C_WaitAck()) {
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA3);
|
||||
if (I2C_WaitAck()) {
|
||||
val = I2C_ReadByte();
|
||||
I2C_SendAck(0);
|
||||
I2C_Stop();
|
||||
Uart_SendString("[I2C-DIAG] (swapped=1, 200us) Success! Reg 0x00=0x");
|
||||
Uart_SendHex8(val);
|
||||
Uart_SendString("\r\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
I2C_Stop();
|
||||
|
||||
// 恢复默认设置
|
||||
i2c_swapped = 0;
|
||||
i2c_delay_us = 30;
|
||||
Uart_SendString("[I2C-DIAG] Both 30us and 200us diagnostic trial failed.\r\n");
|
||||
return 0;
|
||||
}
|
||||
59
Drivers/pcf8563.h
Normal file
59
Drivers/pcf8563.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef __PCF8563_H__
|
||||
#define __PCF8563_H__
|
||||
|
||||
#include "../App/config.h"
|
||||
|
||||
/* ====== PCF8563 器件地址与寄存器定义 ====== */
|
||||
#define PCF8563_ADDR_READ 0xA3 // PCF8563 读地址 (0xA2 | 0x01)
|
||||
#define PCF8563_ADDR_WRITE 0xA2 // PCF8563 写地址 (0xA2)
|
||||
|
||||
#define REG_CTRL_STATUS1 0x00 // 控制/状态寄存器 1
|
||||
#define REG_CTRL_STATUS2 0x01 // 控制/状态寄存器 2
|
||||
#define REG_VL_SECONDS 0x02 // 秒寄存器 (bit7 为 VL 保证标志)
|
||||
#define REG_MINUTES 0x03 // 分钟寄存器
|
||||
#define REG_HOURS 0x04 // 小时寄存器
|
||||
#define REG_DAYS 0x05 // 日寄存器
|
||||
#define REG_WEEKDAYS 0x06 // 星期寄存器
|
||||
#define REG_MONTHS 0x07 // 月份寄存器 (bit7 为 C 世纪位)
|
||||
#define REG_YEARS 0x08 // 年份寄存器
|
||||
|
||||
/**
|
||||
* @brief 初始化 PCF8563 RTC 芯片
|
||||
* @details 初始化 I2C 管脚高电平,并检测芯片启动状态
|
||||
* @return bit 1: 芯片响应正常, 0: 通讯异常
|
||||
*/
|
||||
bit PCF8563_Init(void);
|
||||
|
||||
/**
|
||||
* @brief 从 PCF8563 芯片读取当前时间 (时、分、秒)
|
||||
* @param hour 小时指针 (0~23)
|
||||
* @param min 分钟指针 (0~59)
|
||||
* @param sec 秒钟指针 (0~59)
|
||||
* @return bit 1: 读取成功, 0: 通讯失败
|
||||
*/
|
||||
bit PCF8563_ReadTime(u8 *hour, u8 *min, u8 *sec);
|
||||
|
||||
/**
|
||||
* @brief 将指定的时间 (时、分、秒) 写入 PCF8563 芯片
|
||||
* @param hour 小时 (0~23)
|
||||
* @param min 分钟 (0~59)
|
||||
* @param sec 秒钟 (0~59)
|
||||
* @return bit 1: 写入成功, 0: 通讯失败
|
||||
*/
|
||||
bit PCF8563_WriteTime(u8 hour, u8 min, u8 sec);
|
||||
|
||||
/**
|
||||
* @brief I2C 总线器件扫描诊断函数
|
||||
* @details 逐个发送 I2C 写地址,检测总线上是否有响应的器件,并在串口打印发现的设备地址
|
||||
*/
|
||||
void PCF8563_ScanDiagnostic(void);
|
||||
|
||||
/**
|
||||
* @brief 尝试直接用最底层时序试读 RTC 控制寄存器并输出 ACK 调试日志
|
||||
*/
|
||||
bit PCF8563_TryReadDiagnostic(void);
|
||||
|
||||
// 动态引脚对调标志 (由 pcf8563.c 维护,0: 默认, 1: 互换)
|
||||
extern volatile u8 i2c_swapped;
|
||||
|
||||
#endif // __PCF8563_H__
|
||||
Reference in New Issue
Block a user