基于Win10下Linux子系统的STM32开发----第一个STM32应用程序

246 阅读7分钟

开发环境搭建完成后,我们就开始了在Linux环境下的STM32应用程序开发之旅了。

为了验证我们的开发环境配置是否正确,下面就通过创建一个点亮开发板上的LED灯珠的实例来进行检验。

在这个验证实例中,笔者选用的硬件是自己设计的基于STM32F103ZET6的核心板。

其中有两个LED灯珠,分别接在STM32F103ZET6的PE2、PE3管脚上。原理图如下:

在验证实例中,需要实现的就是两个LED灯循环亮灭交替。

LED灯珠的位置如图所示。

首先我们任意选择一个磁盘分区,新建一个目录名为led_test的文件夹,用来作为测试实例的根目录。

在根目录下新建app、doc、drv、hal四个目录,用来存放我们的测试代码和文档。其中:

app目录下新建src和inc两个目录,分别用来存在app层的源码文件和头文件。

doc目录用来存放项目开发日志和相关的文档文件。

drv下新建core、user两个目录,每个目录下再新建src和inc目录,其中core用来存放mcu内核相关的源码文件和头文件,user目录用 来存放驱动源码文件和头文件。

hal目录下新建src和inc两个目录,分别存放中间层源码文件和头文件。在源码中增加中间件的目的是为了在以后的项目中,便于工程代码在不同mcu中进行移植。

创建好目录结构后,将ST官方库STM32F10x_StdPeriph_Lib_V3.5.0文件中的汇编文件Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\TrueSTUDIO\startup_stm32f10x_hd.s复制到项目中的drv\core\src目录下。同时将system_stm32f10x.c文件也复制到该文件夹下。再将core_cm3.h、stm32f10x.h、system_stm32f10x.h、stm32f103_conf.h复制到drv\core\inc目录下。

至此,测试实例的基本框架结构创建完成。 下一步,打开Visual Studio Code,将led_test文件夹通过添加工作区文件夹的方式导入到Visual Studio Code工作区中。

导入后在Visual Studio Code的资源管理器中能够看到led_test实例的结构树。

在Visual Studio Code的资源管理器的led_test根目录下新建stm32f103zet6_flash.ld、makefile两个文件。

在stm32f103zet6_flash.ld文件中添加如下内容:

/******************************************************************************
*
* @FileName     : stm32f103zet6_flash.ld
*
* @Author       : WeiShuangbo
*
* @Version      : 1.0
*
* @Date         : 2020-04-12
*
* @Description  : The LD file with led_test
*
* @Target       : STMicroelectronics STM32F103
*
*
******************************************************************************/

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = 0x20010000;    /* 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 = 64K
    MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
}

/* 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

    /* MEMORY_bank1 section, code must be located here explicitly            */

    /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */
    .memory_b1_text :
    {
        *(.mb1text)        /* .mb1text sections (code) */
        *(.mb1text*)       /* .mb1text* sections (code)  */
        *(.mb1rodata)      /* read-only data (constants) */
        *(.mb1rodata*)
    } >MEMORY_B1

    /* Remove information from the standard libraries */
    /DISCARD/ :
    {
        libc.a ( * )
        libm.a ( * )
        libgcc.a ( * )
    }
}

在makefile文件中添加如下内容:

#project name is led_test
TARGET = led_test

export CC   = arm-none-eabi-gcc
export AS   = arm-none-eabi-as
export LD   = arm-none-eabi-ld
export OBJCOPY  = arm-none-eabi-objcopy

top = $(shell pwd)

inc = -I $(top)/drv/core/inc \
      -I $(top)/drv/user/inc \
      -I $(top)/hal/inc \
      -I $(top)/app/inc 

obj_flag    = -W -Wall -g -mcpu=cortex-m3 -mthumb -D STM32F10X_HD -D USE_STDPERIPH_DRIVER -D BOARD_V2 $(inc) -O0 -std=gnu11 
target_flag = -mthumb -mcpu=cortex-m3 -Wl,--start-group -lc -lm -Wl,--end-group \
          -specs=nano.specs -specs=nosys.specs -static -Wl,-cref,-u,Reset_Handler \
          -Wl,-Map=./project.map -Wl,--gc-sections -Wl,--defsym=malloc_getpagesize_P=0x80
as_flag     = -c -mthumb -mcpu=cortex-m3 -g -Wa,--warn -o

src = $(shell find ./ -name '*.c')
obj = $(src:%.c=%.o)

all:$(obj)
    $(CC) $(as_flag) ./drv/core/src/startup_stm32f10x_hd.o ./drv/core/src/startup_stm32f10x_hd.s
    $(CC) $(obj) ./drv/core/src/startup_stm32f10x_hd.o -T ./stm32f103zet6_flash.ld -o ./output/$(TARGET).elf $(target_flag)
    $(OBJCOPY) ./output/$(TARGET).elf ./output/$(TARGET).bin -Obinary
    $(OBJCOPY) ./output/$(TARGET).elf ./output/$(TARGET).hex -Oihex
    $(OBJCOPY) ./output/$(TARGET).elf ./output/$(TARGET).srec -Osrec

$(obj):%.o:%.c
    $(CC) -c $(obj_flag) -o $@ $<

project:
    mkdir output
clean:
    rm -f $(shell find ./ -name '*.o')
    rm -f $(shell find ./ -name '*.d')
    rm -f $(shell find ./ -name '*.map')
    rm -f $(shell find ./ -name '*.elf')
    rm -f $(shell find ./ -name '*.bin')
    rm -f $(shell find ./ -name '*.hex')

完成stm32f103zet6_flash.ld和makefile文件的编写后,在app/src下添加test_main.c文件,并在文件中添加如下内容:

/******************************************************************************
*
* @FileName     : test_main.c
*
* @Author       : WeiShuangbo
*
* @Version      : 1.0
*
* @Date         : 2020-04-12
*
* @Description  : The main file with led_test
*
******************************************************************************/

int main(void)
{
    return 0;
}

完成以上工作后,打开Linux子系统终端,进入led_test目录。Linux子系统访问Windnows下的分区是通过/mnt/分区盘符/….来实现的。

在led_test根目录下输入make指令,出现如下问题:

原来是output文件夹不存在,所以要在led_test根目录下创建output文件夹,通过指令make project创建output文件夹。

再一次执行make指令。

我们可以看到在output文件夹下已经能够生成目标文件了。但现在生成的目标文件烧录到mcu上,并不会有得到我们需要的led灯珠交替闪烁的运行结果,因为我们的main函数中并没有执行任何操作。

下一步,我们添加让led灯珠交替闪烁的代码。

在drv/user./src文件夹下增加gpio的驱动文件drv_gpio.c,在drv/user/inc文件夹下增加drv_gpio.h头文件。

drv_gpio.c文件的内容如下:

/******************************************************************************
*
* @FileName     : drv_gpio.c
*
* @Author       : WeiShuangbo
*
* @Version      : 1.0
*
* @Date         : 2020-04-12
*
* @Description  : The Drvice File of the GPIO with led_test
*
*
******************************************************************************/
#include "drv_gpio.h"

#include "stm32f10x.h"

/*******************************************************
*
* Function name     : drv_gpio_config_led
* Description       : config the gpio to led
* Parameter         : NULL
* Return            : NULL
**********************************************************/
void drv_gpio_config_led(void)
{
    RCC->APB2ENR |=RCC_APB2ENR_IOPEEN;

    GPIOE->CRL |=  GPIO_CRL_MODE2 | GPIO_CRL_MODE3 ;
}

/*******************************************************
*
* Function name     : drv_gpio_output_led1_high
* Description       : set the gpio is high for led1
* Parameter         : NULL
* Return            : NULL
**********************************************************/
void drv_gpio_output_led1_high(void)
{
    GPIOE->ODR|= GPIO_ODR_ODR2;
}

/*******************************************************
*
* Function name     : drv_gpio_output_led1_low
* Description       : set the gpio is low for led1
* Parameter         : NULL
* Return            : NULL
**********************************************************/
void drv_gpio_output_led1_low(void)
{
    GPIOE->ODR&=  ~(GPIO_ODR_ODR2);
}

/*******************************************************
*
* Function name     : drv_gpio_get_led1
* Description       : get the gpio level for led1
* Parameter         : NULL
* Return            : NULL
**********************************************************/
uint8_t drv_gpio_get_led1(void)
{
    return (GPIOE->ODR & GPIO_ODR_ODR2);
}

/*******************************************************
*
* Function name     : drv_gpio_output_led2_high
* Description       : set the gpio is high for led2
* Parameter         : NULL
* Return            : NULL
**********************************************************/
void drv_gpio_output_led2_high(void)
{
    GPIOE->ODR|= GPIO_ODR_ODR3;
}

/*******************************************************
*
* Function name     : drv_gpio_output_led2_low
* Description       : set the gpio is low for led2
* Parameter         : NULL
* Return            : NULL
**********************************************************/
void drv_gpio_output_led2_low(void)
{
    GPIOE->ODR&=  ~(GPIO_ODR_ODR3);
}

/*******************************************************
*
* Function name     : drv_gpio_get_led2
* Description       : get the gpio level for led2
* Parameter         : NULL
* Return            : NULL
**********************************************************/
uint8_t drv_gpio_get_led2(void)
{
    return (GPIOE->ODR & GPIO_ODR_ODR3);
}

drv_gpio.h文件的内容如下:

/******************************************************************************
*
* @FileName     : drv_gpio.h
*
* @Author       : WeiShuangbo
*
* @Version      : 1.0
*
* @Date         : 2020-04-12
*
* @Description  : The Drvice Header File of the GPIO with led_test
*
*
******************************************************************************/
#ifndef __DRV_GPIO_H__
#define __DRV_GPIO_H__

