极海APM32芯得 EP.15 | VScode开发APM32F1过程分享
1.环境支持
极海MCU本文开发环境(Windows 10 环境下):
1.VS Code 安装好以下插件,以具有代码补全,工程设置以及调试功能。
  VSCode下载地址:https://code.visualstudio.com/
  C/C++插件(代码补全支持):https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools
  Cortex-Debug(仿真支持):https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug
  Embedded IDE(工程配置支持):https://marketplace.visualstudio.com/items?itemName=CL.eide
  Chinese (Simplified) (中文支持):https://marketplace.visualstudio.com/items?itemName=MS-CEINTL.vscode-language-pack-zh-hans
  关于插件的安装,这里就不在赘述,对于各位小伙伴来说是轻而易举的。
2.GCC工具链支持
  GNU Arm Embedded Toolchain Downloads:https://developer.arm.com/downloads/-/gnu-rm
  我这里选用的是“gcc-arm-none-eabi-10.3-2021.10-win32.exe”软件安装后会提示添加进系统的环境变量,若未提示添加至环境变量或使用的是全量压缩包,请自行添加至系统的环境变量中。检验方法为在任意一处调用CMD,在窗口打入:

arm-none-eabi-gcc -v会在命令行窗口显示相应的gcc版本号等信息。
3.仿真支持01,J-Link v9(v7.62a)
  我这里选用了“国产”的J-Link V9作为相应的仿真器。其软件服务可以从其官网下载:
  J-Link Software and Documentation pack:https://www.segger.com/downloads/jlink
  其最新版本的驱动已经支持了许多Geehy的芯片,若使用的是老版本的驱动需要添加Geehy芯片支持,添加方法请见本人之前的的分享:J-Link 添加Geehy APM32系列MCU:https://bbs.21ic.com/icview-3223010-1-1.html。
4.仿真支持02,Geehy-Link(OpenOCD)
  Geehy-Link是一款基于CMSIS-DAP的仿真器,我们对这种类型的仿真器也可以使用OpenOCD来进行使用。若使用该服务需要我们进行一些操作,这部分内容请看后续内容。OpenOCD我们可以在其官网:https://openocd.org/对其进行了解。其Windows的程序下载地址是:https://github.com/xpack-dev-tools/openocd-xpack/releases/。由于官方提供的为全量包(非安装包),我们解压至相应目录后需要把其添加至环境变量。
  检验方法为在任意一处调用CMD,在窗口打入:

openocd -v会在命令行窗口显示相应的OpenOCD版本号等信息。
  其支持APM32F1的芯片需我们额外做一些工作,请看后文。
5.APM32 F1 的SDK包
  Geehy提供的APM32F10x标准外设库是进行APM32F1开发的一个利器,我们可以在其官网进行下载。
  APM32F10x_SDK_V1.7:https://geehy.com/uploads/tool/APM32F10x_SDK_V1.7.zip

  准备好以上环境后,我们将相应的软硬件配合使用,完成工程创建编译,下载与调试等功能。本文介绍的VSCode开发APM32F1主要思想是使用GCC编译相应的工程代码,以完成一个免费的开发环境,这里将涉及到一些编译的基础知识和文件的创建。下面先简单介绍一下从代码到固件中间经历了什么(代码编译链接过程)。

3 编译链接基础
  我们固件(一般来说最终产物是bin文件)的生成,主要经历了程序编写,编译和链接的过程。
图片
  其中我们比较关注的重要文件有以下几个:
   .c/c++文件:为我们编写好的代码,这里包括APM32F1的标准外设库代码以及我们自己编写的代码。
   .s/S文件:为芯片的启动文件,其内容一般包括芯片的中断向量号以及芯片启动前的一些初始化操作。
   .ld文件:为芯片的链接文件,其内容一般为代码存储的Flash大小,RAM大小以及一些数据段的保存位置。
   .bin/hex文件:为代码经过编译链接后的程序内容,其中hex文件带相应的地址及校验信息。
  我们需要完成GCC编译的支持,基础情况下我们要准备好相应的.c/c++、.s/.S、.ld文件。了解各个文件的基本功能,已经编写规范后,我们可以很简单的写出相应的文件内容(由于APM32F1的最新SDK中GCC编译支持文件尚未提供,这部分内容我们自行准备)。
