James Lui
James Lui

Reputation: 195

Why does this state machine not maintain its state?

Hey there StackOverflow!

In the following code I have a simple state machine that changes the operation of some external lighting device (as the comments imply). The state is changed via the pressing of the button connected to GP1. The circuit connected to GP1 is a comparator debouncing circuit that compares VDD to 0.6VDD (I've also tried an RC/diode/schmitt trigger circuit), which then forces the signal LO. On a scope, we see a clean square wave when the button is actuated rapidly.

The current (and undesirable) behavior of the PIC10F200 is as follows:

  1. Switch is pressed (state = 0)
  2. State machine variable increments (state = 1)
  3. Lighting goes to case 1, and turns on
  4. Lighting remains on for at least a second
  5. Lighting turns off
  6. System remains in this state until button is actuated again or powered off

The question is: Why does it behave like this? And how if possible, do I fix it such that a single press of the button equates to a single state increment, which the PIC then maintains for as long as the system is powered and the button is not actuated again?

#define SYS_FREQ        8000000L
#define FCY             SYS_FREQ/4
#define _XTAL_FREQ      4000000

/******************************************************************************/
/* User Global Variable Declaration                                           */
/******************************************************************************/


/******************************************************************************/
/* Main Program                                                               */
/******************************************************************************/

__CONFIG(MCLRE_ON & CP_OFF & OSC_IntRC);

void main(void)
{
    TRIS = 0b111110;

    unsigned char state = 0;

    while(1)
    {
        switch (state)
        {
            case 0: // IDLE/OFF
                if (GPIObits.GP0) GPIObits.GP0 = 0;
                break;
            case 1: // ON
                if (!GPIObits.GP0) GPIObits.GP0 = 1;
                break;
            case 2: // BLINK (slow)
                GPIObits.GP0 = !GPIObits.GP0;
                __delay_ms(100);
                break;
            case 3: // BLINK (fast)
                GPIObits.GP0 = !GPIObits.GP0;
                __delay_ms(50);
                break;
            case 4: // BEAT DETECT
                GPIObits.GP0 = GPIObits.GP2;
                break;
            default:
                state = 0;
                break;
        }

        if (!GPIObits.GP1)
        {
            __delay_ms(250);
            state++;
        }
    }
}

UPDATE: Since there seems to be a little confusion as to what I am trying to accomplish with this code/system, lets provide the full context. This microcontroller, the PIC10F200 is part of an overall board design for an electroluminescent (EL) wire driver. The miconcontroller simply controls whether or not the driver circuit is enabled by connecting GP0 to the EN port of the driver IC. The system has four modes of operation, the wire is constantly on, the wire is blinking, the wire is blinking faster, and the wire blinks whenever a low-frequency beat is detected (another circuit in the system). The transition from these modes of operation is governed by a pushbutton (on momentarily) switch to be mounted on the PCB. This necessitates that state in the code above remains stable between button actuations. It currently does not do this and behaves as described in the original part of this post. As the question title states, why isn't state stable currently, and how do I make it so?

UPDATE (2014-03-08): Solution

The following settings need to be set assuming GP0 is the output, GP2 is your T0CKI and you have a switch that drives the signal to LO when actuated.

TRIS = 0b111110;
OPTION = 0b11101111;

Whether or not bits 0-3 for OPTION really matter is a judgement call and whether or not you choose to use the WDT module.

Additionally, the implementation for the button release detection is a simple counter mechanism that resets upon GP2 being LO at any point during the count.

if (TMR0 > 0)
{
    while (count < 20)
    {
        if (!GPIObits.GP2) count = 0;
        __delay_ms(10);
        count++;
    }
    TMR0 = 0;
    state++;
}

Upvotes: 2

Views: 516

Answers (3)

GJ.
GJ.

Reputation: 10937

You have a hardware/software design problem!

  1. When your program is in delay loop than your key button is not checked!
  2. You are checking only on key press event, but you must also on key relase.

My purpose is that you can use GP2 (T0CKI) pin instead GP1 for key buttom. This pin has schmitt trigger input if is used as counter TMR0 input. After that configure your MCPU TMR0 as counter with external clock on GP2 (T0CKI) pin. You must also set the T0SE bit to configure counter that will increment on high-to-low transition on the T0CKI pin. In program after any loop check the TMR0 content if biger than 0 the key was pressed. Wait some ms and check if key was relased if relased than increase the state variable and clear TMR0 content.

Upvotes: 2

rmi
rmi

Reputation: 532

__delay_ms(250); <-- too small delay

While you are pressing the button slowly, the loop may rotate several times. Try increasing it to 1000ms.

Update

You should run the PIC with WDT (watch dog timer) disabled, otherwise it will reset the pic in few seconds. That is similar to your observation. You can blink a LED at the beginning of main function to check if this happen or you can initialization unsigned char state = 1; and see the behavior then.

Try this __CONFIG(MCLRE_ON & CP_OFF & OSC_IntRC & WDT_OFF);

Upvotes: 0

move your

if (!GPIObits.GP1){
    if(isPressed == false){
        //__delay_ms(250); //you dont need the delay
        state++;
        if(state == 5){state = 0;}
        isPressed = true;
    }
}
else{isPressed = false;}

before the switch statement. char isPressed = false; before the while loop

valter

Upvotes: 0

Related Questions