Potential software timer issue

Hi,
The code halts in both the hardware running standalone and in debug mode. I am trying to figure out if it is related to a software timer below which is about debouncing of a switch.

UPDATE: the exception that occurs:

  1. happens either when exiting CBK_ChangeNotif_ProdTrigger() or after executing taskEXIT_CRITICAL() which is just the previous line. Or perhaps after starting the timer because it never gets to the timer callback function.
  2. the exception is a interrupt vector exception and seems to point to a memory address that does not appear in the disassembly listing (i.e. there’s nothing there)

Here is what I do…

During initialization I create the timer which I assume persists forever because I never delete it (only stop it and restart it). I also check that it is is created by checking the handle:

app_applyData.xTimer_Handle_ApplyDebounce = xTimerCreate("xSoftTmr_ApplyDebounce",
                                                            BTN_DEBOUNCE_MS_INITIAL_DEBOUNCE,
                                                            pdFALSE,
                                                            ( void * ) 0,
                                                            CBK_SoftTmr_ApplyDebounce);

if (app_applyData.xTimer_Handle_ApplyDebounce == NULL)
    {
       ErrorMessage();
       While(1);
    }

The timer is started only when a switch is sensed by this INT callback function:

void CBK_ChangeNotif_ProdTrigger(GPIO_PIN pin, uintptr_t context)
{
    BaseType_t xHigherPriorityTaskWoken;
    xHigherPriorityTaskWoken = pdFALSE;

    taskENTER_CRITICAL();
    uint32_t tmpDebounceState = app_applyData.ui32ProdTrigState;

    if ((tmpDebounceState == PROD_TRIG_STATE_STANDBY) && //In in standby and
            (PROD_TRIG_Get() == PROD_TRIG_PRESSED))         //and button is pressed
    {
        PROD_TRIG_InterruptDisable();  //disable this int
        xTimerStop(app_applyData.xTimer_Handle_ApplyDebounce, 0);
        xTimerChangePeriod(app_applyData.xTimer_Handle_ApplyDebounce, BTN_DEBOUNCE_MS_INITIAL_DEBOUNCE, 0); //reset time period to initial debounce
        xTimerReset(app_applyData.xTimer_Handle_ApplyDebounce, 0); 
        //start debouncing (timer) - after the debounce happened then will also send the message to the apply
        xTimerReset(app_applyData.xTimer_Handle_ApplyDebounce, 0); //reset just in case
        xTimerStart(app_applyData.xTimer_Handle_ApplyDebounce, 0);
        app_applyData.ui32ProdTrigState = PROD_TRIG_STATE_PROD_DETECTED;
    }
    
    if ((tmpDebounceState == BTN_DEBOUNCE_STATE_PROD_PRESENT) && //State is "pressed" 
            (PROD_TRIG_Get() == PROD_TRIG_RELEASED))        //and button is released
    {
        PROD_TRIG_InterruptDisable(); //disable this int
        //start debouncing (timer) - after the debounce happened then will also send the message to the apply
        xTimerStop(app_applyData.xTimer_Handle_ApplyDebounce, 0);
        xTimerChangePeriod(app_applyData.xTimer_Handle_ApplyDebounce, BTN_DEBOUNCE_MS_INITIAL_DEBOUNCE, 0); //reset time period to initial debounce
        xTimerReset(app_applyData.xTimer_Handle_ApplyDebounce, 0); 
        xTimerStart(app_applyData.xTimer_Handle_ApplyDebounce, 0);
        app_applyData.ui32ProdTrigState = BTN_DEBOUNCE_STATE_RELEASE_DETECTED;
    }
    
    taskEXIT_CRITICAL();
    portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}

The callback is

void CBK_SoftTmr_ApplyDebounce(TimerHandle_t pxExpiredTimer)
{
    _nop();
    switch(app_applyData.ui32ProdTrigState)
    {
        case PROD_TRIG_STATE_STANDBY:
        {
            break;
        }

        case PROD_TRIG_STATE_PROD_DETECTED: 
        {//here the initial debouncing has elapsed 
            //check if the button is still pressed
            _nop();
            if (PROD_TRIG_Get() == PROD_TRIG_PRESSED) 
            {//if still pressed...
                //so ONLY if stepper is not running then send message
                if (! app_applyData.bApplyStepperIsRunning) //only if previous cycle is complete then send message
                {
                    xTaskNotify(xAPP_APPLY_Tasks, NOTIF__APPLY__PRODUCT_SENSED, eSetBits);
                }
                app_applyData.ui32ProdTrigState = BTN_DEBOUNCE_STATE_PROD_PRESENT;
            }
            else
            {//if released
                //go back to standby state
                app_applyData.ui32ProdTrigState = PROD_TRIG_STATE_STANDBY;
            }
            //in both cases re-enable prod trigger int
            PROD_TRIG_InterruptEnable();
            break;
        }
        
        case BTN_DEBOUNCE_STATE_PROD_PRESENT:
        {
            break;
        }        

        case BTN_DEBOUNCE_STATE_RELEASE_DETECTED:
        {//here the initial debouncing has elapsed 
            _nop();
            //check if the button is still released
            if (PROD_TRIG_Get() == PROD_TRIG_RELEASED) 
            {
                app_applyData.ui32ProdTrigState = PROD_TRIG_STATE_STANDBY;
            }
            else
            {//if released
                //go back to standby state
                app_applyData.ui32ProdTrigState = BTN_DEBOUNCE_STATE_PROD_PRESENT;
            }
            //in both cases re-enable prod trigger int
            PROD_TRIG_InterruptEnable();
            break;
        }

        default:
        {
            while(1);
    //        send debug error...
            break;
        }
    }
}