3.1 启动文件编写
  GCC环境下,芯片的启动文件一般是完成芯片的内核,中断向量号的声明,以及一些数据段的初始化操作,最终在Reset_Handler函数中调用相应的系统时钟初始化函数(“SystemInit”函数)然后执行“main”函数。
  我们新建一个文件“startup_apm32f10x_hd.S”其代码具体为:

其代码具体为:

.syntax unified
.cpu cortex-m3
.fpu softvfp
.thumb

.global g_pfnVectors
.global Default_Handler

/* start address for the initialization values of the .data section.
defined in linker script */
.word _sidata
/* start address for the .data section. defined in linker script */
.word _sdata
/* end address for the .data section. defined in linker script */
.word _edata
/* start address for the .bss section. defined in linker script */
.word _sbss
/* end address for the .bss section. defined in linker script */
.word _ebss

/**
* [url=home.php?mod=space&uid=247401]@brief[/url] This is the code that gets called when the processor first
*         starts execution following a reset event. Only the absolutely
*         necessary set is performed, after which the application
*         supplied main() routine is called.
* @param None
* @retval : None
*/

.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:

/* Copy the data segment initializers from flash to SRAM */
 ldr r0, =_sdata
 ldr r1, =_edata
 ldr r2, =_sidata
 movs r3, #0
 b LoopCopyDataInit

CopyDataInit:
ldr r4, [r2, r3]
 str r4, [r0, r3]
adds r3, r3, #4

LoopCopyDataInit:
 adds r4, r0, r3
cmp r4, r1
bcc CopyDataInit

/* Zero fill the bss segment. */
 ldr r2, =_sbss
 ldr r4, =_ebss
 movs r3, #0
b LoopFillZerobss

FillZerobss:
str  r3, [r2]
adds r2, r2, #4

LoopFillZerobss:
cmp r2, r4
bcc FillZerobss

/* Call the clock system intitialization function.*/
 bl  SystemInit
/* Call static constructors */
bl __libc_init_array
/* Call the application's entry point.*/
 bl main
bx lr
.size Reset_Handler, .-Reset_Handler
/**

* [url=home.php?mod=space&uid=247401]@brief[/url] This is the code that gets called when the processor receives an
*         unexpected interrupt. This simply enters an infinite loop, preserving
*         the system state for examination by a debugger.
*
* @param None
* @retval : None
*/
 .section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
 b Infinite_Loop
.size Default_Handler, .-Default_Handler
/*
*
* The minimal vector table for a Cortex M3. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
*
**/

.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
.size g_pfnVectors, .-g_pfnVectors

g_pfnVectors:

