Search code examples
c++hardware

Problem with programming a basic hardware


I have an animation shown on LEDs. When the button is pressed, the animation has to stop and then continue after the button is pressed again. There is a method that processes working with the button:

void checkButton(){

GPIO_PinState state;

state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
if (state == GPIO_PIN_RESET) {
    while(1){
        state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
        if (state == GPIO_PIN_SET){
            break;
        }
    }
    //while (state == GPIO_PIN_RESET) {
        //state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
    //}
}
}

GPIO_PIN_SET is the default button position. GPIO_PIN_RESET is the condition when the button is pressed. The commented section is what I tried instead of the while(1){...} loop. The checkButton() method is called in the main loop from time to time to be run. The program runs on STM32 with an extension module (here the type of an extension module does not matter). The fact is that this method stops animation just for a moment and does not work as I would like it to. Could you correct anything about this program to make it work properly?


Solution

  • Could you correct anything about this program to make it work properly?

    My guess is that you are trying to add a 'human interaction' aspect to your design. Your current approach relies on a single (button position) sample randomly timed by a) your application and b) a human finger. This timing is simply not reliable, but the correction is possibly not too difficult.


    Note 1: A 'simple' mechanical button will 'bounce' during it's activation or release (yes, either way). This means that the value which the software 'sees' (in a few microseconds) is unpredictable for several (tbd) milliseconds(?) near the button push or release.

    Note 2: Another way to look at this issue, is that your state value exists two places: in the physical button AND in the variable "GPIO_PinState state;". IMHO, a state value can only reside in one location. Two locations is always a mistake.

    The solution, then (if you believe) is to decide to keep one state 'record', and eliminate the other. IMHO, I think you want to keep the button, which seems to be your human input. To be clear, you want to eliminate the variable "GPIO_PinState state;"


    This line:

    state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
    

    samples the switch state one time.

    HOWEVER, you already know that this design can not rely on the one read being correct. After all, your user might have just pressed or released the button, and it is simply bouncing at the time of the sample.

    Before we get to accumulating samples, you should be aware that the bouncing can last much more than a few microseconds. I've seen some switches bounce up to 10 milliseconds or more. If test equipment is available, I would hook it up and take a look at the characteristics of your button. If not, well, you can try the adjusting the controls of the following sample accumulator.


    So, how do we 'accumulate' enough samples to feel confident we can know the state of the switch?

    Consider multiple samples, spaced-in-time by short delays (2 controls?). I think you can simply accumulate them. The first count to reach tbr - 5 (or 10 or 100?) samples wins. So spin sample, delay, and increment one of two counters:

    stateCount [2] = {0,0};  // state is either set or reset, init both to 0
    //              vvv-------max samples
    for (int i=0; i<100; ++i)  // worst case how long does your  switch bounce
    {
        int sample = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);  // capture 1 sample
    
        stateCount[sample] += 1;  // increment based on sample
    
        // if 'enough' samples are the same, kick out early
        //                       v ---- how long does your switch bounce
        if (stateCount[sample] > 5) break; // 5 or 10 or 100 ms
    
        // to-be-determined --------vvv --- how long does switch bounce
        std::this_thread::sleep_for(1ms);  // 1, 3, 5 or 11 ms between samples
        // C++ provides, but use what is available for your system
        // and balanced with the needs of your app
    }
    

    FYI - The above scheme has 3 adjustments to handle different switch-bounce durations ... You have some experimenting to do. I would start with max samples at 20. I have no recommendation for sleep_for ... you provided no other info about your system.

    Good luck.

    It has been a long time, but I think I remember the push-buttons on a telecom infrastructure equipment bounced 5 to 15 ms.