Manual
do
Maker
.
com
Apesar de ser um artigo do Laboratório Maker, ele está diretamente relacionado à série sobre Debug da Raspberry que venho escrevendo. Nesse artigo vamos fazer uma ferramenta; um SWD debugger para debug da Raspberry e gravação de nossos programas, partindo de uma Raspberry Pi Pico para outra!
O legal de criar essa ferramenta é que poderemos depurar uma Raspberry Pi Pico de qualquer computador, não só do Raspberry Pi 400, como mostrado nos artigos anteriores. Claro, não tem como não recomendar a Raspberry Pi 400 da Saravati, que está com um preço ótimo. Além disso, eles tem também a Raspberry Pi Pico e com isso você já pode se presentear (ou presentear alguém) nesse natal (ou aniversário etc) com algo bem legal; ou até um kit com ambas!
Nos artigos anteriores mostrei como preparar o SDK na Raspberry Pi 400, mas como agora vamos depurar uma Raspberry Pi Pico utilizando outra Raspberry Pi Pico, não faz sentido que o processo seja feito a partir de uma Raspberry Pi 400, 4, 3, 2, 1, 0. Nesse caso, vamos ver primeiramente como preparar o SDK da Raspberry Pi Pico para computadores tradicionais.
Esse procedimento serve para qualquer distribuição baseada em Debian (como o próprio Ubuntu o é). Vou descrever o procedimento de instalação em outras plataformas, pule para a sua caso não use Linux. Se bem que no Windows você pode usar WSL e ter um Linux instalado dentro do Windows, sem consumo excessivo de recursos e funcionando tudo nativamente. Vale experimentar.
Vamos fazer no estilo "receita de bolo" para o artigo não ficar muito maior do que o necessário. Confie e proceda como descrito.
Crie a base:
cd ~/
mkdir pico
cd pico
Clone os repositórios:
sudo apt-get update && sudo apt-get install -y git
git clone -b master https://github.com/raspberrypi/pico-sdk.git
cd pico-sdk
git submodule update --init
cd ..
git clone -b master https://github.com/raspberrypi/pico-examples.git
Se desejar, temos alguns repositórios a mais; o pico-extras e o pico-playground. Mas sugiro que não se apegue a esses repositórios, já que vamos programar usando o VS Code com PlatformIO à posteriori. Utilizando PlatformIO, estaremos usando a API do Arduino e programaremos da forma tradicional, além de desfrutar das bibliotecas já disponíveis.
Quando criamos binários para uma plataform diferente daquela em que estamos programando, é chamado "cross-compiling" - ou em português, "compilação cruzada". Não depende exclusivamente da plataforma, mas não vou entrar em detalhes. Aqui no blog tem diversos artigos específicos sobre toolchains, arquiteturas, descompilação de firmwares etc. Divirta-se!
Para a pico vamos usar o compilador gcc-arm-none-eabi. Embedded Application Binary Interface para eabi. Quando contém o none, não tem fornecedor nem sistema operacional alvo e está em conformidade com ARM. Quando não contém o none, então a compilação referenciará a libc do Linux, que implica em chamadas do kernel, como IOCTL, para alocação de páginas de memória para seu processo (porque todo o programa que roda no sistema operacional é um processo com identificação e tudo o mais referente à plataforma).
Me desculpe por ter escrito demais para passar essas duas linhas:
sudo apt update
$ sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential libstdc++-arm-none-eabi-newlib
Essa dica é extra. Quando sair uma nova versão e precisar atualizar seu SDK, entre no diretório pico-sdk novamente e siga com os comandos para baixar e atualizar os submódulos:
cd pico-sdk
git pull
git submodule update
Hora do teste.
export PICO_SDK_PATH=~/pico/pico-sdk
cd ~/pico/pico-examples
mkdir build && cd build
cmake ..
Sem erros? Tudo bem até aqui? Então apenas compile o blink:
cd blink
make
Ao final, um diretório contendo (entre outros) os arquivos blink.uf2 e blink.elf. Já expliquei em outros artigos, mas se por acaso é seu primeiro contato com essa Raspberry Pi Pico: O arquivo uf2 é para carregar pelo BOOTSEL. O elf é para depuração, subindo o firmware por SWD, como propósito desse artigo.
Se chegou até aqui sem erro, passemos para a fase seguinte. Mas antes, se quiser ter o path do SDK disponível sempre que abrir um terminal, adicione a linha export PICO_SDK_PATH=~/pico/pico-sdk ao arquivo ~/.bashrc. Simplesmente edite esse arquivo e adicione essa linha ao final. Depois, feche o terminal e abra novamente, ou então faça:
source ~/.bashrc
Se não sabe como editar o arquivo, deixe que o sistema decida por você:
xdg-open ~/.bashrc
A última linha é a que importa e que você deve copiar.
A picotool é uma ferramenta para inspeção de binários para RP2040. Ela também interage com essa MCU (que é a MCU do Raspberry Pi Pico) quando em modo BOOTSEL. A partir da versão 1.1 também é possível interagir com a RP2040 mesmo que não esteja no modo BOOTSEL, desde que usando o suporte ao USB stdio, usando a flag -f da picotool. Essa ferramenta é bem documentada.
Antes de vermos isso nesse ou em outro artigo, instale também as dependências. Em linux baseado em Debian (Ubuntu, por exemplo), faça:
sudo apt install build-essential pkg-config libusb-1.0-0-dev
cd ~/pico
git clone https://github.com/raspberrypi/picotool.git
Para Windows, instale a libusb daqui: https://libusb.info/
Agora a compilação:
cd picotool
mkdir build && cd build
cmake ..
make
Ainda no Windows, será necessário ajustar a variável de ambiente LIBUSB_ROOT. Se estiver usando MinGW/WSL:
mkdir build
cd build
cmake -G "NMake Makefiles" ..
nmake
A compilação é bem rápida e deve ter uma saída limpa e clara:
Para transformar uma RPi Pico em um SWD debugger devemos clonar esse repositório e compilar o firmware. Mais uma vez, volte ao diretório base e proceda com os comandos abaixo:
cd ~/pico
git clone https://github.com/raspberrypi/picoprobe.git
cd picoprobe
mkdir build
cd build
cmake -G “Unix Makefiles” ..
make
O parâmetro de -G é para Linux e MacOS. Se for usar Windows com MinGW, troque por "MinGW Makefiles".
Agora basta copiar o firmware picoprobe.uf2 para a RPi Pico. O resultado da compilação deve ser limpo como:
A RPi Pico que se tornará um SWD debugger deve ser gravada da maneira tradicional, usando o BOOTSEL. Se não conhece o processo, é mais simples que qualquer placa. Tendo o firmware uf2, faça o seguinte:
Ao arrastar o arquivo de firmware para o dispositivo que aparecerá como se fosse um pendrive, ele reiniciará automaticamente, pronto para uso.
Se não quiser ter o trabalho de compilar (não sei qual seria a razão, mas...) baixe o firmware picoprobe compilado oficialmente. Estará no último tópico do subtítulo "Debugging using another Raspberry Pi Pico". Para gravá-lo, não haverá diferença; proceda como supracitado.
Dei um Ctrl+Chups do google images (ou foi da documentação? não lembro) e maquiei a imagem em um "pseudo Photoshop online grátis". Essa imagem está horrível, mas há alguns meses venho fazendo todas as capas nesse programa, recomendo bastante. E é "for free". Grátis mesmo.
Minha recomendação maior é (parafraseando a documentação): Faça o wiring diretamente na placa. Não use protoboard para não ter problema de sinal.
Me parece óbvio, mas acho melhor citar que à esquerda está a RPi Pico que gravamos o Picoprobe e à direita temos o Raspberry Pi Pico que será depurado, através dos pinos SWD.
Infelizmente o suporte à Raspberry Pi Pico ainda não está implementado oficialmente no OpenOCD. Faz um bom tempo já que saiu a Pico e, considerando a demora, não sei se veremos isso em um pacote nativo ou um fork no Linux. De qualquer modo, a compilação não é um trabalho complexo então arregace as mangas e bora fazer.
Antes que você coloque o carro na frente dos bois, não instale pelo apt porque é necessário compilar com suporte a multidrop. Vale sempre salientar, por mais que pareça repetitivo. Eu me repito, mas você não se frustra.
Para acessar a porta SWD de qualquer ARM que suporte SWD, será necessário ter um programa na máquina host chamado "debug translator", que entende o protocolo SWD e save como controlar o processador. No caso da RP2040, ela é dual core, mas não se espante com esse controle. O ESP32 também é dual core e já mostrei como fazer debug usando o ESP-PROG e VS Code. E tem vídeo sobre a ESP-PROG no canal também. Só que para a RPi Pico tem esse trabalhinho extra.
Seguindo o padrão de construção, tudo dentro do diretório ~/pico:
sudo apt install automake autoconf build-essential texinfo libtool libftdi-dev libusb-1.0-0-dev
cd ~/pico
git clone https://github.com/raspberrypi/openocd.git --recursive --branch rp2040 --depth=1
cd openocd
./bootstrap
./configure --enable-ftdi --enable-sysfsgpio --enable-bcm2835gpio
make
sudo make install
A saída do comando sudo make install deve ser limpa como essa:
Esse é o debugger que iremos utilizar. No caso, a versão multi-arquitetura. A instalação é padrão do sistema, então é o processo mais simples desse tutorial.
sudo apt-get install gdb-multiarch
Nada melhor que o teste nativo primeiro, porque quanto mais camadas de abstração, mais dependente de terceiros ficamos. Após gravar o firmware picoprobe, constatei pelo shell sua identificação, observando o arquivo de logs do sistema (no caso, Linux). Depois iniciei o OpenOCD por linha de comando.
sudo openocd -f interface/picoprobe.cfg -f target/rp2040.cfg
Os resultados:
Agora vamos conectar o GDB a essa ponte. Eu compilei o mesmo blink do vídeo sobre o debug na Raspberry Pi 400. Entrei no diretório em que estava o firmware (firmware.elf) e procedi com os seguintes comandos:
gdb-multiarch firmware.elf
target remote localhost:3333
load
Após digitar load e apertar [Enter], começará uma paginação gigantesca. Aperte c e [Enter] para continuar sem paginar. O firmware será carregado, daí podemos começar a enviar comandos. O código do blink que usei está mais abaixo. Coloquei breakpoints nas linhas 14 e 15:
b 14
b 15
monitor reset init
continue
n
n
n
n
n
n
Cada n faz ele avançar para o próximo (next). Foram necessários 6 passos desde o início do programa até a escrita no pino. Quando ele mudou o estado para HIGH, o LED permaneceu ligado, pois estava parado no breakpoint. Abaixo está o print desde o breakpoint e minha afobação dando "next" antes de iniciar o processo. Mas é legal de ver a verbosidade em caso de erro do usuário. Verbosidade é tudo!
Para monitorar uma variável, use watch variavel_desejada. Fiz alguns steps mais monitorando a função digitalWrite para ver o valor mudando de 0 para -1. Com -1 o LED se acende, assim como com 1.
A saída é tão verbosa que vemos claramente a chamada e os valores. Repare:
Fantástico ou não? Mas por que -1 invés de 1? Agora vamos ver o código desse blink para facilitar o entendimento.
#include <Arduino.h>
struct biting {
int state :1;
} one_bit;
void setup() {
pinMode(LED_BUILTIN,OUTPUT); //pin 25
one_bit.state = 0;
}
void loop() {
sleep_ms(200);
one_bit.state += 1;
digitalWrite(LED_BUILTIN, one_bit.state);
}
Repare na função loop() primeiro. Perceba que só há incremento da variável dentro da struct. Só há 1 delay. Só há 1 digitalWrite aplicando o valor do incremento. Como isso pode ser um blink? - Simples!
Na struct a variável continua sendo um inteiro, ocupando o tamanho de um inteiro, conforme a plataforma (2 Bytes, 4 Bytes etc). Porém, usando :1 limitei o uso da reserva de memória dessa variável a 1 bit. Um único bit comporta 0 ou 1, certo? Se a variável for 1 e adicionarmos mais 1, acontece o estouro de base. para ficar claro, pense em nossa base decimal. Temos dez números: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. Se somarmos 9+1, teremos o estouro de base decimal. Nesse caso, simplesmente voltamos o valor a 0 e adicionamos o primeiro dígito à direita: 10. Só que reservamos um bit em nossa variável e não havia memória para alocar a soma, então estouramos a memória da variável e ela sempre vai para -1, por ser apenas 1 bit. Mas ao somarmos novamente, ele volta para 0. E assim temos o nosso blink funcional, como se nenhum erro estivesse acontecendo (mas está acontecendo de forma controlada).
Já vimos como fazê-lo usando o Raspberry Pi 400. Esse vídeo tem quase 5 minutos. Já adianto; é um vídeo chato, mas informativo. E é complementar aos tutoriais relacionados ao debug da Raspberry Pi Pico.
Fiz o que pude para o debug acontecer, mas tudo resultou em erro. Abaixo, troquei picotool (já explicado anteriormente) por picoprobe, que é a o dispositivo usado para upload. Tentei usar ambos também em debug_tool, mas sem esperança em relação a isso, porque a própria documentação do PlatformIO não mostra um parâmetro para essa placa (o link está mais abaixo).
[env:pico]
platform = raspberrypi
board = pico
;adicione a linha abaixo:
upload_protocol = picotool
Pois é. Não tem um parâmetro de debug. Na documentação oficial do PlatformIO só tem alguns parâmetros; platform, board, board_build.mcu, board_build.f_cpu. Em upload_protocol temos picotool, mas não tem opção para debug_tool. Será que é "1-click"? - Não era. Só deu problema de diversos tipos, pesquisei bastante e não encontrei nada. Se alguém tiver solução para o problema e quiser compartilhar essa informação, posta em nossa página no facebook ou em um dos grupos. Ou ainda, comenta lá no canal dobitaobytebrasil no Youtube.
Debug pela IDE é muito melhor, sem dúvidas. Vou continuar em busca da solução, acompanhe aqui e no Youtube!
Inscreva-se no nosso canal Manual do Maker no YouTube.
Também estamos no Instagram.
Autor do blog "Do bit Ao Byte / Manual do Maker".
Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.