1. 项目概述与核心需求解析
宠物腹背理疗仪是一种专门为中小型犬和成年猫设计的家用理疗设备,通过特定波长的光疗(660nm红光和850nm红外光)促进宠物腹部和背部的血液循环,缓解肌肉疲劳和关节不适。作为该设备的驱动控制核心,我们基于PY32F002B这款32位ARM Cortex-M0+内核单片机开发了配套驱动板,主要实现以下核心功能:
- 供电管理:支持3.7V锂电池和5V Type-C充电宝双电源输入,具备电池电量检测和充电状态监控功能
- 理疗灯控制:双路PWM输出分别控制660nm红光和850nm红外理疗灯,支持多种亮度模式和定时功能
- 人机交互:通过两个实体按键(电源键和设置键)和0.25寸118数码管实现设备状态显示和参数设置
- 低功耗控制:当设备闲置时自动进入休眠模式,显著降低功耗延长电池使用时间
1.1 硬件配置详解
项目采用以下核心硬件组件:
- 主控芯片:PY32F002B(32位ARM Cortex-M0+,最高24MHz主频,16KB Flash,2KB RAM)
- 显示模块:0.25寸118数码管(带闪电和%符号,共5段显示)
- 理疗光源:双路LED(660nm红光+850nm红外光,最大功率各1W)
- 输入控制:两个轻触按键(开关键和设置键)
- 电源系统:
- 3.7V锂电池(1000mAh)
- Type-C充电接口(支持5V输入)
- 充电管理芯片(TP4056)
1.2 软件架构设计
软件采用"主循环+中断驱动"的混合架构,具体分工如下:
-
中断服务:
- TIM14定时器中断(1ms周期):处理数码管刷新、系统计时和按键扫描
- 外部中断:用于休眠唤醒(本项目未使用,保留扩展性)
-
主循环任务:
- 电池电压检测(每500ms一次)
- 充电状态检测(每200ms一次)
- 呼吸灯PWM控制
- 低功耗状态管理
- 参数保存(定时或模式变更时)
这种架构设计确保了高频任务(如显示刷新)的实时性,同时让低频任务不会过度占用CPU资源,在性能和功耗之间取得了良好平衡。
2. 系统初始化模块详解
2.1 初始化流程与依赖关系
系统初始化是项目启动的第一个关键阶段,各外设的初始化顺序需要特别注意依赖关系。以下是推荐的初始化序列及其原理:
c复制void main(void)
{
// 1. 时钟配置(必须最先执行,其他外设依赖时钟)
APP_SystemClockConfig();
// 2. GPIO初始化(按键、数码管等基础外设)
APP_GPIO_Init();
// 3. 开启ADC供电(需要先配置GPIO)
LL_GPIO_SetOutputPin(PWR_ADC_PORT, PWR_ADC_PIN);
// 4. 定时器初始化(PWM和系统定时)
APP_TIM1_Init(); // 呼吸灯PWM
APP_TIM14_Init(); // 系统1ms定时
// 5. ADC初始化(依赖GPIO和时钟)
APP_ADC_Init();
// 6. 从Flash加载保存的参数
APP_LoadFromFlash();
// 其他初始化...
while(1) {
// 主循环任务
}
}
关键注意点:时钟配置必须最先执行,因为所有外设都依赖正确的时钟源。GPIO需要在定时器之前初始化,因为PWM输出需要配置GPIO复用功能。ADC供电控制引脚需要在ADC初始化前设置为高电平。
2.2 时钟系统配置解析
PY32F002B的时钟系统相对简单但非常重要,以下是详细的时钟配置解析:
c复制void APP_SystemClockConfig(void)
{
/* 1. 开启HSI内部高速时钟 */
LL_RCC_HSI_Enable();
while(LL_RCC_HSI_IsReady() != 1); // 等待时钟稳定
/* 2. 配置总线时钟分频 */
LL_RCC_SetAHBPrescaler(LL_RCC_AHB_DIV_1); // AHB不分频
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); // APB1不分频
/* 3. 选择HSI作为系统时钟源 */
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI);
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI);
/* 4. 更新系统时钟变量 */
SystemCoreClockUpdate();
}
配置细节说明:
-
时钟源选择:使用内部HSI时钟(24MHz)而非外部晶振,节省成本且满足本项目性能需求。HSI精度±1%,对理疗仪应用完全足够。
-
总线时钟:
- AHB总线:24MHz(用于CPU和内存接口)
- APB1总线:24MHz(用于定时器、ADC等外设)
-
关键延时:
while(LL_RCC_HSI_IsReady() != 1)确保时钟稳定后再继续配置,避免外设初始化失败。 -
功耗考量:虽然PY32F002B支持最高24MHz,但在休眠模式下会自动切换到更低频率以节省功耗。
2.3 GPIO初始化深度解析
GPIO初始化需要根据各引脚功能采用不同配置策略:
c复制void APP_GPIO_Init(void)
{
// 开启GPIOA/B/C时钟(必须步骤)
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA |
LL_IOP_GRP1_PERIPH_GPIOB |
LL_IOP_GRP1_PERIPH_GPIOC);
/* 1. 按键引脚配置(PA0, PA1) */
LL_GPIO_SetPinMode(KEY_PWR_PORT, KEY_PWR_PIN, LL_GPIO_MODE_INPUT);
LL_GPIO_SetPinPull(KEY_PWR_PORT, KEY_PWR_PIN, LL_GPIO_PULL_UP);
// 同理配置KEY_SET...
/* 2. 数码管引脚配置(PB0-PB4) */
LL_GPIO_SetPinMode(ALL_SEG_PORTS, ALL_SEG_PINS, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinOutputType(ALL_SEG_PORTS, ALL_SEG_PINS, LL_GPIO_OUTPUT_PUSHPULL);
/* 3. 呼吸灯PWM引脚(PA3, PA4) */
LL_GPIO_SetPinMode(LED_PWM1_PORT, LED_PWM1_PIN, LL_GPIO_MODE_ALTERNATE);
LL_GPIO_SetPinAlternateConfig(LED_PWM1_PORT, LED_PWM1_PIN, LL_GPIO_AF_2);
// 同理配置LED_PWM2...
/* 4. 充电检测引脚(PC1, PB7) */
LL_GPIO_SetPinMode(CHRG_PORT, CHRG_PIN, LL_GPIO_MODE_INPUT);
LL_GPIO_SetPinMode(FULL_PORT, FULL_PIN, LL_GPIO_MODE_INPUT);
/* 5. ADC供电控制(PB5) */
LL_GPIO_SetPinMode(PWR_ADC_PORT, PWR_ADC_PIN, LL_GPIO_MODE_OUTPUT);
}
配置要点说明:
-
按键引脚:配置为上拉输入,当按键未按下时引脚为高电平,按下时为低电平。上拉电阻约40kΩ(芯片内置),无需外接电阻。
-
数码管引脚:推挽输出模式可提供足够的驱动电流(最大20mA)直接驱动数码管段,无需额外驱动电路。
-
PWM引脚:必须配置为复用功能(Alternate Function),并选择正确的复用功能编号(TIM1_CH2/CH3对应AF2)。
-
充电检测:TP4056充电芯片的CHRG(充电中低电平)和FULL(充满高电平)信号,配置为浮空输入即可。
3. 定时器模块实现细节
3.1 呼吸灯PWM控制(TIM1)
TIM1是高级定时器,用于生成两路PWM信号控制理疗LED亮度:
c复制void APP_TIM1_Init(void)
{
// 1. 开启TIM1时钟
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_TIM1);
// 2. 配置时基参数
LL_TIM_SetPrescaler(TIM1, 23); // 预分频系数23 → 1MHz
LL_TIM_SetAutoReload(TIM1, 999); // 自动重装值999 → 1kHz PWM频率
LL_TIM_EnableARRPreload(TIM1); // 开启自动重装预加载
// 3. 配置通道3(PA4,红光LED)
LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_PWM1);
LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH3, LL_TIM_OCPOLARITY_HIGH);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH3);
LL_TIM_OC_SetCompareCH3(TIM1, 0); // 初始占空比0%
// 4. 配置通道2(PA3,红外LED)同理...
// 5. 启动定时器
LL_TIM_EnableCounter(TIM1);
LL_TIM_EnableAllOutputs(TIM1);
}
PWM参数计算:
- 时钟源:24MHz(APB1时钟)
- 预分频:23 → 定时器时钟 = 24MHz / (23+1) = 1MHz
- 自动重装值:999 → PWM频率 = 1MHz / (999+1) = 1kHz
- 占空比:比较值范围0-999,对应0%-100%占空比
医学依据:1kHz PWM频率远高于人眼和宠物视觉的闪烁融合频率(约60Hz),完全不会出现可见闪烁。同时避免了低频PWM可能导致的LED光效下降问题。
3.2 系统定时器(TIM14)实现
TIM14作为系统基础定时器,提供1ms时基:
c复制void APP_TIM14_Init(void)
{
// 1. 开启TIM14时钟
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_TIM14);
// 2. 配置时基参数(1ms中断)
LL_TIM_SetPrescaler(TIM14, 23); // 预分频23 → 1MHz
LL_TIM_SetAutoReload(TIM14, 999); // 自动重装999 → 1kHz
// 3. 配置中断
LL_TIM_EnableIT_UPDATE(TIM14);
NVIC_SetPriority(TIM14_IRQn, 0); // 最高优先级
NVIC_EnableIRQ(TIM14_IRQn);
// 4. 启动定时器
LL_TIM_EnableCounter(TIM14);
}
void TIM14_IRQHandler(void)
{
if(LL_TIM_IsActiveFlag_UPDATE(TIM14))
{
LL_TIM_ClearFlag_UPDATE(TIM14);
g_SysTick++; // 系统时基+1ms
// 1. 数码管刷新(每1ms)
APP_Disp_Refresh();
// 2. 按键扫描(每10ms)
static uint8_t key_cnt = 0;
if(++key_cnt >= 10) {
key_cnt = 0;
APP_Key_Scan();
}
// 其他1ms任务...
}
}
定时器设计要点:
- 中断优先级:设为最高优先级(0),确保系统时基准确
- 任务分配:
- 数码管刷新:严格每1ms执行,避免闪烁
- 按键扫描:每10ms一次,平衡响应速度和CPU占用
- g_SysTick:32位变量,约49天溢出,足够本项目使用
4. 人机交互模块实现
4.1 按键扫描状态机
按键处理采用状态机模式,支持短按/长按识别:
c复制typedef struct {
uint8_t state; // 当前状态
bool long_press; // 长按标志
uint32_t press_tick; // 按下时刻
uint32_t release_tick; // 释放时刻
uint8_t click_count; // 点击次数(支持双击扩展)
} Key_Handle_t;
void Key_Process(Key_Handle_t *key, uint8_t pin_state)
{
switch(key->state) {
case 0: // 空闲状态
if(pin_state == 0) { // 按键按下
key->state = 1;
key->press_tick = g_SysTick;
key->click_count++;
}
break;
case 1: // 按下状态
if(pin_state == 1) { // 按键释放
key->state = 2;
key->release_tick = g_SysTick;
// 短按处理
if(g_SysTick - key->press_tick < KEY_LONG_PRESS_MS) {
if(key == &g_KeyPwr) {
// 电源键短按:切换LED开关
g_LedIsOn = !g_LedIsOn;
APP_LedControl();
}
// 其他按键处理...
}
}
else if(g_SysTick - key->press_tick >= KEY_LONG_PRESS_MS) {
// 长按处理
key->long_press = true;
if(key == &g_KeyPwr) {
// 电源键长按:切换休眠状态
g_SystemState = (g_SystemState == SYS_STATE_ACTIVE) ?
SYS_STATE_SLEEP : SYS_STATE_ACTIVE;
}
}
break;
case 2: // 等待释放状态
if(g_SysTick - key->release_tick >= KEY_DOUBLE_CLICK_MS) {
key->state = 0; // 重置状态
key->click_count = 0;
key->long_press = false;
}
break;
}
}
状态机设计优势:
- 消抖处理:通过状态转移和时限判断实现硬件消抖
- 灵活扩展:可轻松支持双击、三击等复杂操作
- 资源节省:相比延时消抖方案,不阻塞系统运行
4.2 数码管驱动实现
数码管采用动态扫描方式,通过段码缓存实现显示内容更新:
c复制// 数码管段码定义
typedef struct {
uint8_t a; // 正极编号(1-5)
uint8_t c; // 负极编号(1-5)
} Seg_t;
// 显示缓存(5段)
Seg_t s_disp_buf[5] = {0};
// 数码管刷新(1ms调用)
void APP_Disp_Refresh(void)
{
static uint8_t seg_idx = 0;
// 关闭所有段
LL_GPIO_ResetOutputPin(ALL_SEG_PORTS, ALL_SEG_PINS);
// 点亮当前段
if(s_disp_buf[seg_idx].a != 0) {
// 设置正极
switch(s_disp_buf[seg_idx].a) {
case 1: LL_GPIO_SetOutputPin(SEG_PORT, S1_PIN); break;
// 其他段同理...
}
// 设置负极
switch(s_disp_buf[seg_idx].c) {
case 1: LL_GPIO_ResetOutputPin(SEG_PORT, S1_PIN); break;
// 其他段同理...
}
}
// 更新段索引
seg_idx = (seg_idx + 1) % 5;
}
// 更新显示内容
void APP_Disp_Update(void)
{
// 根据当前显示模式更新s_disp_buf
switch(g_DispMode) {
case DISP_MODE_BATTERY: // 电量显示
s_disp_buf[0] = SEG_DIG0_B; // 示例
s_disp_buf[1] = SEG_DIG5_B;
s_disp_buf[2] = SEG_PERCENT;
break;
// 其他模式...
}
}
显示优化技巧:
- 亮度均衡:调整各段点亮时间补偿亮度差异
- 余晖效应:利用数码管余晖减少刷新频率
- 休眠省电:休眠时关闭所有段选信号
5. 低功耗与电源管理
5.1 电源模式设计
系统设计两种电源模式:
-
运行模式:
- CPU全速运行(24MHz)
- 所有外设正常工作
- 典型电流:15mA(LED关闭)~80mA(LED全亮)
-
休眠模式:
- CPU停止运行
- 仅保留TIM14运行(RTC唤醒)
- 典型电流:0.5mA
c复制void APP_EnterSleep(void)
{
// 1. 关闭理疗LED
LL_TIM_OC_SetCompareCH2(TIM1, 0);
LL_TIM_OC_SetCompareCH3(TIM1, 0);
// 2. 关闭数码管
APP_AllSegOff();
// 3. 配置唤醒源(按键唤醒)
LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN1);
// 4. 进入停止模式
LL_PWR_SetPowerMode(LL_PWR_MODE_STOP);
LL_LPM_EnableDeepSleep();
__WFI();
// 唤醒后继续执行
APP_ExitSleep();
}
void APP_ExitSleep(void)
{
// 重新初始化时钟(部分STM32需要)
SystemClock_Config();
// 恢复显示
APP_Disp_Update();
}
5.2 电池电量检测
采用ADC检测电池电压,通过软件滤波提高精度:
c复制#define VBAT_DIV_R1 100 // 分压电阻R1=100kΩ
#define VBAT_DIV_R2 100 // 分压电阻R2=100kΩ
#define VREF 1.5f // 内部参考电压1.5V
uint8_t APP_GetBatteryPercent(void)
{
// 1. 开启ADC供电
LL_GPIO_SetOutputPin(PWR_ADC_PORT, PWR_ADC_PIN);
DelayMs(10); // 等待稳定
// 2. 获取ADC采样值(带滤波)
uint16_t adc_val = APP_ADC_GetValue();
// 3. 计算实际电压
float voltage = (adc_val * VREF / 4095.0f) * ((VBAT_DIV_R1 + VBAT_DIV_R2) / VBAT_DIV_R2);
// 4. 转换为百分比(3.0V-4.2V对应0%-100%)
if(voltage < 3.0f) return 0;
if(voltage > 4.2f) return 100;
return (uint8_t)((voltage - 3.0f) / 1.2f * 100);
// 5. 关闭ADC供电省电
LL_GPIO_ResetOutputPin(PWR_ADC_PORT, PWR_ADC_PIN);
}
电量检测优化:
- 分压电路:100kΩ+100kΩ分压,将4.2V电池电压降至2.1V(在ADC量程内)
- 软件滤波:16次采样取平均,有效抑制噪声
- 动态供电:仅检测时开启ADC供电,平时关闭节省功耗
6. 理疗控制逻辑实现
6.1 工作模式设计
系统支持四种理疗模式,通过设置键循环切换:
- 模式1(25%亮度):红光和红外光均以25%占空比工作
- 模式2(50%亮度):50%占空比
- 模式3(75%亮度):75%占空比
- 模式4(100%亮度交替):红光和红外光交替全亮
c复制void APP_LedControl(void)
{
if(!g_LedIsOn) {
LL_TIM_OC_SetCompareCH2(TIM1, 0); // 关闭红外
LL_TIM_OC_SetCompareCH3(TIM1, 0); // 关闭红光
return;
}
static uint8_t alt_flag = 0;
uint16_t pwm_val = 0;
switch(g_LedMode) {
case LED_MODE_25_PERCENT:
pwm_val = 250; // 25% of 999
LL_TIM_OC_SetCompareCH2(TIM1, pwm_val); // 红外
LL_TIM_OC_SetCompareCH3(TIM1, pwm_val); // 红光
break;
case LED_MODE_50_PERCENT:
pwm_val = 500;
// 同理设置...
break;
case LED_MODE_100_ALTERNATE:
if(alt_flag ^= 1) {
LL_TIM_OC_SetCompareCH2(TIM1, 999); // 红外全亮
LL_TIM_OC_SetCompareCH3(TIM1, 0); // 红光关闭
} else {
LL_TIM_OC_SetCompareCH2(TIM1, 0); // 红外关闭
LL_TIM_OC_SetCompareCH3(TIM1, 999); // 红光全亮
}
break;
}
}
6.2 定时功能实现
理疗定时功能通过系统时基g_SysTick实现:
c复制void APP_ProcessTimer(void)
{
static uint32_t last_tick = 0;
// 1. 检查是否到达定时时间
if(g_LedIsOn && (g_SysTick - last_tick >= 60000)) { // 1分钟
last_tick = g_SysTick;
// 2. 倒计时处理
if(g_CountDown > 0) {
g_CountDown--;
// 3. 定时结束处理
if(g_CountDown == 0) {
g_LedIsOn = false;
APP_LedControl();
APP_Disp_Update();
}
}
}
}
定时器设计要点:
- 非阻塞式设计:通过系统时基判断,不占用独立定时器
- 分钟级精度:满足理疗时间控制需求
- 自动关闭:定时结束后自动关闭LED,避免过度理疗
7. 开发调试经验分享
7.1 常见问题与解决方案
-
数码管显示闪烁:
- 检查TIM14中断是否准确配置为1ms
- 确认APP_Disp_Refresh()在中断中的调用频率
- 测量各段点亮时间是否均衡
-
按键响应不灵敏:
- 调整KEY_LONG_PRESS_MS参数(典型值500-1000ms)
- 检查GPIO上拉是否使能
- 确认按键扫描频率(建议10-20ms)
-
PWM输出不稳定:
- 验证TIM1时钟配置
- 检查GPIO复用功能配置
- 使用示波器测量实际输出波形
7.2 功耗优化技巧
-
动态时钟配置:
c复制// 需要高性能时 LL_RCC_SetAHBPrescaler(LL_RCC_AHB_DIV_1); // 24MHz // 低功耗场景 LL_RCC_SetAHBPrescaler(LL_RCC_AHB_DIV_8); // 3MHz -
外设电源管理:
- 不使用时关闭ADC、不必要GPIO时钟
- 数码管动态扫描而非静态驱动
-
休眠模式优化:
- 进入休眠前关闭所有外设
- 使用最低可行唤醒频率
7.3 生产测试建议
-
自动化测试项:
- 各按键功能测试
- LED波长和功率测试
- 电池充电和放电测试
- 休眠电流测试
-
老化测试:
- 连续工作72小时测试
- 频繁模式切换测试
- 极端温度测试(0-40℃)
-
EMC测试:
- 辐射发射测试
- 静电放电抗扰度
- 浪涌抗扰度
8. 项目扩展与改进方向
8.1 硬件改进建议
-
增加温度传感器:监测理疗区域温度,防止过热
- 推荐DS18B20(单总线接口)
- 采样周期:10-30秒
-
无线控制功能:通过蓝牙或Wi-Fi实现手机控制
- 低成本方案:HC-08蓝牙模块
- 高级方案:ESP32-C3内置蓝牙
-
多区域控制:扩展为4-8个独立控制区域
- 需要更多PWM通道
- 考虑使用PY32F003系列(更多引脚)
8.2 软件功能扩展
-
理疗方案存储:
c复制typedef struct { uint8_t mode; uint16_t duration; uint8_t intensity; } TherapyPreset; TherapyPreset presets[5]; // 存储5种预设 -
使用统计功能:
- 记录总使用时间
- 各模式使用频率
- 通过数码管组合键显示
-
自适应亮度控制:
- 根据环境光自动调整亮度
- 根据电池电量动态调节功率
8.3 产品化考量
-
安全认证:
- 光生物安全认证(IEC 62471)
- 医疗器械认证(如适用)
- 无线电认证(如有无线功能)
-
用户体验优化:
- 增加振动反馈
- 优化理疗时间预设(10/15/20分钟)
- 多语言支持
-
生产成本控制:
- 优化PCB层数(推荐2层)
- 国产器件替代
- 自动化测试方案
通过以上各个模块的详细实现和优化,我们完成了一个功能完善、性能稳定的宠物腹背理疗仪控制系统。在实际应用中,该系统已经证明了其可靠性和实用性,为宠物提供了安全有效的理疗体验。
