VSCode+GCC开发STM32

  • 前言
  • 一、准备好官方提供的库文件
  • 二、创建工作目录
  • 三、编写相关文件
  • 1.在user目录中编写main.c文件,用来进行最终的效果测试
  • 2.stm32f10x_conf.h
  • 3.stm32f10x_flash_extsram.ld
  • 4.编写Makefile文件
  • 5.相关文件的修改
  • 6.最后使用Make编译

  • 前言

    目前主流开发STM32的工具有Keil等工具,但个人认为其界面复杂,操作繁琐,导致开发效率低下。故自行寻找STM32提供的交叉编译工具的使用方法,自行搭建交叉编译环境,进行STM32的开发,后续也可沿用此方法进行其他嵌入式的开发,


    提示:这一套开发环境使用工具有VSCode,Make,WSL/Centos,arm-none-eabi-gcc。其中前三者的安装过程在这里不再赘述。
    提示:WSL/CentOS在本文中统一名称为编译服务器
    arm-none-eabi-gcc的安装请点击这里

    一、准备好官方提供的库文件

    在例程中,本人使用STM32C8T6开发,故下载ST官网提供相关的库函数包。
    官网地址为:https://www.st.com/en/embedded-software/stm32-embedded-software.html
    打开后选择标准库

    随后找到自己开发板对应的型号

    最后下载并解压,文件夹内容如下

    二、创建工作目录

    在编译服务器中,创建好文件夹,并在文件夹中创建user目录和Makefile文件,再把官方库文件的Libraries复制过来,我的目录如下:

    三、编写相关文件

    1.在user目录中编写main.c文件,用来进行最终的效果测试

    本人使用的是stm32c8t6最小系统板,使用led灯闪烁例程进行测试。
    代码如下(示例):

    /**
     ******************************************************************************
     * @file    Project/STM32F10x_StdPeriph_Template/main.c
     * @author  MCD Application Team
     * @version V3.6.0
     * @date    20-September-2021
     * @brief   Main program body
     ******************************************************************************
     * @attention
     *
     * Copyright (c) 2011 STMicroelectronics.
     * All rights reserved.
     *
     * This software is licensed under terms that can be found in the LICENSE file
     * in the root directory of this software component.
     * If no LICENSE file comes with this software, it is provided AS-IS.
     *
     ******************************************************************************
     */
     
    /* Includes ------------------------------------------------------------------*/
    #include "stm32f10x_conf.h"
    #include "stm32f10x.h"
    #include <stdio.h>
    
    //位带操作,实现51类似的GPIO控制功能
    //具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
    //IO口操作宏定义
    #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
    #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
    #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
    //IO口地址映射
    #define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
    #define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
    #define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
    #define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
    #define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
    #define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
    #define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    
    
    #define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
    #define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
    #define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
    #define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
    #define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
    #define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
    #define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08 
     
    //IO口操作,只对单一的IO口!
    //确保n的值小于16!
    #define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
    #define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 
    #define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
    #define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 
    #define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
    #define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 
    #define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
    #define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 
    #define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
    #define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入
    #define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
    #define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入
    #define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
    #define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入
    void GPIO_Configuration(void);
    //=============================================================================
    //文件名称:Delay
    //功能概要:延时
    //参数说明:nCount:延时长短
    //函数返回:无
    //=============================================================================
    void Delay(uint32_t nCount)
    {
      for(; nCount != 0; nCount--);
    }
    /**
     * @brief  Main program.
     * @param  None
     * @retval None
     */
    int main(void)
    {
        GPIO_Configuration();
        while (1)
        {
            PCout(13) = 1;
            Delay(0xfffff);
            Delay(0xfffff);
            PCout(13) = 0;
            Delay(0xfffff);
            Delay(0xfffff);
        }
    }
    void GPIO_Configuration(void)
    {
      GPIO_InitTypeDef GPIO_InitStructure;
      
      RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC , ENABLE); 						 
    //=============================================================================
    //LED -> PC13
    //=============================================================================			 
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 
      GPIO_Init(GPIOC, &GPIO_InitStructure);
    }
    
    /**
     * @brief  Retargets the C library printf function to the USART.
     * @param  None
     * @retval None
     */
    
    #ifdef USE_FULL_ASSERT
    
    /**
     * @brief  Reports the name of the source file and the source line number
     *         where the assert_param error has occurred.
     * @param  file: pointer to the source file name
     * @param  line: assert_param error line source number
     * @retval None
     */
    void assert_failed(uint8_t *file, uint32_t line)
    {
        /* User can add his own implementation to report the file name and line number,
           ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    
        /* Infinite loop */
        while (1)
        {
        }
    }
    #endif
    
    /**
     * @}
     */
    

    2.stm32f10x_conf.h

    把库文件的stm32f10x_conf.h 复制到user目录中。该文件在
    STM32F10x_StdPeriph_Lib_V3.6.0\Project\STM32F10x_StdPeriph_Template
    目录下。
    

    3.stm32f10x_flash_extsram.ld

    在Makefile平级的目录中自行创建该文件,该文件用于交叉编译的链接阶段,规定flash、ram等大小。
    

    若使用的芯片不同,则该文件的内容不同

    /* Default linker script for STM32F103C8T6 */
    MEMORY
    {
      /* STM32F103C8T6 内存区域 */
      RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
      FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
    }
    
    __Stack_Size = 1024;                /* 默认栈大小 */
    PROVIDE ( _Stack_Size = __Stack_Size );
    
    __Stack_Init = _estack - __Stack_Size;
    PROVIDE ( _Stack_Init = __Stack_Init );
    _Minimum_Stack_Size = 0x100;
    _estack = 0x20005000;                /* 栈的高地址,接近 RAM 的顶部 */
    
    /* 代码段和数据段的定义 */
    SECTIONS
    {
        /* 启动代码和中断向量表 */
        .isr_vector :
        {
            . = ALIGN(4);
            KEEP(*(.isr_vector))          /* 中断向量表 */
            . = ALIGN(4);
        } >FLASH
    
        /* 程序代码段,放在 Flash 中 */
        .text :
        {
            . = ALIGN(4);
            *(.text)                     /* 程序代码 */
            *(.text.*)                   /* 其他代码 */
            *(.rodata)                  /* 只读数据(常量) */
            *(.rodata*)
            . = ALIGN(4);
            _etext = .;
            _sidata = _etext;            /* 用于初始化数据段 */
        } >FLASH
    
        /* 初始化数据段 */
        .data : AT (_sidata)
        {
            . = ALIGN(4);
            _sdata = .;
            *(.data)                    /* 初始化数据 */
            *(.data.*)
            . = ALIGN(4);
            _edata = .;
        } >RAM
    
        /* 未初始化的数据段 */
        .bss :
        {
            . = ALIGN(4);
            _sbss = .;
            *(.bss)
            *(COMMON)
            . = ALIGN(4);
            _ebss = .;
        } >RAM
    
        PROVIDE ( end = _ebss );
        PROVIDE ( _end = _ebss );
    
        /* 用户栈 */
        ._usrstack :
        {
            . = ALIGN(4);
            _susrstack = . ;
            . = . + _Minimum_Stack_Size ;  /* 最小栈大小 */
            . = ALIGN(4);
            _eusrstack = . ;
        } >RAM
    
        /* 丢弃调试信息 */
        DISCARD :
        {
            libc.a (*)
            libm.a (*)
            libgcc.a (*)
        }
    
        /* 调试信息(可选) */
        .stab          0 : { *(.stab) }
        .stabstr       0 : { *(.stabstr) }
        .debug          0 : { *(.debug) }
        .line           0 : { *(.line) }
    }
    
    

    4.编写Makefile文件

    注意:在这里我把库文件的目录改为stm32f10x_lib
    最终目录结构如下:
    最终如下
    Makefile文件内容如下

    # toolchain
    TOOLCHAIN    = arm-none-eabi-
    CC           = $(TOOLCHAIN)gcc
    CP           = $(TOOLCHAIN)objcopy
    AS           = $(TOOLCHAIN)gcc -x assembler-with-cpp
    # all the files will be generated with this name (main.elf, main.bin, main.hex, etc)
    PROJECT_NAME=stm32f10x_project
    
    # define include dir
    INCLUDE_DIRS =
    
    # define stm32f10x lib dir
    STM32F10x_LIB_DIR      = ./stm32f10x_lib
    
    # define user dir
    USER_DIR     = ./user
    
    # link file
    LINK_SCRIPT  = ./stm32f10x_flash_extsram.ld
    
    # user specific
    SRC       =
    ASM_SRC   =
    SRC      += $(USER_DIR)/main.c
    # user include
    INCLUDE_DIRS  = 
    
    # source director
    STM32F1_STD_LIB     = $(STM32F10x_LIB_DIR)/STM32F10x_StdPeriph_Driver
    STM32F1_CORE_DIR    = $(STM32F10x_LIB_DIR)/CMSIS/CM3/CoreSupport
    STM32F1_DEVICE_DIR  = $(STM32F10x_LIB_DIR)/CMSIS/CM3/DeviceSupport/ST/STM32F10x
    STM32F1_SRC_DIR     = $(STM32F1_STD_LIB)/src
    STM32F1_INC_DIR     = $(STM32F1_STD_LIB)/inc
    
    # startup
    ASM_SRC  += $(STM32F1_DEVICE_DIR)/startup/gcc_ride7/startup_stm32f10x_md.s
    
    # CMSIS
    SRC  += $(STM32F1_DEVICE_DIR)/system_stm32f10x.c
    SRC  += $(STM32F1_CORE_DIR)/core_cm3.c
    
    # use libraries, please add or remove when you use or remove it.
    SRC  += $(STM32F1_SRC_DIR)/stm32f10x_rcc.c
    SRC  += $(STM32F1_SRC_DIR)/stm32f10x_gpio.c
    SRC  += $(STM32F1_SRC_DIR)/stm32f10x_exti.c
    SRC  += $(STM32F1_SRC_DIR)/stm32f10x_usart.c
    SRC  += $(STM32F1_SRC_DIR)/misc.c
    
    # include directories
    INCLUDE_DIRS += $(STM32F1_CORE_DIR)
    INCLUDE_DIRS += $(STM32F1_DEVICE_DIR)
    INCLUDE_DIRS += $(STM32F1_INC_DIR)
    INCLUDE_DIRS += $(STM32F1_STD_LIB)
    INCLUDE_DIRS += $(USER_DIR)
    INC_DIR  = $(patsubst %, -I%, $(INCLUDE_DIRS))
    OBJECTS  = $(ASM_SRC:.s=.o) $(SRC:.c=.o)
    
    # Define optimisation level here
    MC_FLAGS = -mcpu=cortex-m3
    AS_FLAGS = $(MC_FLAGS) -g -gdwarf-2 -mthumb  -Wa,-amhls=$(<:.s=.lst)
    CP_FLAGS = $(MC_FLAGS) -Os -g -gdwarf-2 -mthumb -fomit-frame-pointer -Wall -fverbose-asm -Wa,-ahlms=$(<:.c=.lst) 
    LD_FLAGS = $(MC_FLAGS) -g -gdwarf-2 -mthumb -nostartfiles -Xlinker --gc-sections -T$(LINK_SCRIPT) -Wl,-Map=$(PROJECT_NAME).map,--cref,--no-warn-mismatch
    
    # makefile rules
    all: $(OBJECTS) $(PROJECT_NAME).elf  $(PROJECT_NAME).hex $(PROJECT_NAME).bin
    	$(TOOLCHAIN)size $(PROJECT_NAME).elf
    
    %.o: %.c
    	$(CC) -c $(CP_FLAGS) -I . $(INC_DIR) $< -o $@
    
    %.o: %.s
    	$(AS) -c $(AS_FLAGS) $< -o $@
    
    %.elf: $(OBJECTS)
    	$(CC) $(OBJECTS) $(LD_FLAGS) -o $@
    
    %.hex: %.elf
    	$(CP) -O ihex $< $@
    
    %.bin: %.elf
    	$(CP) -O binary -S  $< $@
    
    clean:
    	rm -f $(OBJECTS) $(PROJECT_NAME).elf $(PROJECT_NAME).bin $(PROJECT_NAME).hex $(PROJECT_NAME).map $(PROJECT_NAME).lst
    	rm -f $(STM32F10x_LIB_DIR)/**/*.lst $(USER_DIR)/**/*.lst
    # del /Q  $(PROJECT_NAME).elf $(PROJECT_NAME).hex $(PROJECT_NAME).bin
    
    
    

    5.相关文件的修改

    如果这时候编译会报相关错误。
    在stm32f10x_lib/CMSIS/CM3/DeviceSupport/ST/STM32F10x/目录下找到stm32f10x.h文件,找到67行把#define STM32F10X_MD一行注释掉不同的芯片这里解除注释的内容不同
    找到该文件104行,解除掉#define USE_STDPERIPH_DRIVER的注释
    找到stm32f10x_lib/CMSIS/CM3/CoreSupport下的core_cm3.c文件,736行和753行,改为如下代码

    __ASM volatile ("strexb %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r" (value) );
    __ASM volatile ("strexh %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r" (value) );
    

    6.最后使用Make编译

    成功的结果如下,可以烧录进行测试


    例程模板在https://gitee.com/iFENG-123/template-stm32

    作者:⁢⁢⁢iFeng

    物联沃分享整理
    物联沃-IOTWORD物联网 » VSCode+GCC开发STM32

    发表回复