最近的项目中用到了DAP_Link,但是原版STM32F103CBT6的Demo不太适用,于是基于该主控进行了一点修改。

操作系统:Windows 10
IDE:MDK 5.38

新建工程

自选一个路径,建议不要有中文和空格。

git clone https://github.com/ARMmbed/DAPLink #从github获取源码,需要先安装git,或者直接从github打包下载。
cd DAPLink
pip install virtualenv #此处需要Python3。
virtualenv venv
venv/Scripts/activate.bat #此处会进入venv环境。
pip install -r requirements.txt #安装需要的Python包。
progen generate -f projects.yaml -p stm32f103xb_bl -t uvision #生成BL的MDK工程。
progen generate -f projects.yaml -p stm32f103xb_if -t uvision #生成APP的MDK工程。

然后在 DAPLink\projectfiles\uvision 里面可以找到这两个工程。

分配IO

使用MDK打开工程,在左边”Project“栏里,打开 stm32f103xb_bl/default/IO_Config.h,该文件里定义了SWD、UART、LED的引脚。

//USB control pin
#define USB_CONNECT_PORT_ENABLE()    __HAL_RCC_GPIOA_CLK_ENABLE()
#define USB_CONNECT_PORT_DISABLE()   __HAL_RCC_GPIOA_CLK_DISABLE()
#define USB_CONNECT_PORT             GPIOA
#define USB_CONNECT_PIN              GPIO_PIN_15
#define USB_CONNECT_ON()             (USB_CONNECT_PORT->BSRR = USB_CONNECT_PIN)
#define USB_CONNECT_OFF()            (USB_CONNECT_PORT->BRR  = USB_CONNECT_PIN)

该段定义了USB的使能和失能,USB2.0有1.5Mbps、12Mbps、480Mbps三种速率,也分别称为低俗、全速和高速,对于低速模式,需要在D-信号线上接上拉电阻,对于全速模式,需要在D+信号线上接上拉电阻。该上拉电阻可以直接连接到3.3V,也可以连接到单片机的IO口上,通过IO控制上拉。从该处源码的定义来看,设计的是使用PA15控制上拉。但是实际上,查看很多DAP_Link的设计并未使用IO上拉而是直接上拉到3.3V。

//Connected LED
#define CONNECTED_LED_PORT           GPIOB
#define CONNECTED_LED_PIN            GPIO_PIN_6
#define CONNECTED_LED_PIN_Bit        6

//USB status LED
#define RUNNING_LED_PORT             GPIOA
#define RUNNING_LED_PIN              GPIO_PIN_9
#define RUNNING_LED_Bit              9

#define PIN_HID_LED_PORT             GPIOA
#define PIN_HID_LED                  GPIO_PIN_9
#define PIN_HID_LED_Bit              9

#define PIN_CDC_LED_PORT             GPIOA
#define PIN_CDC_LED                  GPIO_PIN_9
#define PIN_CDC_LED_Bit              9

#define PIN_MSC_LED_PORT             GPIOA
#define PIN_MSC_LED                  GPIO_PIN_9
#define PIN_MSC_LED_Bit              9

以上是LED的定义部分,这个没有什么可讲的,可以任意分配,当不需要使用LED的时候,可以将其分配到无关的引脚上面。或者删减源码去掉LED功能。

//When bootloader, disable the target port(not used)
#define POWER_EN_PIN_PORT            GPIOB
#define POWER_EN_PIN                 GPIO_PIN_15
#define POWER_EN_Bit                 15

该段是定义了一个电源使能,意识是说当DAP处于Bootloader时关掉目标板的供电,但该功能只有定义,并没有实现。

// nRESET OUT Pin
#define nRESET_PIN_PORT              GPIOB
#define nRESET_PIN                   GPIO_PIN_0
#define nRESET_PIN_Bit               0

这是DAP_Link对目标板的复位信号,可以使用任意IO,另外需要注意该引脚需要在板子上添加上拉电阻,因为DAP_Link上电检测到该引脚是低电平的话就会进入固件升级模式。

//SWD
#define SWCLK_TCK_PIN_PORT           GPIOB
#define SWCLK_TCK_PIN                GPIO_PIN_13
#define SWCLK_TCK_PIN_Bit            13

#define SWDIO_OUT_PIN_PORT           GPIOB
#define SWDIO_OUT_PIN                GPIO_PIN_14
#define SWDIO_OUT_PIN_Bit            14

#define SWDIO_IN_PIN_PORT            GPIOB
#define SWDIO_IN_PIN                 GPIO_PIN_12
#define SWDIO_IN_PIN_Bit             12

最主要的部分,SWDIO和SWCLK,它是将SWDIO拆分成SWDIN和SWDOUT,分别作为数据输入输出。需要注意的是,很多DAP_Link将SWCLK同时接到了PA5和PB13,实际上PA5完全没用。因为SWD工作速度可以达到很高,所以只能使用PA和PB,不要使用PC的引脚。

修改RCC

原版工程使用的是8MHz的晶振,但是在我的工程中为了减少晶振数量,和别的IC共用了一个12MHz的有源晶振。在 stm32f103xb_bl/hic_hal/sdk.c 中可以修改RCC,在34行注释中标出了时钟配置:

/**
    * @brief  Switch the PLL source from HSI to HSE bypass, and select the PLL as SYSCLK
  *         source.
  *         The system Clock is configured as follow :
  *            System Clock source            = PLL (HSE bypass)
  *            SYSCLK(Hz)                     = 72000000
  *            HCLK(Hz)                       = 72000000
  *            AHB Prescaler                  = 1
  *            APB1 Prescaler                 = 2
  *            APB2 Prescaler                 = 1
  *            HSE Frequency(Hz)              = 8000000
  *            HSE PREDIV1                    = 1
  *            PLLMUL                         = 9
  *            Flash Latency(WS)              = 2
  * @param  None
  * @retval None
  */

