2
votes

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++;
}
3
So gp1 is the button, gp0 the led and gp2 is what? Also, how the button works? One click(press and release) gives 1 and another one 0?γηράσκω δ' αεί πολλά διδασκόμε
Yes, GP1 is connected to the button, GP0 the LED/EL wire driver EN port (eventually), GP2 is the input from a low-frequency detection circuit (bass from... loud music), which isn't connected yet since I can't even get the state machine to work properly. The button is an on-momentarily button, like the really basic kind you find in most cheap electronics. So when its depressed the contacts are connected and VDD goes through to the comparator circuit (LM324N op-amp).James Lui
why are you trying to do it with "states". The thing you are trying to achieve is easy. You dont need to complicate the code.γηράσκω δ' αεί πολλά διδασκόμε
With state, I am trying to simply toggle between modes of operation for the device which are labelled in the code. With my current knowledge of the PIC, how XC8 works, and C, this is the simplest way I could come up with to accomplish this task. I am totally willing to try different approaches, with the exception of any solution in assembly, because not only does changing the language not address the problem, it introduces unnecessary implementation and support complexity.James Lui
What you describe is one state. When the button is pressed. In the future you may want to add something else but as it is right now it is one state. if (!GPIObits.GP1){GPIObits.GP0 = 1; __delay_ms(1000); GPIObits.GP0 = 0;}γηράσκω δ' αεί πολλά διδασκόμε

3 Answers

2
votes

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.

0
votes

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

0
votes
__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);