這篇文章主要描述利用RT-THREAD+CherryUSB制作DapLink調試器(R_DapLink)全流程。
這里先感謝網友:sakumisu提供cherryUSB協議棧的技術支持。
什么是下載調試器簡單來說,下載調試器是將PC(例如通過USB協議)發送的命令轉換為MCU(負責MCU內部外圍設備)理解的語言(例如SWD或JTAG協議)的設備,加載代碼并精確控制執行。
什么是標準簡單來說,標準是一組規則和協議,特定行業中的每個參與者都同意遵循并執行。符合某種內核的單片機Q,都可以使用這種協議來下載程序。JTAG和SWD其實都是一種標準的協議。比如JTAG和SWD,都支特下載ARMQ內核單片機的程序。
各種調試器的區別
「J-Link:」最有名氣、各種渠道版本最多,號稱支持芯片量最多。
適合場景:如果項目文件比較大,首選!下載又快又穩,仿真調試也是杠杠的神器。
優點:最快、穩如老狗。STM32F407芯片+109K代碼實測8秒完成燒錄過程。
缺點:最貴,和諧版都四五十元起步,正版上至數千元。注意:V9版本以下的J-Link大多數不支持STM8。
「ST-Link:」隨著STM32這十年八年的壟斷,ST-Link也跟著發大火了, 妥妥銷量一哥。
適合場景:學校里開STM32課程的,幾乎每個宿舍都有吧~
優點:便宜、便宜,便宜!x寶13元包郵。特別注意一下:驅動包就在KEIL本身的文件夾里頭,你說它多火。
缺點:不夠穩定! 可能不時的來個彈窗。
「CMSIS-DAP:」軟硬件開源!這兩三年,含量在火箭式起飛。很多人知道它是因為技小新和立創EDA的開源工程。比STLink稍貴。
適合場景:玩stm32的,這個是最優選擇。
優點:開源、虛擬串口、免驅。大愛的虛擬串口,很爽、很爽, (有些下載器也有虛擬串口),更愛它開源沒心病。
缺點:真沒啥缺點,能拼JLink的穩定, 也能拼ST-Link的便宜,還沒版權問題!109K代碼燒錄實測10秒,也算杠杠的吧。
「DAP-Link:」CMSIS-DAP的升級版。
適合場景:嗯,如果你的動手能力高超,這個最合適,軟硬都開源,可玩性極高,比如做個拖拽的脫機下載
優點:拖拽燒錄、升級固件。包括了CMSIS-DAP的優點:開源+虛擬串口+免驅。
缺點:真沒啥缺點,能拼JLink的穩定, 也能拼ST-Link的便宜,還沒版權問題!
R_DapLink說明
R_DapLink支持:DapLink+CDC(虛擬串口),采用USB2.1協議。采用GPIO模擬SWD時序
硬件準備
R_DapLink的硬件采用stm32f103c8t6,內核:ARM Cortex-M3,主頻:72MHz,Flash:64KB,RAM:20KB。
開發板我們需要做一些改動,由于SWD的數據線有輸入和輸出,我們這里采用兩個引腳合并成一個引腳作為SWD的數據線。stm32的GPIOB0作為SWD的數據線的輸出,stm32的GPIOA7作為SWD的數據線的輸入。
準備一個現成的調試器來調試我們的R_DapLink,這里采用正點原子的DapLink。
R_DapLink支持CDC(虛擬串口),所以我們還需要一個串口工具,這里采用CH340。
軟件準備
R_DapLink采用RT-Thread作為我們實時系統,提供系統調度,IPC通信。選擇RT-Thread的原因:RT-Thread已經包含了cherryUSB協議棧軟件,這給我減少了很多移植的工作量。
R_DapLink的USB協議棧采用cherryUSB協議棧,其代碼鏈接:https://github.com/cherry-embedded/CherryUSB。cherryUSB協議棧提供了對應的文檔,其文檔鏈接:https://cherryusb.readthedocs.io/zh-cn/latest/
DAPLink:Arm Mbed DAPLink是一個開源軟件項目,可以對Arm Cortex CPU上運行的應用程序進行編程和調試。其鏈接:https://github.com/ARMmbed/DAPLink
移植DapLink
準備rt-thread工程
下載rt-thread的源碼,源碼鏈接:https://gitee.com/rtthread/rt-thread,我們采用4.1.1的版本,所以下載完源碼需要切換到4.1.1版本中。
下載完源碼,進入stm32f103-blue-pill這個BSP,路徑:xxxspstm32stm32f103-blue-pill,通過env工具dist出來,
從dist目錄下拷貝stm32f103-blue-pill工程出來,并修改名字為:r_daplink。
增加cherryUSB軟件包
進入上面準備好的工程:r_daplink,在工程目錄中打開env工具,輸入menuconfig。
配置增加cherryUSB
r_daplink的USB是作為device,所以選擇選擇Device mode
r_daplink的usb的速度為全速,選擇FS,stm32f103c86的USB IP為FSDEV,并選擇上cdc,用于實現虛擬串口。
增加DAPLink源碼
下載DAPLink代碼,鏈接:https://github.com/ARMmbed/DAPLink
DAPLink代碼很多,但實際我們只用核心的部分,將DAPLink代碼中:sourcedaplinkcmsis-dap目錄拷貝到r_daplink工程中。
r_daplink工程中增加兩個文件:DAP_config.h和IO_Config.h文件。其中:DAP_config.h用于配置DAPLink的配置,并適配SWD時序模擬的GPIO,IO_Config.h用于配置SWD使用到的GPIO的描述。具體內容看我的開源倉:https://gitee.com/RiceChen0/r_daplink。
USB適配
r_daplink的daplink采用winusb+cdc,其中包含3個接口,4個端點,其設備描述符適配:
constuint8_tcdc_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), /////////////////////////////////////// ///string0descriptor /////////////////////////////////////// USB_LANGID_INIT(USBD_LANGID_STRING), /////////////////////////////////////// ///string1descriptor /////////////////////////////////////// 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*/ /////////////////////////////////////// ///string2descriptor /////////////////////////////////////// 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*/ /////////////////////////////////////// ///string3descriptor /////////////////////////////////////// 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 };
winusb端點適配
voidusbd_winusb_out(uint8_tep,uint32_tnbytes) { usbd_ep_start_read(WINUSB_OUT_EP,usb2dap_buff[usb2dap_index],USB2DAP_PACK_SIZE); } voidusbd_winusb_in(uint8_tep,uint32_tnbytes) { if((nbytes%USB_MAX_MPS)==0&&nbytes){ usbd_ep_start_write(WINUSB_IN_EP,NULL,0); } } structusbd_endpointwinusb_out_ep={ .ep_addr=WINUSB_OUT_EP, .ep_cb=usbd_winusb_out }; structusbd_endpointwinusb_in_ep={ .ep_addr=WINUSB_IN_EP, .ep_cb=usbd_winusb_in };
cdc端點適配
voidusbd_cdc_acm_bulk_out(uint8_tep,uint32_tnbytes) { usbd_ep_start_read(CDC_OUT_EP,usb2uart_buff,USB2UART_PACK_SIZE); } voidusbd_cdc_acm_bulk_in(uint8_tep,uint32_tnbytes) { if((nbytes%USB_MAX_MPS)==0&&nbytes){ usbd_ep_start_write(CDC_IN_EP,NULL,0); } } structusbd_endpointcdc_out_ep={ .ep_addr=CDC_OUT_EP, .ep_cb=usbd_cdc_acm_bulk_out }; structusbd_endpointcdc_in_ep={ .ep_addr=CDC_IN_EP, .ep_cb=usbd_cdc_acm_bulk_in };
usb初始化
intusb_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(); returnRT_EOK; }
以上適配完將板子的USB插上電腦,通過設備管理器查看是否成功
cdc適配
我們采用串口3作為我們USB到串口的轉發。
cdc虛擬串口的配置傳給串口3,具體實現如下:
staticvoiduart_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) { structserial_configureserial_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); } }
cdc虛擬串口數據到串口3的實現如下:
voidusb2uart_handler(rt_uint8_t*data,rt_uint16_tlen) { if(uart_dev) { rt_device_write(uart_dev,0,data,len); } }
串口3數據到cdc虛擬串口的實現如下:
staticrt_err_tuart_recv_isr(rt_device_tdev,rt_size_tsize) { if(size>0) { rt_sem_release(&uart_rx_sem); } returnRT_EOK; } staticvoiduart2usb_handler(void*param) { rt_uint16_trx_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適配
daplink的實現原理:將usb接收到的數據傳輸到
DAP_ExecuteCommand()
函數,并且從這個函數獲取返回數據,將數據傳輸到usb上。
我們將usb接收到數據通過郵箱的方式傳輸到數據處理現成,具體實現如下:
staticvoidusb2dap_handler(rt_uint8_t*data,rt_uint16_tlen) { rt_mb_send(&dap2usb_mb,(rt_ubase_t)data); } staticvoiddap2usb_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((constuint8_t*)rx_data,dap2usb_buff); usb_service_dap2usb(dap2usb_buff,dap2usb_size); } } }
驗證:我們keil里面選擇我們dap,可以正常的識別到DAP,并且能識別鏈接的設備
r_daplink的燒錄驗證
審核編輯:黃飛
-
usb
+關注
關注
60文章
7891瀏覽量
263978 -
數據傳輸
+關注
關注
9文章
1836瀏覽量
64475 -
調試器
+關注
關注
1文章
300瀏覽量
23689 -
虛擬串口
+關注
關注
3文章
62瀏覽量
13866 -
設備管理器
+關注
關注
0文章
6瀏覽量
5320
原文標題:手把手教你制作DAPLink
文章出處:【微信號:風火輪技術團隊,微信公眾號:風火輪技術團隊】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論