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

2023-12-13 15:50

本文主要是介绍[MM32生态]有免费且更简便的开发环境?支持GCC?可以用VS Code里的EIDE,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

整体概览
作为 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 软件开发者需要清楚完成一个软件项目的大致流程是需经过以下几步的:

  1. 编写代码。通常是 C/C++ 语言,但是在考究执行效率的时候就得用到 ASM 汇编,讲究面向对象的结构化编程时甚至用上 Python、Lua 等其它高级语言。
  2. 将各种语言进行编译和链接最终生成机器二进制文件。相信绝大部分人都亲身体验过这一步,在很多集成环境中就是一键 Build ,然后默认生成 hex 格式烧录文件。
  3. 烧录和调试。一般烧录方式分为 ISP 、ICP 和 IAP ,根据 MCU 的不同选择的烧录工具也不尽相同。 有些厂商,例如国产的 STC,只支持串口方式的 ISP ,且配套的在线仿真调试器也是基于串口形式的;而 ARM 内核的大部分 MCU 都支持 JTAG/SWD 接口形式的 ICP 方式,配套的调试工具可以是 J-Link、U-Link、CMSIS DAP-Link 和 ST-Link 等等,也几乎都有配套的上位机给予支持。
  4. 运行程序。基本经过一番调试后,最终能够得到满足功能需求的代码了,此时终于可以结案交付了,当然,后面可能还会面对大大小小的 Bug ,还得继续 “升级打怪”。

以上内容简述了整个软件开发的流程,其中涉及到的编译和链接这个知识点比较重要。为了对这些名词有更深的理解,特意查了资料。通俗讲,编译器会将 C 程序转换成一种机器能理解的符号形式的汇编语言程序,包括了各种伪指令和符号表,然后汇编器将这些代码转换成目标文件,包括了机器语言指令、数据和指令正确放入内存所需要的信息,最后由系统程序(链接编译器) 将各个独立汇编的机器语言程序组合起来并且解释所有未定义的标记,直到生成可执行文件。这其中也会涉及到很多文件,比如后缀名为 .c / .s / .o / .a / .lib / .elf / .axf / .bin / .hex 等等文件,有些是中间文件,另外一些是结果可执行文件。以下这幅图用于帮助理解这个过程:

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

用到比较多的 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 里面的 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 版

软件资源如下:
 

  • 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 工具安装路径加入系统环境变量中,保险起见其它几个也可以一并添加。

由于官方不提供 MM32F0144C6P GCC 启动文件对应的链接文件,那自己动手制作,思路是找到 STM32F030x 相关的文件来做修改,因为它们两者外设资源上极为相似。要注意的是,需要根据 MM32F0144C6P 实际的中断向量表去做修改。以下为修改好的 startup_mm32f0140_gcc.s 文件:

