1. 项目概述
沁恒CH32系列单片机作为国产RISC-V架构的微控制器,在物联网和智能家居领域有着广泛应用。今天我要分享的是一个基于CH32实现的一键开关机功能设计方案,这个方案特别适合需要低功耗和用户友好操作的产品场景。
在实际产品开发中,我们经常遇到这样的需求:设备需要具备物理按键控制开关机,同时还要支持通过同一个按键实现模式切换等功能。传统方案往往需要复杂的电路设计或者占用多个IO口,而本方案仅需两个GPIO引脚(一个输入检测,一个输出控制)就能实现完整的开关机逻辑。
这个设计有以下几个核心特点:
- 硬件电路简单可靠,仅需少量外围元件
- 软件实现采用非阻塞式轮询,不影响主程序运行
- 支持长短按区分,实现开关机和模式切换双功能
- 低功耗设计,关机状态下几乎不耗电
- 代码模块化设计,方便移植到其他CH32系列芯片
2. 硬件电路设计
2.1 核心元件选型
一键开关机电路的核心在于电源管理部分的设计。我们选用了以下关键元件:
| 元件 | 型号/参数 | 功能说明 | 选型理由 |
|---|---|---|---|
| Q4 | AO3401A (P-MOS) | 主电源开关管 | 低导通电阻(45mΩ),适合3-5V系统 |
| Q6 | S8050 (NPN) | 驱动三极管 | 通用型,成本低,驱动电流足够 |
| D1/D2 | 1N5819W | 肖特基二极管 | 低正向压降(0.3V),快速开关特性 |
| R10/R11/R12 | 10K/1K/10K | 限流分压电阻 | 常规0805封装,满足功耗需求 |
| C5 | 100nF | 滤波电容 | 消除按键抖动,稳定电路状态 |
2.2 电路工作原理详解
2.2.1 关机状态分析
在初始关机状态下,整个电路的电流路径和关键点电压如下:
code复制VBAT(3.7V) → R10(10K) → Q4栅极
↘
R12(10K) → GND
此时:
- Q4栅极电压:约3.7V (Vgs=0V,PMOS截止)
- Q6基极电压:0V (通过R12下拉)
- 系统VCC:0V (Q4截止)
- 静态电流:仅R10/R12分压回路的微安级漏电流
这种设计确保了关机状态下的超低功耗,特别适合电池供电设备。
2.2.2 开机过程电流路径
当用户长按按键时,电流路径发生变化:
code复制VBAT → R10 → D1 → 按键 → GND
此时:
- D1正向导通,压降约0.3V
- Q4栅极电压被拉低至约0.3V (Vgs=-3.4V,PMOS导通)
- VCC输出建立,系统开始供电
- 单片机初始化后拉高PC3,使Q6导通,形成自锁
2.2.3 自锁维持机制
自锁状态的维持依赖于Q6的导通:
code复制VBAT → R11 → Q6 CE结 → GND
↘
Q4栅极 → GND
此时即使松开按键,Q4栅极仍通过Q6保持低电平,系统持续供电。这种设计避免了需要持续按住按键的不便。
3. 软件实现细节
3.1 系统状态机设计
整个按键处理逻辑采用状态机实现,主要状态转换如下:
mermaid复制stateDiagram-v2
[*] --> POWER_OFF
POWER_OFF --> POWER_ON: 长按≥1秒
POWER_ON --> MODE_CHANGE: 短按30ms-3秒
POWER_ON --> POWER_OFF: 长按≥3秒
MODE_CHANGE --> POWER_ON: 释放按键
3.2 关键代码实现
3.2.1 电源管理模块
电源引脚初始化需要特别注意GPIO的配置方式:
c复制void PowerOn_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
// 特别注意:推挽输出模式选择低速2MHz
// 高速模式可能导致开关瞬态电流过大
GPIO_InitStructure.GPIO_Pin = POWER_ON_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(POWER_ON_PORT, &GPIO_InitStructure);
GPIO_ResetBits(POWER_ON_PORT, POWER_ON_PIN);
g_system_power_on = 0;
}
3.2.2 按键检测算法
按键检测采用非阻塞式计时方式,确保不影响主程序运行:
c复制void Key_Scan(void)
{
static uint8_t g_key_pressed = 0;
static uint32_t g_key_press_ms = 0;
// 系统未开机时只检测开机条件
if (g_system_power_on == 0) {
Check_Power_On();
return;
}
uint8_t key_level = GPIO_ReadInputDataBit(MODE_DET_PORT, MODE_DET_PIN);
if (key_level == 0) { // 按键按下
if (g_key_pressed == 0) { // 首次按下
g_key_pressed = 1;
g_key_press_ms = 0;
} else {
g_key_press_ms += 10; // 主循环周期为10ms
// 长按关机检测
if (g_key_press_ms >= POWER_SHUTDOWN_TIME_MS) {
GPIO_ResetBits(POWER_ON_PORT, POWER_ON_PIN);
g_system_power_on = 0;
while (1); // 等待硬件断电
}
}
} else if (g_key_pressed == 1) { // 按键释放
// 短按模式切换
if (g_key_press_ms >= SHORT_PRESS_TIME_MS &&
g_key_press_ms < POWER_SHUTDOWN_TIME_MS) {
LED_Switch_Mode();
}
g_key_pressed = 0;
g_key_press_ms = 0;
}
}
3.3 LED模式控制
LED控制采用状态机设计,支持多种显示模式:
c复制typedef enum {
LED_MODE_BLINK_ALL = 0, // 全灯同步闪烁
LED_MODE_FLOW, // 流水灯效果
LED_MODE_SINGLE_BLINK, // 单灯轮流闪烁
LED_MODE_MAX
} LED_Mode_TypeDef;
void LED_Run_Current_Mode(void)
{
static uint32_t last_tick = 0;
uint32_t current_tick = Get_System_Tick();
// 500ms周期检测
if (current_tick - last_tick >= 500) {
last_tick = current_tick;
switch (g_led_mode) {
case LED_MODE_BLINK_ALL:
LED_ALL_TOGGLE();
break;
case LED_MODE_FLOW:
LED_ALL_OFF();
g_flow_step = (g_flow_step + 1) % 4;
if (g_flow_step < 3) {
GPIO_ResetBits(LED_PORT, LED4_PIN << g_flow_step);
}
break;
case LED_MODE_SINGLE_BLINK:
LED_ALL_OFF();
g_single_led = (g_single_led + 1) % 3;
GPIO_ResetBits(LED_PORT, LED4_PIN << g_single_led);
break;
}
}
}
4. 实际应用中的优化技巧
4.1 功耗优化措施
-
GPIO配置优化:
- 关机状态下,将未使用的GPIO设置为模拟输入模式
- 上拉/下拉电阻根据实际需要配置,避免不必要的电流消耗
- 按键检测引脚配置为内部上拉,省去外部电阻
-
时钟配置:
c复制// 进入低功耗模式前执行 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); -
唤醒后处理:
c复制// 从STOP模式唤醒后需要重新配置系统时钟 SystemCoreClockUpdate();
4.2 可靠性增强方案
-
防误触设计:
- 增加开机延时确认(如长按1秒后LED闪烁一次再开机)
- 关机需要持续按住3秒,避免误操作
-
电源稳定性处理:
c复制// 上电延时确保电源稳定 void Power_On_Delay(void) { uint32_t i; for (i = 0; i < 0xFFFF; i++); // 约100ms延时 } -
状态保存与恢复:
c复制// 关机前保存状态到Flash void Save_System_State(void) { FLASH_Unlock(); FLASH_ErasePage(0x0800F000); FLASH_ProgramWord(0x0800F000, g_led_mode); FLASH_Lock(); }
5. 常见问题与解决方案
5.1 硬件相关问题
问题1:按键按下但无法开机
- 检查D1是否焊反或损坏
- 测量Q4栅极电压,按下时应低于1V
- 确认VBAT电压正常(≥3.3V)
问题2:松开按键后无法维持供电
- 检查Q6是否正常导通
- 确认PC3引脚输出高电平
- 测量R11阻值是否正确(1KΩ)
5.2 软件相关问题
问题1:按键反应不灵敏
- 调整消抖时间常数
c复制#define DEBOUNCE_TIME 20 // 改为20ms消抖 - 检查主循环周期是否稳定
问题2:模式切换不准确
- 优化按键检测算法
c复制// 增加中间状态检测 if (g_key_press_ms > SHORT_PRESS_TIME_MS && g_key_press_ms < POWER_SHUTDOWN_TIME_MS - 500) { // 确认为短按操作 }
5.3 性能优化建议
-
中断替代轮询:
c复制// 配置按键引脚为外部中断 void EXTI_Config(void) { EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource2); EXTI_InitStructure.EXTI_Line = EXTI_Line2; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } -
低功耗模式集成:
c复制void Enter_Low_Power_Mode(void) { LED_ALL_OFF(); RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); PWR_EnterSTANDBYMode(); // 待机模式,最低功耗 }
6. 项目扩展与进阶应用
6.1 无线控制集成
结合CH32的蓝牙或WiFi功能,可以实现手机APP远程控制:
c复制void BLE_Control_Handler(uint8_t *data)
{
switch(data[0]) {
case 0x01: // 开机指令
GPIO_SetBits(POWER_ON_PORT, POWER_ON_PIN);
g_system_power_on = 1;
break;
case 0x02: // 关机指令
GPIO_ResetBits(POWER_ON_PORT, POWER_ON_PIN);
g_system_power_on = 0;
break;
case 0x03: // 模式切换
LED_Switch_Mode();
break;
}
}
6.2 多设备联动设计
通过I2C或UART接口,可以实现主从设备控制:
c复制void Master_Control_Slaves(uint8_t cmd)
{
UART_SendData(UART1, cmd);
while(UART_GetFlagStatus(UART1, UART_FLAG_TXE) == RESET);
// 等待从机响应
uint32_t timeout = 1000;
while(timeout-- && UART_GetFlagStatus(UART1, UART_FLAG_RXNE) == RESET);
if (timeout) {
uint8_t ack = UART_ReceiveData(UART1);
// 处理应答
}
}
6.3 能耗监测功能
增加电流检测电路,实现功耗统计:
c复制void Power_Consumption_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
float Get_Current_Consumption(void)
{
uint16_t adc_value = ADC_GetConversionValue(ADC1);
return (adc_value * 3.3 / 4095) / 0.1; // 假设检测电阻为0.1Ω
}
在实际项目中,这个一键开关机方案已经成功应用于多个智能家居产品,包括智能灯具、温控器等。它的优势在于硬件成本低、软件实现简单,同时又能提供良好的用户体验。通过适当的修改,这个方案也可以移植到其他单片机平台上。
