一键控制开关机电路(软件控制)

一、简介

        便携式设备在嵌入式开发中非常普遍,按键电路的使用种类也非常广泛。其中有一种比较特殊的设计,就是一个按键同时实现开机和关机两个控制功能。元件组成也十分简单,由电阻、PMOS管(或CH217K)、NPN三极管、按键和MCU控制实现,具体详细请见下文!

二、目录

1、开关机电路原理图

CH217K替代PMOS的方案(兼容设计,不太常用):

2、开关机功能实现原理

开机:按下S1按键瞬间,PMOS(Q1) 栅极被拉低,PMOS (Q1)导通,电源通过 PMOS(Q1) 给后级电路供电实现开机。若按住超过2 秒,单片机(ESPC2_IO18)将 SW_OFF 输出高电平,使拉低 PMOS (Q1)栅极,使PMOS(Q1) 保持导通(Vgs < Vgs(th),此时松手可保持开机状态,开启电源输出。

关机:开机后按住开关 2 秒,单片机将 SW_OFF 置为低电平,使拉高PMOS(Q1) 栅极,PMOS(Q1) 保持截止(Vgs>Vgs(th)),关闭电源输出,程序停止运行,此时松手实现真正关机,且漏电流趋近于0uA。

肖特基二极管 D4 和 D6 的作用

D4 的作用:由于部分单片机的 GPIO 默认并非高阻态,当 PMOS 栅极通过按键接地时,可能会有电流通过 GPIO 流出,设置 D4 可以防止这种漏电流,保证 PMOS 栅极电位稳定,避免误触发。

D6 的作用:当 SW_01为低电平时,NPN 三极管导通,若没有 D6,NPN 的 Vce 可能拉低 SW_01(ESPC2_IO3)。D6 阻止电流经 NPN 到地,机械开关断开时,单片机通过上拉电阻读 3.3V 高电平,仅按键按下时SW_01(ESPC2_IO3) 被拉低,实现按键状态准确检测。

输入电压 MAX 与 PMOSVds 关系

输入电压 VBUST_OUT主要受 PMOS 的 Vds 限制。因为 PMOS 导通时,其源极接输入电源,漏极接后级电路,若输入电压过高,超过 PMOS 的 Vds 耐压值,可能会导致 PMOS 损坏,所以需要根据 PMOS 的 Vds 参数来选择合适的输入电压范围,以确保电路安全稳定运行。

PMOS(参考AOS(万代)AO3401A):

3、代码分享

① ESP-IDF 5.1代码:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
static const char *TAG = "One-click power on/off!";

#define Key_Detect 3        // 按键输入引脚
#define GPIO_INPUT_PIN_SEL (1ULL << Key_Detect)

#define Power_Control 18    // 电源控制输出引脚                
#define GPIO_OUTPUT_PIN_SEL ((1ULL << Power_Control))

#define Power_state_on   1
#define Power_state_off  0

static void gpio_input_mode_setup();
static void gpio_output_mode_setup(); 

static void gpio_input_mode_setup()
{
    // pio input mode setup
    // zero-initialize the config structure.
    gpio_config_t io_conf = {};                         //创建IO配置结构体
    // interrupt of rising edge
    io_conf.intr_type = GPIO_INTR_POSEDGE;              //低电平触发
    // bit mask of the pins, use GPIO4/5 here
    io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;          //输入使能
    // set as input mode
    io_conf.mode = GPIO_MODE_INPUT;                     //设置输入模式
    // enable pull-up mode
    io_conf.pull_up_en = 1;                             // 上拉开启:1 关闭:0
    // configure GPIO with the given settings
    gpio_config(&io_conf);                              // GPIO配置
}

static void gpio_output_mode_setup()
{
    // gpio output mode setup
    // zero-initialize the config structure.
    gpio_config_t io_conf = {};                         //创建IO配置结构体
    // disable interrupt
    io_conf.intr_type = GPIO_INTR_DISABLE;              //禁用中断
    // set as output mode
    io_conf.mode = GPIO_MODE_OUTPUT;                    //设置为输出模式
    // bit mask of the pins that you want to set
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;         //寄存器操作对应IO模式使能
    // disable pull-down mode
    io_conf.pull_down_en = 0;                           //下拉模式使能关闭
    // disable pull-up mode 
    io_conf.pull_up_en = 0;                             //上拉模式使能关闭
    // configure GPIO with the given settings
    gpio_config(&io_conf);                              //GPIO配置 Configure GPIO's Mode,pull-up,PullDown,IntrType
}

// 检测按键状态并控制PMOS(或CH217K)
void key_scan(void *pvParameters){
    volatile int16_t key_state = 1;                               // 按键状态变量  0:按下 1:未按下
    volatile int16_t last_key_state = 1;                          // 上一次按键状态,初始为未按下(高电平)
    volatile int16_t key_press_time = 0;                          // 按键按下的时间
    volatile int16_t long_press_time = 2000;                      // 延时时间2000ms=2s
    while(1){
        if(0 == gpio_get_level(Key_Detect)){                      // 检测到按键按下,并且上次是关闭状态       
            vTaskDelay(pdMS_TO_TICKS(long_press_time));           // 延时等待2s
            key_press_time = long_press_time;                     // 记录按键等待时间
            key_state = gpio_get_level(Key_Detect);               // 获取按键状态
            if(key_press_time == long_press_time){                // 判断按键是否长按超过2s
                if(1 == last_key_state && 0 == key_state){
                    gpio_set_level(Power_Control, Power_state_on); // 打开输出电源
                    ESP_LOGI(TAG,"Power ON!\n");
                    last_key_state = 0;                            
                }else if(0 == last_key_state && 0 == key_state){   
                    gpio_set_level(Power_Control, Power_state_off); // 关闭输出电源
                    ESP_LOGI(TAG,"Power OFF!\n");
                    last_key_state = 1;                             
                }
            }
        }else{
            key_press_time = 0;                                    // 按键抬起,则清零
        }
        vTaskDelay(pdMS_TO_TICKS(10));                             // 短暂延时,减少CPU占用
        }
}

void app_main(void)
{
    gpio_input_mode_setup();
    gpio_output_mode_setup(); 
    gpio_set_level(Power_Control, Power_state_off);         // 初始化时控制引脚输出低电平,确保PMOS截止
    xTaskCreate(&key_scan, "key_scan", 2048, NULL, 5, NULL); // 创建按键扫描任务
}

② STM32(仅供参考):

#include "stm32f10x.h"

#define KEY_PIN GPIO_Pin_0 // 按键连接的GPIO引脚
#define KEY_PORT GPIOA     // 按键连接的GPIO端口
#define POWER_PIN GPIO_Pin_1 // 控制PMOS的GPIO引脚
#define POWER_PORT GPIOA   // 控制PMOS的GPIO端口

volatile int key_press_time = 0; // 按键按下时间
volatile int key_state = 0;      // 按键状态

// 初始化GPIO
void GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    // 初始化按键引脚
    GPIO_InitStructure.GPIO_Pin = KEY_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 输入浮空
    GPIO_Init(KEY_PORT, &GPIO_InitStructure);
    // 初始化控制PMOS的引脚
    GPIO_InitStructure.GPIO_Pin = POWER_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(POWER_PORT, &GPIO_InitStructure);
    // 初始化按键引脚为高电平
    GPIO_SetBits(POWER_PORT, POWER_PIN);
}

