CW32的李总早就就悄悄咪咪的透露了很多的关于这个充电诱骗器的一些:
这次是一个针压住烧录和调试
我还多问了一嘴,为什么会设计这个东西?因为在我看来这个USB电流表这块从几年前兴起,现在广泛流行,再做也是八二年的国军了。
就像他说的,便宜也是优点,假如还有点好用的话,那就更好了。
在硬件上面没有特别难的信号链调理电路,而且早上看见24c01也发了硬件解读,那我就没有必要再写硬件了,那我就写一个固件的解读吧,一般只是用还没有人知道里面的奥秘。
主控芯片 : CW32L011K8 (Cortex-M0+内核,48MHz)
PD控制芯片 : FS2711 (支持多种快充协议)
电流检测 : INA199 电流传感器
显示屏 : LCD彩屏显示
按键 : 支持上下按键操作
这个我也能理解,开发的时候以明文寄存器的方式给出,优点是这个片子支持不少的私有协议,不错!具体也在下面有写。
FS2711.c 和 FS2711.h 实现了对 FS2711 USB PD 诱骗芯片的完整驱动控制。
FS2711 是一款专业的 USB PD 诱骗芯片,支持多种快充协议,通过 I2C 接口与主控通信。
I2C 地址: 0x5A
通信方式: I2C (SCL: PA09, SDA: PA10)
时钟频率: 390kHz
核心函数: FS2711_Write, FS2711_Read
支持的快充协议
USB PD 协议
固定电压 PDO: 支持 5V、9V、12V、15V、20V 等标准档位
PDO 的定义
PDO = Power Data Object(电源数据对象)
是 USB Power Delivery (PD) 协议中的一个基本数据单元。
Source(电源适配器/充电器)通过 Source_Capabilities 消息,把它能提供的电压/电流能力告诉 Sink(设备)。
Sink(比如笔记本、手机)从中选择一个 PDO,再发送 Request 消息,去申请合适的供电。
可编程电源 PPS: 支持电压范围可调,精度更高
在 PD2.0 下,电压只能在 5V、9V、12V、15V、20V 固定档位中选。 PD3.0 引入 PPS(Programmable Power Supply,可编程电源),允许以更小步进调节电压/电流,从而减少不必要的转换损耗,降低热量。
核心函数: FS2711_Open_PD, FS2711_Open_PPS
QC 快充协议
QC2.0: 支持 5V/9V/12V/20V 固定档位
QC3.0: 支持 3.6V~12V 连续可调 (步进200mV)
核心函数: FS2711_Open_QC2, FS2711_Open_QC3
华为快充协议(值回票价了)
FCP: 支持 5V/9V/12V/20V,最高功率18W
SCP: 支持 3.3V+10mV×N 精细调节
Hi-SCP: 支持 5.5V+10mV×N,最高11V
核心函数: FS2711_Open_FCP, FS2711_Open_SCP, FS2711_Open_HISCP
三星 AFC 协议
AFC: 支持 5V/9V/12V,最大输出功率18W (9V/2A)
核心函数: FS2711_Open_AFC
关键寄存器定义
#define FS2711_REG_SR 0x4a // 系统复位寄存器
#define FS2711_REG_MC 0xa0 // 模式设置寄存器
#define FS2711_REG_PR 0x49 // 端口复位寄存器
#define FS2711_REG_DPDM 0x51 // DPDM寄存器
#define FS2711_REG_SS 0x40 // 协议扫描寄存器
#define FS2711_REG_STATE1 0xb2 // 状态寄存器1
核心功能实现
PD 信息获取
FS2711_Get_PD_Information 函数实现:
自动解析 PDO 列表(最多7个),区分固定电压和 PPS 类型;提取电压、电流参数;存储到 FS2711_PDO_List 结构体.
协议切换流程;每个协议启动都遵循标准流程:
-
退出当前协议: FS2711_Enable_Protocol(0) -
选择目标协议: FS2711_Select_Protocol(protocol) -
启动协议: FS2711_Enable_Protocol(1) -
设置电压参数:写入 0xF4-0xF7 寄存器 -
启动诱骗: FS2711_Enable_Voltage_Current() 3. 协议扫描与验证
FS2711_prot_exist : 扫描支持的协议
FS2711_QC2_scan : QC2.0 电压档位验证
FS2711_QC3_scan : QC3.0 电压范围验证
cmp_volt : 电压比较验证 4. 系统控制
FS2711_System_Reset : 系统复位
FS2711_Port_Reset : 端口复位
FS2711_DPDM_Reset : DP/DM 线复位
数据结构
PDO 列表结构
typedef struct {
uint8_t pdo_num; // PDO数量
uint16_t pdo_type[7]; // PDO类型 (0:固定, 1:PPS)
uint16_t pdo_max_volt[7]; // 最大电压 (mV)
uint16_t pdo_min_volt[7]; // 最小电压 (mV)
uint16_t pdo_max_curr[7]; // 最大电流 (mA)
} FS2711_PDO_List_t;
协议库结构
typedef struct {
uint32_t prot_exists; // 支持的协议位图
uint16_t qc_max_volt; // QC最大电压
uint16_t afc_max_volt; // AFC最大电压
uint16_t fcp_max_volt; // FCP最大电压
uint16_t scp_max_volt; // SCP最大电压
uint16_t scp_min_volt; // SCP最小电压
uint16_t scp_max_curr; // SCP最大电流
uint16_t scp_min_curr; // SCP最小电流
uint8_t vivo_max_power; // VIVO最大功率
} FS2711_Lib_t;
使用示例
在 main.c 中的使用:
// 初始化
FS2711_Init();
FS2711_System_Reset();
FS2711_Get_PD_Information();
// 启动不同协议(注释中的示例)
// FS2711_Open_QC2(9000); // QC2.0 9V
// FS2711_Open_AFC(12000); // AFC 12V
// FS2711_Open_FCP(9000); // FCP 9V
// FS2711_Open_QC3(3600); // QC3.0 3.6V
// FS2711_Open_SCP(3300); // SCP 3.3V
// FS2711_Open_HISCP(5500); // Hi-SCP 5.5V
接下来是有意思的采集部分,算是把这颗料也完全用起来了。
INA199 是 TI 公司的高精度电流检测放大器,通过检测分流电阻上的压降来测量电流,具有高精度、低偏移的特点。
ADC.c 和 ADC.h 实现了基于 INA199 电流传感器 的多通道 ADC 采集系统,用于实时监测电源参数。
ADC 采集通道配置 6路 ADC 通道
-
ADC_IN0 (PA01/CH1) : 电流偏置电压检测 -
ADC_IN1 (PA02/CH2) : 电流检测输出 -
ADC_IN2 (PA03/CH3) : 总线电压检测 -
ADC_IN3 (内部Vref) : 1.2V 基准电压 -
ADC_IN4 (PA11/CH13) : NTC 温度传感器 -
ADC_IN5 (内部Ts) : MCU 内部温度传感器 采样配置
ADC 时钟 : 48MHz/8 = 6MHz
采样模式 : 连续转换模式
采样时间 : 54个时钟周期 (约173kHz采样率)
缓冲区大小 : 20个样本 ( ADC_SAMPLE_SIZE )
触发方式 : 1ms定时器触发 (BTIM1)
核心功能实现
电流检测 – get_curr_value
// 电流计算公式
// 差分电压 = |Vout - Vref|
// 电流 = 差分电压 / 分流电阻 / 增益
// 实际实现中使用了校准系数 KT1 = 0.0029
iout = t / 4095 * vdda * 4;
首先差分测量,消除共模干扰;38mA 对应 0.038V 输出电压
增益系数: 50倍 (INA199-A1);分流电阻: 5mΩ
总线电压检测 – get_vbus_value
// 电压分压检测
// R1=33kΩ, R2=220kΩ
// Vbus = V1 * (R1+R2) / R1 = V1 * 253/33 ≈ V1 * 7.67
Vbus = V1 * (33 + 220) / 33;
分压比 : 约 1:7.67,可检测最高约 25V 电压
NTC 温度检测 – get_ntc_value
// NTC 阻值计算
res = (meam_ntc * R1) / (4095 - meam_ntc);
// 查表法温度转换
// 二分查找 NTC_Table[]
10kΩ NTC 热敏电阻
温度范围: 0-100°C
分辨率: 0.1°C
101点查找表,精度高
MCU 内部温度 – get_mcu_ts_value
// 使用芯片内部温度传感器
Temp = ADC_GetTs(AVCCvalue, Temp_Code);
数据处理算法
均值滤波 – Mean_Value_Filter
// 使用芯片内部温度传感器
Temp = ADC_GetTs(AVCCvalue, Temp_Code);
电压校准 – voltage_calibration
// 基于内部1.2V基准的AVCC校准
AVCCvalue = ADC_BgrResult2Avcc(BGR_Code);
return AVCCvalue * 1000;
定时采集系统
BTIM1 定时器配置 – _btim_config
定时周期 : 1ms
预分频 : 8分频 (48MHz/8 = 6MHz)
计数值 : 5999 (6MHz/6000 = 1kHz)
中断触发 : 每1ms触发一次ADC采集
数据缓冲区
uint16_t Volt_Buffer[ADC_SAMPLE_SIZE]; // 电压缓冲区
uint16_t Vout_Buffer[ADC_SAMPLE_SIZE]; // 电流输出缓冲区
uint16_t V12V_Buffer[ADC_SAMPLE_SIZE]; // 基准电压缓冲区
uint16_t Vref_Buffer[ADC_SAMPLE_SIZE]; // 参考电压缓冲区
uint16_t NTC_Buffer[ADC_SAMPLE_SIZE]; // NTC温度缓冲区
uint16_t MCUTs_Buffer[ADC_SAMPLE_SIZE]; // MCU温度缓冲区
NTC 温度查找表
预定义了101个温度点的阻值表 ( NTC_Table[] ),覆盖0-100°C:
-
0°C : 32814Ω -
50°C : 3454Ω -
100°C : 675Ω 使用二分查找算法快速定位温度值,提高转换效率。
工作流程
初始化阶段 :配置GPIO为模拟输入,初始化ADC多通道连续转换,启动BTIM1定时器,使能中断系统
采集阶段 :1ms定时器中断触发,6路ADC同时采集,数据存入循环缓冲区,20个样本滚动更新 计算阶段 :均值滤波处理,电压校准补偿,物理量转换,返回测量结果
测量精度
电流测量 : ±1% (基于INA199精度)
电压测量 : ±0.5% (12位ADC + 校准)
温度测量 : ±0.1°C (NTC查表法)
采样率 : 1kHz (实时性好)
滤波深度 : 20点均值 (噪声抑制)
key没什么好说的,使用的是轮询和事件驱动设计:
uint8_t key = key_get_event();
switch(key) {
case KEY_UP: /* 上键处理 */ break;
case KEY_DOWN: /* 下键处理 */ break;
case KEY_LEFT: /* 左键处理 */ break;
case KEY_RIGHT: /* 右键处理 */ break;
case KEY_OK: /* 确认键处理 */ break;
}
lcd.c 、 lcd.h 、 lcd_init.c 、 lcd_init.h 等文件实现了一个完整的 彩色LCD显示驱动系统 ,支持图形绘制、文字显示和图片显示。
显示屏规格
分辨率 : 240×135 像素 (横屏模式)
颜色深度 : 16位 RGB565 格式
显示方向 : 支持4种方向 ( USE_HORIZONTAL = 3 )
接口类型 : SPI 接口 引脚连接
// 控制引脚定义
#define LCD_RES_Clr() GPIO_WritePin(CW_GPIOB,
GPIO_PIN_0, GPIO_Pin_RESET) // 复位
#define LCD_DC_Clr() GPIO_WritePin(CW_GPIOA,
GPIO_PIN_6, GPIO_Pin_RESET) // 数据/命令
#define LCD_BLK_Clr() GPIO_WritePin(CW_GPIOB,
GPIO_PIN_1, GPIO_Pin_SET) // 背光
#define LCD_CS_Clr() PA04_SETLOW
() // 片选
LCD_Init(); // 初始化LCD
LCD_Fill(0, 0, LCD_W, LCD_H, BLACK); // 清屏
LCD_ShowPicture(58,5,124,125,gImage_cw_logo); // 显示开机Logo
gui.c 和 gui.h 实现了一个完整的 多级菜单用户界面系统 ,为USB PD电源管理提供直观的操作界面。
核心调度器: screen_run()
↓
状态变量: screen_mode
↓
┌─────────────────────────────────────┐
│ 主菜单 (0) → PD模式 (1) → 实时状态 (0x11) │
│ ↓ ↓ │
│ PDO数据 (2) 充电标准 (3) │
└─────────────────────────────────────┘
状态机驱动
核心函数 : screen_run – 主界面调度器
状态变量 : screen_mode – 控制当前显示的界面
刷新机制 : refresh_flag – 控制界面重绘
界面层次结构
主菜单 (screen_mode = 0)
├── PD Power MODE → PD模式选择 (screen_mode = 1)
│ └── 实时状态显示 (screen_mode = 0x11)
├── PDO Data → PDO信息显示 (screen_mode = 2)
└── Charging Standard → 充电标准 (screen_mode = 3)
核心界面功能
主菜单界面 – screen_main
// 三个主要选项
LCD_ShowString(42, 8, "PD Power MODE", ...); // PD电源模式
LCD_ShowString(72, 52, "PDO Data", ...); // PDO数据查看
LCD_ShowString(18, 96, "Charging Standard", ...); // 充电标准
交互逻辑 :
UP/DOWN键 : 菜单项选择 ( sel_id 0-2循环)
OK键 : 进入选中的子菜单
高亮显示 : 当前选项显示为白色,其他为灰色
PD模式选择界面 – screen_pd_mode
固定电压PDO显示
// 三个主要选项
LCD_ShowString(42, 8, "PD Power MODE", ...); // PD电源模式
LCD_ShowString(72, 52, "PDO Data", ...); // PDO数据查看
LCD_ShowString(18, 96, "Charging Standard", ...); // 充电标准
PPS可编程电源显示
else {
LCD_ShowString(10, 4, "PPS", ...); // PPS标识
sprintf(buf, "%.2d.%.2d~%.2d.%.2dV", min_volt, max_volt);
sprintf(buf, "%.2d.%.2dA", max_curr);
}
交互功能 :
UP/DOWN键 : 切换不同PDO ( pdo_id )
OK键 : 选择当前PDO并进入实时状态界面
LEFT键 : 返回主菜单
实时状态显示界面 – screen_power_state_0x11
界面布局
┌─────────────────┬─────────────────┐
│ PD信息 (左上) │ 输出状态 (右上) │
├─────────────────┼─────────────────┤
│ 实时参数 (左下) │ 设置参数 (右下) │
│ V: 电压显示 │ Max U/Set U │
│ A: 电流显示 │ Max C │
│ W: 功率显示 │ Temp/MCU │
└─────────────────┴─────────────────┘
实时数据显示
// 获取实时数据
uint16_t vbus = get_vbus_value(); // 总线电压
uint16_t curr = get_curr_value(); // 输出电流
uint16_t pw = curr*vbus/1000; // 功率计算
float ntc = get_ntc_value()/10.0; // NTC温度
float mcu_temp = get_mcu_ts_value(); // MCU温度
// 显示格式化数据
sprintf(buf, "%.2d.%.3d", vbus/1000, vbus%1000);
LCD_ShowString(47, 27, buf, WHITE, BLACK, 24, 0);
PPS参数调节
数据结构 : struct pps_param_t now_pps 存储PPS参数
调节精度 : 支持多级精度调节 (10V/1V/0.1V/1A/0.1A)
参数限制 : 自动限制在PDO规定的范围内
实时更新 : 参数修改后立即生效
// PPS参数结构
struct pps_param_t {
uint16_t volt; // 设置电压
uint16_t curr; // 设置电流
uint16_t max_volt; // 最大电压
uint16_t min_volt; // 最小电压
uint16_t max_curr; // 最大电流
uint16_t set_type; // 当前调节位 (0-4)
uint16_t set_mode; // 设置模式
};
安全保护机制
// 过压过流保护
if ((vbus > (over_volt + 300)) || (curr > (over_curr + 300))) {
close_mos_out(); // 关闭输出MOS管
}
PDO数据显示界面 – screen_pd_data
分类显示
固定电压PDO : 显示电压和电流规格
PPS PDO : 显示电压范围和最大电流
布局优化 : 最多显示6个固定PDO (3×2布局)
// 固定PDO显示格式
sprintf(buf, "%dV %d.%.2dA", volt/1000, curr/1000, curr/10%100);
// PPS PDO显示格式
sprintf(buf, "%d.%dV~%d.%dV %d.%.2dA",
min_volt/1000, min_volt/100%10,
max_volt/1000, max_volt/100%10,
max_curr/1000, max_curr/10%100);
核心算法 PPS参数调节算法
pps_add_val / pps_dec_val
// 多级精度调节
switch (now_pps.set_type) {
case 0: add_interval = 10000; break; // 10V
case 1: add_interval = 1000; break; // 1V
case 2: add_interval = 100; break; // 0.1V
case 3: add_interval = 1000; break; // 1A
case 4: add_interval = 100; break; // 0.1A
}
设置电压: [1][2].[3][4]V
↑ ↑ ↑ ↑
10V 1V 0.1V 固定位
设置电流: [0][1].[2][3]A
↑ ↑ ↑ ↑
固定 1A 0.1A 固定位
基于 screen_mode 的多级状态切换,按键事件触发界面状态变化
实时参数显示:
V: 12.345 ← 电压 (橙色标识)
A: 02.150 ← 电流 (粉色标识)
W: 26.543 ← 功率 (绿色标识)
看完这些模块,看看整体设计:
应用层: GUI界面 + 业务逻辑
↓
驱动层: FS2711 + INA199 + LCD + Key
↓
硬件抽象层: CW32L011 HAL库
↓
硬件层: CW32L011 MCU
主程序实现 (main.c)
系统配置
#define HCLKFREQ 48000000 // 系统时钟48MHz