And through the code I use the following functions

    xTimerStop(app_applyData.xTimer_Handle_ApplyDebounce, 0);
    xTimerChangePeriod(app_applyData.xTimer_Handle_ApplyDebounce, BTN_DEBOUNCE_MS_INITIAL_DEBOUNCE, 0); //reset time period to initial debounce
    xTimerReset(app_applyData.xTimer_Handle_ApplyDebounce, 0); 
    xTimerStart(app_applyData.xTimer_Handle_ApplyDebounce, 0);

Few questions:

  1. am I doing anything wrong in the above code?
  2. is there any advantage in creating the timer statically?
  3. being set to “single shot” it does not need any particular attention, correct? It still persists forever in memory and it just calls the callback after it is started and the timeout is expired. Correct?

Thank you :slight_smile:

Is CBK_ChangeNotif_ProdTrigger a function called by an ISR ?
You mentioned it’s an INT callback.
In this case it’d be crucial to use FromISR API calls.

Please always say what ‘halts’ means. I don’t think it ever truly halts. It may be spinning in an exception handler, or an assert or whatever - what the processor is actually doing is always vital information when trying to offer assistance as to what the cause might be.

Thank you Richard and Hartmut,
answers to both below.

Hartmut,
I did have portEND_SWITCHING_ISR(xHigherPriorityTaskWoken); as last line before exiting but it made no difference. I removed it while testing something and posted the code before putting it back in. I now updated the OP.

Richard,
when using the debugger it jumps to the following built-in code

##################################################################
# General Exception Vector Handler
# Jumps to _general_exception_context
##################################################################
.section .gen_handler,code
.align 2
.set noreorder
.ent _gen_exception
_gen_exception:
0: la k0,_general_exception_context
jr k0
nop
.end _gen_exception

which then calls the following “locking” function with while(1) loop:

/*******************************************************************************
Function:
void _general_exception_handler ( void )

  Description:
    A general exception is any non-interrupt exception which occurs during program
    execution outside of bootstrap code.

  Remarks:
    Refer to the XC32 User's Guide for additional information.
 */

void __attribute__((noreturn)) _general_exception_handler ( void )
{
    /* Mask off the ExcCode Field from the Cause Register
    Refer to the MIPs Software User's manual */
    _excep_code = (_CP0_GET_CAUSE() & 0x0000007C) >> 2;
    _excep_addr = _CP0_GET_EPC();

    while (1)
    {
        #if defined(__DEBUG) || defined(__DEBUG_D) && defined(__XC32)
            __builtin_software_breakpoint();
        #endif
    }
}

It happens either:

  • after executing portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
  • or after exiting the callback function which is just the next line
  • or after executing taskEXIT_CRITICAL() in the line above portEND_SWITCHING_ISR (because for some reason the debugger skips portEND_SWITCHING_ISR - that maybe just the debugger

In there the _excep_code is 0x0A
and the address value 0x9D017164

The call stack is empty (shows only “runtime exception at that address shown above: 0x9D017164”).
But when I look at the disassembly, the address 0x9D017164 doe not appear.

I thought of a memory leak but I have absolutely no code of mine that uses dynamic allocation or passes pointers.

That is all I managed to find out. Any ideas or suggestions?

Thank you as usual! :slight_smile:

@Rik001 Please re-read your other related thread.
You’ve to use e.g. xTimerStartFromISR etc. along with taskENTER/EXIT_CRITICAL_FROM_ISR in ISRs.

I believe you are liberally mixing ISR and non ISR code in this function?

I changed all functions in the ISR to their FromISR version and it seems to work.

Thank you both!! :slight_smile:

Use of configASSERT helps a lot in these cases, as rather than faulting it will go into an assertion loop showing where the bad line is.

Rik, you should also explain what the critical section is used for here. I don’t think you need it, and I’ve tried to explain several times why. In any case, it’s just coincidential that taskENTER_CRITICAL
works in an ISR. For portability reasons, IF you need the CS here, you should be alert when you read the task prefix in ISR code. It shouldn’t be there.

Thank you Richard and RAc,

RAc,
yes, you previously mentioned using a mutex instead. The issues is that I had a lot of code already done this way and being a novice at freeRTOS I am a bit worried it might break. The client has been putitng a lot of pressure because they need a demo for an exhibition coming up, so working 14 hours/day does not help. And being a newbie worried about making mistakes I might also overthink/complicate things or misunderstand the explanation. :slight_smile:

That’s why I am very appreciative of all the help you guys have been providing here! :slight_smile: :slight_smile:

I’m sorry, Rik, I give up. Best of luck to you. There appears to be no way to help you, at least not in my words.

If I had managed to bring over the message to you, you wouldn’t even think about a mutex in conjunction with ISRs. It can’t and won’t work, and I’ve told you so several times, yet to no avail.

Again, best of luck with your project.

Sorry RAc,
I have been reading multiple posts and got confused. Just too tired at the moment. :frowning:

The issue is that a Mutex/Semaphore provides NO protection from an ISR. The ISR could at best check to see if the guard was taken and not interfere, but it is can work with the ISR sometimes putting off the update, maybe it is better to just aways put it off in the ISR.

This is one reason I like to put my updates in a limited number of small functions that include the locking inside them and are used by multiple threads. That says that if I need to change the type of locking to use, its use is localized. At the minimum, you use an (inline) function to get and give the lock for the resource so it is easy to change.

The key is that sometimes a short update that might seem to want to use a mutex, might be better of (i.e. quicker) using a simple critical section that is very short, but it it get longer for some reason, it is nice to be able to easily change it to something else.

Thank you Richard :slight_smile: