
Freqüentemente, ao depurar o software do microcontrolador, torna-se necessário enviar mensagens, registros, dados capturados e outras coisas para a tela do PC. Ao mesmo tempo, quero que a saída seja mais rápida e que as linhas não sejam exibidas em nenhum lugar, mas diretamente no IDE - sem sair do código, por assim dizer. Na verdade, isso é sobre o artigo - como eu tentei imprimir e exibir no meu ambiente favorito, mas não muito microcontrolador, do Qt Creator.
Em geral, você pode criar um grande número de maneiras de gerar informações de texto a partir do microcontrolador. No entanto, as técnicas mais usadas não são tantas:
A semi-hospedagem é bastante lenta, o RTT está vinculado às soluções de hardware e software Segger *, o USB não está presente em todos os microcontroladores. Portanto, geralmente, prefiro os dois últimos - o uso de UART e ITM. Sobre eles e será discutido abaixo.
* Upd. - de fato, como sugerido nos comentários, não é assim. Existem opções no lado do software e do hardware. Portanto, dos métodos acima, o RTT será talvez o mais universal.
E imediatamente algumas explicações sobre o software que será usado a seguir. Como sistema operacional, agora tenho o Fedora 28, e o pacote atual de software para trabalhar com microcontroladores é:
Redirecionar printf () no GCC
Portanto, para redirecionar a saída de printf () no GCC, você precisa adicionar chaves de vinculador
-specs=nosys.specs -specs=nano.specs
Se você precisar exibir números de ponto flutuante, lembre-se da tecla
-u_printf_float
E implemente a função _write (). Por exemplo, algo como isto
int _write(int fd, char* ptr, int len) { (void)fd; int i = 0; while (ptr[i] && (i < len)) { retarget_put_char((int)ptr[i]); if (ptr[i] == '\n') { retarget_put_char((int)'\r'); } i++; } return len; }
onde retarget_put_char () é uma função que carrega o caractere diretamente na interface desejada.
printf () -> ITM -> Qt Creator
O Instrumentation Trace Macrocell (ITM) é um bloco dentro do núcleo Cortex-M3 / M4 / M7 usado para saída não invasiva (rastreamento) de vários tipos de informações de diagnóstico. Para implementar printf () sobre ITM, você precisa saber o seguinte:
- Usa o relógio TRACECLKIN, cuja frequência geralmente é igual à frequência principal
- Possui 32 peças das chamadas portas de estímulo para saída de dados
- O CMSIS incorpora a função ITM_SendChar (), que carrega um símbolo na porta de estímulo 0
- Os dados são enviados por um barramento síncrono (TRACEDATA, TRACECLK) ou por uma linha SWO assíncrona de fio único (TRACESWO)
- A linha SWO geralmente é multiplexada com JTDO, o que significa que funciona apenas no modo de depuração pelo SWD
- A retirada pelo SWO é realizada usando o código Manchester ou NRZ (UART 8N1)
- Os dados são transmitidos em quadros de um determinado formato - você precisa de um analisador no lado de recebimento
- O ITM geralmente é configurado a partir do IDE ou do utilitário correspondente (no entanto, ninguém proíbe a configuração no código do programa - a saída para o SWO funcionará sem uma sessão de depuração elevada)
A maneira mais conveniente de usar o ITM é enviar via SWO usando a codificação NRZ - portanto, você precisa de apenas uma linha e será possível receber dados não apenas usando um depurador com uma entrada especial, mas também um adaptador USB-UART comum, embora a uma velocidade mais baixa.
Eu segui o caminho usando um depurador e fui forçado a modificar meu STLink-V2 chinês para dar suporte ao SWO. Então tudo é simples - conectamos o microcontrolador JTDO / TRACESWO ao pino do depurador correspondente e vamos configurar o software.
O Openocd possui o comando "tpiu config" - com ele você pode configurar o método para exibir informações de rastreamento (em mais detalhes no Guia do Usuário do OpenOCD ). Então, por exemplo, usando argumentos
tpiu config internal /home/esynr3z/itm.fifo uart off 168000000
configure a saída para o arquivo /home/esynr3z/itm.fifo, use a codificação NRZ e calcule a velocidade máxima de transferência com base na frequência TRACECLKIN de 168 MHz - para o STLink é 2 MHz. E outra equipe
itm port 0 1
habilitará a porta zero para transferência de dados.
O código fonte do OpenOCD inclui o utilitário itmdump (contrib / itmdump.c) - com ele você pode analisar as strings dos dados recebidos.
Para compilar, entramos
gcc itmdump.c -o itmdump
Na inicialização, especifique o arquivo / pipe / ttyUSB * necessário e a opção -d1 para exibir os bytes de dados recebidos como cadeias
./itmdump -f /home/esynr3z/itm.fifo -d1
E o último. Para enviar um personagem via SWO, complementamos _write (), descrito acima, com uma função
int retarget_put_char(int ch) { ITM_SendChar((uint32_t)ch); return 0; }
Portanto, o plano geral é o seguinte: dentro do Qt Creator, configuramos o openocd para salvar todas as informações recebidas no SWO em um pipe nomeado criado anteriormente, e podemos ler pipe, analisar linhas e exibir usando itmdump em execução como uma ferramenta externa. Obviamente, existe uma maneira mais elegante de resolver o problema - escrever o plugin apropriado para o Qt Creator. No entanto, espero que a abordagem descrita abaixo seja útil para alguém.
Vá para as configurações do plug-in Bare Metal (Ferramentas-> Opções-> Dispositivos-> Bare Metal).

