返回列表 发新帖我要提问本帖赏金: 200.00元(功能说明)

[MM32生态] 有免费且更简便的开发环境?支持GCC?可以用VS Code里的EIDE

[复制链接]
4827|11
 楼主| yang377156216 发表于 2022-4-16 21:50 | 显示全部楼层 |阅读模式
本帖最后由 yang377156216 于 2022-4-17 15:34 编辑
#申请原创#  @21小跑堂   

整体概览
作为 ARM Cotex M 系列内核的 32 位单片机开发者,在平时工作中经常会因为芯片平台的不同而去切换各种开发环境,幸好市面上有着各式各样的工具能够满足开发攻城狮们的需求,大体上这些开发环境可以分为几大类:较为通用的集成环境(编辑、编译、链接、下载和调试等功能合为一体),比较有代表性的有 Keil MDK、IAR EWARM、SEGGER Embedded Studio 、Mbed Studio 等等;厂家自制的专用 IDE ,主流的代表作有 STM32CubeIDE 、MCUXpresso IDE 、RT-Thread Studio 、MPLAB IDE 等等;还有一种 ”混搭版“ 的风格,开发者可以在 Eclipse 、VS Code 、Visual Studio 等工具中搭配 ARM GCC 编译工具链进行项目软件开发。在眼花缭乱的工具中,根据是否免费、编辑功能体验度是否高、上手与熟悉难度是否适宜等因素综合考虑,今天选择了一种开源、免费且更加简便的 VS Code +EIDE + GCC + J-Link 方式进行 MM32F0144C6P 芯片的软件开发。接下来的内容整体可分为以下几点:
  • 单片机软件开发流程和其中一些知识点
  • VS Code 优势和 EIDE 插件介绍
  • 准备资源
  • 着手搭建环境
  • 编译、烧录及调试演示视频
  • 附件内容
  • 参考资源

一、单片机软件开发流程和其中一些知识点
一个 ARM Cortex M 系列内核的 MCU 软件开发者需要清楚完成一个软件项目的大致流程是需经过以下几步的:
  • 编写代码。通常是 C/C++ 语言,但是在考究执行效率的时候就得用到 ASM 汇编,讲究面向对象的结构化编程时甚至用上 Python、Lua 等其它高级语言。
  • 将各种语言进行编译和链接最终生成机器二进制文件。相信绝大部分人都亲身体验过这一步,在很多集成环境中就是一键 Build ,然后默认生成 hex 格式烧录文件。
  • 烧录和调试。一般烧录方式分为 ISP 、ICP 和 IAP ,根据 MCU 的不同选择的烧录工具也不尽相同。 有些厂商,例如国产的 STC,只支持串口方式的 ISP ,且配套的在线仿真调试器也是基于串口形式的;而 ARM 内核的大部分 MCU 都支持 JTAG/SWD 接口形式的 ICP 方式,配套的调试工具可以是 J-Link、U-Link、CMSIS DAP-Link 和 ST-Link 等等,也几乎都有配套的上位机给予支持。
  • 运行程序。基本经过一番调试后,最终能够得到满足功能需求的代码了,此时终于可以结案交付了,当然,后面可能还会面对大大小小的 Bug ,还得继续 “升级打怪”。
MCU 软件开发流程.png
以上内容简述了整个软件开发的流程,其中涉及到的编译和链接这个知识点比较重要。为了对这些名词有更深的理解,特意查了资料。通俗讲,编译器会将 C 程序转换成一种机器能理解的符号形式的汇编语言程序,包括了各种伪指令和符号表,然后汇编器将这些代码转换成目标文件,包括了机器语言指令、数据和指令正确放入内存所需要的信息,最后由系统程序(链接编译器) 将各个独立汇编的机器语言程序组合起来并且解释所有未定义的标记,直到生成可执行文件。这其中也会涉及到很多文件,比如后缀名为 .c / .s / .o / .a / .lib / .elf / .axf / .bin / .hex 等等文件,有些是中间文件,另外一些是结果可执行文件。以下这幅图用于帮助理解这个过程:
编译链接过程.jpg

既然编译链接这个过程有着举足轻重的地位,那市面上的又有哪些主流工具可供选择呢?据了解,目前针对于 ARM平台的主流编译器主要有以下一些:

ARM 编译器.png

