A primeira coisa que verifico em uma nova placa, seja usando um oscilador interno ou um cristal externo, é que tenho a freqüência do relógio configurada corretamente. Isso é importante porque muitos dos periféricos, como UART, SPI, I2C e temporizadores dependem disso.
A maneira como eu verifico é escrever um programa com um loop curto, na linguagem assembly, onde posso contar os ciclos manualmente, ou C, desde que você possa obter uma lista de desmontagem e fazer a mesma coisa - e acender um LED e desligar. Eu configurei um loop para que ele seja executado uma vez por segundo. Eu corro o código e verifico se o LED pisca 60 vezes em um minuto.
No que diz respeito aos periféricos, a melhor maneira de verificá-los é usar um osciloscópio, se você tiver um, e olhar para a linha RX para UART, CLK, MOSI e linhas de seleção de chips para SPI e SDA e SCL para I2C e verifique se as linhas estão alternando e o tempo parece correto.
Se você não possui um osciloscópio, pode colocar LEDs nessas linhas e ativar ou desativar os periféricos. Quando desativadas, a maioria das linhas fica baixa (LED apagado), mas algumas ficam altas, como o cabo RX do UART (LED aceso). Quando o periférico está ativado, a maioria dos LEDs deve escurecer, pois as linhas serão alternadas. Ao executar em um loop (desativado / ativado), é mais fácil ver a diferença entre ativado ou desativado.
Para o UART, você pode conectar a linha TX à linha RX como um loop. Você também pode conectar-se a um cabo UART para USB e, no PC, conectar um terminal a um programa como o RealTerm . Além de testar a interface, isso será útil para outras depurações posteriormente.
Para outras partes do código, eu uso vários LEDs conforme necessário para mostrar que vários caminhos no código estão sendo executados. Se o UART estiver funcionando e conectado a um PC, você poderá polvilhar seu código com chamadas para uma sub-rotina para enviar uma mensagem para mostrar quais pontos o programa alcançou (ou usar printf se você tiver as bibliotecas C padrão disponíveis). Mas, como Vladimir Cravero aponta em um comentário abaixo, isso pode atrasar um pouco o seu código (em 115.200 baud, não muito, pois o tempo de um caractere é <10 µs). Mas em ISRs e outros códigos críticos de tempo, use apenas LEDs.
Como Al Bundy aponta em um comentário abaixo, os depuradores de circuito também podem ser úteis, principalmente se for possível definir vários pontos de interrupção e ainda mais úteis se você pode alterar o ponto de interrupção em um local de memória que está sendo alterado. Nem todos os depuradores têm esse recurso.
No entanto, não uso muito depuradores, a menos que seja necessário, por exemplo, procurar bits em um registro periférico; ou rastrear um bug que não consigo encontrar por inspeção; ou para análise de cobertura de código rudimentar. Mas, em geral, eu gosto de executar programas na velocidade "normal", pois muitos problemas geralmente aparecem, o que pode não acontecer quando o programa é executado em uma única etapa. A maioria dos meus programas usa muitas interrupções, o que interfere no uso de um depurador.