// 全局变量
uint16_t g_u16_vbus; // 总线电压
uint16_t g_u16_vref; // 参考电压
uint16_t g_u16_curr; // 输出电流
初始化序列 – main
int32_t main(void) {
// 1. 时钟配置
RCC_Configuration(); // 配置系统时钟
InitTick(HCLKFREQ); // 初始化SysTick
// 2. 外设初始化
LCD_Init(); // LCD显示初始化
LCD_Fill(0, 0, LCD_W, LCD_H, BLACK); // 清屏
key_init(); // 按键初始化
ADC_init(); // ADC采集初始化
// 3. 开机画面
SysTickDelay(100);
LCD_ShowPicture(58,5,124,125,gImage_cw_logo); // 显示开机Logo
// 4. PD芯片初始化
FS2711_Init(); // FS2711芯片初始化
FS2711_System_Reset(); // 系统复位
FS2711_Get_PD_Information(); // 获取PD信息
// 5. GPIO初始化
gpio_init(); // GPIO配置
// 6. 主循环
while (1) {
screen_run(); // GUI界面运行
}
}
分层初始化 : 从底层硬件到上层应用逐步初始化
开机体验 : 显示Logo提升用户体验
PD协议 : 自动获取连接设备的PD能力信息
无限循环 : 主循环专注于GUI界面处理
时钟配置 – RCC_Configuration
void RCC_Configuration(void) {
FLASH_SetLatency(FLASH_Latency_4); // Flash延迟配置
SYSCTRL_HSI_Enable(SYSCTRL_HSIOSC_DIV2); // HSI时钟2分频
}
HSI内部时钟 : 使用内部高速时钟,无需外部晶振
48MHz主频 : 2分频后达到48MHz系统时钟
Flash延迟 : 配置Flash访问延迟适应高频运行
中断系统设计
中断向量表
interrupts_cw32l011.h 定义了完整的中断处理函数:
// 系统级中断
extern void NMI_Handler(void); // 不可屏蔽中断
extern void HardFault_Handler(void); // 硬件错误中断
extern void SVC_Handler(void); // 系统服务调用
extern void PendSV_Handler(void); // 可挂起系统调用
// 外设中断
extern void BTIM1_IRQHandler(void); // 基本定时器1中断
extern void ADC_IRQHandler(void); // ADC转换完成中断
extern void GPIOA_IRQHandler(void); // GPIOA中断
extern void I2C_IRQHandler(void); // I2C通信中断
extern void SPI1_IRQHandler(void); // SPI1通信中断
// ... 其他外设中断
核心中断处理
BTIM1定时器中断 – BTIM1_IRQHandler
void BTIM1_IRQHandler(void) {
static int key_cnt = 0;
if (BTIM_GetITStatus(CW_BTIM1, BTIM_IT_UPDATE)) {
BTIM_ClearITPendingBit(CW_BTIM1, BTIM_IT_UPDATE);
// 1ms执行一次ADC采集
ADC_Result_Scan();
// 10ms执行一次按键扫描
if (key_cnt >= 10) {
key_cnt = 0;
key_scan();
} else {
key_cnt++;
}
}
}
1ms周期 : 定时器每1ms触发一次中断
ADC采集 : 每1ms进行一次多通道ADC采集
按键扫描 : 每10ms进行一次按键状态扫描
时间复用 : 一个定时器同时服务两个功能
整体的启动序列:
1. 复位向量 → main()
2. RCC时钟配置 → 48MHz系统时钟
3. SysTick初始化 → 1ms时基
4. 外设初始化 → LCD/Key/ADC/GPIO
5. 开机Logo显示 → 用户体验
6. FS2711初始化 → PD协议支持
7. 进入主循环 → GUI界面运行
运行时调度
主循环 (连续执行)
├── screen_run() → GUI界面更新
中断服务 (1ms周期)
├── ADC_Result_Scan() → 实时数据采集
└── key_scan() (10ms) → 按键状态检测
BTIM1_IRQHandler 中断处理函数解读
这段代码是 BTIM1 基本定时器的中断服务程序 ,是整个系统的 核心时基中断 ,负责驱动ADC采集和按键扫描两个关键功能。
void BTIM1_IRQHandler(void)
{
static int key_cnt = 0; // 静态变量,按键扫描计数器
if (BTIM_GetITStatus(CW_BTIM1, BTIM_IT_UPDATE)) {
// 1. 清除中断标志
BTIM_ClearITPendingBit(CW_BTIM1, BTIM_IT_UPDATE);
// 2. 每1ms执行ADC采集
ADC_Result_Scan();
// 3. 每10ms执行按键扫描
if (key_cnt >= 10) {
key_cnt = 0;
key_scan();
} else {
key_cnt++;
}
}
}
时序设计
中断周期 : 1ms
BTIM1定时器 : 配置为1ms周期触发中断
系统时基 : 为整个系统提供精确的1ms时间基准
任务调度 :
时间轴: 0ms 1ms 2ms 3ms 4ms 5ms 6ms 7ms 8ms 9ms 10ms
ADC: ● ● ● ● ● ● ● ● ● ● ●
按键: ● ●
ADC采集 : 每1ms执行一次 ( ADC_Result_Scan() )
按键扫描 : 每10ms执行一次 ( key_scan() )
功能实现
中断状态检查
if (BTIM_GetITStatus(CW_BTIM1, BTIM_IT_UPDATE))
检查是否为BTIM1的更新中断
中断标志清除
BTIM_ClearITPendingBit(CW_BTIM1, BTIM_IT_UPDATE);
清除中断挂起标志,防止中断重复触发
ADC数据采集
ADC_Result_Scan();
频率 : 1kHz (每1ms一次)
功能 : 采集6路ADC数据
电压检测 (VBUS)
电流检测 (INA199输出)
参考电压 (1.2V基准)
NTC温度
MCU内部温度
void ADC_Result_Scan(void)
{
static uint8_t cnt; // 静态计数器,循环缓冲区索引
// 6路ADC通道数据采集
ADC_GetSqr0Result(&Vref_Buffer[cnt]); // 通道0: 参考电压
ADC_GetSqr1Result(&Vout_Buffer[cnt]); // 通道1: 电流输出电压
ADC_GetSqr2Result(&Volt_Buffer[cnt]); // 通道2: 总线电压
ADC_GetSqr3Result(&V12V_Buffer[cnt]); // 通道3: 1.2V基准电压
ADC_GetSqr4Result(&NTC_Buffer[cnt]); // 通道4: NTC温度
ADC_GetSqr5Result(&MCUTs_Buffer[cnt]); // 通道5: MCU内部温度
// 循环缓冲区管理
cnt++;
if(cnt >= ADC_SAMPLE_SIZE) { // ADC_SAMPLE_SIZE = 20
cnt = 0; // 循环复位
}
}
实时性 : 保证电源参数的实时监测
6路ADC通道 : 一次中断采集所有关键参数
通道映射
SQR0 → Vref_Buffer → INA199参考电压
SQR1 → Vout_Buffer → INA199输出电压 (电流检测)
SQR2 → Volt_Buffer → 总线电压检测
SQR3 → V12V_Buffer → 1.2V基准电压
SQR4 → NTC_Buffer → 外部NTC温度
SQR5 → MCUTs_Buffer → MCU内部温度
循环缓冲区机制
static uint8_t cnt; // 静态变量保持状态
// 缓冲区结构 (每个缓冲区20个元素)
uint16_t Vref_Buffer[ADC_SAMPLE_SIZE]; // 20个采样点
uint16_t Vout_Buffer[ADC_SAMPLE_SIZE]; // 滑动窗口
uint16_t Volt_Buffer[ADC_SAMPLE_SIZE]; // 实现均值滤波
// ...
20个采样点的滑动平均,通过多点采样降低噪声影响,每1ms更新一个采样点
时间轴: 0ms 1ms 2ms 3ms ... 19ms 20ms 21ms
采集: [0] [1] [2] [3] ... [19] [0] [1]
↑
循环复位
采集频率 : 1kHz (每1ms一次)
缓冲周期 : 20ms (20个采样点)
数据更新 : 连续滚动更新.
按键扫描调度
static int key_cnt = 0; // 静态计数器
if (key_cnt >= 10) {
key_cnt = 0;
key_scan(); // 执行按键扫描
} else {
key_cnt++; // 计数递增
}
key_cnt 在中断间保持状态; 用软件计数器实现10分频; 一个定时器服务两个功能;10ms扫描周期天然提供防抖效果.
性能分析
中断开销
触发频率 : 1kHz (每秒1000次)
执行时间 : 约几十微秒
CPU占用 : < 5% (估算) 实时性保证
ADC更新 : 1ms延迟
按键响应 : 最大10ms延迟
GUI刷新 : 基于实时ADC数据
系统集成
与ADC模块的协作
BTIM1中断 → ADC_Result_Scan() → 更新缓冲区 → GUI显示
与按键模块的协作
BTIM1中断 → key_scan() → key_get_event() → GUI响应
与GUI系统的协作
主循环: screen_run() → 获取实时数据 → 界面更新
中断: BTIM1_IRQHandler() → 数据采集 → 按键检测
_btim_config() 定时器配置
定时器参数配置
static void _btim_config(void)
{
BTIM_TimeBaseInitTypeDef BTIM_TimeBaseInitStruct = {0};
// 1. 使能BTIM时钟
__SYSCTRL_BTIM123_CLK_ENABLE();
// 2. 定时器配置
BTIM_TimeBaseInitStruct.BTIM_Mode = BTIM_MODE_TIMER; // 定时器模式
BTIM_TimeBaseInitStruct.BTIM_Period = 5999; // 计数周期
BTIM_TimeBaseInitStruct.BTIM_Prescaler = 7; // 预分频系数
// 3. 初始化定时器
BTIM_TimeBaseInit(CW_BTIM1, &BTIM_TimeBaseInitStruct);
// 4. 使能中断
BTIM_ITConfig(CW_BTIM1, BTIM_IT_UPDATE, ENABLE);
// 5. 启动定时器
BTIM_Cmd(CW_BTIM1, ENABLE);
}
时钟计算
系统时钟: 48MHz
预分频器: 7+1 = 8
定时器时钟: 48MHz / 8 = 6MHz
计数周期: 5999+1 = 6000
中断周期: 6MHz / 6000 = 1kHz = 1ms
配置解析 :
BTIM_MODE_TIMER : 基本定时器模式
Period = 5999 : 计数到5999后溢出 (实际6000个计数)
Prescaler = 7 : 8分频,降低计数频率
中断使能 : 溢出时触发中断
系统集成关系
调用链路
BTIM1中断 (1ms) → BTIM1_IRQHandler() → ADC_Result_Scan()
↓
更新6个循环缓冲区
↓
get_vbus_value() / get_curr_value()
↓
GUI界面显示
数据流向
硬件ADC → ADC寄存器 → 循环缓冲区 → 均值滤波 → 物理量转换 → GUI显示