一、简介
便携式设备在嵌入式开发中非常普遍,按键电路的使用种类也非常广泛。其中有一种比较特殊的设计,就是一个按键同时实现开机和关机两个控制功能。元件组成也十分简单,由电阻、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 与 PMOS 的 Vds 关系
输入电压 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、实现效果
① 样板介绍:
② 测试视频:
一键开关机