/********************************************************************************* [url=home.php?mod=space&uid=288409]@file[/url]      startup_mm32f0140_gcc.s* [url=home.php?mod=space&uid=187600]@author[/url]    * [url=home.php?mod=space&uid=247401]@brief[/url]     MM32F014x devices vector table for GCC toolchain.*            This module performs:*                - Set the initial SP*                - Set the initial PC == Reset_Handler,*                - Set the vector table entries with the exceptions ISR address*                - Branches to main in the C library (which eventually*                  calls main()).*            After Reset the Cortex-M0 processor is in Thread mode,*            priority is Privileged, and the Stack is set to Main.********************************************************************************/.syntax unified.cpu cortex-m0.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, %functionReset_Handler:ldr   r0, =_estackmov   sp, r0          /* set stack pointer *//* Copy the data segment initializers from flash to SRAM */ldr r0, =_sdataldr r1, =_edataldr r2, =_sidatamovs r3, #0b LoopCopyDataInitCopyDataInit:ldr r4, [r2, r3]str r4, [r0, r3]adds r3, r3, #4LoopCopyDataInit:adds r4, r0, r3cmp r4, r1bcc CopyDataInit/* Zero fill the bss segment. */ldr r2, =_sbssldr r4, =_ebssmovs r3, #0b LoopFillZerobssFillZerobss:str  r3, [r2]adds r2, r2, #4LoopFillZerobss:cmp r2, r4bcc FillZerobss/* Call the clock system intitialization function.*/bl  SystemInit/* Call static constructors */bl __libc_init_array/* Call the application's entry point.*/bl mainLoopForever:b LoopForever.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",%progbitsDefault_Handler:Infinite_Loop:b Infinite_Loop.size Default_Handler, .-Default_Handler/******************************************************************************** The minimal vector table for a Cortex M0.  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_pfnVectorsg_pfnVectors:.word  _estack.word  Reset_Handler.word  NMI_Handler.word  HardFault_Handler.word  0.word  0.word  0.word  0.word  0.word  0.word  0.word  SVC_Handler.word  0.word  0.word  PendSV_Handler.word  SysTick_Handler.word  WWDG_IRQHandler                   /* Window WatchDog              */.word  PVD_IRQHandler                    /* PVD through EXTI Line detect */.word  MIPI_IRQHandler                   /* MIPI                         */.word  FLASH_IRQHandler                  /* FLASH                        */.word  RCC_IRQHandler                    /* RCC                          */.word  EXTI0_1_IRQHandler                /* EXTI Line 0 and 1            */.word  EXTI2_3_IRQHandler                /* EXTI Line 2 and 3            */.word  EXTI4_15_IRQHandler               /* EXTI Line 4 to 15            */.word  HWDIV_IRQHandler                  /* HWDIV                        */.word  DMA1_Channel1_IRQHandler          /* DMA1 Channel 1               */.word  DMA1_Channel2_3_IRQHandler        /* DMA1 Channel 2 and Channel 3 */.word  DMA1_Channel4_5_IRQHandler        /* DMA1 Channel 4 and Channel 5 */.word  ADC1_COMP_IRQHandler              /* ADC1 & COMP                  */.word  TIM1_BRK_UP_TRG_COM_IRQHandler    /* TIM1 Break, Update, Trigger and Commutation */.word  TIM1_CC_IRQHandler                /* TIM1 Capture Compare         */.word  TIM2_IRQHandler                   /* TIM2                         */.word  TIM3_IRQHandler                   /* TIM3                         */.word  0                                 /* Reserved                     */.word  0                                 /* Reserved                     */.word  TIM14_IRQHandler                  /* TIM14                        */.word  0                                 /* Reserved                     */.word  TIM16_IRQHandler                  /* TIM16                        */.word  TIM17_IRQHandler                  /* TIM17                        */.word  I2C1_IRQHandler                   /* I2C1                         */.word  0                                 /* Reserved                     */.word  SPI1_IRQHandler                   /* SPI1                         */.word  SPI2_IRQHandler                   /* SPI2                         */.word  UART1_IRQHandler                  /* UART1                        */.word  UART2_IRQHandler                  /* UART2                        */.word  UART3_IRQHandler                  /* UART3                        */.word  FLEX_CAN_IRQHandler               /* FLEX_CAN                     */.word  0                                 /* Reserved                     *//********************************************************************************* 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      SVC_Handler.thumb_set SVC_Handler,Default_Handler.weak      PendSV_Handler.thumb_set PendSV_Handler,Default_Handler.weak      SysTick_Handler.thumb_set SysTick_Handler,Default_Handler.weak      WWDG_IRQHandler.thumb_set WWDG_IRQHandler,Default_Handler.weak      PVD_IRQHandler.thumb_set PVD_IRQHandler,Default_Handler.weak      MIPI_IRQHandler.thumb_set MIPI_IRQHandler,Default_Handler.weak      FLASH_IRQHandler.thumb_set FLASH_IRQHandler,Default_Handler.weak      RCC_IRQHandler.thumb_set RCC_IRQHandler,Default_Handler.weak      EXTI0_1_IRQHandler.thumb_set EXTI0_1_IRQHandler,Default_Handler.weak      EXTI2_3_IRQHandler.thumb_set EXTI2_3_IRQHandler,Default_Handler.weak      EXTI4_15_IRQHandler.thumb_set EXTI4_15_IRQHandler,Default_Handler.weak      HWDIV_IRQHandler.thumb_set HWDIV_IRQHandler,Default_Handler.weak      DMA1_Channel1_IRQHandler.thumb_set DMA1_Channel1_IRQHandler,Default_Handler.weak      DMA1_Channel2_3_IRQHandler.thumb_set DMA1_Channel2_3_IRQHandler,Default_Handler.weak      DMA1_Channel4_5_IRQHandler.thumb_set DMA1_Channel4_5_IRQHandler,Default_Handler.weak      ADC1_COMP_IRQHandler.thumb_set ADC1_COMP_IRQHandler,Default_Handler.weak      TIM1_BRK_UP_TRG_COM_IRQHandler.thumb_set TIM1_BRK_UP_TRG_COM_IRQHandler,Default_Handler.weak      TIM1_CC_IRQHandler.thumb_set TIM1_CC_IRQHandler,Default_Handler.weak      TIM2_IRQHandler.thumb_set TIM2_IRQHandler,Default_Handler.weak      TIM3_IRQHandler.thumb_set TIM3_IRQHandler,Default_Handler.weak      TIM14_IRQHandler.thumb_set TIM14_IRQHandler,Default_Handler.weak      TIM16_IRQHandler.thumb_set TIM16_IRQHandler,Default_Handler.weak      TIM17_IRQHandler.thumb_set TIM17_IRQHandler,Default_Handler.weak      I2C1_IRQHandler.thumb_set I2C1_IRQHandler,Default_Handler.weak      SPI1_IRQHandler.thumb_set SPI1_IRQHandler,Default_Handler.weak      SPI2_IRQHandler.thumb_set SPI2_IRQHandler,Default_Handler.weak      UART1_IRQHandler.thumb_set UART1_IRQHandler,Default_Handler.weak      UART2_IRQHandler.thumb_set UART2_IRQHandler,Default_Handler.weak      UART3_IRQHandler.thumb_set UART3_IRQHandler,Default_Handler.weak      FLEX_CAN_IRQHandler.thumb_set FLEX_CAN_IRQHandler,Default_Handler/************************ (C) COPYRIGHT *****END OF FILE****/
以下为修改好的 MM32F0144C6P_FLASH 文件:
/**********************************************************************************  File        : mm32_flash.ld****  Abstract    : Linker script for MM32F0144C6P Device with**                64KByte FLASH, 16KByte RAM****                Set heap size, stack size and stack location according**                to application requirements.****                Set memory bank area and size if external memory is used.****  Target      : MM32****  Environment : VScode****  Distribution: The file is distributed “as is,” without any warranty**                of any kind.******************************************************************************//* Entry Point */ENTRY(Reset_Handler)/* Highest address of the user mode stack */_estack = 0x20002000;    /* end of 16K RAM *//* Generate a link error if heap and stack don't fit into RAM */_Min_Heap_Size = 0x200;      /* required amount of heap  */_Min_Stack_Size = 0x400; /* required amount of stack *//* Specify the memory areas */MEMORY{FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 64KRAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 8K}/* 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) */*(.glue_7)         /* glue arm to thumb code */*(.glue_7t)        /* glue thumb to arm code */*(.eh_frame)KEEP (*(.init))KEEP (*(.fini)). = ALIGN(4);_etext = .;        /* define a global symbols at end of code */} >FLASH/* Constant data goes into FLASH */.rodata :{. = ALIGN(4);*(.rodata)         /* .rodata sections (constants, strings, etc.) */*(.rodata*)        /* .rodata* sections (constants, strings, etc.) */. = ALIGN(4);} >FLASH.ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH.ARM : {__exidx_start = .;*(.ARM.exidx*)__exidx_end = .;} >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 (*(SORT(.fini_array.*)))KEEP (*(.fini_array*))PROVIDE_HIDDEN (__fini_array_end = .);} >FLASH/* used by the startup to initialize data */_sidata = LOADADDR(.data);/* Initialized data sections goes into RAM, load LMA copy after code */.data : {. = 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 AT> FLASH/* 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/* User_heap_stack section, used to check that there is enough RAM left */._user_heap_stack :{. = ALIGN(4);PROVIDE ( end = . );PROVIDE ( _end = . );. = . + _Min_Heap_Size;. = . + _Min_Stack_Size;. = ALIGN(4);} >RAM/* Remove information from the standard libraries *//DISCARD/ :{libc.a ( * )libm.a ( * )libgcc.a ( * )}.ARM.attributes 0 : { *(.ARM.attributes) }}
四、着手搭建环境
有了前面的准备就可以开始配置整合开发环境了。限于篇幅,这里略过在 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 路径
  • 串口默认设置信息
  • 以下为我的配置情况:

      

    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 中进行调试

     

    实际调试过程中,遇到 2 个问题:

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




    第二个是由于官方 lib samples 里面的串口重定向并未考虑到 GCC 平台的使用,未适配好导致 printf 打印功能失效,于是使用了自定义 printf 方法改造了程序,解决。