它是将8MHz的HSE通过PLLMUL之后x9得到72MHz的SYSCLK。若是将HSE换成12MHz,那么只需要将PLLMUL改为x6即可。修改 sdk.c 中的73行,将 RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; 改为 RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6;

串口

串口的定义和初始化在 stm32f103xb_bl/hic_hal/uart.c 中。

// For usart
#define CDC_UART                     USART2
#define CDC_UART_ENABLE()            __HAL_RCC_USART1_CLK_ENABLE()
#define CDC_UART_DISABLE()           __HAL_RCC_USART1_CLK_DISABLE()
#define CDC_UART_IRQn                USART2_IRQn
#define CDC_UART_IRQn_Handler        USART2_IRQHandler

#define UART_PINS_PORT_ENABLE()      __HAL_RCC_GPIOA_CLK_ENABLE()
#define UART_PINS_PORT_DISABLE()     __HAL_RCC_GPIOA_CLK_DISABLE()

#define UART_TX_PORT                 GPIOA
#define UART_TX_PIN                  GPIO_PIN_2

#define UART_RX_PORT                 GPIOA
#define UART_RX_PIN                  GPIO_PIN_3


#define UART_CTS_PORT                GPIOA
#define UART_CTS_PIN                 GPIO_PIN_0

#define UART_RTS_PORT                GPIOA
#define UART_RTS_PIN                 GPIO_PIN_1

原版中使用的USART2,PA2用作TXD,PA3用作RXD,不需要Remap,如果要使用别的引脚来作为UART2的TXD、RXD则不仅需要修改引脚号,还需要加上Remap。以上还定义并初始化了流控制的引脚,但源码中并没有启用流控制的功能,有需要的话可以自行修改。

串口我是使用的USART1,TXD是PB6,RXD是PB7,需要使用Remap。

// For usart
#define CDC_UART                     USART1
#define CDC_UART_ENABLE()            __HAL_RCC_USART1_CLK_ENABLE()
#define CDC_UART_DISABLE()           __HAL_RCC_USART1_CLK_DISABLE()
#define CDC_UART_IRQn                USART1_IRQn
#define CDC_UART_IRQn_Handler        USART1_IRQHandler

#define UART_PINS_PORT_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()
#define UART_PINS_PORT_DISABLE()     __HAL_RCC_GPIOB_CLK_DISABLE()

#define UART_TX_PORT                 GPIOB
#define UART_TX_PIN                  GPIO_PIN_6

#define UART_RX_PORT                 GPIOB
#define UART_RX_PIN                  GPIO_PIN_7

若是使用PA9 PA10作为USART1的TXD和RXD,仅需要修改以上部分,若是使用PB7、PB7,那么久需要在 114 行额外写上:

__HAL_AFIO_REMAP_USART1_ENABLE();

有时候画板子省事会懒得连复位引脚,使用软复位进行复位,但是在MDK中勾选了reset and run之后DAP的软复位似乎不起效,可以通过修改PIN_nRESET_OUT函数,使DAP输出复位信号时并进行软复位操作。在DAP_config.h中的430行:

/** nRESET I/O pin: Set Output.
\param bit target device hardware reset pin status:
           - 0: issue a device hardware reset.
           - 1: release device hardware reset.
*/
// TODO - sw specific implementation should be created

__STATIC_FORCEINLINE void     PIN_nRESET_OUT(uint32_t bit)
{
    if (bit & 1)
        nRESET_PIN_PORT->BSRR = nRESET_PIN;
    else
        nRESET_PIN_PORT->BRR = nRESET_PIN;
}

改为如下:

/** nRESET I/O pin: Set Output.
\param bit target device hardware reset pin status:
           - 0: issue a device hardware reset.
           - 1: release device hardware reset.
*/
// TODO - sw specific implementation should be created

extern uint8_t swd_write_word(uint32_t addr, uint32_t val);

__STATIC_FORCEINLINE void     PIN_nRESET_OUT(uint32_t bit)
{
    if (bit & 1)
        nRESET_PIN_PORT->BSRR = nRESET_PIN;
    else
        nRESET_PIN_PORT->BRR = nRESET_PIN;
    
    swd_write_word((uint32_t)&SCB->AIRCR, ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | SCB_AIRCR_SYSRESETREQ_Msk));
}

生成

DAP_Link的固件分为BL和APP两个部分,需要先给MCU烧录BL,然后再通过BL写入APP。BL和APP的配置的源文件都是同一个,所以在BL中修改了IO和RCC就不用在APP的工程中修改了。

编译 stm32f103xb_bl 工程,可以得到 stm32f103xb_bl.bin 。可以使用SWD或者UART烧录到MCU中。但是脚本生成的工程是旧版的,无法在MDK中点击Download按钮,需要点击”Project“-”Manage“-”Migrate to version 5 format“才行。

编译 stm32f103xb_if 工程,可以得到 stm32f103xb_if.bin 。此文件是APP,不能够直接用MDK中的Download,在MCU烧录 stm32f103xb_bl.bin 之后,会在资源管理器中出现一个”MAINTENANCE“的USB储存设备,将 stm32f103xb_if.bin 复制到该设备里面,几秒钟之后APP就写入完成。

注意事项

github上的DAP_link现在是V2的,MDK5.26及更新版本才能识别到V2的DAP_Link。
如果写入APP之后制作成功,但是重新连接之后又变成了MAINTENANCE,那就说明nRESET引脚被错误拉低了,检查是否有短路,或者缺失了上拉电阻。

参考图