O modelo de execução do FreeRTOS e o papel das interrupções
Para compreender corretamente a interação entre ISR e FreeRTOS, é fundamental entender como o kernel enxerga o sistema como um todo. Diferentemente de um firmware bare-metal, onde o fluxo de execução é essencialmente linear e interrompido apenas por ISRs, no FreeRTOS o controle do processador é mediado por um escalonador preemptivo, orientado por prioridades e eventos.
No FreeRTOS, o código da aplicação é dividido em tasks, cada uma com sua própria pilha, prioridade e estado. Essas tasks alternam entre estados como Running, Ready, Blocked e Suspended. O escalonador decide qual task executa a cada instante, normalmente em resposta a eventos como expiração de tempo (tick interrupt), liberação de recursos ou sinalização vinda de uma interrupção.
As interrupções, por sua vez, existem fora desse modelo de escalonamento. Uma ISR:
- Interrompe qualquer task em execução, independentemente da prioridade.
- Executa com contexto mínimo (registradores salvos automaticamente pelo hardware).
- Não é gerenciada pelo escalonador.
- Não pode ser interrompida por tasks, apenas por interrupções de maior prioridade.
Isso significa que, do ponto de vista do FreeRTOS, a ISR é um evento assíncrono externo ao kernel, que pode — ou não — interagir com ele de forma controlada. Essa interação ocorre exclusivamente por meio de APIs específicas para ISR, projetadas para manter a integridade do kernel e preservar o determinismo do sistema.
Um ponto essencial é que o FreeRTOS também depende de interrupções para funcionar. A principal delas é a Tick Interrupt, normalmente gerada por um timer de hardware (SysTick, TIMx, etc.). Essa interrupção é responsável por:
- Atualizar o contador de tempo do sistema.
- Desbloquear tasks que estavam aguardando timeout.
- Forçar uma troca de contexto (context switch) quando necessário.
Por isso, nem todas as interrupções são iguais no contexto do FreeRTOS. O kernel estabelece uma clara distinção entre:
- Interrupções que podem chamar APIs do FreeRTOS
- Interrupções que não podem interagir com o kernel
Essa distinção é controlada por prioridades de interrupção e por macros de configuração, como configMAX_SYSCALL_INTERRUPT_PRIORITY. Em arquiteturas Cortex-M, isso é particularmente crítico, pois o NVIC (Nested Vectored Interrupt Controller) permite preempção entre interrupções.
Se uma ISR com prioridade mais alta do que a permitida tentar chamar uma API do FreeRTOS, o comportamento será indefinido — e, na prática, frequentemente resulta em hard faults, travamentos silenciosos ou corrupção de estruturas internas do kernel.
Portanto, o papel correto de uma ISR em um sistema FreeRTOS não é “resolver o problema”, mas sim:
- Capturar rapidamente o evento de hardware.
- Armazenar dados mínimos, se necessário.
- Sinalizar uma task apropriada.
- Retornar o controle o mais rápido possível.
Essa filosofia é a base para todo o restante do artigo. A partir daqui, vamos detalhar:
- Como o FreeRTOS impõe regras para chamadas a partir de ISRs
- O conceito de “API FromISR”
- Como ocorre a troca de contexto provocada por uma interrupção