void vprint(const char *fmt, va_list argp){char string[200];if(0 < vsprintf(string,fmt,argp)) // build string{for (int i = 0; i < strlen(string); i++) {while ((UART1->CSR & UART_IT_TXIEN) == 0); // The loop is sent until it is finishedUART1->TDR = (u8)string[i];}}}void my_printf(const char *fmt, ...) // custom printf() function{va_list argp;va_start(argp, fmt);vprint(fmt, argp);va_end(argp);}

操作到这里已经可以尽情享受 VS Code 开发 MM32 MCU 的快乐了,既不用写 makefile ,还能保持原来在 KEIL IDE 中的一些打包工程的操作习惯。另外,依托于工具强大的插件库,我们还可以在 VS Code 中安装配置 Astyle 格式化工具,使得代码结构整洁美观并且规范;还可以在 VS Code 部署好本地 Git 仓,利用 Github 进行工程迭代管理,使得项目开发变得井然有序;还可以使用 Settings Sync 插件进行多台 PC 机上的 VS Code 配置同步,再也不用担心更换电脑后重新又得重新安装配置之前一直使用的那么多插件了。总而言之,VS Code 真香!
---------------------
作者:yang377156216
链接:https://bbs.21ic.com/icview-3213464-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。
 

这篇关于[MM32生态]有免费且更简便的开发环境?支持GCC?可以用VS Code里的EIDE的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/489032

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

从去中心化到智能化:Web3如何与AI共同塑造数字生态

在数字时代的演进中,Web3和人工智能(AI)正成为塑造未来互联网的两大核心力量。Web3的去中心化理念与AI的智能化技术,正相互交织,共同推动数字生态的变革。本文将探讨Web3与AI的融合如何改变数字世界,并展望这一新兴组合如何重塑我们的在线体验。 Web3的去中心化愿景 Web3代表了互联网的第三代发展,它基于去中心化的区块链技术,旨在创建一个开放、透明且用户主导的数字生态。不同于传统

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

阿里开源语音识别SenseVoiceWindows环境部署

SenseVoice介绍 SenseVoice 专注于高精度多语言语音识别、情感辨识和音频事件检测多语言识别: 采用超过 40 万小时数据训练,支持超过 50 种语言,识别效果上优于 Whisper 模型。富文本识别:具备优秀的情感识别,能够在测试数据上达到和超过目前最佳情感识别模型的效果。支持声音事件检测能力,支持音乐、掌声、笑声、哭声、咳嗽、喷嚏等多种常见人机交互事件进行检测。高效推

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

安装nodejs环境

本文介绍了如何通过nvm(NodeVersionManager)安装和管理Node.js及npm的不同版本,包括下载安装脚本、检查版本并安装特定版本的方法。 1、安装nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash 2、查看nvm版本 nvm --version 3、安装

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6+Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant(hass),来控制你的设备呢?@智能家居 @万物互联