用到比较多的 KEIL AC5/AC6 是闭源和收费的,编译速度在大型项目上 AC6 非常有优势,它们都具备多种优化等级可调,编出来的代码大小较小且运行更为安全,另外也都可以在 ARM 官网单独下载,比较适合用于产品开发中; IAR 的 ICC 编译器也更加高性能而被广泛使用,在很多 benchmark 跑分测试中同颗芯片的运行结果效率都更高些,且编出来的代码大小也适中;Keil MDK、IAR 等工具都是收费的,在使用中很可能牵扯到一定的版权问题,而 GCC(GNU Compiler Collection)作为GNU计划的一部分,**是完全免费的,这就是最大的优势**,尽管使用 GCC 是需要付出一定代价的——对编译后造成的不良后果负全责(比如编译出来的代码量非常大,程序跑飞从而致使板级器件烧毁,系统死机崩溃导致丢失关键数据之类的情况)。
这里特别感谢硬汉大哥和傻孩子大哥针对各家编译器做出的实测比较和探讨,可参见:https://www.amobbs.com/thread-5709400-1-1.html

二、VS Code 优势和 EIDE 插件介绍
这里选择 ARM GCC 交叉编译工具链作为最重要的一环,除了看重它开源、免费、资料多等优势以外,更重要的是可以跨平台。除了“内核”,那再来聊聊为什么选择 VS Code 作为外壳吧。
考虑 Windows 环境下能够使用 Eclipse IDE for C/C++ Developers 来搭建 ARM 开发环境,但是整个 JAVA 环境占用了太大的 PC 资源,完整地安装下来会非常臃肿,性价比不高,所以转而会考虑一个跨平台且非常流行的编辑器 VS Code ,其特点有:
  • 免费、开源且跨平台
  • 轻量、解耦,本身只有编辑器的功能,安装包大小仅不到100MB,但可以按需安装形形色色的插件使得整个工具链的集成度非常高
  • 支持几乎所有语言的编辑,且有配套插件
  • 编辑功能完善,行编辑、多行注释、多行选择、自动补全、自动跳转等功能支持到位
  • 界面好看,不会崩溃
  • 等等……


好的开发环境就像一把好刀,能让我们开发速度达到事半功倍,主流的就是对的,下图显示了 VS Code 的受欢迎程度:


vs code 大众选择.png

VS Code 里面的 EIDE 插件是个什么东西?很多人可能没有接触过,不知道它能让 KEIL 工程导入到 VS Code 中有多方便。下面来简单介绍。
EIDE是 keil-assistant 插件的升级版,它们同属一个开发团队,这是一款适用于 8051/STM8/Cortex-M/RISC-V 的单片机开发环境。能够在 VS Code 上提供 8051, STM8, Cortex-M, RISC-V 项目的开发, 编译, 烧录功能。通俗点说,它就是那个披上 VS Code 外衣然后可以将 GCC 工具、各种调试工具集大成的 “后来者”,有多种实用功能,能让开发工作变得更加简单高效。更多相关资源可以查看官方提供的文档:https://docs.em-ide.com/#/ 。

三、准备资源
硬件资源如下:
  • 一块 MM32F0144C6P 芯片的核心板,这里选用灵动微官方提供的红色最小系统板
  • 一根 Micro USB 插头的数据线,用作串口输出和供电
  • 一个 J-Link 调试器,这里用的网上买的盗版便宜货 V9 版
0144 核心板原理图.png
软件资源如下:
  • Visual Studio Code for windows
  • gcc-arm-none-eabi for windows
  • JLink_Windows_V670g.exe 以及 灵动官方 J-Flash 插件安装包 (让 J-Link 可以搜索到 MM32F0144C6P 这颗芯片,不然只能选择 Cortex M0)
  • CH340 USB-Serial Port Driver
  • 一份已经调试好的 KEIL template 工程代码包
  • MM32F0144C6P GCC 启动文件和链接文件  (由于官方不提供,所以得先办法自己编制)
  • MindMotion.MM32F0140_DFP.0.0.6  (可选,在 EIDE 中可以安装上它后具备芯片信息)
  • Mingw-w64 for windows(可选,可用于 Makefile 驱动一键 make 进行编译)


