基于RP2040GameKit实现DAPLink调试器
使用RP2040制作一个DAPLink,可以实现对单片机的下载调试
标签
嵌入式系统
RP2040
DAPLink
CMSIS-DAP
2022寒假在家练
yekai
更新2022-03-01
中国计量大学
3360

硬禾学堂2022年寒假在家一起练项目

任务需求及分析

我选择的是任务四:制作一个调试器

树莓派团队提供了一个PicoProbe项目用于调试,同时提供了他们修改过的OpenOCD。由于PicoProbe的兼容性有限,仅支持他们修改过的OpenOCD,对于Keil、pyOCD以及OpenOCD原项目等的支持不佳,我这边选择了移植DAPLink,原仓库地址ARMmbed/DAPLink (github.com)。当然也可以使用CMSIS_5/CMSIS/DAP/Firmware at develop · ARM-software/CMSIS_5 (github.com)这个仓库的源码,这个似乎更为简单。两个都是ARM提供的固件,ARMmbed的似乎实现了一个环形池和WEBUSB等好多内容,虽然我本次移植并没使用,CMSIS库中的例程更为简洁易懂。

ARM团队去年1月还在PicoProbe项目的issue中提到了要使DAPLink支持RP2040,似乎暂时还咕咕咕着。

练手项目:USB-CDC

选择USB-CDC作为练手一方面是因为它不是一个调试器的必备选项,但是常见的调试器大多在版本迭代中加上了这个功能;另一方面是它可以让我们对tinyUSB有个初步的了解。例程如下,去掉第二个串口即可:

FgEG4ZCVpuJ8Om_W0thAacX1JCzJ

主要函数如下:

// USB->LINK回调
void tud_cdc_rx_cb(uint8_t itf) {
    char buf[CFG_TUD_CDC_RX_BUFSIZE] = {0};
    tud_cdc_read(buf, CFG_TUD_CDC_RX_BUFSIZE);
    uart_puts(PICO_LINK_UART_ID, buf);
}

// MCU->LINK回调
void on_uart_rx() {
    while (uart_is_readable(PICO_LINK_UART_ID)) {
        uint8_t ch = uart_getc(PICO_LINK_UART_ID);
        tud_cdc_write_char(ch);
    }
    tud_cdc_write_flush();
}

void VCOM_Init() {
    cdc_line_coding_t uart_config;
    tud_cdc_get_line_coding(&uart_config);
    uart_init(PICO_LINK_UART_ID, uart_config.bit_rate);
    gpio_set_function(PICO_LINK_UART_RX, GPIO_FUNC_UART);
    gpio_set_function(PICO_LINK_UART_TX, GPIO_FUNC_UART);
    irq_set_exclusive_handler(PICO_LINK_UART_IRQ, on_uart_rx);
    irq_set_enabled(PICO_LINK_UART_IRQ, true);
    uart_set_irq_enables(PICO_LINK_UART_ID, true, false);
}

void VCOM_SendString(char *str) {
    while (*str) {
        tud_cdc_write_char(*str++);
    }
    tud_cdc_write_char('\r');
    tud_cdc_write_char('\n');
    tud_cdc_write_flush();
}

// 检测到上位机串口助手开启串口回调
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
    VCOM_Init();
}

CDC在Windows10上免驱,打开设备管理器即可看到串口设备

FtAfuRX4CyFGihfz1BHipjwIMNzk

回环测试:

Fgc2MeeIHtbXBVDCNL9WGBJoU5FK

和CH340收发测试:

FtldzJ7z59bZRY_XK3eaY0VzjNbE

移植DAPLink

注:在此仅实现了基于HID传输的DAPLink,暂未实现基于Bulk传输的DAPLink。

USB配置

这一步可以参考TinyUSB的例程:

FnKc_PGI4hcBx0UJ6yGSpjr1Nhzp

在tinyUSB的tusb_config.h文件中使能和添加这些宏:

#define CFG_TUD_HID               1
#define CFG_TUD_HID_EP_BUFSIZE    64

在usb_descriptors.h文件中参照例程修改

//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+

enum {
    ITF_NUM_HID,
    ITF_NUM_CDC_COM,
    ITF_NUM_CDC_DATA,
    ITF_NUM_TOTAL
};

#define  CONFIG_TOTAL_LEN  (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN + TUD_CDC_DESC_LEN)

#define EPNUM_HID       0x01
#define EPNUM_CDC_NOTIF 0x83
#define EPNUM_CDC_OUT   0x02
#define EPNUM_CDC_IN    0x82

