
A menudo, al depurar el software del microcontrolador, es necesario enviar mensajes de depuración, registros, datos capturados y otras cosas a la pantalla de la PC. Al mismo tiempo, quiero que la salida sea más rápida y que las líneas no se muestren en ningún lado, sino directamente en el IDE, sin apartarse del código, por así decirlo. En realidad, se trata del artículo: cómo intenté imprimir printf () para mostrar y mostrar dentro de mi entorno favorito, pero no muy microcontrolador, Qt Creator.
En general, puede encontrar una gran cantidad de formas de generar información de texto desde el microcontrolador. Sin embargo, las técnicas más utilizadas no son tantas:
El semihosting es bastante lento, RTT está vinculado a las soluciones de hardware y software Segger *, el USB no está en todos los microcontroladores. Por lo tanto, por lo general, prefiero los dos últimos: el uso de UART e ITM. Sobre ellos y se discutirá a continuación.
* Upd. - De hecho, como se sugiere en los comentarios, esto no es así. Hay opciones tanto en el lado del software como del hardware. Por lo tanto, de los métodos anteriores, RTT será quizás el más universal.
Y enseguida alguna explicación sobre el software que se usará a continuación. Ahora tengo Fedora 28 como sistema operativo, y el paquete actual de software para trabajar con microcontroladores es:
Redirigir printf () en GCC
Entonces, para redirigir la salida de printf () en GCC, debe agregar claves de enlazador
-specs=nosys.specs -specs=nano.specs
Si necesita mostrar números de coma flotante, debe recordar la clave
-u_printf_float
E implemente la función _write (). Por ejemplo, algo como esto
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; }
donde retarget_put_char () es una función que cargará el carácter directamente en la interfaz deseada.
printf () -> ITM -> Qt Creator
Instrumentation Trace Macrocell (ITM) es un bloque dentro del núcleo Cortex-M3 / M4 / M7 utilizado para la salida no invasiva (rastreo) de varios tipos de información de diagnóstico. Para implementar printf () sobre ITM, debe saber lo siguiente:
- Utiliza el reloj TRACECLKIN, cuya frecuencia suele ser igual a la frecuencia central
- Tiene 32 piezas de los llamados puertos de estímulo para la salida de datos
- CMSIS incorpora la función ITM_SendChar (), que carga un símbolo en el puerto de estímulo 0
- Los datos se emiten a través de un bus síncrono (TRACEDATA, TRACECLK) o de una línea SWO asíncrona de un solo cable (TRACESWO)
- La línea SWO generalmente se multiplexa con JTDO, lo que significa que solo funciona en modo de depuración por SWD
- La retirada por SWO se lleva a cabo utilizando el código Manchester o NRZ (UART 8N1)
- Los datos se transmiten en cuadros de cierto formato: necesita un analizador en el lado receptor
- ITM generalmente se configura desde el IDE o la utilidad correspondiente (sin embargo, nadie prohíbe configurarlo en el código del programa; luego, la salida a SWO funcionará sin una sesión de depuración elevada)
La forma más conveniente de usar ITM es emitir a través de SWO usando la codificación NRZ; por lo tanto, solo necesita una línea, y será posible recibir datos no solo usando un depurador con una entrada especial, sino también un adaptador USB-UART normal, aunque a una velocidad menor.
Seguí el camino usando un depurador, y me vi obligado a modificar mi STLink-V2 chino para admitir SWO. Entonces todo es simple: conectamos el microcontrolador JTDO / TRACESWO al pin del depurador correspondiente y vamos a configurar el software.
Openocd tiene el comando "tpiu config": con él puede configurar el método para mostrar información de rastreo (con más detalle en la Guía del usuario de OpenOCD ). Entonces, por ejemplo, usando argumentos
tpiu config internal /home/esynr3z/itm.fifo uart off 168000000
configure la salida en el archivo /home/esynr3z/itm.fifo, use la codificación NRZ y calcule la velocidad de transferencia máxima en función de la frecuencia TRACECLKIN de 168 MHz, para STLink es de 2 MHz. Y otro equipo
itm port 0 1
habilitará el puerto cero para la transferencia de datos.
El código fuente de OpenOCD incluye la utilidad itmdump (contrib / itmdump.c): con ella puede analizar cadenas de los datos recibidos.
Para compilar entramos
gcc itmdump.c -o itmdump
Al inicio, especifique el archivo / pipe / ttyUSB * necesario y el modificador -d1 para mostrar los bytes de datos recibidos como cadenas
./itmdump -f /home/esynr3z/itm.fifo -d1
Y el último. Para enviar un carácter a través de SWO, complementamos _write (), descrito anteriormente, con una función
int retarget_put_char(int ch) { ITM_SendChar((uint32_t)ch); return 0; }
Entonces, el plan general es el siguiente: dentro de Qt Creator, configuramos openocd para guardar toda la información recibida en SWO en una tubería nombrada previamente creada, y podemos leer tuberías, analizar cadenas y mostrarlas usando itmdump, que se ejecuta como una herramienta externa. Por supuesto, hay una forma más elegante de resolver el problema: escribir el complemento apropiado para Qt Creator. Sin embargo, espero que el enfoque descrito a continuación resulte útil para alguien.
Vaya a la configuración del complemento Bare Metal (Herramientas-> Opciones-> Dispositivos-> Bare Metal).