提示:本文中的展示基于  WIN10 64 位 PC 系统,用户需要根据自己的电脑系统下载对应版本的资源。既可通过上述超链接获取,也可直接使用压缩包内的,为更好对照文中步骤实现环境搭建,建议尽量使用附件提供的资源包。工具软件的安装可以根据自己的习惯自定义路径,也可以一直 next 选择默认模式,记得将  gcc-arm-none-eabi 工具安装路径加入系统环境变量中,保险起见其它几个也可以一并添加。
添加系统环境变量.png
由于官方不提供 MM32F0144C6P GCC 启动文件对应的链接文件,那自己动手制作,思路是找到 STM32F030x 相关的文件来做修改,因为它们两者外设资源上极为相似。要注意的是,需要根据 MM32F0144C6P 实际的中断向量表去做修改。以下为修改好的 startup_mm32f0140_gcc.s 文件:
  1. /**
  2.   ******************************************************************************
  3.   * [url=home.php?mod=space&uid=288409]@file[/url]      startup_mm32f0140_gcc.s
  4.   * [url=home.php?mod=space&uid=187600]@author[/url]   
  5.   * [url=home.php?mod=space&uid=247401]@brief[/url]     MM32F014x devices vector table for GCC toolchain.
  6.   *            This module performs:
  7.   *                - Set the initial SP
  8.   *                - Set the initial PC == Reset_Handler,
  9.   *                - Set the vector table entries with the exceptions ISR address
  10.   *                - Branches to main in the C library (which eventually
  11.   *                  calls main()).
  12.   *            After Reset the Cortex-M0 processor is in Thread mode,
  13.   *            priority is Privileged, and the Stack is set to Main.
  14.   *
  15.   ******************************************************************************
  16.   */

  17.   .syntax unified
  18.   .cpu cortex-m0
  19.   .fpu softvfp
  20.   .thumb

  21. .global g_pfnVectors
  22. .global Default_Handler

  23. /* start address for the initialization values of the .data section.
  24. defined in linker script */
  25. .word _sidata
  26. /* start address for the .data section. defined in linker script */
  27. .word _sdata
  28. /* end address for the .data section. defined in linker script */
  29. .word _edata
  30. /* start address for the .bss section. defined in linker script */
  31. .word _sbss
  32. /* end address for the .bss section. defined in linker script */
  33. .word _ebss

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

  42.   .section .text.Reset_Handler
  43.   .weak Reset_Handler
  44.   .type Reset_Handler, %function
  45. Reset_Handler:
  46.   ldr   r0, =_estack
  47.   mov   sp, r0          /* set stack pointer */

  48. /* Copy the data segment initializers from flash to SRAM */
  49.   ldr r0, =_sdata
  50.   ldr r1, =_edata
  51.   ldr r2, =_sidata
  52.   movs r3, #0
  53.   b LoopCopyDataInit

  54. CopyDataInit:
  55.   ldr r4, [r2, r3]
  56.   str r4, [r0, r3]
  57.   adds r3, r3, #4

  58. LoopCopyDataInit:
  59.   adds r4, r0, r3
  60.   cmp r4, r1
  61.   bcc CopyDataInit
  62.   
  63. /* Zero fill the bss segment. */
  64.   ldr r2, =_sbss
  65.   ldr r4, =_ebss
  66.   movs r3, #0
  67.   b LoopFillZerobss

  68. FillZerobss:
  69.   str  r3, [r2]
  70.   adds r2, r2, #4

  71. LoopFillZerobss:
  72.   cmp r2, r4
  73.   bcc FillZerobss

  74. /* Call the clock system intitialization function.*/
  75.   bl  SystemInit
  76. /* Call static constructors */
  77.   bl __libc_init_array
  78. /* Call the application's entry point.*/
  79.   bl main

  80. LoopForever:
  81.     b LoopForever


  82. .size Reset_Handler, .-Reset_Handler

  83. /**
  84. * [url=home.php?mod=space&uid=247401]@brief[/url]  This is the code that gets called when the processor receives an
  85. *         unexpected interrupt.  This simply enters an infinite loop, preserving
  86. *         the system state for examination by a debugger.
  87. *
  88. * @param  None
  89. * @retval : None
  90. */
  91.     .section .text.Default_Handler,"ax",%progbits
  92. Default_Handler:
  93. Infinite_Loop:
  94.   b Infinite_Loop
  95.   .size Default_Handler, .-Default_Handler
  96. /******************************************************************************
  97. *
  98. * The minimal vector table for a Cortex M0.  Note that the proper constructs
  99. * must be placed on this to ensure that it ends up at physical address
  100. * 0x0000.0000.
  101. *
  102. ******************************************************************************/
  103.    .section .isr_vector,"a",%progbits
  104.   .type g_pfnVectors, %object
  105.   .size g_pfnVectors, .-g_pfnVectors


  106. g_pfnVectors:
  107.   .word  _estack
  108.   .word  Reset_Handler
  109.   .word  NMI_Handler
  110.   .word  HardFault_Handler
  111.   .word  0
  112.   .word  0
  113.   .word  0
  114.   .word  0
  115.   .word  0
  116.   .word  0
  117.   .word  0
  118.   .word  SVC_Handler
  119.   .word  0
  120.   .word  0
  121.   .word  PendSV_Handler
  122.   .word  SysTick_Handler
  123.   .word  WWDG_IRQHandler                   /* Window WatchDog              */
  124.   .word  PVD_IRQHandler                    /* PVD through EXTI Line detect */
  125.   .word  MIPI_IRQHandler                   /* MIPI                         */
  126.   .word  FLASH_IRQHandler                  /* FLASH                        */
  127.   .word  RCC_IRQHandler                    /* RCC                          */
  128.   .word  EXTI0_1_IRQHandler                /* EXTI Line 0 and 1            */
  129.   .word  EXTI2_3_IRQHandler                /* EXTI Line 2 and 3            */
  130.   .word  EXTI4_15_IRQHandler               /* EXTI Line 4 to 15            */
  131.   .word  HWDIV_IRQHandler                  /* HWDIV                        */
  132.   .word  DMA1_Channel1_IRQHandler          /* DMA1 Channel 1               */
  133.   .word  DMA1_Channel2_3_IRQHandler        /* DMA1 Channel 2 and Channel 3 */
  134.   .word  DMA1_Channel4_5_IRQHandler        /* DMA1 Channel 4 and Channel 5 */
  135.   .word  ADC1_COMP_IRQHandler              /* ADC1 & COMP                  */
  136.   .word  TIM1_BRK_UP_TRG_COM_IRQHandler    /* TIM1 Break, Update, Trigger and Commutation */
  137.   .word  TIM1_CC_IRQHandler                /* TIM1 Capture Compare         */
  138.   .word  TIM2_IRQHandler                   /* TIM2                         */
  139.   .word  TIM3_IRQHandler                   /* TIM3                         */
  140.   .word  0                                 /* Reserved                     */
  141.   .word  0                                 /* Reserved                     */
  142.   .word  TIM14_IRQHandler                  /* TIM14                        */
  143.   .word  0                                 /* Reserved                     */
  144.   .word  TIM16_IRQHandler                  /* TIM16                        */
  145.   .word  TIM17_IRQHandler                  /* TIM17                        */
  146.   .word  I2C1_IRQHandler                   /* I2C1                         */
  147.   .word  0                                 /* Reserved                     */
  148.   .word  SPI1_IRQHandler                   /* SPI1                         */
  149.   .word  SPI2_IRQHandler                   /* SPI2                         */
  150.   .word  UART1_IRQHandler                  /* UART1                        */
  151.   .word  UART2_IRQHandler                  /* UART2                        */
  152.   .word  UART3_IRQHandler                  /* UART3                        */
  153.   .word  FLEX_CAN_IRQHandler               /* FLEX_CAN                     */
  154.   .word  0                                 /* Reserved                     */


  155. /*******************************************************************************
  156. *
  157. * Provide weak aliases for each Exception handler to the Default_Handler.
  158. * As they are weak aliases, any function with the same name will override
  159. * this definition.
  160. *
  161. *******************************************************************************/

  162.   .weak      NMI_Handler
  163.   .thumb_set NMI_Handler,Default_Handler

  164.   .weak      HardFault_Handler
  165.   .thumb_set HardFault_Handler,Default_Handler

  166.   .weak      SVC_Handler
  167.   .thumb_set SVC_Handler,Default_Handler

  168.   .weak      PendSV_Handler
  169.   .thumb_set PendSV_Handler,Default_Handler

  170.   .weak      SysTick_Handler
  171.   .thumb_set SysTick_Handler,Default_Handler

  172.   .weak      WWDG_IRQHandler
  173.   .thumb_set WWDG_IRQHandler,Default_Handler

  174.   .weak      PVD_IRQHandler
  175.   .thumb_set PVD_IRQHandler,Default_Handler

  176.   .weak      MIPI_IRQHandler
  177.   .thumb_set MIPI_IRQHandler,Default_Handler

  178.   .weak      FLASH_IRQHandler
  179.   .thumb_set FLASH_IRQHandler,Default_Handler

  180.   .weak      RCC_IRQHandler
  181.   .thumb_set RCC_IRQHandler,Default_Handler

  182.   .weak      EXTI0_1_IRQHandler
  183.   .thumb_set EXTI0_1_IRQHandler,Default_Handler

  184.   .weak      EXTI2_3_IRQHandler
  185.   .thumb_set EXTI2_3_IRQHandler,Default_Handler

  186.   .weak      EXTI4_15_IRQHandler
  187.   .thumb_set EXTI4_15_IRQHandler,Default_Handler

  188.   .weak      HWDIV_IRQHandler
  189.   .thumb_set HWDIV_IRQHandler,Default_Handler
  190.   
  191.   .weak      DMA1_Channel1_IRQHandler
  192.   .thumb_set DMA1_Channel1_IRQHandler,Default_Handler

  193.   .weak      DMA1_Channel2_3_IRQHandler
  194.   .thumb_set DMA1_Channel2_3_IRQHandler,Default_Handler

  195.   .weak      DMA1_Channel4_5_IRQHandler
  196.   .thumb_set DMA1_Channel4_5_IRQHandler,Default_Handler

  197.   .weak      ADC1_COMP_IRQHandler
  198.   .thumb_set ADC1_COMP_IRQHandler,Default_Handler

  199.   .weak      TIM1_BRK_UP_TRG_COM_IRQHandler
  200.   .thumb_set TIM1_BRK_UP_TRG_COM_IRQHandler,Default_Handler

  201.   .weak      TIM1_CC_IRQHandler
  202.   .thumb_set TIM1_CC_IRQHandler,Default_Handler

  203.   .weak      TIM2_IRQHandler
  204.   .thumb_set TIM2_IRQHandler,Default_Handler

  205.   .weak      TIM3_IRQHandler
  206.   .thumb_set TIM3_IRQHandler,Default_Handler

  207.   .weak      TIM14_IRQHandler
  208.   .thumb_set TIM14_IRQHandler,Default_Handler

  209.   .weak      TIM16_IRQHandler
  210.   .thumb_set TIM16_IRQHandler,Default_Handler

  211.   .weak      TIM17_IRQHandler
  212.   .thumb_set TIM17_IRQHandler,Default_Handler

  213.   .weak      I2C1_IRQHandler
  214.   .thumb_set I2C1_IRQHandler,Default_Handler

  215.   .weak      SPI1_IRQHandler
  216.   .thumb_set SPI1_IRQHandler,Default_Handler

  217.   .weak      SPI2_IRQHandler
  218.   .thumb_set SPI2_IRQHandler,Default_Handler
  219.   
  220.   .weak      UART1_IRQHandler
  221.   .thumb_set UART1_IRQHandler,Default_Handler
  222.   
  223.   .weak      UART2_IRQHandler
  224.   .thumb_set UART2_IRQHandler,Default_Handler

  225.   .weak      UART3_IRQHandler
  226.   .thumb_set UART3_IRQHandler,Default_Handler
  227.   
  228.   .weak      FLEX_CAN_IRQHandler
  229.   .thumb_set FLEX_CAN_IRQHandler,Default_Handler
  230.   
  231. /************************ (C) COPYRIGHT *****END OF FILE****/
以下为修改好的 MM32F0144C6P_FLASH 文件:
  1. /*
  2. *****************************************************************************
  3. **
  4. **  File        : mm32_flash.ld
  5. **
  6. **  Abstract    : Linker script for MM32F0144C6P Device with
  7. **                64KByte FLASH, 16KByte RAM
  8. **
  9. **                Set heap size, stack size and stack location according
  10. **                to application requirements.
  11. **
  12. **                Set memory bank area and size if external memory is used.
  13. **
  14. **  Target      : MM32
  15. **
  16. **  Environment : VScode
  17. **
  18. **  Distribution: The file is distributed “as is,” without any warranty
  19. **                of any kind.
  20. *****************************************************************************
  21. */

  22. /* Entry Point */
  23. ENTRY(Reset_Handler)

  24. /* Highest address of the user mode stack */
  25. _estack = 0x20002000;    /* end of 16K RAM */

  26. /* Generate a link error if heap and stack don't fit into RAM */
  27. _Min_Heap_Size = 0x200;      /* required amount of heap  */
  28. _Min_Stack_Size = 0x400; /* required amount of stack */

  29. /* Specify the memory areas */
  30. MEMORY
  31. {
  32.   FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 64K
  33.   RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 8K
  34. }

  35. /* Define output sections */
  36. SECTIONS
  37. {
  38.   /* The startup code goes first into FLASH */
  39.   .isr_vector :
  40.   {
  41.     . = ALIGN(4);
  42.     KEEP(*(.isr_vector)) /* Startup code */
  43.     . = ALIGN(4);
  44.   } >FLASH

  45.   /* The program code and other data goes into FLASH */
  46.   .text :
  47.   {
  48.     . = ALIGN(4);
  49.     *(.text)           /* .text sections (code) */
  50.     *(.text*)          /* .text* sections (code) */
  51.     *(.glue_7)         /* glue arm to thumb code */
  52.     *(.glue_7t)        /* glue thumb to arm code */
  53.     *(.eh_frame)

  54.     KEEP (*(.init))
  55.     KEEP (*(.fini))

  56.     . = ALIGN(4);
  57.     _etext = .;        /* define a global symbols at end of code */
  58.   } >FLASH

  59.   /* Constant data goes into FLASH */
  60.   .rodata :
  61.   {
  62.     . = ALIGN(4);
  63.     *(.rodata)         /* .rodata sections (constants, strings, etc.) */
  64.     *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
  65.     . = ALIGN(4);
  66.   } >FLASH

  67.   .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
  68.   .ARM : {
  69.     __exidx_start = .;
  70.     *(.ARM.exidx*)
  71.     __exidx_end = .;
  72.   } >FLASH

  73.   .preinit_array     :
  74.   {
  75.     PROVIDE_HIDDEN (__preinit_array_start = .);
  76.     KEEP (*(.preinit_array*))
  77.     PROVIDE_HIDDEN (__preinit_array_end = .);
  78.   } >FLASH
  79.   .init_array :
  80.   {
  81.     PROVIDE_HIDDEN (__init_array_start = .);
  82.     KEEP (*(SORT(.init_array.*)))
  83.     KEEP (*(.init_array*))
  84.     PROVIDE_HIDDEN (__init_array_end = .);
  85.   } >FLASH
  86.   .fini_array :
  87.   {
  88.     PROVIDE_HIDDEN (__fini_array_start = .);
  89.     KEEP (*(SORT(.fini_array.*)))
  90.     KEEP (*(.fini_array*))
  91.     PROVIDE_HIDDEN (__fini_array_end = .);
  92.   } >FLASH

  93.   /* used by the startup to initialize data */
  94.   _sidata = LOADADDR(.data);

  95.   /* Initialized data sections goes into RAM, load LMA copy after code */
  96.   .data :
  97.   {
  98.     . = ALIGN(4);
  99.     _sdata = .;        /* create a global symbol at data start */
  100.     *(.data)           /* .data sections */
  101.     *(.data*)          /* .data* sections */

  102.     . = ALIGN(4);
  103.     _edata = .;        /* define a global symbol at data end */
  104.   } >RAM AT> FLASH

  105.   /* Uninitialized data section */
  106.   . = ALIGN(4);
  107.   .bss :
  108.   {
  109.     /* This is used by the startup in order to initialize the .bss secion */
  110.     _sbss = .;         /* define a global symbol at bss start */
  111.     __bss_start__ = _sbss;
  112.     *(.bss)
  113.     *(.bss*)
  114.     *(COMMON)

  115.     . = ALIGN(4);
  116.     _ebss = .;         /* define a global symbol at bss end */
  117.     __bss_end__ = _ebss;
  118.   } >RAM

  119.   /* User_heap_stack section, used to check that there is enough RAM left */
  120.   ._user_heap_stack :
  121.   {
  122.     . = ALIGN(4);
  123.     PROVIDE ( end = . );
  124.     PROVIDE ( _end = . );
  125.     . = . + _Min_Heap_Size;
  126.     . = . + _Min_Stack_Size;
  127.     . = ALIGN(4);
  128.   } >RAM

  129.   /* Remove information from the standard libraries */
  130.   /DISCARD/ :
  131.   {
  132.     libc.a ( * )
  133.     libm.a ( * )
  134.     libgcc.a ( * )
  135.   }

  136.   .ARM.attributes 0 : { *(.ARM.attributes) }
  137. }
四、着手搭建环境
有了前面的准备就可以开始配置整合开发环境了。限于篇幅,这里略过在 VS Code 中下载安装 EIDE  和  Cortex-Debug(可以让 VS Code + EIDE 环境具备调试功能,非必须,可以使用 O-Zone 调试) 插件,可点击参考超链接文章说明,这里重点说明一下关键配置。
Cortex-Debug 插件的配置页需要配置两个:Arm Toolchain Path 和 Jlink GDBserver Path。如果 GNU for Arm和 Jlink GDBserver Path 已经加入到系统环境变量中,就可以不用配置了,另外如果在 EIDE 中已经配置了 GCC 工具链和 J-Link 驱动安装路径的话,那在这也可以不再配置。而重点的 EIDE 插件配置信息需要根据自己安装情况来填写,主要包括以下几个内容:
  • ARM GCC GNU tool gcc.exe 安装路径
  • KEIL MDK UV4.exe 安装路径和 ARM TOOLS.INI 文件路径(如果用到 AC5/AC6 作为编译器的话那就需要填写)
  • J-Link GDBserver 安装路径和 J-Link Device xml 路径
  • 串口默认设置信息

以下为我的配置情况:

EIDE 设置1.png EIDE 设置2.png EIDE 设置3.png

1. 按照上述配置好后基本就可以使用 EIDE 和 Cortex-Debug 插件了
2. 首先将准备好的 KEIL 工程导入到 EIDE 中,建立好一个位于 VS Code 中的 EIDE KEIL 工程,名为 KEILPRJ.code-workspace,该文件后面可以直接在 EIDE 中打开 类似 KEIL 的 .uvprojx 工程描述文件
3. 然后在项目资源包中将原先 KEIL 的启动文件替换为 GCC 平台的
4. 添加 MM32F0140_DFP 芯片支持包并且选择对应芯片 MM32F0144C6P
5. Build 配置选择 GCC ,链接脚本选择准备好的 MM32F0144C6P_FLASH.ld 所在路径
6. 烧录器选项选择 Jlink,对应好芯片名称
7. 项目属性中的包含目录将之前 KEIL 平台相关的替换为 GCC 平台的即可,不动也没关系因为就差了个 .s 文件
8. 其它按照默认配置即可,最后类似在 KEIL 中操作一样,一键编译和烧录
9. 进而转到 Cortex-Debug 中进行调试
EIDE 操作步骤.png M0 Debug 插件调试成功.png

实际调试过程中,遇到 2 个问题:
第一个是由于意识里认为 MM32F0144C6P 芯片的 RAM 大小为 16KB ,在 .ld 链接文件中填写的也自然是 16KB,编译烧录后发现 LED 并未闪烁且串口无打印输出,再使用  Cortex-Debug 调试,发现只要一运行  bl  SystemInit 就会跳到 HardFault 里面去,心里面慌了,会是 .s 没做好?再调试也未定位到问题点,于是转而使用更加出色的 O-Zone 工具去调试,结合丰富的资源显示 ,好不容易定位到一 PUSH 就会触发错误,想来应该是栈大小和地址的问题,最后查到原来使用的芯片应该是 8KB 的 RAM 才对。.s 文件并无问题,改过 .ld 文件后,解决。

HardFault定位问题.png
链接文件RAM大小定义错误.jpg
第二个是由于官方 lib samples 里面的串口重定向并未考虑到 GCC 平台的使用,未适配好导致 printf 打印功能失效,于是使用了自定义 printf 方法改造了程序,解决。
串口重定向问题.png
  1. void vprint(const char *fmt, va_list argp)
  2. {
  3.     char string[200];
  4.     if(0 < vsprintf(string,fmt,argp)) // build string
  5.     {
  6.         for (int i = 0; i < strlen(string); i++) {
  7.             while ((UART1->CSR & UART_IT_TXIEN) == 0)
  8.                 ; // The loop is sent until it is finished
  9.             UART1->TDR = (u8)string[i];
  10.         }
  11.     }
  12. }

  13. void my_printf(const char *fmt, ...) // custom printf() function
  14. {
  15.     va_list argp;
  16.     va_start(argp, fmt);
  17.     vprint(fmt, argp);
  18.     va_end(argp);
  19. }
