基于STM32F103自制CMSISDAP下载器.docx
- 文档编号:6083538
- 上传时间:2023-01-03
- 格式:DOCX
- 页数:20
- 大小:346.94KB
基于STM32F103自制CMSISDAP下载器.docx
《基于STM32F103自制CMSISDAP下载器.docx》由会员分享,可在线阅读,更多相关《基于STM32F103自制CMSISDAP下载器.docx(20页珍藏版)》请在冰豆网上搜索。
基于STM32F103自制CMSISDAP下载器
基于STM32F103自制CMSIS-DAP下载器
[导读]市面上针对Cortex-M处理器的下载器,有很多是基于CMSIS-DAP演变而来,比如:
e-Link、GD-Link等。
之前给大家分享过自制ST-Link的教程,今天继续为大家分享一篇:
基于STM32F103C8,自制CMSIS-DAP下载器。
关注+星标公众号,不错过精彩内容
编排| strongerHuang
微信公众号| strongerHuang
市面上针对Cortex-M处理器的下载器,有很多是基于CMSIS-DAP演变而来,比如:
e-Link、GD-Link等。
之前给大家分享过自制ST-Link的教程,今天继续为大家分享一篇:
基于STM32F103C8,自制CMSIS-DAP下载器。
关于CMSIS-DAP
CMSIS-DAP是支持访问 CoreSight调试访问端口(DAP)的固件规范和实现,以及各种Cortex处理器提供CoreSight调试和跟踪。
地址:
https:
//arm-software.github.io/CMSIS_5/DAP/html/index.html
CMSIS-DAP固件作为源代码提供,并且可以完全配置为新的调试单元。
这里相关的更多内容,可以参看我之前分享过的一篇文章:
Cortex-M软件接口标准CMSIS那些重要内容。
CMSIS-DAP固件
CMSIS-DAP固件Arm以源码形式提供,不存在版权问题(因为针对ArmCortex处理器,他们还希望更多人使用)。
1.固件版本目前有两个版本:
版本1配置使用USBHID作为与主机PC的接口。
版本2配置使用WinUSB作为与主机PC的接口,并提供高速SWO跟踪流。
2.源码位置目前源码提供在KeilMDKV5版本,安装好KeilMDK,你在安装目录下就能找到源码。
C:
\Keil_v5\ARM\Packs\ARM\CMSIS\5.7.0\CMSIS\DAP\Firmware
(目前MDKV5.33,CMSIS版本为5.7.0)
3.源码描述
从文件目录可以看出,官方源码提供了一些模板和例子。
目前只提供了LPC处理器的例子,如果你有这个处理器对应的板卡,可以直接使用该源码做一个下载调试器。
(下面就针对于LPC这个例子进行“改装”)
配置
利用STM32CubeMX图形化配置工具,帮助用户选择单片机引脚的功能,并自动生成外设初始化代码。
配置了USB、SPI1和USART1,并选择了USB的CustomHIDmiddleware模式。
GPIOB10到GPIO15被配置为JTAG调试需要的引脚。
GPIOC13用于驱动单片机上的LED灯。
ST公司也开发了他们自己的JTAG调试器——STLink。
当然它并不是必要的,你也可以使用J-Link或者其他种类的调试器。
STLink的驱动和程序可以在ST官网上下载。
在网站里还有一个基于Eclipse开发环境开发的IDE,STM32CubeMX也被包含其中。
我选择的IDE是基于CodeBlocks的Embitz,IDE中的arm_none_eabi_gcc版本是5.4.1。
在我完成我的CMSIS-DAP之前,我必须使用STLink来调试我的代码。
现在我在使用新做出来的CMSIS-DAP结合OpenOCD进行日常的开发。
从CMSIS-DAP的源码开始
源码可以在官网下载:
也可以直接在 KeilMDK 安装目录下获取:
C:
\Keil_v5\ARM\Packs\ARM\CMSIS\5.7.0\CMSIS\DAP
将从示例V1的头文件DAP_config.h开始分析。
选择LPC-Link-IIV1作为我的参考是因为它是通过USBHID实现的(V2是通过WinUSB实现)。
我分析的第一个文件是DAP_Config.h。
第一个关键位置如下:
·
·
·
·
·
·
#ifdef_RTE_#include"RTE_Components.h"#includeCMSIS_device_header#else#include"device.h"#endif
不用RTE的相关文件,创建我自己的device.h。
我将参数CPU_CLOCK重定义为72000000(72MHz)。
根据文件里的注释,参数DAP_PACKET_SIZE必须重新定义为64U。
我把SWO_UART改为0,这让我的工作轻松不少。
参数TIMESTAMP_CLOCK也要重定义为72000000。
LPC-Link-II使用Cortex-M3的DWT模块实现时间戳(TIMESTAMP),这也是为什么我想在CubeMX中尝试配置STM32F103的DWT。
最后我自己写了一小段代码来实现这个功能(在device.c中):
CoreDebug->DEMCR|=CoreDebug_DEMCR_TRCENA_Msk;
/**
*OnCortex-M7corethereisaLARregisterinDWTdomain.
*AnytimeweneedtosetupDWTregisters,weMUSTwrite
*0xC5ACCE55intoLARfirst.LARmeansLockAccessRegister.
*/
DWT->CYCCNT=0;
DWT->CTRL|=DWT_CTRL_CYCCNTENA_Msk;
我定义的引脚和NXPLPCxx的完全不同。
我为STM32F103重写了所有的引脚的操作代码。
在DAP_Config.h这个文件中还有一些奇怪的地方,比如:
//SWCLK/TCKI/Opin-------------------------------------
/**SWCLK/TCKI/Opin:
GetInput.
\returnCurrentstatusoftheSWCLK/TCKDAPhardwareI/Opin.
*/
__STATIC_FORCEINLINEuint32_tPIN_SWCLK_TCK_IN(void){
return((LPC_GPIO_PORT->PIN[PIN_SWCLK_TCK_PORT]>>PIN_SWCLK_TCK_BIT)&1U);
}
我不明白为什么DAP的代码里需要读取SWCLK/TCK引脚的电平,这个引脚明明是被配置为推挽输出来产生时钟信号输送给JTAG从机的。
从上面列出来的代码可以看出,它是希望返回当前引脚的电平值,我的实现方式稍微有点不同:
__STATIC_FORCEINLINEuint32_tPIN_SWCLK_TCK_IN(void){
return(uint32_t)(JTAG_TCK_GPIO_Port->ODR&JTAG_TCK_Pin?
1:
0);
}
我返回的是当前引脚的输出值。
我不确定这是否正确。
在整个代码中,这句话只被DAP.c中的一个叫DAP_SWJ_Pins的函数调用了两次。
我猜测DAP_SWJ_Pins这个函数是用来测试IO口是否工作正常的。
另一个奇怪的地方是PIN_nRESET_OUT:
/**nRESETI/Opin:
SetOutput.
\parambittargetdevicehardwareresetpinstatus:
-0:
issueadevicehardwarereset.
-1:
releasedevicehardwarereset.
*/
__STATIC_FORCEINLINEvoidPIN_nRESET_OUT(uint32_tbit){
if(bit){
LPC_GPIO_PORT->DIR[PIN_nRESET_PORT]&=~(1U< LPC_GPIO_PORT->CLR[PIN_nRESET_OE_PORT]=(1U< }else{ LPC_GPIO_PORT->SET[PIN_nRESET_OE_PORT]=(1U< LPC_GPIO_PORT->DIR[PIN_nRESET_PORT]|=(1U< } } 为猜测release的意思可能是将nRESET引脚重新配置为一个失能的引脚。 我的代码如下: __STATIC_FORCEINLINEvoidPIN_nRESET_OUT(uint32_tbit){ GPIO_InitTypeDefGPIO_InitStruct={0}; if((bit&1U)==1){ JTAG_nRESET_GPIO_Port->BSRR=JTAG_nRESET_Pin; GPIO_InitStruct.Pin=JTAG_nRESET_Pin; GPIO_InitStruct.Mode=GPIO_MODE_INPUT; GPIO_InitStruct.Pull=GPIO_NOPULL; GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(JTAG_nRESET_GPIO_Port,&GPIO_InitStruct); }else{ JTAG_nRESET_GPIO_Port->BRR=JTAG_nRESET_Pin; GPIO_InitStruct.Pin=JTAG_nRESET_Pin; GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_OD; GPIO_InitStruct.Pull=GPIO_PULLUP; GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(JTAG_nRESET_GPIO_Port,&GPIO_InitStruct); } } 给你一个特殊的提示: ·你可能在一些地方看见我写了if(...)之类的代码,像下面这样: if((bit&1U)==1){ ... }else{ ... } 我使用参数bit的最低位是因为有时候这个bit既不是0也不是1,它可能是2或者其他奇怪的值,所以不要把你的代码写成这样: if(bit){ ... }else{ ... } 这会出问题的! ·osObjects.h #ifndef__osObjects_h__ #define__osObjects_h__ #include"cmsis_os2.h" #ifdefosObjectsExternal externosThreadId_tDAP_ThreadId; #else externosThreadId_tDAP_ThreadId; osThreadId_tDAP_ThreadId; #endif externvoidDAP_Thread(void*argument); externvoidapp_main(void*argument); #endif/*__osObjects_h__*/ 这是一个很简单的头文件。 但它引用了另一个叫做cmsis_os2.h的头文件,这是CMSIS库的一部分,但我没有没有从CMSIS库中复制到我的工程中,因为并不是其中所有的内容我都需要。 我选择写一个“假”的cmsis_os2.h头文件而不是直接使用原有的头文件。 这里还有另一个叫做DAP.h的头文件,它属于DAP的核心模块,在这里面引用了cmsis_compiler.h文件,这也是CMSIS库的一部分。 毫无疑问,我也写了一个“假”的cmsis_compiler.h。 分析到现在,我需要创建三个头文件(device.h&cmsis_os2.h&cmsis_compiler.h)来实现我的DAP工程。 接下来我会对main.c和USBD_User_HID_0.c做一些一些简单的介绍。 ·main.c 我在尽可能地精简我下载下来的这些源文件,所以我也没有要示例工程中的rl_usb.h文件。 于是我还需要一个头文件来定义一些关于USB通信的函数和参数。 这里有一些来自CMSISRTOS库的函数,其中最重要的一个是osThreadNew,在我的工程中我把它实现如下: osThreadId_tosThreadNew(void(*func)(void*),void*n,void*ctx) { (void)n; (*func)(ctx); return0; } 我直接“跳转”到本需要被创建的线程函数中,这就意味着main.c中的osKernelGetState&osKernelStart&osDelay三个函数永远不会被执行。 下一个重要的函数是USBD_Configured,我将在使用STM32CubeMX生成初始化代码那一节解释这个函数。 ·USBD_User_HID_0.c 我移除了RTE\USB\USBD_Config_HID_0.h并在我自己的rl_usb.h中重新定义了USBD_HID0_OUT_REPORT_MAX_SZ & USBD_HID0_IN_REPORT_MAX_SZ两个参数。 USBHID通信的核心是由两个接口中断函数管理的两个循环队列: int32_tUSBD_HID0_GetReport(uint8_trtype,uint8_treq,uint8_trid,uint8_t*buf){ (void)rid; switch(rtype){ caseHID_REPORT_INPUT: ... break; } return(0); } boolUSBD_HID0_SetReport(uint8_trtype,uint8_treq,uint8_trid,constuint8_t*buf,int32_tlen){ (void)req; (void)rid; switch(rtype){ caseHID_REPORT_OUTPUT: ... break; } returntrue; } 当上位机向DAP发送OUTPUTREPORT报文后,DAP会调用USBD_HID0_SetReport函数,该参数的输入形参rtype必须为HID_REPORT_OUTPUT。 当DAP成功向上位机发送INPUTREPORT报文时,函数USB_HID0_GetReport被调用,该函数的输入形参rtype必须为HID_REPORT_INPUT,并且形参req必须为USBD_HID_REQ_EP_INT。 这意味着我们所有的报文必须通过64B数据包大小的USB中断端点传输。 线程DAP_Thread只是一个命令判断选择器。 在这个函数中有一个很重要的语句: USBD_HID_GetReportTrigger(0U,0U,USB_Response[n],DAP_PACKET_SIZE); 我们必须实现一个叫做USBD_HID_GetReportTrigger的函数来想上位机发送INPUTREPORT。 使用STM32CubeMX生成初始化代码 在我的单片机上有一个8MHz的晶振,所以我选择HSE为时钟信号源。 PLLMul配置为x9,得到72MHz的PLLCLK,提供给CPU和AHB/APB2总线,提供给APB1总线的PCLK1配置为36MHz。 USB预分频配置为1.5分频,得到48MHz的USB时钟。 SPI1配置为Full-DuplexMaster,舍去NSS信号,USART1配置为Asynchronous。 USB设备进一步配置为CustomHIDClass,USBD_CUSTOMHID_OUTREPORT_BUF_SIZE设置为64Bytes。 注意: 我没有修改设备的VID和PID。 但我猜测有些上位机软件会检测这两个ID 如果你发现你的软件不能识别我这个CMSIS-DAP,或许你需要恰当的VID和PID。 可以试试示例代码中的VID/PID,它在一个叫做USBD_Config_0.c的文件中,我的工程中没有这个文件。 有STM32CubeMX生成的代码需要一些修改。 在usbd_customhid.h中,CUSTOM_HID_EPIN_SIZE和CUSTOM_HID_EPOUT_SIZE必须设置为0x40U。 我把CUSTOM_HID_FS_BINTERVAL改为0x01来尝试提升HID的通信速度。 在_USBD_CUSTOM_HID_Itf结构体中,我新增了一个成员: typedefstruct_USBD_CUSTOM_HID_Itf { uint8_t*pReport; int8_t(*Init)(void); int8_t(*DeInit)(void); int8_t(*OutEvent)(uint8_tevent_idx,uint8_tstate); /*Iaddanextrainterfacefuncbelow.ZachLee*/ int8_t(*InEvent)(uint8_tevent_idx,uint8_tstate); }USBD_CUSTOM_HID_ItfTypeDef; 当INPUTREPORT报文成功发给上位机时,InEvent函数应当被调用,所以usbd_customhid.c中的USBD_CUSTOM_HID_DataIn函数需要修改如下: staticuint8_tUSBD_CUSTOM_HID_DataIn(USBD_HandleTypeDef*pdev, uint8_tepnum) { /*EnsurethattheFIFOisemptybeforeanewtransfer,thisconditioncould becausedbyanewtransferbeforetheendoftheprevioustransfer*/ USBD_CUSTOM_HID_HandleTypeDef*hhid=(USBD_CUSTOM_HID_HandleTypeDef*)pdev->pClassData; hhid->state=CUSTOM_HID_IDLE; /*IaddanewinterfacefuncinthestructureUSBD_CUSTOM_HID_ItfTypeDef.ZachLee*/ ((USBD_CUSTOM_HID_ItfTypeDef*)pdev->pUserData)->InEvent(hhid->Report_buf[0], hhid->Report_buf[1]); returnUSBD_OK; } 设备描述符CUSTOM_HID_ReportDesc_FS被定义在usbd_suctom_hid_if.c中,我定义了一个简单的描述符: /**UsbHIDreportdescriptor.*/ __ALIGN_BEGINstaticuint8_tCUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE]__ALIGN_END= { /*USERCODEBEGIN0*//*AminimalReportDescwithINPUT/OUTPUT/FEATUREreport.ZachLee*/ 0x06,0x00,0xFF,/*UsagePage(vendordefined)($FF00)global*/ 0x09,0x01,/*Usage(vendordefined)($01)local*/ 0xA1,0x01,/*Collection(Application)*/ 0x15,0x00,/*LOGICAL_MINIMUM(0)*/ 0x25,0xFF,/*LOGICAL_MAXIMUM(255)*/ 0x75,0x08,/*REPORT_SIZE(8bit)*/ //InputReport 0x95,64,/*ReportLength(64REPORT_SIZE)*/ 0x09,0x01,/*USAGE(VendorUsage1)*/ 0x81,0x02,/*Input(data,var,absolute)*/ //OutputReport 0x95,64,/*ReportLength(64REPORT_SIZE)*/ 0x09,0x01,/*USAGE(VendorUsage1)*/ 0x91,0x02,/*Output(data,var,absolute)*/ //FeatureReport 0x95,64,/*ReportLength(64REPORT_SIZE)*/ 0x09,0x01,/*USAGE(VendorUsage1)*/ 0xB1,0x02,/*Feature(data,var,absolute)*/ /*USERCODEEND0*/ 0xC0/*END_COLLECTION*/ }; 可能FeatureReport在CMSIS-DAP中不是必要的,就留着它吧。 我在这个C文件中还实现了一个新的接口函数CUSTOM_HID_InEvent_FS: staticint8_tCUSTOM_HID_InEvent_FS(uint8_tevent_idx,uint8_tstate);/*Anextrainterfacefunc.*/ USBD_CUSTOM_HID_ItfTypeDefUSBD_CustomHID_fops_FS= { CUSTOM_HID_ReportDesc_FS, CUSTOM_HID_Init_FS, CUSTOM_HID_DeInit_FS, CUSTOM_HID_OutEvent_FS, /*Iaddanextrainterfacefuncbelow.ZachLee*/ CUSTOM_HID_InEvent_FS }; externvoidUSBD_OutEvent(void);/*Implementedinfile"device
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 基于 STM32F103 自制 CMSISDAP 下载