.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
.word WWDT_IRQHandler
.word PVD_IRQHandler
.word TAMPER_IRQHandler
.word RTC_IRQHandler
.word FLASH_IRQHandler
.word RCM_IRQHandler
.word EINT0_IRQHandler
.word EINT1_IRQHandler
.word EINT2_IRQHandler
.word EINT3_IRQHandler
.word EINT4_IRQHandler
.word DMA1_Channel1_IRQHandler
.word DMA1_Channel2_IRQHandler
.word DMA1_Channel3_IRQHandler
.word DMA1_Channel4_IRQHandler
.word DMA1_Channel5_IRQHandler
.word DMA1_Channel6_IRQHandler
.word DMA1_Channel7_IRQHandler
.word ADC1_2_IRQHandler
.word USBD_HP_CAN1_TX_IRQHandler
.word USBD_LP_CAN1_RX0_IRQHandler
.word CAN1_RX1_IRQHandler
.word CAN1_SCE_IRQHandler
.word EINT9_5_IRQHandler
.word TMR1_BRK_IRQHandler
.word TMR1_UP_IRQHandler
.word TMR1_TRG_COM_IRQHandler
.word TMR1_CC_IRQHandler
.word TMR2_IRQHandler
.word TMR3_IRQHandler
.word TMR4_IRQHandler
.word I2C1_EV_IRQHandler
.word I2C1_ER_IRQHandler
.word I2C2_EV_IRQHandler
.word I2C2_ER_IRQHandler
.word SPI1_IRQHandler
.word SPI2_IRQHandler
.word USART1_IRQHandler
.word USART2_IRQHandler
.word USART3_IRQHandler
.word EINT15_10_IRQHandler
.word RTC_Alarm_IRQHandler
.word USBDWakeUp_IRQHandler
.word TMR8_BRK_IRQHandler
.word TMR8_UP_IRQHandler
.word TMR8_TRG_COM_IRQHandler
.word TMR8_CC_IRQHandler
.word ADC3_IRQHandler
.word FSMC_IRQHandler
.word SDIO_IRQHandler
.word TMR5_IRQHandler
.word SPI3_IRQHandler
.word UART4_IRQHandler
.word UART5_IRQHandler
.word TMR6_IRQHandler
.word TMR7_IRQHandler
.word DMA2_Channel1_IRQHandler
.word DMA2_Channel2_IRQHandler
.word DMA2_Channel3_IRQHandler
.word DMA2_Channel4_5_IRQHandler
.word 0                                  // Reserved
.word USBD2_HP_CAN2_TX_IRQHandler        // USBD2 High Priority or CAN2 TX
.word USBD2_LP_CAN2_RX0_IRQHandler       // USBD2 Low Priority or CAN2 RX0
.word CAN2_RX1_IRQHandler                // CAN2 RX1
.word CAN2_SCE_IRQHandler                // CAN2 SCE
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0

/*
*

* Provide weak aliases for each Exception handler to the Default_Handler.
* As they are weak aliases, any function with the same name will override
* this definition.
*

*/

.weak NMI_Handler
.thumb_set NMI_Handler,Default_Handler

.weak HardFault_Handler
.thumb_set HardFault_Handler,Default_Handler

.weak MemManage_Handler
.thumb_set MemManage_Handler,Default_Handler

.weak BusFault_Handler
.thumb_set BusFault_Handler,Default_Handler

.weak UsageFault_Handler
.thumb_set UsageFault_Handler,Default_Handler

.weak SVC_Handler
.thumb_set SVC_Handler,Default_Handler

.weak DebugMon_Handler
.thumb_set DebugMon_Handler,Default_Handler


.weak PendSV_Handler
.thumb_set PendSV_Handler,Default_Handler

.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler

.weak WWDT_IRQHandler
.thumb_set WWDT_IRQHandler,Default_Handler


.weak PVD_IRQHandler
.thumb_set PVD_IRQHandler,Default_Handler

.weak TAMPER_IRQHandler
.thumb_set TAMPER_IRQHandler,Default_Handler

.weak RTC_IRQHandler
.thumb_set RTC_IRQHandler,Default_Handler

.weak FLASH_IRQHandler
.thumb_set FLASH_IRQHandler,Default_Handler

.weak RCM_IRQHandler
.thumb_set RCM_IRQHandler,Default_Handler
.weak EINT0_IRQHandler
.thumb_set EINT0_IRQHandler,Default_Handler

.weak EINT1_IRQHandler
.thumb_set EINT1_IRQHandler,Default_Handler
.weak EINT2_IRQHandler
.thumb_set EINT2_IRQHandler,Default_Handler

.weak EINT3_IRQHandler
.thumb_set EINT3_IRQHandler,Default_Handler

.weak EINT4_IRQHandler
.thumb_set EINT4_IRQHandler,Default_Handler

.weak DMA1_Channel1_IRQHandler
.thumb_set DMA1_Channel1_IRQHandler,Default_Handler

.weak DMA1_Channel2_IRQHandler
.thumb_set DMA1_Channel2_IRQHandler,Default_Handler


.weak DMA1_Channel3_IRQHandler
.thumb_set DMA1_Channel3_IRQHandler,Default_Handler


.weak DMA1_Channel4_IRQHandler
.thumb_set DMA1_Channel4_IRQHandler,Default_Handler



.weak DMA1_Channel5_IRQHandler