操作到这里已经可以尽情享受 VS Code 开发 MM32 MCU 的快乐了,既不用写 makefile ,还能保持原来在 KEIL IDE 中的一些打包工程的操作习惯。另外,依托于工具强大的插件库,我们还可以在 VS Code 中安装配置 Astyle 格式化工具,使得代码结构整洁美观并且规范;还可以在 VS Code 部署好本地 Git 仓,利用 Github 进行工程迭代管理,使得项目开发变得井然有序;还可以使用 Settings Sync 插件进行多台 PC 机上的 VS Code 配置同步,再也不用担心更换电脑后重新又得重新安装配置之前一直使用的那么多插件了。总而言之,VS Code 真香!

五、编译、烧录及调试演示视频
实验中导入的示例工程为自己移植的 nr micro shell 组件测试代码,可以作为 Template 使用。编译烧录好程序后,开发板的串口会输出调试信息, LED 灯快速闪烁一会儿然后停止,此时可以使用 EIDE 中的串口工具终端与开发板进行指令交互,根据 LED 的亮灭状态来看终端给开发板发出的命令是否有被正确执行。

EIDE Program Test.gif

六、附件内容
附件资源包中有以下内容可供参考下载:
  • MM32F0144C6P LQFP48 封装芯片官方最小系统板原理图 ——  1. MM32_LQFP48_CoreBoard V1.0.pdf.zip
  • 文中涉及的一些驱动软件及ARM GCC 交叉编译工具链 —— 2. 软件资源下载链接.txt.zip
  • nr micro shell 示例软件工程代码包 —— 3. MM32F0144C6P_nr_micro_shell_VScode_GCC_Keil.zip

