由CPU控制开机或关机,有时很有必要,特别是在需要保存数据的场合,如随时保存数据到EEPROM,FLASH和SD中,正在保存数据时发生误关机,就会导致保存错误,或SD卡中的文件损坏。在实际设计中,我们也想把产品做得很完美,但确实不容易,一不小心就容易触雷了。总结过去,不是我们想布雷,确实是有时候考虑不周或经验不足。CPU控制的”一键开关机电路“,就是为了解决这个问题而设计的。有了它,就可以避免出现上述问题。同样,计算机开机关机原理也是如此。特别是在编写文件时,突然断电,就可能会发现文件损坏,打不开了。有些编译器有自动保存功能,如果正在保存,这时突然断电了,这个文件基本上已经损坏了,也没有办法修复,就只能删除了,再重建。于是,有些重要部门就引用了UPS不间断电源,就是这个原因。希望能在后期的电路设计中,要能体现这个功能。早发现,早治疗,早解决。
以前,有位同事设计过一个一键开关机电路,网上也有类似电路,但问题还有的。有的理论上可以,但实际使用时问题很多。主要表现为是关不了机,或者是荡机。或者电解电容增大,就会有问题。下面是我设计的电路和流程图,供大家一起交流学习。
流程图中的VCC_Flag是变量,用来记录电源开,还是电源关闭。Keyin和PWREN连接到CPU引脚,LED灯由CPU控制,告诉外部是开机,还是关机。灯亮表示CPU有电,灯灭通常表示CPU在关机中。
CPU进入工作后,会扫描Keyin引脚,无操作,则退出去执行其他任务。扫描的目的,是检查是否需要执行关机。
前面的电路设计,主要是为了便于理解流程图。电路简化后,可以少用一个CPU引脚,电路如下:
//函数功能:一键电源开关机
void PowerOn_And_PowerOff(void)
{
u8 ch;
ch=0;
if(Keyin==0)ch=1;
if(ch)//电源按钮被按下
{
if(PowerWork!=PowerWork_OnValue)//电源原来处于"关状态"
{
PWREN_ON();//开灯,自锁
while(Keyin==0)//等待按钮松开
{
delay_ms(10);
}
delay_ms(500);//延时500ms,需要再次确认按钮是否松开
while(Keyin==0)
{
delay_ms(10);
}
PowerWork=PowerWork_OnValue;
}
else//电源原来处于"开状态"
{
PWREN_OFF();//关灯,自锁
while(Keyin==0)//等待按钮松开
{
delay_ms(10);
}
delay_ms(1000);//延时1000ms,需要再次确认按钮是否松开
while(Keyin==0)
{
delay_ms(10);
}
PowerWork=PowerWork_OffValue;
}
}
}
点动按钮开机的缺点:CPU复位时会自动关机,因为CPU引脚通常默认是输入口
#include "Power.h"
#include "Task_Variable.h"
#include "delay.h"
#include "My_Task_Priority.h"
u8 PWREN_OFF_Flag;
//函数功能:初始化一键开关机的Keyin为输入上拉
void Keyin_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//使用GPIO_InitTypeDef定义一个结构变量GPIO_InitStructure;
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOE, ENABLE ); //在配置外设之前,必须先使能GPIOE的外设时钟
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12; //选择第12脚
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //设置引脚的最高输出速率为50MHz
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; //输入上拉,按钮输入低电平有效
GPIO_Init( GPIOE, &GPIO_InitStructure);
//根据GPIO_InitStructure结构变量指定的参数初始化GPIOE的外设寄存器
}
//函数功能:初始化一键开关机的PWREN为输出口
void PWREN_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//使用GPIO_InitTypeDef定义一个结构变量GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE); //使能GPIOD的外设时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //选择第11脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置引脚工作模式为推挽输出方式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的最高输出速率为50MHz
GPIO_Init(GPIOD, &GPIO_InitStructure);
//根据GPIO_InitStructure结构变量指定的参数初始化GPIOD的外设寄存器
}
//函数功能:初始化一键开关机的Keyin为输入上拉,PWREN为输出口
void Power_Init(void)
{
PWREN_Init();//初始化一键开关机的PWREN为输出口
Keyin_Init();//初始化一键开关机的Keyin为输入上拉
PWREN_OFF_Flag=0;
}
const char POWER_ON_Str[]="\r\nPOWER ON";
const char POWER_OFF_Str[]="\r\nPOWER OFF";
//函数功能:一键电源开关机
void PowerOn_And_PowerOff(void)
{
u8 ch;
if(Keyin==0)//电源按钮被按下,开机
{
if(PowerWork!=PowerWork_OnValue)//电源原来处于"关状态"
{
PWREN_ON();//开灯,自锁
PowerWork=PowerWork_OnValue;
printf("%s",POWER_ON_Str);
}
}
if(PowerWork==PowerWork_OnValue)
{
ch=0;
if(Keyin)//电源按钮被释放,开始关机
{
ch=1;
delay_ms(200);
IWDG_ReloadCounter(); //喂狗
printf("%s",POWER_OFF_Str);
}
if(Keyin)//电源按钮被释放,开始关机
{
ch=(u8)(ch<<1);
delay_ms(200);
IWDG_ReloadCounter(); //喂狗
printf("%s",POWER_OFF_Str);
}
if(Keyin)//电源按钮被释放,开始关机
{
ch=(u8)(ch<<1);
delay_ms(200);
IWDG_ReloadCounter(); //喂狗
printf("%s",POWER_OFF_Str);
}
if(Keyin)//电源按钮被释放,开始关机
{
ch=(u8)(ch<<1);
delay_ms(200);
IWDG_ReloadCounter(); //喂狗
printf("%s",POWER_OFF_Str);
}
if(ch==0x08)//经过多次判断,确实要关机
{
PWREN_OFF_Flag=1;
while(PWREN_OFF_Flag==1)
{
delay_ms(200);
IWDG_ReloadCounter(); //喂狗
printf("%s",POWER_OFF_Str);
}
PWREN_OFF();//关灯,自锁
vTaskSuspend(V9203_Task_Handler);//挂起V9203_Task_Handler任务
vTaskSuspend(LCD_DISPLAY_Task_Handler);//挂起LCD_DISPLAY_Task_Handler任务
vTaskSuspend(GPRS_And_ZigBee_Task_Handler);//挂起GPRS_And_ZigBee_Task_Handler任务
PowerWork=PowerWork_OffValue;
while(1)
{
delay_ms(200);
IWDG_ReloadCounter(); //喂狗
printf("%s",POWER_OFF_Str);
}
}
}
}
#ifndef _Power_H
#define _Power_H
#include "stm32f10x.h"
#include "sys.h"
#define Keyin PEin(12) //PE12
#define Keyin_OFF() GPIO_SetBits(GPIOE,GPIO_Pin_12) //定义Keyin输出高电平
#define Keyin_ON() GPIO_ResetBits(GPIOE,GPIO_Pin_12) //定义Keyin输出低电平
#define PWREN PDout(11) //PD11
#define PWREN_ON() GPIO_SetBits(GPIOD,GPIO_Pin_11) //定义PWREN点亮
#define PWREN_OFF() GPIO_ResetBits(GPIOD,GPIO_Pin_11) //定义PWREN关闭
extern u8 PWREN_OFF_Flag;
extern void Power_Init(void); /* Power 端口初始化 */
extern void PowerOn_And_PowerOff(void);
#endif
#include "Power_Task.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "delay.h"
#include "RTC.h"
#include "My_Task_Priority.h"
#include "Task_Variable.h"
#include "Power.h"
#include "Relay.h"
void Power_Task(void *pvParameters);
uint8_t PowerTask_cnt;
const char Power_rn_REG[]="\r\n";
const char Power_Initialise_REG[]="Power Initialise";
const char PowerTask_cnt_REG[]="PowerTask_cnt=";
void Power_Task(void *pvParameters)
{
printf("%s",Power_rn_REG);
printf("%s",Power_Initialise_REG);
Power_Init();
Relay_Init();
PowerTask_cnt=0;
while(1)
{
PowerTask_cnt++;
if(PowerTask_Second_Flag)
{
PowerOn_And_PowerOff();//一键电源开关机
/每秒种任务执行多少次///
printf("%s",Power_rn_REG);
printf("%s",PowerTask_cnt_REG);
printf("%u",PowerTask_cnt);
PowerTask_cnt=0;
PowerTask_Second_Flag=FALSE;
}
vTaskDelay(50);
IWDG_ReloadCounter(); //喂狗
}
}
欢迎大家多提意见,我也正准备使用这种电路。