.thumb_set DMA1_Channel5_IRQHandler,Default_Handler



.weak DMA1_Channel6_IRQHandler

.thumb_set DMA1_Channel6_IRQHandler,Default_Handler



.weak DMA1_Channel7_IRQHandler

.thumb_set DMA1_Channel7_IRQHandler,Default_Handler



.weak ADC1_2_IRQHandler

.thumb_set ADC1_2_IRQHandler,Default_Handler



.weak USBD_HP_CAN1_TX_IRQHandler

.thumb_set USBD_HP_CAN1_TX_IRQHandler,Default_Handler



.weak USBD_LP_CAN1_RX0_IRQHandler

.thumb_set USBD_LP_CAN1_RX0_IRQHandler,Default_Handler



.weak CAN1_RX1_IRQHandler

.thumb_set CAN1_RX1_IRQHandler,Default_Handler



.weak CAN1_SCE_IRQHandler

.thumb_set CAN1_SCE_IRQHandler,Default_Handler



.weak EINT9_5_IRQHandler

.thumb_set EINT9_5_IRQHandler,Default_Handler



.weak TMR1_BRK_IRQHandler

.thumb_set TMR1_BRK_IRQHandler,Default_Handler



.weak TMR1_UP_IRQHandler

.thumb_set TMR1_UP_IRQHandler,Default_Handler



.weak TMR1_TRG_COM_IRQHandler

.thumb_set TMR1_TRG_COM_IRQHandler,Default_Handler



.weak TMR1_CC_IRQHandler

.thumb_set TMR1_CC_IRQHandler,Default_Handler



.weak TMR2_IRQHandler

.thumb_set TMR2_IRQHandler,Default_Handler



.weak TMR3_IRQHandler

.thumb_set TMR3_IRQHandler,Default_Handler



.weak TMR4_IRQHandler

.thumb_set TMR4_IRQHandler,Default_Handler



.weak I2C1_EV_IRQHandler

.thumb_set I2C1_EV_IRQHandler,Default_Handler



.weak I2C1_ER_IRQHandler

.thumb_set I2C1_ER_IRQHandler,Default_Handler



.weak I2C2_EV_IRQHandler

.thumb_set I2C2_EV_IRQHandler,Default_Handler



.weak I2C2_ER_IRQHandler

.thumb_set I2C2_ER_IRQHandler,Default_Handler



.weak SPI1_IRQHandler

.thumb_set SPI1_IRQHandler,Default_Handler



.weak SPI2_IRQHandler

.thumb_set SPI2_IRQHandler,Default_Handler



.weak USART1_IRQHandler

.thumb_set USART1_IRQHandler,Default_Handler



.weak USART2_IRQHandler

.thumb_set USART2_IRQHandler,Default_Handler



.weak USART3_IRQHandler

.thumb_set USART3_IRQHandler,Default_Handler



.weak EINT15_10_IRQHandler

.thumb_set EINT15_10_IRQHandler,Default_Handler



.weak RTC_Alarm_IRQHandler

.thumb_set RTC_Alarm_IRQHandler,Default_Handler



.weak USBDWakeUp_IRQHandler

.thumb_set USBDWakeUp_IRQHandler,Default_Handler



.weak TMR8_BRK_IRQHandler

.thumb_set TMR8_BRK_IRQHandler,Default_Handler



.weak TMR8_UP_IRQHandler

.thumb_set TMR8_UP_IRQHandler,Default_Handler



.weak TMR8_TRG_COM_IRQHandler

.thumb_set TMR8_TRG_COM_IRQHandler,Default_Handler



.weak TMR8_CC_IRQHandler

.thumb_set TMR8_CC_IRQHandler,Default_Handler



.weak ADC3_IRQHandler

.thumb_set ADC3_IRQHandler,Default_Handler



.weak FSMC_IRQHandler

.thumb_set FSMC_IRQHandler,Default_Handler



.weak SDIO_IRQHandler

.thumb_set SDIO_IRQHandler,Default_Handler



.weak TMR5_IRQHandler

.thumb_set TMR5_IRQHandler,Default_Handler



.weak SPI3_IRQHandler

