1

I've written a timer class to be able to software debounce the readings of a gpio expander with sixteen buttons wired to a teensy 4.0. In order for it to work as intended I need to create the timer object as static. However - even though the code compiles - this causes my teensy to crash.

After some troubleshooting it seems to me that it boils down to the static keyword, because creating a non-static Timer object does not cause the crash (although, the code doesn't work as intended, obviously).

I've likely missed out on the finer details in creating static objects, and therefore I'm asking for feedback.

In short, here's the code:


//Timer.h:
class Timer {
   public:
    Timer() = default;

    // Array
    void setTimeLimit(const uint8_t debounceTime, const uint8_t button);
    void stop(const uint8_t button);
    bool running(const uint8_t button);
    bool limitReached(const uint8_t button);

    // Single variable
    void setTimeLimit(const uint8_t debounceTime);
    void stop();
    bool running();
    bool limitReached();

   private:
    // Array
    uint16_t m_running{};
    uint32_t m_timeStamp[16]{};
    uint8_t m_debounceTime[16]{};

    // Single variable
    bool m_runningSingle{};
    uint32_t m_timeStampSingle{};
    uint8_t m_debounceTimeSingle{};
};



//Timer.cpp:
// Array
void Timer::setTimeLimit(const uint8_t debounceTime, const uint8_t button) {
    m_timeStamp[button] = millis();
    m_debounceTime[button] = debounceTime;
    bitSet(m_running, button);
}

void Timer::stop(const uint8_t button) {
    bitClear(m_running, button);
}

bool Timer::running(const uint8_t button) {
    return bitRead(m_running, button);
}

bool Timer::limitReached(const uint8_t button) {
    return (millis() - m_timeStamp[button]) >= m_debounceTime[button];
}

// Single variable
void Timer::setTimeLimit(const uint8_t debounceTime) {
    m_timeStampSingle = millis();
    m_debounceTimeSingle = debounceTime;
    m_runningSingle = true;
}

void Timer::stop() {
    m_runningSingle = false;
}

bool Timer::running() {
    return m_runningSingle;
}

bool Timer::limitReached() {
    return (millis() - m_timeStampSingle) >= m_debounceTimeSingle;
}



//Crashing function:
void ReadGPIOExpanderBase::debounceButtons(const uint8_t firstButton) {
    static Timer timer;      //<<<< Declaration causing problems

    for (uint8_t button = firstButton; button <= 15; ++button) {
        if (timer.limitReached(button)) {
            if (timer.running(button)) {
                timer.stop(button);
            }

            else {
                if (bitRead(m_inputStates, button) < bitRead(m_portReading, button)) {
                    bitSet(m_inputStates, button);
                    timer.setTimeLimit(m_risingDebounceTime, button);
                }

                else if (bitRead(m_inputStates, button) > bitRead(m_portReading, button)) {
                    bitClear(m_inputStates, button);
                    timer.setTimeLimit(m_fallingDebounceTime, button);
                }
            }
        }
    }
}
Erik
  • 133
  • 1
  • 5
  • 1
    1) [initialization order ‘fiasco’ (problem)”](https://isocpp.org/wiki/faq/ctors#static-init-order) . 2) [What is a debugger and how can it help me diagnose problems?](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) – Jesper Juhl Oct 30 '22 at 10:33
  • @JesperJuhl You nailed, it, thank you very much! However, is it possible to adapt the COFU idiom for multiple instantiations? I need to create a timer object at several instances... – Erik Oct 30 '22 at 15:43
  • Never rely on static initialization order. Don't use global variables. You don't actually need to. – Jesper Juhl Oct 30 '22 at 15:47
  • @JesperJuhl I don't know any alternatives to static initialization in order to retain value out of scope. If I don't need it, what are the alternatives on an embedded system? – Erik Oct 30 '22 at 17:45

0 Answers0