uint8_t const desc_configuration[] = {
        // Config number, interface count, string index, total length, attribute, power in mA
        TUD_CONFIG_DESCRIPTOR(
                1,
                ITF_NUM_TOTAL,
                0,
                CONFIG_TOTAL_LEN,
                TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
                100
        ),

        // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
        TUD_HID_INOUT_DESCRIPTOR(
                ITF_NUM_HID,
                0,
                HID_ITF_PROTOCOL_NONE,
                sizeof(desc_hid_report),
                EPNUM_HID,
                0x80 | EPNUM_HID,
                CFG_TUD_HID_EP_BUFSIZE,
                1
        ),

        TUD_CDC_DESCRIPTOR(
                ITF_NUM_CDC_COM,
                0,
                EPNUM_CDC_NOTIF,
                64,
                EPNUM_CDC_OUT,
                EPNUM_CDC_IN,
                64
        )
};

文件复制

dap中负责处理usb发来的数据并控制io口输出的文件主要就是以下几个:

Fm-fqFKvSzAjvODMCG7E1LWdOMfr

DAP主要处理下发的命令,SW_DP和JTAG_DP主要将命令发送出去,DAP_config.h(在生成的各工程下)主要是DAP的配置和IO口的控制。

DAP配置

修改DAP_config.h里面的一些函数和宏定义,基本就是把接口适配一下。接口适配时更推荐使用接近底层的寄存器或是直接能就地解析的inline函数写法来提升效率。本次移植中有几个适配RP2040的点:RESET原本设计为开漏输出,由于RP2040似乎没有开漏输出,在这里需稍加修改。DAP原本的原理图将SWDIO的输入和输出分为了两个IO,用100R进行了相连。为了适配GameKit,将其并为了一个IO。

代码较多,在此贴一个例子。

__STATIC_FORCEINLINE void PIN_SWDIO_OUT(uint32_t bit) {
    if (bit & 1) {
        gpio_set_mask(PICO_LINK_SWDIO_MASK);
    } else {
        gpio_clr_mask(PICO_LINK_SWDIO_MASK);
    }
}

USB任务接口配置

USB在接收到hid数据后会进入tud_hid_set_report_cb函数,具体可参考hid例程。在这里不同的项目有着不同的处理方式,ARMmbed版本实现了一个环形缓冲区,CMSIS库中收到数据后主要调用了DAP_ExecuteCommand函数。我这边还是简单地使用DAP_ExecuteCommand函数进行了处理。

// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
void tud_hid_set_report_cb(
        uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const *RxDataBuffer,
        uint16_t bufsize) {

    static uint8_t TxDataBuffer[CFG_TUD_HID_EP_BUFSIZE];
    uint32_t response_size = TU_MIN(CFG_TUD_HID_EP_BUFSIZE, bufsize);

    DAP_ExecuteCommand(RxDataBuffer, TxDataBuffer);

    tud_hid_report(0, TxDataBuffer, response_size);
}

接着整个项目就差不多了。

主要的流程图如图:

Fqib8J_tx6-SDorm3hxztVhD4CIx

项目占用如图,-Og编译,可以看到对于这颗iot方向的mcu,资源占用率就十分的低。

FtRqATKLpwBNRDDpeD0al7kbHQQB

实现结果

使用GameKit作为调试器,连接STM32G071板

Ft1S8gE_2ju6_DB0mobbWb5cVpko

MDK读取设备:

FqX_Weqn0XGMKeAObBZ_sPIYj28e

MDK下载:

FhldpHcOR0W0eG2-_bl3MjyxBTX2

OpenOCD下载:

Fuh1stsb5GtYc25WKim2qTwxpBVc

pyOCD下载:

FlUejAe0Viz8kuT9fDk6zb0Dvai0

MDK调试:

FoT_ScJNaxLYlT9Ouh1YZFE_9rcv

心得体会

DAPLink的移植并没有我们想象中的那么难,核心内容均被包装好,配置宏和接口,调用入口即可使用。主要还是USB协议栈的学习使用。

RP2040作为树莓派出的第一颗单片机,堆料方面诚意很足,也有着PIO之类有特色的外设,但整体设计风格与ST之类的传统单片机大厂略有不同,完全基于XiP外置Flash的存储方案可能对于固件加密方面有着不小的问题。

代码仓库:kaidegit/pico-link at eetree-gamekit (github.com)

 

团队介绍
团队成员
yekai
一个大三的电子小白
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号