Boas práticas e padrões arquiteturais para ISR + FreeRTOS
Quando um sistema cresce, o uso correto de ISR + RTOS deixa de ser apenas uma questão de sintaxe e passa a ser uma decisão arquitetural. Sistemas bem projetados seguem padrões claros; sistemas problemáticos acumulam lógica em interrupções e se tornam impossíveis de manter ou certificar.
Nesta seção, vamos consolidar boas práticas reconhecidas na indústria e padrões amplamente usados em sistemas críticos.
6.1 ISR mínima: a regra mais importante
Uma ISR deve:
- Executar o mínimo possível
- Não conter lógica de negócio
- Não realizar cálculos complexos
- Não acessar periféricos desnecessários
Modelo correto de ISR:
Interrupção
├─ Captura evento
├─ Copia dado mínimo (se necessário)
├─ Sinaliza task
└─ Retorna
Exemplo correto:
void ADC_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
uint16_t sample = ADC->DR;
xQueueSendFromISR(adcQueue, &sample, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
Exemplo incorreto (anti-padrão):
void ADC_IRQHandler(void)
{
uint16_t sample = ADC->DR;
/* Processamento pesado */
sample = filter(sample);
updateControlLoop(sample);
logData(sample);
}
Esse erro gera:
- Latência elevada
- Jitter
- Bloqueio de outras interrupções
- Sistema não determinístico
6.2 Deferred Interrupt Processing (DIP)
O padrão conhecido como Deferred Interrupt Processing consiste em:
- Usar a ISR apenas para sinalizar
- Delegar o processamento para uma task
No FreeRTOS, isso é feito naturalmente com:
- Notificações
- Filas
- Semáforos
Esse padrão:
- Reduz tempo de ISR
- Melhora previsibilidade
- Facilita testes e manutenção
6.3 Escolha correta do mecanismo de sinalização
Regra prática:
- Evento simples, 1 consumidor
→ Task Notification - Evento simples, múltiplos consumidores
→ Semáforo binário - Evento com dados
→ Queue - Estados ou múltiplas condições
→ Event Group
Evite usar filas para tudo. Muitos sistemas sofrem com:
- Uso excessivo de memória
- Overhead desnecessário
- Latência acumulada
6.4 ISR não é lugar para HAL complexa
Bibliotecas HAL (como STM32 HAL):
- Não são projetadas para ISR longa
- Podem usar bloqueios internos
- Podem depender de delays
Boa prática:
- ISR acessa registradores diretamente
- HAL é usada nas tasks
Exemplo comum:
void USART_IRQHandler(void)
{
uint8_t byte = USART1->DR;
xQueueSendFromISR(rxQueue, &byte, &xHigherPriorityTaskWoken);
}
E a HAL na task:
HAL_UART_Transmit(&huart1, buffer, len, HAL_MAX_DELAY);
6.5 Prioridades de tasks e interrupções alinhadas
Outro erro comum é:
- ISR sinaliza uma task
- Task tem prioridade baixa
- Evento demora a ser tratado
Regra:
Tasks que recebem sinais de ISR devem ter prioridade adequada à criticidade do evento.
Se a task é tempo real:
- Prioridade alta
- Código enxuto
- Sem bloqueios longos
6.6 Checklist profissional ISR + FreeRTOS
Antes de validar um projeto, revise:
- ISR é curta?
- ISR não usa API padrão do FreeRTOS?
- Prioridade da ISR respeita
configMAX_SYSCALL_INTERRUPT_PRIORITY? - Mecanismo de sinalização é o mais eficiente?
- Task associada tem prioridade adequada?
- Não há lógica de negócio em ISR?
Se todas as respostas forem “sim”, o sistema tende a ser robusto.