// 延时函数
void Delay(uint32_t time) {
    while(time--) {
        // 简单延时
    }
}

// 按键检测函数
void Key_Detect(void) {
    if(GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN) == 0) { // 按键按下
        key_press_time++;
        if(key_press_time > 3000) { // 按键长按3秒
            if(key_state == 0) { // 当前状态为关机
                // 开机操作
                GPIO_ResetBits(POWER_PORT, POWER_PIN); // 拉低PMOS栅极,导通PMOS
                key_state = 1;
            } else { // 当前状态为开机
                // 关机操作
                GPIO_SetBits(POWER_PORT, POWER_PIN); // 拉高PMOS栅极,截止PMOS
                key_state = 0;
            }
        }
    } else { // 按键未按下
        key_press_time = 0;
    }
}

int main(void) {
    // 系统初始化
    SystemInit();
    // GPIO初始化
    GPIO_Init();
    while(1) {
        // 按键检测
        Key_Detect();
        // 其他功能逻辑
        if(key_state == 1) { // 开机状态
            // 执行开机后的功能
        }
    }
}

③ Arudino:

const int keyPin = 2; // 按键连接的引脚
const int powerPin = 3; // 控制PMOS的引脚

volatile int keyPressTime = 0; // 按键按下时间
volatile int keyState = 0;     // 按键状态

void setup() {
    pinMode(keyPin, INPUT); // 设置按键引脚为输入
    pinMode(powerPin, OUTPUT); // 设置控制PMOS的引脚为输出
    digitalWrite(powerPin, HIGH); // 初始化PMOS栅极为高电平
}

void loop() {
    if(digitalRead(keyPin) == LOW) { // 按键按下
        keyPressTime++;
        if(keyPressTime > 3000) { // 按键长按3秒
            if(keyState == 0) { // 当前状态为关机
                // 开机操作
                digitalWrite(powerPin, LOW); // 拉低PMOS栅极,导通PMOS
                keyState = 1;
            } else { // 当前状态为开机
                // 关机操作
                digitalWrite(powerPin, HIGH); // 拉高PMOS栅极,截止PMOS
                keyState = 0;
            }
        }
    } else { // 按键未按下
        keyPressTime = 0;
    }
    // 其他功能逻辑
    if(keyState == 1) { // 开机状态
        // 执行开机后的功能
    }
}

 4、实现效果

