Interrupt signal gets lost

I have a continuously rotating servo motor that is connected to the rotary encoder which calculates the exact angle of rotation. I need to stop the spinning of the servo motor once it rotates 90 degrees.

I am using an interrupt signal to calculate the rotation angle of my rotary encoder. Sometimes some of the interrupt calls get lost, so the angle is calculated incorrectly, I think probably it happens when another process (servo motor) is running while the interrupt happens. When I rotate the rotary encoder by hand it always shows the correct angle.

Is there a way to queue those interruptions somehow so they will never be lost? I am using FreeRTOS and STM32

Here is the pseudocode of my interrupt handler.

 volatile int rotationAngle = 0;

 void handleRotaryEncoderInterrupt(uint8_t index) {
     GPIO_PinState clk = HAL_GPIO_ReadPin(clkPort, clkPin);
     GPIO_PinState dt = HAL_GPIO_ReadPin(dtPort, dtPin);

     if(clk == GPIO_PIN_SET && dt == GPIO_PIN_RESET) {
        rotationCount++;
        rotationAngle = rotationCount * STEPS_TO_ANGLE;
     }
 }

And another task that runs periodically and stops the servo motor when it rotates 90 degrees.

void myTask() 
{
    while(1) 
   {
        myServo.duty = MOVE_FORWARD;

        while(rotationAngle % 90 != 0)
        {
            osDelay(100);
        }

        myServo.duty = STOP;
        
        osDelay(10000);
   }
}

What is this another process? Are you saying that your interrupt handling code takes too long and you miss other instances of the same interrupt during that time? If yes, you can defer bulk of the processing to a high priority task to ensure that you do not miss any event, something like the scheme described here - Safer Interrupt Handling Demo for NXP LPCXpresso55S69 Development Board - FreeRTOS

The thing is that my interrupt handler is already very fast, and it works fine when I move the rotary encoder by hand, the problem arises only when I run it with a servo motor. Please check also the pseudocode that I have attached.

Is the encoder ISR a fairly high priority interrupt?

Do you have any tasks that uses critical sections that might be too long?

Your encoder counting looks incorrect, as it presumes the encoder can NEVER step backwards, which is normally a possibility, even if only sitting on an edge and toggling back and forth.

I would tend to see if your processor has a timer that can put the encoder into to keep a count of position in hardware.

Since you are not using any FreeRTOS APIs in the ISR, make sure that its priority is high enough to be not effected by FreeRTOS critical sections. Assuming that you are using Cortex-M, this page provides useful details - RTOS for ARM Cortex-M

Also, check that osDelay is not a busy wait and maps to vTaskDelay.

How did you verify that the rotationAngle is wrong ?
The check in the task while(rotationAngle % 90 != 0) might behave unexpected / fail if 2 or more interrupts occur while waiting in osDelay. The poll loop with this delay is wrong or unsafe.
You could signal an event (task notification, semaphore) to the task and do the calculation and servo control completely in the task or do the calculation in the ISR and send just the STOP event to the task or something similar.
Key is to make the task waiting for an event signaled by the hardware/ISR instead of wrongly polling (volatile ?) variables with a delay.

Are there any other tasks running? Could it be that one of those tasks claims the critical section for a long time? what is your max sys call priority, and what interrupt priority does your isr have?

Not really, all the tasks have osPriorityBelowNormal priority.

I am printing the value on the 7-segment display. I think if it miscalculates in one iteration, it will fix itself on the next one. But actually, the rotary encoder misses the step and shows the wrong number.

That does not answer the question, sorry for being unclear. If your isr is under os control (interrupt priority below max syscall) AND any task with ANY task priority claims the critical section for an edtended amount of time, your interrupts may get starved out.

Hey @davithbul I wanted to reach out and see if you’re still seeing this same issue, or if you’ve gotten everything working as you expect? If you’re still blocked is there anything different you’ve tried based off the information supplied in this thread?

Sorry for my late response @RAc , my configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY has a value of 5, and my interrupts also have a priority of 5.

Hey @skptak , not yet. I have tried reading pin rotary encoder pin statuses from the task, instead of using interrupt calls, but it is still not 100% stable.

Ok. Again, that implies that any claim for the critical section from any task at any task priority will suppress interrupts including yours. You may want to check if this happens. For example, sloppy implementations of the backend of printf() may do that, so if you have excessive diagnostic output and that implies a CS claim, that may account for your problem.

I think I don’t have critical sections. Is there a way to increase the priority of my ISR so no task can claim priority?

If your ISR does not call any FreeRTOS API (which seems to be the case for you), you can set your interrupt priority higher (numerically lower number).

I think priority 5 is the lowest I can set (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY = 5). I can not set anything lower than that, at least from stm32cubemx.

You could manually edit the generated prio and test. Cube seemingly assumes that ISRs make use of FreeRTOS API and helps you by restricting the prio to the valid range.
Or can you tell cube that an interrupt resp. its corresponding ISR doesn’t make use of FreeRTOS API when configuring the prio ?
BTW What’s the minimum/worst case time between encoder pulses ? I mean do you know that your MCU is basically fast enough ?

Look for the function call NVIC_SetPriority or something like that where the priority of your interrupt is set and edit that code directly.