.thumb_set SPI3_IRQHandler,Default_Handler



.weak UART4_IRQHandler

.thumb_set UART4_IRQHandler,Default_Handler



.weak UART5_IRQHandler

.thumb_set UART5_IRQHandler,Default_Handler



.weak TMR6_IRQHandler

.thumb_set TMR6_IRQHandler,Default_Handler



.weak TMR7_IRQHandler

.thumb_set TMR7_IRQHandler,Default_Handler



.weak DMA2_Channel1_IRQHandler

.thumb_set DMA2_Channel1_IRQHandler,Default_Handler



.weak DMA2_Channel2_IRQHandler

.thumb_set DMA2_Channel2_IRQHandler,Default_Handler



.weak DMA2_Channel3_IRQHandler

.thumb_set DMA2_Channel3_IRQHandler,Default_Handler



.weak DMA2_Channel4_5_IRQHandler

.thumb_set DMA2_Channel4_5_IRQHandler,Default_Handler



.weak USBD2_HP_CAN2_TX_IRQHandler

.thumb_set USBD2_HP_CAN2_TX_IRQHandler,Default_Handler



.weak USBD2_LP_CAN2_RX0_IRQHandler

.thumb_set USBD2_LP_CAN2_RX0_IRQHandler,Default_Handler



.weak CAN2_RX1_IRQHandler

.thumb_set CAN2_RX1_IRQHandler,Default_Handler



.weak CAN2_SCE_IRQHandler

.thumb_set CAN2_SCE_IRQHandler,Default_Handler

3.2 链接文件编写
  链接脚本文件一般为芯片的主要存储空间信息以及一些程序段保存位置信息,在了解其编写规范后编写也比较简单的将其写出。我们这里新建一个文件“apm32_flash.ld”,其内容为:


/* Entry Point */

ENTRY(Reset_Handler)



/* Highest address of the user mode stack */

_estack = 0x20020000;    /* end of 64K RAM */



/* Generate a link error if heap and stack don't fit into RAM */

_Min_Heap_Size = 0;      /* required amount of heap */

_Min_Stack_Size = 0x200; /* required amount of stack */



/* Specify the memory areas */

MEMORY

{

 FLASH (rx)     : ORIGIN = 0x08000000, LENGTH = 512K

 RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 128K

}



/* Define output sections */

SECTIONS

{

 /* The startup code goes first into FLASH */

.isr_vector :

{

  . = ALIGN(4);

   KEEP(*(.isr_vector)) /* Startup code */

  . = ALIGN(4);

} >FLASH



 /* The program code and other data goes into FLASH */

.text :

{

  . = ALIGN(4);

   *(.text)           /* .text sections (code) */

   *(.text*)          /* .text* sections (code) */

   *(.rodata)         /* .rodata sections (constants, strings, etc.) */

   *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */

   *(.glue_7)         /* glue arm to thumb code */

   *(.glue_7t)        /* glue thumb to arm code */



   KEEP (*(.init))

   KEEP (*(.fini))



  . = ALIGN(4);

   _etext = .;        /* define a global symbols at end of code */

} >FLASH





  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH

  .ARM : {

   __exidx_start = .;

     *(.ARM.exidx*)

     __exidx_end = .;

  } >FLASH



.ARM.attributes : { *(.ARM.attributes) } > FLASH



.preinit_array     :

{

   PROVIDE_HIDDEN (__preinit_array_start = .);

   KEEP (*(.preinit_array*))

   PROVIDE_HIDDEN (__preinit_array_end = .);

} >FLASH

.init_array :

{

   PROVIDE_HIDDEN (__init_array_start = .);

   KEEP (*(SORT(.init_array.*)))

   KEEP (*(.init_array*))

   PROVIDE_HIDDEN (__init_array_end = .);

} >FLASH

.fini_array :

{

   PROVIDE_HIDDEN (__fini_array_start = .);

   KEEP (*(.fini_array*))

   KEEP (*(SORT(.fini_array.*)))

   PROVIDE_HIDDEN (__fini_array_end = .);

} >FLASH



 /* used by the startup to initialize data */

 _sidata = .;



 /* Initialized data sections goes into RAM, load LMA copy after code */

.data : AT ( _sidata )

{

  . = ALIGN(4);

   _sdata = .;        /* create a global symbol at data start */

   *(.data)           /* .data sections */

   *(.data*)          /* .data* sections */



  . = ALIGN(4);

   _edata = .;        /* define a global symbol at data end */

} >RAM



 /* Uninitialized data section */

. = ALIGN(4);

.bss :

{

   /* This is used by the startup in order to initialize the .bss secion */

   _sbss = .;         /* define a global symbol at bss start */

   __bss_start__ = _sbss;

   *(.bss)

   *(.bss*)

   *(COMMON)



  . = ALIGN(4);

   _ebss = .;         /* define a global symbol at bss end */

   __bss_end__ = _ebss;

} >RAM



 PROVIDE ( end = _ebss );

 PROVIDE ( _end = _ebss );



 /* User_heap_stack section, used to check that there is enough RAM left */

._user_heap_stack :

{

  . = ALIGN(4);

  . = . + _Min_Heap_Size;

  . = . + _Min_Stack_Size;

  . = ALIGN(4);

} >RAM



 /* Remove information from the standard libraries */

 /DISCARD/ :

{

   libc.a ( * )

   libm.a ( * )

   libgcc.a ( * )

}

}
  这里需要注意的是我的链接文件是基于APM32F103ZET6写的,若使用的是其他的芯片,需要把FLASH和RAM大小设置为相应芯片的大小,否则程序下载进芯片后将会无法运行。