Seleccione el servidor GDB usado y agregue los comandos de inicialización de línea al final de la lista
monitor tpiu config internal /home/esynr3z/itm.fifo uart off 168000000 monitor itm port 0 1
Ahora, justo antes de que el depurador coloque el cursor al principio de main (), se configurará ITM.
Agregue itmdump como la herramienta externa (Herramientas-> Externo-> Configurar ...).

No olvides configurar la variable
QT_LOGGING_TO_CONSOLE=1
para mostrar la salida de la utilidad a la consola Qt Creator (panel 7 Mensajes generales).
Ahora encienda itmdump, active el modo de depuración, inicie la ejecución del código y ... no pasa nada. Sin embargo, si interrumpe la depuración, la ejecución de itmdump finalizará y todas las líneas impresas a través de printf () aparecerán en la pestaña Mensajes generales.
Después de una breve investigación, se descubrió que las líneas de itmdump deben almacenarse en búfer y mostrarse en stderr, luego aparecen en la consola de forma interactiva, mientras se depura el programa. Subí una versión modificada de itmdump a GitHub .
Hay una advertencia más. La depuración en el inicio dependerá de la ejecución del comando "monitor tpiu config ..." si itmdump no se ejecutó previamente. Esto sucede debido al hecho de que la apertura de la tubería (/home/esynr3z/itm.fifo) dentro de openocd para escritura está bloqueando, y el depurador se bloqueará hasta que la tubería se abra para leer desde el otro extremo.
Esto es algo desagradable, especialmente si en algún momento no se necesita ITM, pero tiene que ejecutarlo inactivo, cambiar constantemente el servidor GDB o eliminar / agregar líneas en su configuración. Por lo tanto, tuve que cavar un poco de fuentes abiertas y encontrar el lugar donde necesita sustituir una pequeña muleta.
En el archivo src / target / armv7m_trace.c hay una línea con el procedimiento de apertura deseado
armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab");
necesita ser reemplazado por
int fd = open(CMD_ARGV[cmd_idx], O_CREAT | O_RDWR, 0664); armv7m->trace_config.trace_file = fdopen(fd, "ab");
Ahora nuestra pipa se abrirá inmediatamente y no brillará. Por lo tanto, puede dejar la configuración de Bare Metal sola y ejecutarla solo cuando sea necesario.
Como resultado, la salida de mensajes durante la depuración se ve así

printf () -> UART -> Qt Creator
En este caso, todo es aproximadamente igual:
- Agregue una función con inicialización UART al código
- Implementamos retarget_put_char (), donde el carácter se enviará al búfer del transceptor
- Conectamos el adaptador USB-UART
- Agregue una utilidad a Herramientas externas que leerá líneas del puerto COM virtual y las mostrará en la pantalla.
Bosquejé tal utilidad en C - uartdump . El uso es bastante simple: solo necesita especificar el nombre del puerto y la velocidad en baudios.

Sin embargo, vale la pena señalar una característica. Esta utilidad no depende de la depuración, y Qt Creator no ofrece ninguna opción para cerrar la ejecución de herramientas externas. Por lo tanto, para dejar de leer el puerto COM, agregué otra herramienta externa.

Bueno, por si acaso, adjuntaré un enlace a la plantilla CMake para el proyecto que apareció en las capturas de pantalla: GitHub .