cportgpiobatteryfirmware

How to detect If a digital pin is blinking (from high to low) in C?


I am writing a firmware application. I must read a GPIO pin connected to a smart battery, which communicates the state of the battery. A high value in the pin means the battery is in an unknow state, a low value means the battery is disconnected, and an error is indicated by a 2.5 Hz blink. So far I have managed to detect the disconnection and the unknown state with the following function:

void Check_Bat_Connected(void)
{ 
static int Low_Level_debounce = 0
if(IOPORT_LEVEL_LOW == Pin_Level)
    {
        if(25 == Low_Level_debounce)
        {
            BAT_Status = BAT_Not_Connect;
        }
        else if(Low_Level_debounce < 25)
        {
            Low_Level_debounce++;
        }
        else
        {
            /**/
        }
    }
    else
    {
        Low_Level_debounce = 0;
        BAT_Status = BAT_Unknown;
    }
}

This function is being called in a loop by a battery handler which has the role of getting info about the battery. I added a debounce variable to increase robustness againt false disconnects of the battery.

My question is now, how do I go around detecting if the LED is blinking at 2.5 Hz? I dont have any information on how much time passes between two calls of the Check_Bat_Connected function, even if I assume that the frequency is much higher than 2.5 Hz. How can I implement this last check in a robust way?


Solution

  • Presuming the hardware can provide an interrupt on a fairly reasonable interval, there's a way to debounce and detect that 2.5Hz signal.

    Use an static unsigned 32 bit variable to accumulate signal samples (0 or 1) by left shifting and OR'ing:

    sample = (sample << 1) | !!currentVal; // shift and store one more bit until 32 gathered
    

    Imagine a boucey 2.5Hz square wave sampling at about 15ms (you want 32 samples to capture slightly more than one period of the input signal):

    00000000000011111111111111000000 // 50% clean duty cycle (not to scale)
    T           T             T
    01010000000010101111111111010100 // bouncing samples gathered
    

    Shifting the 32 samples right until a stable value is detected. Here "stable" is 5 consecutive samples all high:

                               vvvvv // 5 lowest bits with iterative shifting
    01010000000010101111111111010100 // fails 0x14 
    00101000000001010111111111101010 // fails 0x0A 
    00010100000000101011111111110101 // fails 0x05
    00001010000000010101111111111010 // fails 0x02
    00000101000000001010111111111101 // fails 0x01
    00000101000000001011001011111111 // Succeeds 0x1F
                ^----1/2 period----^    XOR of 1 indicates signal is toggling
    

    This example data has captured a recent transition from hi to low. This works equally well if the transition were from low to hi. Convince yourself the following while() loop detects either 5 set bits or 5 reset bits as the low order bits when shifting:

    int range = 0;
    while( range++ < 10 && (sample & 0x1F) < 0x1F || (sample & 0x1F) > 0 ) )
        sample >>= 1;
    
    toggling = range < 10 && ((sample & 0x1) ^ ((sample >> 20) & 0x1));
    

    Once you have a stable value in the low order bits, XOR'ing with a bit to the left at 1/2 the period of the signal will compare stable values.
    If the result of the XOR is 1, then the signal is flashing.
    If the result of the XOR is 0, the signal is stable (may be high, may be low.)

    The range counter is necessary to prevent a signal that is ON from shifting far enough to cause the prepended zeros of shifting to be considered.

    If this detection is critical, one could involve more bits being XOR'd to form a majority decision as to whether or not the input signal is alternating high/low.