#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); }