c-打开和关闭PIC24上的LED.我认为代码没有问题.然而,状态机似乎并没有正确执行



问题是在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。

  1. 按下并释放后,闪烁2次并冻结。

  2. 按下并释放后,如果Switch输入为0,则转到1,否则转到下一步。

  3. 按下并按住时闪烁。松开并按下后,继续闪烁。再次发布后,转到5。

  4. 快速闪烁。新闻发布后,转到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。它使状态机变得相当冗余,因为一切都以线性方式运行,并且只能表示一种非常有限的状态机形式,本质上只是一个序列。

您应该稍微修改一下您的状态机,这很好。

可以反复循环,每次只执行一个案例。但你应该做两件事:

  1. 取消弹出键。例如,使用计数器和小延迟。

  2. 读取机器状态外的按键状态,只需在一个位置即可。

类似这样的东西:

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也会闪烁,所以也不会浪费周期无所事事,但重要的是,每个按钮的改变都会导致一个状态机的改变。

或者换一种说法:如果按钮改变了机器的状态,您必须假设在新状态下按钮没有再次改变。当它再次执行时,您将再次更改状态。也许我不是很清楚,但如果是的话,我可以试着做得更好。

最新更新