问题是在case语句BLINK_PRESSED中,我构建的代码并闪烁到微控制器上,一切都很好,直到BLINK_PPRESSED状态,只是当我达到BLINK_PRESSED状态时,我预计while循环会持续闪烁LED,直到PB_RELEASED((使条件为假,然后退出while循环并转到其他状态。但是,在BLINK_PRESSED状态下,LED只是持续亮起,而不是闪烁,并且逻辑不执行。然而,我通过切换状态机的输入来测试MPLAB Sim中的逻辑,状态机检查逻辑,但无法在硬件上实时运行。希望这能清楚地说明问题。非常感谢您的回复。
我面临的问题是在BLINK_PRESSED的情况下:while(PB_PRESSED(((行,我的理解是,只要按下PB,while就应该返回true,否则就应该更改状态。请帮助理解为什么while语句似乎是一个问题。提前感谢!!!
步骤:
1.打开LED。按下并松开时,关闭LED,转到2。
-
按下并释放后,闪烁2次并冻结。
-
按下并释放后,如果Switch输入为0,则转到1,否则转到下一步。
-
按下并按住时闪烁。松开并按下后,继续闪烁。再次发布后,转到5。
-
快速闪烁。新闻发布后,转到1
# define PB_RELEASED() (_RB13 == 1) //push button released # define PB_PRESSED() (_RB13 == 0) // Push button pressed #define LED1 (_LATB14) //led1 state as output #define SW (_RB12) // Switch as input,
/未显示输入/输出端口配置/
typedef enum { //states STATE_RELEASED1, STATE_PRESSED1, STATE_RELEASED2, STATE_PRESSED2, STATE_RELEASED3_BLINK, STATE_PRESSED3, STATE_RELEASED4, BLINK_PRESSED, STATE_RELEASED5, STATE_PRESSED4, STATE_RELEASED6, STATE_RELEASED7, STATE_RELEASED8, } state_t; void update_state(void) { //state machine static state_t e_state = STATE_RELEASED1; uint8_t toggles = 0; switch(e_state){ case STATE_RELEASED1: LED1 = 1; //Starts in LED On if(PB_PRESSED()){ //PB is pressed Step1 e_state = STATE_PRESSED1; } break; case STATE_PRESSED1: if(PB_RELEASED()){ //PB is releases Step1 e_state = STATE_RELEASED2; } break; case STATE_RELEASED2: LED1 = 0; //LED goes off per step 1 if(PB_PRESSED()){ //PB is pressed step2 e_state = STATE_PRESSED2; } break; case STATE_PRESSED2: if(PB_RELEASED()){ //PB is released Step2 e_state = STATE_RELEASED3_BLINK; } break; case STATE_RELEASED3_BLINK: while(toggles < 4){ // On, Off, On, Off per step2 LED1 = !LED1; DELAY_MS(250); toggles++; } LED1 = !LED1; //Freeze On per Step 2 e_state = STATE_PRESSED3; break; case STATE_PRESSED3: if(PB_PRESSED()){ //PB is pressed again per step3 e_state = STATE_RELEASED4; } break; case STATE_RELEASED4: if(PB_RELEASED() && !SW){ //PB is released and sw is checked step3 e_state = STATE_RELEASED1; } if(PB_RELEASED() && SW){ e_state = BLINK_PRESSED; } break; case BLINK_PRESSED: while(PB_PRESSED()){ //As long as PB is pressed, LED toggles per step4 LED1 = !LED1; DELAY_MS(250); } e_state = STATE_RELEASED5; break; case STATE_RELEASED5: if(PB_RELEASED()){ //PB is released step4 e_state = STATE_PRESSED4; } break; case STATE_PRESSED4: while(PB_PRESSED()){ //PB is pressed and held, led toggles step4 LED1 = !LED1; DELAY_MS(250); } e_state = STATE_RELEASED6; break; case STATE_RELEASED6: if(PB_RELEASED()){ //PB is released and pressed and held again step4 while(PB_PRESSED()){ LED1 = !LED1; DELAY_MS(250); if(PB_RELEASED()){ //step4 PB if released e_state = STATE_RELEASED7; } } } break; case STATE_RELEASED7: while(1){ LED1 = !LED1; DELAY_MS(250); //delay of 250ms if(PB_PRESSED()){ //PB is pressed last step e_state = STATE_RELEASED8; break; } } case STATE_RELEASED8: if(PB_RELEASED()) { e_state = STATE_RELEASED1; }//PB is released last step break; default: //default case ASSERT(0); }
请帮助确定问题的根本原因。提前谢谢。
您还不清楚它是如何不按预期工作的,但您的状态机设计方法是有缺陷的,并且您没有延迟开关反弹。
给定一个函数,返回以毫秒为单位的经过时间,例如:
#include <stdint.h>
#include <time.h>
uint32_t time_ms()
{
return (clock() * 1000ULL) / CLOCKS_PER_SEC ;
}
您的系统可能已经有合适的功能可以使用。
以及一个去抖动按钮功能,如:
#include <stdbool.h>
bool button_pressed()
{
#define DEBOUNCE_MS 25ul
static uint32_t timestamp = time_ms() ;
static bool debounced_state = (_RB13 == 0) ;
bool current_state = (_RB13 == 0) ;
if( current_state != debounced_state &&
time_ms() - timestamp >= DEBOUNCE_MS )
{
debounced_state = current_state ;
}
return debounced_state ;
}
BLINK_PRESSED
:
case BLINK_PRESSED:
{
if( button_pressed() )
{
if( time_ms() % 250 )
{
LED1 = !LED1 ;
}
}
else
{
e_state = STATE_RELEASED5;
}
}
break ;
类似地,对于具有不明智的等待循环的其他状态也是如此。
等待循环的问题是,当您处于状态时,无法响应可能导致状态更改的其他事件。唯一可以导致状态更改的事件是按钮释放,然后您可以进入的唯一状态是STATE_RELEASED5
。它使状态机变得相当冗余,因为一切都以线性方式运行,并且只能表示一种非常有限的状态机形式,本质上只是一个序列。
您应该稍微修改一下您的状态机,这很好。
可以反复循环,每次只执行一个案例。但你应该做两件事:
-
取消弹出键。例如,使用计数器和小延迟。
-
读取机器状态外的按键状态,只需在一个位置即可。
类似这样的东西:
loop forever:
delay 1 ms
if key_pressed { // hardware read, just in a single place
if keydebounce<5 keydebounce++
} else {
if keydebounce>0 keydebounce--
}
// now you have keydebounce which is: ==5? pressed - else not
switch (machinestate) {
...
}
如果你想使LED闪烁,写一个状态并使用每毫秒递增的计数器;闪烁";状态等待,直到计数器达到一个值,然后重置它,反转led并继续。当然,它会检查状态是否应该再次更改。
您的代码可以通过其他方式进行更正,但现在它是状态机和序列机的混合体。。。
去抖动可以用不同的方式进行,例如,通过定时器中断,这样就不会有延迟,也不会浪费CPU的电源,LED也会闪烁,所以也不会浪费周期无所事事,但重要的是,每个按钮的改变都会导致一个状态机的改变。
或者换一种说法:如果按钮改变了机器的状态,您必须假设在新状态下按钮没有再次改变。当它再次执行时,您将再次更改状态。也许我不是很清楚,但如果是的话,我可以试着做得更好。