Casos Reais de Depuração em STM32 com GDB
Agora vamos sair do nível “comando isolado” e entrar no que realmente acontece no campo: firmware travando, HardFault misterioso, interrupção que nunca retorna, stack corrompida, task do FreeRTOS que some. Aqui eu vou mostrar o raciocínio técnico e como o GDB entra como ferramenta cirúrgica.
3.1 Debug de HardFault no Cortex-M (passo a passo real)
HardFault é o clássico pesadelo em STM32. Pode ser causado por:
- Ponteiro inválido
- Acesso a região proibida
- Execução de instrução inválida
- Stack overflow
- Divisão por zero (se habilitada)
Quando o firmware cai em HardFault_Handler, a primeira coisa que você faz no GDB é:
break HardFault_Handler
continue
Assim que ele parar:
bt
Se o stack ainda estiver íntegro, o backtrace já pode mostrar a função que causou o problema. Mas muitas vezes ele para direto no handler e o backtrace não ajuda.
Estratégia avançada: inspecionar stack frame salvo
No Cortex-M, quando ocorre exceção, o hardware empilha automaticamente:
- r0
- r1
- r2
- r3
- r12
- lr
- pc
- xPSR
O sp naquele momento aponta para esse frame.
Então:
info registers
Observe $sp. Agora:
x/8xw $sp
O valor na posição correspondente ao PC salvo normalmente é o 7º item (offset 6 * 4 bytes).
Exemplo:
p/x *((uint32_t*)$sp + 6)
Isso revela o endereço onde a falha ocorreu.
Depois:
list *0x08001234
Agora você sabe exatamente a linha que causou o HardFault.
Esse procedimento separa um depurador amador de um engenheiro embarcado sério.
3.2 Detectando Stack Overflow
Sintomas típicos:
- Firmware trava aleatoriamente
- Variáveis globais mudam sem explicação
- HardFault intermitente
Primeiro, verifique o stack pointer:
p/x $sp
Compare com o mapa de memória do STM32 (por exemplo, RAM começa em 0x20000000).
Se o SP estiver fora da faixa esperada → corrupção.
Você pode inspecionar a área da stack:
x/32xw 0x20000000
Se estiver usando FreeRTOS, cada task tem sua própria stack. Você pode:
info threads
thread 2
bt
Se estiver usando OpenOCD com suporte RTOS, ele já mostra tasks como threads.
3.3 Debug de Interrupção que trava o sistema
Um erro clássico é ISR muito longa ou com erro lógico.
Coloque breakpoint na ISR:
break EXTI0_IRQHandler
Continue.
Quando parar, faça:
bt
Verifique se ela está sendo chamada em loop.
Se você suspeita que a flag não está sendo limpa, pode verificar registradores do periférico diretamente:
x/1xw 0x40010410
Se necessário, force limpeza manual:
set {uint32_t}0x40010410 = 0
Isso permite validar hipóteses sem recompilar.
3.4 Debug de variáveis sendo alteradas por DMA
Cenário clássico: buffer de ADC + DMA e dados aparecem corrompidos.
Você pode colocar watchpoint:
watch adcBuffer[10]
Quando o DMA escrever, o core para.
Isso é extremamente útil para descobrir se:
- DMA está configurado errado
- Overrun está ocorrendo
- Ponteiro está errado
Limitação: watchpoints são limitados por hardware.
3.5 Depuração com FreeRTOS
Se estiver usando FreeRTOS no STM32, o GDB pode enxergar tasks como threads (dependendo da configuração do OpenOCD).
Comandos úteis:
info threads
Selecionar task:
thread 3
Ver backtrace da task:
bt
Isso é fundamental para:
- Task bloqueada em semáforo
- Deadlock
- Stack overflow por task
Você também pode inspecionar TCB manualmente:
p *pxCurrentTCB
Se estiver com símbolos.
3.6 Debug de código otimizado (-O2 / -O3)
Aqui está um ponto crítico.
Quando o firmware está compilado com -O2, você pode observar:
- Variáveis “sumindo”
- Linhas puladas
- Breakpoint não sendo atingido
Por quê?
Porque o compilador:
- Reordena instruções
- Remove variáveis temporárias
- Inline functions
Solução prática durante debug:
Use -Og.
Se for obrigado a usar -O2, utilize:
disassemble
E:
layout asm
Ou:
stepi
Você passa a depurar em nível de assembly.
3.7 Arquivo .gdbinit para automatizar sessão
Você pode criar um .gdbinit no diretório do projeto:
Exemplo:
target extended-remote :3333
monitor reset halt
load
break main
Assim, toda vez que abrir:
arm-none-eabi-gdb firmware.elf
Ele já conecta automaticamente.
Isso economiza tempo e padroniza processo.
3.8 Diagnóstico de travamento por clock ou PLL
Se o firmware altera clock cedo (SystemInit), pode perder comunicação com debugger.
Solução:
- Conectar
- Dar reset halt
- Colocar breakpoint antes da configuração de PLL
break SystemClock_Config
continue
Assim você intercepta antes do clock mudar.
3.9 Quando o STM32 não conecta mais
Cenário: firmware configura pinos SWD como GPIO.
Solução:
Use reset sob controle:
monitor reset halt
Ou inicie OpenOCD com:
reset_config srst_only srst_nogate
Ou conecte segurando reset físico.
Até aqui cobrimos debug real de:
- HardFault
- Stack corruption
- ISR problemáticas
- DMA
- FreeRTOS
- Código otimizado
- Clock misconfiguration
- Perda de debug
- Debug de periféricos específicos (GPIO, USART, ADC, TIM) via registradores
- Uso de GDB para profiling simples
- Script avançado com macros GDB
- Integração com VSCode e CLI
- Técnicas profissionais de depuração em produção