① 样板介绍:

② 测试视频:

一键开关机

<think>好的,用户想了解如何在STM32F103C8T6上实现一键开关机功能。首先,我需要理解这个功能的含义。通常,一键开关机指的是通过一个按键来控制设备的电源开启和关闭,而不仅仅是软件层面的启动或休眠。这意味着需要硬件电路来完全切断电源,而不仅仅是让MCU进入低功耗模式。 根据用户提供的引用,特别是引用[1]提到了低功耗模式,比如停止模式和待机模式。但这些模式主要是软件控制的,比如停止模式需要手动重新初始化时钟,而待机模式则会完全断电,但唤醒后会重新启动。不过,真正的硬件开关机可能需要外部电路控制电源的完全通断。 接下来,我需要考虑硬件设计。常见的方案是使用MOS管或者继电器来控制电源的通断。当按键按下时,MCU检测到信号后,可以控制MOS管维持电源,直到用户再次按下按键,MCU再关闭MOS管,切断电源。这里需要确保按键在按下时能够给MCU供电,同时MCU在启动后能保持电源,直到决定关机。 然后,软件部分需要处理按键检测和电源控制。在启动时,MCU需要配置相应的GPIO引脚,检测按键的长按或短按,可能要用到外部中断或状态机,如引用[3]提到的状态机来识别长按和短按。同时,关机时,MCU需要控制电源维持电路断开,可能需要设置某个GPIO输出低或高电平来关闭MOS管,然后进入低功耗模式或者完全断电。 另外,引用[5]提到FLASH操作需要在关机前保存数据,这可能涉及到在关机前将状态保存到FLASH中,以便下次启动时恢复。但如果是硬件断电,可能需要在关机前完成必要的存储操作,然后切断电源。 需要注意的是,STM32F103C8T6本身无法完全切断自己的电源,所以必须依赖外部电路。因此,硬件设计是关键,确保按键触发电源开启,MCU启动后维持电源,并在需要关机时断开电源。同时,软件需要可靠地检测按键动作,处理可能的抖动,并在关机前执行必要的清理操作,如保存数据、关闭外设等。 可能的问题包括按键消抖、电源维持电路的稳定性、低功耗模式下的唤醒等。需要参考引用中的中断处理(如引用[2]的EXTI配置)和状态机设计(引用[3])来实现可靠的按键检测。同时,引用[1]提到的时钟重新初始化在唤醒后的处理也需要考虑,如果使用停止模式,可能需要重新配置时钟,但如果是硬件断电,则每次启动都是冷启动,需要重新初始化所有配置。 总结来说,实现一键开关机需要硬件电路设计配合软件逻辑,确保按键能控制电源通断,MCU在运行期间维持电源,并在需要时安全关闭。</think>### STM32F103C8T6一键开关机实现方法 #### 一、硬件设计 1. **电源控制电路** 使用N-MOSFET+三极管构成自锁电路,按键触发后通过MCU的GPIO维持电源: $$V_{CC} \xrightarrow{Q1} V_{DD} \xrightarrow{MCU\_CTRL} GND$$ 具体电路参考: ```plaintext SW ---[10k]---+ |-- NPN三极管基极 |-- 1N4148到MCU_CTRL引脚 MOSFET栅极接MCU_CTRL ``` 2. **按键检测电路** 通过GPIO输入模式检测按键状态,推荐使用$10k\Omega$上拉电阻配合$0.1\mu F$电容滤波[^2]。 #### 二、软件实现 1. **初始化配置** ```c void PWR_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 电源维持引脚 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_SetBits(GPIOB, GPIO_Pin_0); // 维持电源 } ``` 2. **按键状态机**(参考引用[3]) ```c typedef enum {IDLE, PRESSED, HOLD} KeyState; void Key_Handler(void) { static KeyState state = IDLE; static uint32_t pressTime = 0; if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0) { if(state == IDLE) { pressTime = GetTickCount(); state = PRESSED; } else if((GetTickCount()-pressTime) > 3000) { // 3秒长按 Shutdown_Procedure(); state = HOLD; } } else { state = IDLE; } } ``` 3. **关机流程** ```c void Shutdown_Procedure(void) { Save_Data_To_Flash(); // 引用[5]的FLASH操作 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); PWR_EnterSTANDBYMode(); // 进入待机模式[^1] GPIO_ResetBits(GPIOB, GPIO_Pin_0); // 切断MOSFET } ``` #### 三、关键参数 1. 按键检测消抖时间:$t_{debounce} \geq 20ms$[^3] 2. 长按判定阈值:$T_{hold} = 3s \pm 10\%$ 3. MOSFET维持电压:$V_{GS(th)} < 2.5V$ #### 四、注意事项 1. 唤醒后必须重新初始化时钟系统(调用`SystemInit()`)[^1] 2. FLASH编程前需关闭中断 3. GPIO配置应避免内部上/下拉与外部电路冲突[^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值