最开始接触8G1K08是因为某九阳豆浆机通电无法进入工作模式,所以用STC8G1K08编写了最简单的定时程序,实现通电等待5秒,然后模拟短按1秒的动作,让豆浆机定时通电(智能插座)后自动煮豆浆。
后来又对桶装水电动抽水器的开关打起了小注意,能不能按一次启动,然后定时若干秒后自动停止抽水呢? 这样就不用看着,防止烧水壶满出。如果固定定时时间,就要根据不同烧水壶的容积计算加水时间,再把定时之间写到代码中,显然这样不够灵活,在deepseek和kimi的加持下,经过多天反复编译测试,已初步实现,各位大佬帮忙看看,还有哪里可以再优化优化,精简或者补充的。
- /*----------------------------------------------------------------------*/
- /* --- STC8G1K08A 按键动作,按键中断唤醒,定时器练习--------------------*/
- /* --- 1.PIN脚低电平触发 */
- /* --- 2.短按启动或关闭 LED_PIN,默认定时5秒 */
- /* --- 3.长按3~9秒,进入设置模式,再短按时,记录长按~短按之间的时长 */
- /* --- 下次短按时,已最新设置的时长进行定时,定时结束前短按,退出定时 */
- /* --- 断电记忆上次的设置时长 */
- /* --- 4.长按10秒以上,释放按键时擦除ERPROM第1扇区 */
- /*----------------------------------------------------------------------*/
- #include "STC8G.H"
-
- #define MAIN_Fosc 24000000L // 定义主时钟
- #define PX0H 0x01 // 定义PX0H为IPH寄存器的第0位
- #define MCU_IDLE() PCON |= 1 /* MCU 进入 IDLE 模式 */
- #define MCU_POWER_DOWN() PCON |= 2 /* MCU 进入 睡眠 模式 */
-
- #define CTR_PIN P55 // 控制/动作指示灯
- #define LED_PIN P54 // LED连接P54 低电平触发,作为对外控制输出脚
- #define KEY_PIN P32 // 按键连接P32
-
- #define SHORT_PRESS_TIME 10 // 短按时间阈值 毫秒
- #define LONG_PRESS_TIME 3000 // 长按时间阈值 毫秒
- #define IDLE_TIME 30000 // 空闲时间阈值 毫秒
- #define KEEP_TIME 5000L // 默认LED保持时长 毫秒
- #define RESET_TIME 10000 // 重置时间阈值 毫秒,长按以上,清除EEPROM
-
- #define EEPROM_ADDR 0x0000 // EEPROM存储地址(根据实际需求设置)
- #define IAP_STANDBY() IAP_CMD = 0 //IAP空闲命令(禁止)
- #define IAP_READ() IAP_CMD = 1 //IAP读出命令
- #define IAP_WRITE() IAP_CMD = 2 //IAP写入命令
- #define IAP_ERASE() IAP_CMD = 3 //IAP擦除命令
- #define IAP_ENABLE() IAP_CONTR = 0x80; IAP_TPS = MAIN_Fosc / 1000000 //激活IAP操作
- #define IAP_DISABLE() IAP_CONTR = 0; IAP_CMD = 0; IAP_TRIG = 0; IAP_ADDRH = 0xff; IAP_ADDRL = 0xff // 禁止IAP操作
-
- extern void _nop_ (void);
-
- // 定义全局变量
- unsigned long msTicks = 0; // 定时器计数 毫秒
- unsigned long led_keep_time = KEEP_TIME; // 默认LED保持时长5秒
- unsigned long led_on_time = 0; // LED已点亮时间 毫秒
- unsigned long idle_time = 0; // 空闲时间计数器 毫秒
-
- bit led_state = 0; // LED状态(0:熄灭,1:点亮)
- bit setting_mode = 0; // 设置模式标志
- bit key_pressed = 0; // 按键按下标志
- bit key_released = 1; // 按键释放标志
- bit key_long_press = 0; // 长按标志
- bit wakeup_status = 1; // 运行标志(0:休眠,1:活跃)
- bit erase_mode = 0; // 设置EEPROM擦除标志
-
- void Timer0_Init(void); // 定时器0初始化
- void Int0_Init(void); // 外部中断0初始化
- void PowerDownMode(void); // 掉电模式
- void WakeUpFromPowerDown(void); // 从掉电模式唤醒
-
- // 初始化I/O端口
- void InitPorts()
- {
- // 初始化为准双向
- P5M0 = 0x00; P5M1 = 0xcf;
- P3M0 = 0x00; P3M1 = 0xf8;
- }
-
- // 初始化全局变量
- void InitGlobals()
- {
- CTR_PIN = 1;
- LED_PIN = 1;
- KEY_PIN = 1;
- msTicks = 0;
- wakeup_status = 1;
- idle_time = msTicks;
- }
-
- // 毫秒级延时函数
- void Delay_Ms(unsigned long ms) {
- unsigned long delay_start = msTicks + ms;
- while(delay_start > msTicks);
- }
-
- void Flash_LED(unsigned char i,unsigned int j) {
- bit CTR_PIN_status = CTR_PIN; // 保存控制指示灯的状态
- do{
- CTR_PIN = !CTR_PIN;
- Delay_Ms(j);
- }while(--i);
- CTR_PIN = CTR_PIN_status; // 恢复控制指示灯的状态
- idle_time = msTicks;
- }
-
- // 触发EEPROM操作
- void EEPROM_Trig(void)
- {
- F0 = EA; //保存全局中断
- EA = 0; //禁止中断, 避免触发命令无效
- IAP_TRIG = 0x5A;
- IAP_TRIG = 0xA5; //先送5AH,再送A5H到IAP触发寄存器,每次都需要如此
- //送完A5H后,IAP命令立即被触发启动
- //CPU等待IAP完成后,才会继续执行程序。
- _nop_();
- _nop_();
- EA = F0; //恢复全局中断
- _nop_();
- }
-
- // 写入EEPROM
- void EEPROM_Write(unsigned int addr, unsigned int dat) {
- IAP_ENABLE();
- IAP_WRITE(); // 设置IAP写命令
- IAP_ADDRL = addr; // 设置低地址
- IAP_ADDRH = addr >> 8; // 设置高地址
- IAP_DATA = dat; // 写入数据
- EEPROM_Trig(); // 触发EEPROM操作
- IAP_DISABLE(); // 关闭IAP功能
- }
-
- // 从EEPROM读取数据
- unsigned char EEPROM_Read(unsigned int addr) {
- unsigned char dat;
- IAP_ENABLE();
- IAP_READ(); // 设置IAP读命令
- IAP_ADDRL = addr; // 设置低地址
- IAP_ADDRH = addr >> 8; // 设置高地址
- EEPROM_Trig(); // 触发EEPROM操作
- dat = IAP_DATA; // 读取数据
- IAP_DISABLE(); // 关闭IAP功能
- return dat;
- }
-
- // 把指定地址的EEPROM扇区擦除
- void EEPROM_SectorErase(unsigned int addr)
- {
- IAP_ENABLE(); //设置等待时间,允许IAP操作,送一次就够
- IAP_ERASE(); //宏调用, 送扇区擦除命令,命令不需改变时,不需重新送命令
- //只有扇区擦除,没有字节擦除,512字节/扇区。
- //扇区中任意一个字节地址都是扇区地址。
- IAP_ADDRH = addr / 256; //送扇区地址高字节(地址需要改变时才需重新送地址)
- IAP_ADDRL = addr % 256; //送扇区地址低字节
- EEPROM_Trig(); //触发EEPROM操作
- IAP_DISABLE(); //禁止EEPROM操作
- }
-
- // 写入32位long类型数据到EEPROM
- void Write_Led_Keep_Time(unsigned long dat) {
- unsigned char byte0, byte1, byte2, byte3;
-
- // 将32位long类型数据分解为4个字节
- byte0 = (dat >> 24) & 0xFF; // 最高字节
- byte1 = (dat >> 16) & 0xFF; // 高次字节
- byte2 = (dat >> 8) & 0xFF; // 低次字节
- byte3 = dat & 0xFF; // 最低字节
-
- // 写入EEPROM
- EEPROM_Write(EEPROM_ADDR, byte0); // 写入最高字节
- EEPROM_Write(EEPROM_ADDR + 1, byte1); // 写入高次字节
- EEPROM_Write(EEPROM_ADDR + 2, byte2); // 写入低次字节
- EEPROM_Write(EEPROM_ADDR + 3, byte3); // 写入最低字节
- }
-
- // 从EEPROM读取32位long类型数据
- int Read_Led_Keep_Time() {
- unsigned char byte0, byte1, byte2, byte3;
-
- // 从EEPROM读取4个字节
- byte0 = EEPROM_Read(EEPROM_ADDR); // 读取最高字节
- byte1 = EEPROM_Read(EEPROM_ADDR + 1); // 读取高次字节
- byte2 = EEPROM_Read(EEPROM_ADDR + 2); // 读取低次字节
- byte3 = EEPROM_Read(EEPROM_ADDR + 3); // 读取最低字节
-
- // 将4个字节组合成一个32位long类型数据
- return ((long)byte0 << 24) | ((long)byte1 << 16) | ((long)byte2 << 8) | byte3;
- }
-
- // 主流程
- void main() {
- InitPorts(); // 初始化I/O端口
- Timer0_Init(); // 初始化定时器0
- Int0_Init(); // 初始化外部中断0
- InitGlobals(); // 初始化全局变量
- Flash_LED(2,1000); // 开机完成提示
-
- // 从EEPROM读取保持时长数据并赋值给led_keep_time,否则保存默认值到EEPROM
- led_keep_time = Read_Led_Keep_Time();
- if (led_keep_time == 0xFFFFFFFF || led_keep_time < 2000L || led_keep_time > 300000L) { // 如果EEPROM未初始化或值不合规
- led_keep_time = KEEP_TIME; // 使用默认值
- Write_Led_Keep_Time(led_keep_time); // 将定时默认值写入EEPROM
- Flash_LED(6,1000); //慢闪,保存成功
- }
-
- while (1) {
- if (key_released && key_pressed) { // 检测按键按下
- key_pressed = 0; // 清除按键标志
- if (erase_mode) { //重置模式
- erase_mode = 0; // 清除重置模式标志
- EEPROM_SectorErase(EEPROM_ADDR);
- Flash_LED(10,150); //执行完成后闪烁提示
- led_keep_time = KEEP_TIME;
- }
- else if (key_long_press) { // 长按按键
- key_long_press = 0;
- if (!setting_mode) { // 进入设置模式
- setting_mode = 1;
- led_state = 1; // 点亮LED
- LED_PIN = 0;
- led_on_time = msTicks; // 重置点亮时间
- }
- }
- else { // 短按按键
- if (setting_mode) { // 在设置模式中
- setting_mode = 0; // 退出设置模式
- led_state = 0; // 熄灭LED
- LED_PIN = 1;
- led_keep_time = msTicks - led_on_time; // 保存点亮时长
- Write_Led_Keep_Time(led_keep_time); // 将new_keep_time写入EEPROM
- Flash_LED(6,1000); //慢闪,保存成功
- } else { // 不在设置模式
- if (led_state) { // 如果LED已点亮
- led_state = 0; // 熄灭LED
- LED_PIN = 1;
- } else { // 如果LED未点亮
- led_state = 1; // 点亮LED
- LED_PIN = 0;
- led_on_time = msTicks; // 重置点亮时间
- }
- }
- }
- idle_time = msTicks; // 重置空闲时间
- }
-
- if (!setting_mode){ // 不在设置模式
- if (led_state) { // LED点亮
- if (msTicks - led_on_time >= led_keep_time) { // 达到保持时长
- led_state = 0; // 熄灭LED
- LED_PIN = 1;
- Flash_LED(6,200); //执行完成后闪烁提示
- }
- }else{ // LED熄灭
- if (msTicks - idle_time >= IDLE_TIME) { // 空闲时间超过30秒
- Flash_LED(10,100); //执行完成后闪烁提示
- PowerDownMode(); // 进入掉电模式
- }
- }
- }else if (led_state && (msTicks - led_on_time > 120000)){
- setting_mode = 0; // 超时退出设置模式
- led_state = 0; // 熄灭LED
- LED_PIN = 1;
- Flash_LED(6,500); //半慢闪2次,超时
- }
- }
- }
-
- // 定时器0初始化
- void Timer0_Init(void) //1毫秒@24.000MHz
- {
- AUXR |= 0x80; //定时器时钟1T模式
- TMOD &= 0xF0; //设置定时器模式 16位自动重载
- TL0 = 0x40; //设置定时初始值
- TH0 = 0xA2; //设置定时初始值
- TF0 = 0; //清除TF0标志
- TR0 = 1; //定时器0开始计时
- ET0 = 1; //使能定时器0中断
- }
-
- // 定时器0中断服务程序
- void Timer0_Isr(void) interrupt 1 {
- static unsigned long key_press_time = 0; // 按键按下时间 毫秒
- msTicks++;
-
- if (!KEY_PIN) { // 按键按下
- key_press_time++;
- CTR_PIN = 0;
- key_released = 0; // 设置按键状态为按下
- } else { // 按键松开
- if (key_press_time >= RESET_TIME) { // 长按 10秒及以上
- erase_mode = 1;
- }
- else if (key_press_time >= LONG_PRESS_TIME && key_press_time < RESET_TIME) { // 长按 3秒 ~ 10秒
- key_long_press = 1;
- key_pressed = 1;
- }
- else if (key_press_time >= SHORT_PRESS_TIME && key_press_time < LONG_PRESS_TIME) { // 短按少于3秒
- key_pressed = 1;
- }
- else {
- key_long_press = 0;
- key_pressed = 0;
- }
- if (!key_released) { // 按键曾按下
- CTR_PIN = 1;
- }
- key_released = 1; // 重置按键状态为释放
- key_press_time = 0; // 重置按键计时
- }
- }
-
- // 外部中断0初始化
- void Int0_Init(void) {
- IT0 = 1; // 使能INT0下降沿中断
- EX0 = 1; // 使能INT0中断0
- EA = 1; // 使能中断总开关
- }
-
-
- // 掉电模式
- void PowerDownMode(void) {
- LED_PIN = 1; // 确保输出高电平
- CTR_PIN = 1;
- KEY_PIN = 1;
- wakeup_status = 0;
- _nop_();
- _nop_();
- MCU_POWER_DOWN(); // MCU进入掉电模式(STC8G系列)
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- }
-
- // 从掉电模式唤醒
- void WakeUpFromPowerDown(void) {
- PCON &= 0xFD; // 清除掉电标志
- msTicks = 0;
- idle_time = msTicks; // 重置空闲时间
- }
-
- // 外部中断0服务程序(按键唤醒)
- void INT0_ISR(void) interrupt 0 {
- if (!wakeup_status){ // 如果在休眠状态,就执行唤醒操作
- wakeup_status = 1;
- WakeUpFromPowerDown(); // 从掉电模式唤醒
- }
- }
复制代码
|