#ifdef __cplusplus
    extern "C" {
#endif
    
    #include <stdint.h>

    // led config
    void drv_gpio_config_led(void);
    
    void drv_gpio_output_led1_high(void);
    void drv_gpio_output_led1_low(void);
    uint8_t drv_gpio_get_led1(void);
    void drv_gpio_output_led2_high(void);
    void drv_gpio_output_led2_low(void);
    uint8_t drv_gpio_get_led1(void);
#ifdef __cplusplus
    }
#endif
#endif

在drv/user./src文件夹下增加led的驱动文件drv_led.c,在drv/user/inc文件夹下增加drv_led.h头文件。

drv_led.c文件的内容如下:

/******************************************************************************
*
* @FileName     : drv_led.c
*
* @Author       : WeiShuangbo
*
* @Version      : 1.0
*
* @Date         : 2020-04-13
*
* @Description  : The Drvice File of the LED with led_test
*
*
******************************************************************************/
#include "drv_led.h"
#include "drv_gpio.h"

/*******************************************************
*
* Function name     : drv_led_init
* Description       : Init the led
* Parameter         : NULL
* Return            : NULL
**********************************************************/
void drv_led_init(void)
{
    drv_gpio_config_led();
}

/*******************************************************
*
* Function name     : drv_led1_on
* Description       : turn on the led1
* Parameter         : NULL
* Return            : NULL
**********************************************************/
void drv_led1_on(void)
{
    drv_gpio_output_led1_low();
}

/*******************************************************
*
* Function name     : drv_led1_off
* Description       : turn off the led1
* Parameter         : NULL
* Return            : NULL
**********************************************************/
void drv_led1_off(void)
{
    drv_gpio_output_led1_high();
}

/*******************************************************
*
* Function name     : drv_led1_statu_change
* Description       : change the statu for led1
* Parameter         : NULL
* Return            : NULL
**********************************************************/
void drv_led1_statu_change(void)
{
    if(drv_gpio_get_led1())
    {
        drv_gpio_output_led1_low();
    }
    else
    {
        drv_gpio_output_led1_high();
    } 
}

/*******************************************************
*
* Function name     : drv_led2_on
* Description       : turn on the led2
* Parameter         : NULL
* Return            : NULL
**********************************************************/
void drv_led2_on(void)
{
    drv_gpio_output_led2_low();
}

/*******************************************************
*
* Function name     : drv_led2_off
* Description       : turn off the led2
* Parameter         : NULL
* Return            : NULL
**********************************************************/
void drv_led2_off(void)
{
    drv_gpio_output_led2_high();
}

/*******************************************************
*
* Function name     : drv_led2_statu_change
* Description       : change the statu for led2
* Parameter         : NULL
* Return            : NULL
**********************************************************/
void drv_led2_statu_change(void)
{
    if(drv_gpio_get_led2())
    {
        drv_gpio_output_led2_low();
    }
    else
    {
        drv_gpio_output_led2_high();
    } 
}

drv_led.h头文件的内容如下:

/******************************************************************************
*
* @FileName     : drv_led.h
*
* @Author       : WeiShuangbo
*
* @Version      : 1.0
*
* @Date         : 2020-04-13
*
* @Description  : The Drvice Header File of the LED with led_test
*
*
******************************************************************************/
#ifndef __DRV_LED_H__
    #define __DRV_LED_H__

    #ifdef __cplusplus
        extern "C" {
    #endif
    #include <stdint.h>

    // led config
    void drv_led_init(void);
    
    void drv_led1_on(void);
    void drv_led1_off(void);
    void drv_led1_statu_change(void);
    void drv_led2_on(void);
    void drv_led2_off(void);
    void drv_led2_statu_change(void);

    #ifdef __cplusplus
        }
    #endif
#endif

led_test实例仅是为了验证搭建的环境是否可以正常进行STM32开发,所以为了简便,就直接在app层上调用驱动层中的函数了。在实际项目中,最好的做法是app层通过hal层间接调用驱动层的函数和方法。

修改test_main.c文件中的内容:

/******************************************************************************
*
* @FileName     : test_main.c
*
* @Author       : WeiShuangbo
*
* @Version      : 1.0
*
* @Date         : 2020-04-12
*
* @Description  : The main file with led_test
*
******************************************************************************/
#include <stdint.h>

#include "drv_led.h"

void delay(uint32_t ms)
{
    uint32_t i = ms;
    while(i--)
    {
        ;
    }
}
int main(void)
{
    drv_led_init();

    drv_led1_on();
    drv_led2_off();

    while(1)
    {
        delay(1000000);
        drv_led1_statu_change();
        drv_led2_statu_change();
    }
    
    return 0;
}

在Linux子系统中运行make指令,生成可烧录的镜像文件。

将镜像文件通过J-FLASH烧录到芯片上,运行结果如下:

LED1和LED2灯珠能够满足预期需求,交替闪烁。