这篇文章主要描述利用RT-THREAD+CherryUSB制作DapLink调试器(R_DapLink)全流程。这里先感谢网友:sakumisu提供cherryUSB协议栈的技术支持。
什么是下载调试器简单来说,下载调试器是将PC(例如通过USB协议)发送的命令转换为MCU(负责MCU内部外围设备)理解的语言(例如SWD或JTAG协议)的设备,加载代码并精确控制执行。
什么是标准简单来说,标准是一组规则和协议,特定行业中的每个参与者都同意遵循并执行。符合某种内核的单片机Q,都可以使用这种协议来下载程序。JTAG和SWD其实都是一种标准的协议。比如JTAG和SWD,都支特下载ARMQ内核单片机的程序。
各种调试器的区别「J-Link:」最有名气、各种渠道版本最多,号称支持芯片量最多。
「ST-Link:」随着STM32这十年八年的垄断,ST-Link也跟着发大火了, 妥妥销量一哥。
「CMSIS-DAP:」软硬件开源!这两三年,含量在火箭式起飞。很多人知道它是因为技小新和立创EDA的开源工程。比STLink稍贵。
「DAP-Link:」CMSIS-DAP的升级版。
const uint8_t cdc_winusb_descriptor[] = {
USB_DEVICE_DESCRIPTOR_INIT(USB_2_1, 0xEF, 0x02, 0x01, USBD_VID, USBD_PID, 0x0100, 0x01),
USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x03, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
USB_INTERFACE_DESCRIPTOR_INIT(0x00, 0x00, 0x02, 0xff, 0x00, 0x00, 0x02),
USB_ENDPOINT_DESCRIPTOR_INIT(WINUSB_OUT_EP, 0x02, USB_MAX_MPS, 0x00),
USB_ENDPOINT_DESCRIPTOR_INIT(WINUSB_IN_EP, 0x02, USB_MAX_MPS, 0x00),
CDC_ACM_DESCRIPTOR_INIT(0x01, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, USB_MAX_MPS, 0x00),
///////////////////////////////////////
/// string0 descriptor
///////////////////////////////////////
USB_LANGID_INIT(USBD_LANGID_STRING),
///////////////////////////////////////
/// string1 descriptor
///////////////////////////////////////
0x12, /* bLength */
USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
'R', 0x00, /* wcChar0 */
'i', 0x00, /* wcChar1 */
'c', 0x00, /* wcChar2 */
'e', 0x00, /* wcChar3 */
'C', 0x00, /* wcChar4 */
'h', 0x00, /* wcChar5 */
'e', 0x00, /* wcChar6 */
'n', 0x00, /* wcChar7 */
///////////////////////////////////////
/// string2 descriptor
///////////////////////////////////////
0x1E, /* bLength */
USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
'R', 0x00, /* wcChar0 */
'i', 0x00, /* wcChar1 */
'c', 0x00, /* wcChar2 */
'e', 0x00, /* wcChar3 */
' ', 0x00, /* wcChar4 */
'C', 0x00, /* wcChar5 */
'M', 0x00, /* wcChar6 */
'S', 0x00, /* wcChar7 */
'I', 0x00, /* wcChar8 */
'S', 0x00, /* wcChar9 */
'-', 0x00, /* wcChar10 */
'D', 0x00, /* wcChar11 */
'A', 0x00, /* wcChar12 */
'P', 0x00, /* wcChar13 */
///////////////////////////////////////
/// string3 descriptor
///////////////////////////////////////
0x1C, /* bLength */
USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
'R', 0x00, /* wcChar0 */
'i', 0x00, /* wcChar1 */
'c', 0x00, /* wcChar2 */
'e', 0x00, /* wcChar3 */
'-', 0x00, /* wcChar4 */
'2', 0x00, /* wcChar5 */
'0', 0x00, /* wcChar6 */
'2', 0x00, /* wcChar7 */
'3', 0x00, /* wcChar8 */
'0', 0x00, /* wcChar9 */
'1', 0x00, /* wcChar10 */
'0', 0x00, /* wcChar11 */
'1', 0x00, /* wcChar12 */
0x00
};
void usbd_winusb_out(uint8_t ep, uint32_t nbytes)
{
usbd_ep_start_read(WINUSB_OUT_EP, usb2dap_buff[usb2dap_index], USB2DAP_PACK_SIZE);
}
void usbd_winusb_in(uint8_t ep, uint32_t nbytes)
{
if ((nbytes % USB_MAX_MPS) == 0 && nbytes) {
usbd_ep_start_write(WINUSB_IN_EP, NULL, 0);
}
}
struct usbd_endpoint winusb_out_ep = {
.ep_addr = WINUSB_OUT_EP,
.ep_cb = usbd_winusb_out
};
struct usbd_endpoint winusb_in_ep = {
.ep_addr = WINUSB_IN_EP,
.ep_cb = usbd_winusb_in
};
void usbd_cdc_acm_bulk_out(uint8_t ep, uint32_t nbytes)
{
usbd_ep_start_read(CDC_OUT_EP, usb2uart_buff, USB2UART_PACK_SIZE);
}
void usbd_cdc_acm_bulk_in(uint8_t ep, uint32_t nbytes)
{
if ((nbytes % USB_MAX_MPS) == 0 && nbytes) {
usbd_ep_start_write(CDC_IN_EP, NULL, 0);
}
}
struct usbd_endpoint cdc_out_ep = {
.ep_addr = CDC_OUT_EP,
.ep_cb = usbd_cdc_acm_bulk_out
};
struct usbd_endpoint cdc_in_ep = {
.ep_addr = CDC_IN_EP,
.ep_cb = usbd_cdc_acm_bulk_in
};
int usb_service_init(void)
{
usbd_desc_register(cdc_winusb_descriptor);
usbd_bos_desc_register(&bos_desc);
usbd_msosv2_desc_register(&msosv2_desc);
usbd_add_interface(&intf0);
usbd_add_endpoint(&winusb_out_ep);
usbd_add_endpoint(&winusb_in_ep);
usbd_add_interface(usbd_cdc_acm_init_intf(&intf0));
usbd_add_interface(usbd_cdc_acm_init_intf(&intf1));
usbd_add_endpoint(&cdc_out_ep);
usbd_add_endpoint(&cdc_in_ep);
usbd_initialize();
return RT_EOK;
}
static void uart_config_set(uart_config_t *config)
{
if(rt_memcmp(&uart_config, (rt_uint8_t *)config, sizeof(uart_config_t)) != 0)
{
rt_memcpy((rt_uint8_t *)&uart_config, config, sizeof(uart_config_t));
uart_is_config = RT_TRUE;
}
if(uart_is_config)
{
struct serial_configure serial_config = RT_SERIAL_CONFIG_DEFAULT;
if(uart_dev != RT_NULL) {
rt_device_close(uart_dev);
uart_dev = RT_NULL;
}
uart_is_config = RT_FALSE;
uart_dev = rt_device_find(UART_NAME);
serial_config.baud_rate = uart_config.baudrate;
serial_config.stop_bits = uart_config.stopbit;
serial_config.parity = uart_config.parity;
serial_config.data_bits = uart_config.databit;
serial_config.bufsz = UART_PACK_SIZE;
rt_device_control(uart_dev, RT_DEVICE_CTRL_CONFIG, &serial_config);
rt_device_open(uart_dev, RT_DEVICE_FLAG_DMA_RX);
rt_device_set_rx_indicate(uart_dev, uart_recv_isr);
}
}
void usb2uart_handler(rt_uint8_t *data, rt_uint16_t len)
{
if(uart_dev)
{
rt_device_write(uart_dev, 0, data, len);
}
}
static rt_err_t uart_recv_isr(rt_device_t dev, rt_size_t size)
{
if(size > 0)
{
rt_sem_release(&uart_rx_sem);
}
return RT_EOK;
}
static void uart2usb_handler(void *param)
{
rt_uint16_t rx_size = 0;
for(;;)
{
rt_sem_take(&uart_rx_sem, RT_WAITING_FOREVER);
if(uart_dev)
{
rx_size = rt_device_read(uart_dev, 0, uart_rx_buff, UART_PACK_SIZE);
usb_service_uart2usb(uart_rx_buff, rx_size);
}
}
}
daplink的实现原理:将usb接收到的数据传输到DAP_ExecuteCommand()函数,并且从这个函数获取返回数据,将数据传输到usb上。
我们将usb接收到数据通过邮箱的方式传输到数据处理现成,具体实现如下:
static void usb2dap_handler(rt_uint8_t *data, rt_uint16_t len)
{
rt_mb_send(&dap2usb_mb, (rt_ubase_t)data);
}
static void dap2usb_handler(void *param)
{
char *rx_data = NULL;
for(;;)
{
if(rt_mb_recv(&dap2usb_mb, (rt_ubase_t *)&rx_data, RT_WAITING_FOREVER) == RT_EOK)
{
if(rx_data[0] == ID_DAP_QueueCommands)
{
rx_data[0] = ID_DAP_ExecuteCommands;
}
dap2usb_size = DAP_ExecuteCommand((const uint8_t *)rx_data, dap2usb_buff);
usb_service_dap2usb(dap2usb_buff, dap2usb_size);
}
}
}
r_daplink的开源链接:https://gitee.com/RiceChen0/r_daplink
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !