如何实现PIC16F1827按键即时切换LED闪烁模式?
解决PIC16F1827按键即时切换LED闪烁模式的问题
嘿,我一眼就看出你代码里的问题了——那些pat1()到pat4()函数里的__delay_ms()都是阻塞式的!一旦主循环进入某个模式函数,就会卡在里面把所有延时执行完,哪怕中断已经检测到按键按下更新了sw_data,也得等当前模式的所有闪烁步骤走完才能切换。
要实现按键按下即时响应切换,核心就是把阻塞式的延时改成非阻塞的状态机+定时器驱动,让主循环能一直“盯着”按键状态,同时LED闪烁的节奏由定时器计数来控制,不会卡住程序。
下面是修改后的完整代码,我会在后面解释关键改动:
#include <stdio.h> #include <stdlib.h> #include <xc.h> #define _XTAL_FREQ 4000000 /* CONFIG 配置请自行补充 */ void internal_osc(); void io_int(); void tmr0_int(); void sw_scan(); // 新增:定义闪烁模式的状态枚举,把每个模式的步骤拆成单独状态 typedef enum { PAT_IDLE, PAT1_STEP1, PAT1_STEP2, PAT1_STEP3, PAT1_STEP4, PAT2_STEP1, PAT2_STEP2, PAT2_STEP3, PAT2_STEP4, PAT3_STEP1, PAT3_STEP2, PAT3_STEP3, PAT3_STEP4, PAT4_STEP1, PAT4_STEP2, PAT4_STEP3, PAT4_STEP4 } PatternState; PatternState current_pat_state = PAT_IDLE; char tmr0_cnt; char sw_data = 1; // 默认上电进入模式1 char SwStatus; char cnt; // 新增:用于非阻塞延时的计数变量,和TMR0中断同步 char blink_delay_cnt = 0; const char BLINK_DELAY_TICKS = 15; // 和你原来的cnt>=15对应,约500ms void main(void){ internal_osc(); io_int(); tmr0_int(); while(1){ // 主循环先检查当前模式,切换模式时重置状态和延时计数器 switch(sw_data){ case 1 : if(current_pat_state != PAT1_STEP1){ current_pat_state = PAT1_STEP1; blink_delay_cnt = 0; } break; case 2 : if(current_pat_state != PAT2_STEP1){ current_pat_state = PAT2_STEP1; blink_delay_cnt = 0; } break; case 3 : if(current_pat_state != PAT3_STEP1){ current_pat_state = PAT3_STEP1; blink_delay_cnt = 0; } break; case 4 : if(current_pat_state != PAT4_STEP1){ current_pat_state = PAT4_STEP1; blink_delay_cnt = 0; } break; default : PORTB = 0x00; current_pat_state = PAT_IDLE; break; } // 只有当延时计数到达设定值时,才切换闪烁状态 if(blink_delay_cnt >= BLINK_DELAY_TICKS){ blink_delay_cnt = 0; switch(current_pat_state){ // 模式1的步骤流转 case PAT1_STEP1: PORTB = 0x01; current_pat_state = PAT1_STEP2; break; case PAT1_STEP2: PORTB = 0x02; current_pat_state = PAT1_STEP3; break; case PAT1_STEP3: PORTB = 0x04; current_pat_state = PAT1_STEP4; break; case PAT1_STEP4: PORTB = 0x08; current_pat_state = PAT1_STEP1; break; // 模式2的步骤流转 case PAT2_STEP1: PORTB = 0x01; current_pat_state = PAT2_STEP2; break; case PAT2_STEP2: PORTB = 0x03; current_pat_state = PAT2_STEP3; break; case PAT2_STEP3: PORTB = 0x07; current_pat_state = PAT2_STEP4; break; case PAT2_STEP4: PORTB = 0x0f; current_pat_state = PAT2_STEP1; break; // 模式3的步骤流转 case PAT3_STEP1: PORTB = 0x0e; current_pat_state = PAT3_STEP2; break; case PAT3_STEP2: PORTB = 0x0d; current_pat_state = PAT3_STEP3; break; case PAT3_STEP3: PORTB = 0x0b; current_pat_state = PAT3_STEP4; break; case PAT3_STEP4: PORTB = 0x07; current_pat_state = PAT3_STEP1; break; // 模式4的步骤流转 case PAT4_STEP1: PORTB = 0x0e; current_pat_state = PAT4_STEP2; break; case PAT4_STEP2: PORTB = 0x0c; current_pat_state = PAT4_STEP3; break; case PAT4_STEP3: PORTB = 0x08; current_pat_state = PAT4_STEP4; break; case PAT4_STEP4: PORTB = 0x00; current_pat_state = PAT4_STEP1; break; default: PORTB = 0x00; break; } } } } void interrupt ISR(void){ if(INTCONbits.TMR0IF){ INTCONbits.TMR0IF = 0; TMR0 = 130; cnt++; blink_delay_cnt++; // 每次中断递增闪烁延时计数器 if(cnt>=15){ cnt=0; sw_scan(); // 每约500ms扫描一次按键 } } } void internal_osc(void){ OSCCON = 0x6a; } void io_int(void){ TRISA = 0x01; TRISB = 0x00; ANSELA = 0x00; ANSELB = 0x00; } void tmr0_int(void){ OPTION_REG = 0x82; TMR0 = 130; INTCONbits.TMR0IE = 1; INTCONbits.GIE = 1; } void sw_scan(void){ if(PORTAbits.RA0 == 0){ __delay_ms(10); // 短延时消抖,只在中断里执行,不影响主循环 if(PORTAbits.RA0 == 0){ if(SwStatus==0){ SwStatus = 1; sw_data++; if(sw_data>4){ sw_data = 1; // 模式循环,超过4回到1 } } } } else { SwStatus = 0; } }
关键改动详解
干掉阻塞延时,用状态机替代
- 原来的每个
pat()函数是一次性跑完所有闪烁步骤,现在把每个步骤拆成单独的状态(比如PAT1_STEP1对应模式1的第一步亮LED1)。主循环每次只处理当前状态的LED输出,然后等定时器计数到了再切换下一个状态,全程不阻塞。
- 原来的每个
主循环实时响应模式切换
- 主循环不再被模式函数卡住,每次循环都会检查
sw_data。一旦检测到按键更新了模式,立刻重置当前闪烁状态和延时计数器,直接进入新模式的第一步,实现即时切换。
- 主循环不再被模式函数卡住,每次循环都会检查
定时器同步闪烁节奏
- 新增
blink_delay_cnt,在TMR0中断里递增,当计数达到BLINK_DELAY_TICKS(对应你原来的500ms)时才切换闪烁状态,和原来的延时效果一致,但完全非阻塞。
- 新增
优化按键逻辑
- 给
sw_data加了初始值1,上电直接进入模式1;调整了模式循环逻辑,超过4后回到1,避免进入默认的全灭状态。 - 按键消抖的短延时只在中断里每500ms执行一次,不会影响主循环的响应速度。
- 给
这样修改后,只要中断检测到按键按下,主循环下一次迭代就会立即切换到新的闪烁模式,再也不用等当前模式的延时走完啦!
内容的提问来源于stack exchange,提问作者yuki
相关产品推荐
相关产品推荐