4 VSCode环境配置
  编写完成GCC环境下的启动文件和链接文件后,我们接下来将VSCode环境的配置,以使得我们可以在VSCode上流畅的进行开发。
4.1 Cortex-Debug 插件配置  
  打开扩展程序,找到Cortex-Debug 插件,右键选择“扩展设置”,配置相应工具链的路径。注意:路径复制后将文件目录分隔符“\”改为“\\”。
    1.配置Arm Toolchain路径。
    2.配置JLinkGDBServer路径。
    3.配置openocd路径。

4.2 Embedded IDE插件配置
  打开扩展程序,找到Embedded IDE插件,右键选择“扩展设置”,配置相应工具链的路径。
    1.配置Arm Toolchain路径。
    2.配置JLink软件安装路径。
    3.配置openocd路径。


5 OpenOCD支持
  若使用Geehy-Link调试,我们需要完成OpenOCD对APM32F1的支持,我们新建“apm32f1x.cfg”,完成对其的内容内容编辑后后将其保存至OpenOCD的安装路径下的“\share\openocd\scripts\target”中。
  apm32f1x.cfg的内容为:


# script for apm32f1x family



#

# apm32 devices support both JTAG and SWD transports.

#

source [find target/swj-dp.tcl]

source [find mem_helper.tcl]



if { [info exists CHIPNAME] } {

   set _CHIPNAME $CHIPNAME

} else {

   set _CHIPNAME apm32f1x

}



set _ENDIAN little



# Work-area is a space in RAM used for flash programming

# By default use 4kB.

if { [info exists WORKAREASIZE] } {

   set _WORKAREASIZE $WORKAREASIZE

} else {

   set _WORKAREASIZE 0x1000

}



# Allow overriding the Flash bank size

if { [info exists FLASH_SIZE] } {

    set _FLASH_SIZE $FLASH_SIZE

} else {

    # autodetect size

    set _FLASH_SIZE 0

}



#jtag scan chain

if { [info exists CPUTAPID] } {

   set _CPUTAPID $CPUTAPID

} else {

      # this is the SW-DP tap id not the jtag tap id

      set _CPUTAPID 0x2ba01477

}



swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID

dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu



if {[using_jtag]} {

   jtag newtap $_CHIPNAME bs -irlen 5

}



set _TARGETNAME $_CHIPNAME.cpu

target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap



$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0



# flash size will be probed

set _FLASHNAME $_CHIPNAME.flash

flash bank $_FLASHNAME stm32f1x 0x08000000 $_FLASH_SIZE 0 0 $_TARGETNAME



