工具 | 版本 |
---|---|
CMake | 3.25.1 |
Arm GNU Toolchain | 12.2.0 |
Python | 3.10.9 |
OpenOCD | 0.11.0 |
DAPLink | REL v0258 |
1、Raspberry Pi Debug
上次我们已经成功构建了树莓派 Pico 的工程,今天我们来研究一下如何使用仿真器对树莓派 Pico 进行仿真与调试。
通过查看 Raspberry Pico 的引脚可以发现,板子预留了 swd 接口,我们可以通过 swd 接口对其进行仿真。
官方提供了两种调试树莓派 Pico 的方法,分别是使用 Raspberry Pi 4 和 Picoprobe 进行调试。
Raspberry Pi 4 现在价格过高,而 Picoprobe 需要额外的一块 Pico ,因为我手上只有一块板子,所以也不可行。
J-Link 也支持调试 Raspberry Pico ,但是只支持以下版本:
- J-Link software V7.64e or later
- J-Link HW revision 11 or later (J-Link EDU Mini and J-Trace PRO V1 or later also supported)
- SEGGER Embedded Studio for ARM V6.30 or later
所以至少需要 J-Link v11 才支持调试 Raspberry Pico ,我手上只有 J-Link ob ,所以也无法进行调试。
那么如果使用 DAPLink 能否仿真调试 Raspberry Pico 呢,答案是肯定的,我们这次就使用 DAPLink 来仿真调试 Raspberry Pico 。
2、DAPLink
Arm Mbed DAPLink 是一个开源软件项目,从 CMSIS-DAP 发展而来,由 ARMmebed 维护和开源,支持在 Arm Cortex CPU 上运行的应用程序软件的编程和调试。DAPLink 在连接到应用程序 MCU 的 SWD 或 JTAG 端口的辅助 MCU 上运行,通常称为接口固件。几乎所有开发板上都有这种配置。作为 USB 复合设备枚举,它在开发计算机和 CPU 调试访问端口之间建立了一个桥梁。
既然 Arm Mbed DAPLink 支持在 Arm Cortex CPU 上运行的应用程序软件的编程和调试,那理论上也应该能调试 Raspberry Pico 上的那两个 Arm Cortex-M0 + 内核。
最后我在 github 找到了官方对 DAPLink 的支持:Add a CMSIS compatible implementation for picoprobe
建议在进行调试前先将 DAPLink 升级为最新的固件,我这里就使用 main 分支的固件了,版本为 REL v0258
3、编译 OpenOCD
Open On-Chip Debugger (OpenOCD) 是一款针对嵌入式系统调试的开源软件,提供调试、烧录、边界扫描等功能。
因为官方的 OpenOCD 没有对 Raspberry Pico 的支持,所以我们要自己编译 Raspberry Pico 官方修改的 OpenOCD 。
1. 获取源码
git clone https://github.com/raspberrypi/openocd.git --recursive --branch rp2040 --depth=1
进入源码目录
cd openocd
2. 配置
./bootstrap
./configure --enable-picoprobe --enable-cmsis-dap --enable-cmsis-dap-v2
3. 编译安装
make -j16
sudo make install
安装完成
4、调试仿真
现在我们可以连接好 DAPLink 和 Raspberry Pico,把 DAPLink 接上电脑。
swd接线:
Pin RP Pico | Pin DAP |
---|---|
“DEBUG” “SWCLK” | SWCLK |
“DEBUG” “GND” | GND |
“DEBUG” “SWDIO” | SWDIO |
“3V3” Pad 36 | VCC |
已经识别到了 DAPLink
输入命令
openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -s tcl -c "adapter speed 5000"
进入工程文件夹,然后输入
arm-none-eabi-gdb LED.elf
target remote localhost:3333
然后就可以正常调试了
如果想要烧录编译好的工程,可以使用以下命令
openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -s tcl -c "adapter speed 5000" -c "program LED.elf verify reset exit"
其中 LED.elf 替换成自己的程序
5、VS Code 配置
我们就使用上次创建的 FreeRTOS 工程进行配置。
进入工程文件夹
cd FreeRTOS
在 .vscode 文件夹创建 launch.json 文件和 tasks.json 文件
launch.json :
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Cortex Debug",
"cwd": "${workspaceRoot}",
"executable": "${workspaceFolder}/build/${workspaceRootFolderName}.elf",
"request": "launch",
"type": "cortex-debug",
"servertype": "openocd",
"device": "RP2040",
"configFiles": [
"board/pico-debug.cfg"
],
"svdFile": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd",
"runToEntryPoint": "main",
// Give restart the same functionality as runToEntryPoint - main
"postRestartCommands": [
"break main",
"continue"
]
}
]
}
tasks.json :
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "mkdir",
"args": [
"build;",
"cd",
"build",
"&&",
"cmake",
"..",
"&&",
"make",
"-j16"
]
},
{
"label": "clean",
"type": "shell",
"command": "rm",
"args": [
"-rf",
"build"
]
},
{
"label": "load",
"type": "shell",
"command": "openocd",
"args": [
"-f",
"interface/cmsis-dap.cfg",
"-f",
"target/rp2040.cfg",
"-s",
"tcl",
"-c",
"adapter speed 5000",
"-c",
"init",
"-c",
"halt",
"-c",
"\"program ${workspaceFolder}/build/${workspaceRootFolderName}.elf verify reset exit\""
]
},
{
"label": "reset",
"type": "shell",
"command": "openocd",
"args": [
"-f",
"interface/cmsis-dap.cfg",
"-f",
"target/rp2040.cfg",
"-s",
"tcl",
"-c",
"adapter speed 5000",
"-c",
"init",
"-c",
"halt",
"-c",
"shutdown"
]
}
]
}
打开 VS Code 工程
首先安装好 Cortex-Debug 插件
写一个点灯程序
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/mutex.h"
#include "pico/sem.h"
#include "FreeRTOS.h" /* Must come first. */
#include "task.h" /* RTOS task related API prototypes. */
#include "queue.h" /* RTOS queue related API prototypes. */
#include "timers.h" /* Software timer related API prototypes. */
#include "semphr.h" /* Semaphore related API prototypes. */
const uint LED_PIN = PICO_DEFAULT_LED_PIN;
void task0(void *pvParam)
{
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
for(;;)
{
gpio_put(LED_PIN, 1);
vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时1秒
gpio_put(LED_PIN, 0);
vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时1秒
}
}
void task1(void *pvParam)
{
for(;;)
{
uint CORE_ID;
CORE_ID = portGET_CORE_ID();
printf("Hello World !\r\n");
printf("Core ID is %d\r\n",CORE_ID);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
int main()
{
stdio_init_all();
/* 创建任务0 */
TaskHandle_t task0_Handle = NULL;
UBaseType_t task0_CoreAffinityMask = (1 << 0); // 指示该任务可以运行在哪个核心上
xTaskCreate(task0, "task0", 1024, NULL, 1, &task0_Handle);
vTaskCoreAffinitySet(task0_Handle, task0_CoreAffinityMask); // 为任务设置核心关联掩码
/* 创建任务1 */
TaskHandle_t task1_Handle = NULL;
UBaseType_t task1_CoreAffinityMask = (1 << 1);
xTaskCreate(task1, "task1", 1024, NULL, 1, &task1_Handle);
vTaskCoreAffinitySet(task1_Handle, task1_CoreAffinityMask);
vTaskStartScheduler();
return 0;
}
选择终端—运行任务—build,编译工程
选择终端—运行任务—clean,清理工程
选择终端—运行任务—load,烧录工程
调试项目
最后放上工程的源码:pico_FreeRTOS