Selecione o servidor GDB usado e adicione os comandos de inicialização de linha ao final da lista
monitor tpiu config internal /home/esynr3z/itm.fifo uart off 168000000 monitor itm port 0 1
Agora, pouco antes do depurador colocar o cursor no início de main (), o ITM será configurado.
Adicione o itmdump como a ferramenta externa (Ferramentas-> Externa-> Configurar ...).

Não se esqueça de definir a variável
QT_LOGGING_TO_CONSOLE=1
para exibir a saída do utilitário no console do Qt Creator (painel 7 Mensagens gerais).
Agora ative o itmdump, ative o modo de depuração, inicie a execução do código e ... nada acontece. No entanto, se você interromper a depuração, a execução do itmdump será encerrada e todas as linhas impressas através de printf () aparecerão na guia Mensagens Gerais.
Após uma breve pesquisa, descobriu-se que as linhas do itmdump deveriam ser armazenadas em buffer e exibidas no stderr - então elas aparecem no console interativamente, enquanto depuram o programa. Carreguei uma versão modificada do itmdump no GitHub .
Há mais uma ressalva. A depuração na inicialização será interrompida na execução do comando "monitor tpiu config ..." se o itmdump não for executado anteriormente. Isso acontece devido ao fato de que o pipe de abertura (/home/esynr3z/itm.fifo) dentro do openocd para gravação está bloqueando, e o depurador irá travar até que o pipe seja aberto para leitura da outra extremidade.
Isso é um pouco desagradável, especialmente se, em algum momento, o ITM não for necessário, mas você precisar executá-lo à toa, alternar constantemente o servidor GDB ou excluir / adicionar linhas em suas configurações. Portanto, eu tive que cavar um pouco de fontes openocd e encontrar o lugar onde você precisa substituir uma muleta pequena.
No arquivo src / target / armv7m_trace.c, há uma linha com o procedimento de abertura desejado
armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab");
precisa ser substituído por
int fd = open(CMD_ARGV[cmd_idx], O_CREAT | O_RDWR, 0664); armv7m->trace_config.trace_file = fdopen(fd, "ab");
Agora, nosso tubo será aberto imediatamente e não brilhará. Assim, você pode deixar as configurações do Bare Metal em paz e o itmdump somente será executado quando necessário.
Como resultado, a saída de mensagens durante a depuração se parece com isso

printf () -> UART -> Qt Creator
Nesse caso, tudo é aproximadamente o mesmo:
- Adicione uma função com inicialização UART ao código
- Implementamos retarget_put_char (), onde o caractere será enviado ao buffer do transceptor
- Conectamos o adaptador USB-UART
- Adicione um utilitário às Ferramentas externas que lerá as linhas da porta COM virtual e as exibirá na tela.
Esbocei esse utilitário no C- uartdump . O uso é bastante simples - você só precisa especificar o nome da porta e a taxa de transmissão.

No entanto, vale a pena notar um recurso. Este utilitário não depende da depuração e o Qt Creator não oferece nenhuma opção para fechar as Ferramentas Externas em execução. Portanto, para parar de ler a porta COM, adicionei outra ferramenta externa.

Bem, apenas no caso, anexarei um link ao modelo do CMake para o projeto que apareceu nas capturas de tela - GitHub .