# JTAG speed should be <= F_CPU/6. F_CPU after reset is 8MHz, so use F_JTAG = 1MHz

adapter speed 1000



adapter srst delay 100

if {[using_jtag]} {

 jtag_ntrst_delay 100

}



reset_config srst_nogate



if {![using_hla]} {

    # if srst is not fitted use SYSRESETREQ to

    # perform a soft reset

    cortex_m reset_config sysresetreq

}



$_TARGETNAME configure -event examine-end {

        # DBGMCU_CR |= DBG_WWDT_STOP | DBG_IWDT_STOP |

        #              DBG_STANDBY | DBG_STOP | DBG_SLEEP

        mmw 0xE0042004 0x00000307 0

}



$_TARGETNAME configure -event trace-config {

        # Set TRACE_IOEN; TRACE_MODE is set to async; when using sync

        # change this value accordingly to configure trace pins

        # assignment

        mmw 0xE0042004 0x00000020 0

}
6 工程创建
  完成了比较核心的VSCode的插件配置,以及仿真器配置,我们接下来需要开始动手编写代码创建工程了,现在我们整体进度完成了百分之七八十咯。
6.1 准备源码目录
  我们在一个文件夹准备好我们的工程文件,我这里是基于“APM32F10x_SDK_V1.7.zip”中的“SysTick_TimeBase”例程直接去创建的,直接在其“Project”文件夹下新建一个“VSCode”文件夹,保存我们的工程文件及编译的中间过程文件。


  然后我们把上文制作好的“apm32_flash.ld”和“startup_apm32f10x_hd.S”文件复制至“VSCode”文件夹内。

6.2 新建工程
  完成源码的准备后我们开始使用“Embedded IDE”插件来完成我们的项目创建。


  然后我们选择刚刚新建的项目路径保存我们的项目。

  这个时候我们最终得到以下目录结构。

  然后我们接着一步步完成我们的源码添加、宏定义声明、头文件包含、链接文件添加等操作。
    1.源码添加,右键点击“项目资源”,选择“虚拟文件夹”,添加“CMSIS”、“StdPeriphDriver”、“Application”和“Board”文件夹目录。然后添加相应的源文件。
    2.芯片支持配置,右键点击“芯片支持”,选择安装然后选择“From Disk”,选择Geehy提供的官方最新支持包。安装成功后芯片选型为“APM32F103ZE”。


    3.“构建配置”选择“GCC”,CPU类型选择“Cortex-M3”,链接脚本路径选择我们的保存路径“../apm32_flash.ld”(这里选用相对路径)。

    4.“构建器选项”中C/C++编译的C标准选择“c99”。

    5.“烧录配置”这里选择“JLink”。芯片名称选择“APM32F103ZE”。接口类型可选“SWD”或“JTAG”,这里选择“SWD”。下载速度选择“8000kHz”。

      若是选择使用Geehy-Link,则配置如下:

    6.“项目属性”将我们源码的头文件路径以及宏定义添加进去。



  至此我们的工程新建完毕。
7 编译与下载
  完成工程创建后我们就可以进行工程的编译与下载了。点击我们的工程,我们会发现出现四个图标,各个功能从左到右依次为:


    构建,即编译工程。
    清理,即清除编译的中间过程文件。
    下载,即下载程序至芯片(需保证硬件连接正常)。
    重新构建,即重新编译工程,一般为修改了部分源码所使用。
  先进行“构建”,在终端窗口查看编译信息。


  然后进行“下载”,此时会调用J-Link的相关下载程序以完成对芯片程序的下载。

8 程序仿真
  完成了程序编译以及下载功能,MCU的仿真功能也是必不可少的。
  点击左侧的“仿真”按钮,进入仿真界面再点击绿色的仿真运行按钮,进入程序仿真界面。


  在默认的仿真界面中主要分别为三个板块。
  最上面是程序运行控制按钮,有“单步运行”、“全速运行”和“退出仿真”等按钮。
  左侧是芯片的主要内部信息,有内核寄存器,变量查看,外设寄存器查看等窗口。
  居中的是源码,通过单击行号左侧,可以设置断点。