七、参考资源
本文创作参阅学习了以下资源,在此声明感谢!

  • https://blog.csdn.net/ZCShouCSDN/article/details/89553323
  • https://makerinchina.cn/%e4%bd%bf%e7%94%a8vscode%e6%8f%92%e4%bb%b6eideembedded-ide%e5%bc%80%e5%8f%91arm%e5%8d%95%e7%89%87%e6%9c%ba%e9%a1%b9%e7%9b%ae-gd32%e5%8d%95%e7%89%87%e6%9c%ba/
  • https://blog.csdn.net/hanhe0551/article/details/97375979


1. MM32_LQFP48_CoreBoard V1.0.pdf (59.92 KB, 下载次数: 0)
2. 软件资源下载链接.txt.zip (344 Bytes, 下载次数: 20)
3. MM32F0144C6P_nr_micro_shell_VScode_GCC_Keil.zip (1.27 MB, 下载次数: 15)

  


打赏榜单

21小跑堂 打赏了 200.00 元 2022-04-20
理由:恭喜通过原创奖文章审核!请多多加油!

评论

请问怎么发表这么长的帖子的,我发表的帖子都是限制我的长度的。说不能超过100000字节,这样根本就发表不了什么长度的帖子。  发表于 2022-12-8 00:13
豌豆爹 发表于 2022-4-18 15:51 来自手机 | 显示全部楼层
下载看看
gouguoccc 发表于 2022-4-19 07:54 来自手机 | 显示全部楼层
学习了,谢谢分享。
xld0932 发表于 2022-4-19 17:01 | 显示全部楼层
介绍楼主来分享原创,果然有干货哦,赞这么快就晋升蓝V了呀,后面再接再厉哦
@21小管家 @21小跑堂
麻花油条 发表于 2022-4-26 15:15 来自手机 | 显示全部楼层
学习了,感谢分享
tpgf 发表于 2022-5-3 15:45 | 显示全部楼层
这是什么开发环境啊
drer 发表于 2022-5-3 15:53 | 显示全部楼层
非常喜欢免费软件
qcliu 发表于 2022-5-3 16:00 | 显示全部楼层
楼主的原创好多啊
coshi 发表于 2022-5-3 16:07 | 显示全部楼层
确实非常简便啊
kxsi 发表于 2022-5-3 16:32 | 显示全部楼层
可惜单位不让安装啊
wiba 发表于 2022-5-3 16:43 | 显示全部楼层
电路很有意思呀
hxck 发表于 2022-12-7 23:21 | 显示全部楼层
能找你开发么MM32F0144CP6,792602592
您需要登录后才可以回帖 登录 | 注册

本版积分规则

40

主题

239

帖子

13

粉丝
快速回复 在线客服 返回列表 返回顶部