stm32新建工程及启动过程了解

Posted otaganyuki

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了stm32新建工程及启动过程了解相关的知识,希望对你有一定的参考价值。

新建工程简单流程

首先,新建文件夹存放总的工程,如test,在test下新建文件夹分类存放工程里的各个文件,我个人分类如下:StartUp、User、Lib、Hardware、Obj

分别存放stm32启动文件,用户文件、库文件、硬件驱动文件、生成目标文件

新建好了文件夹,再复制进相应的文件,Lib直接把全部库文件复制进去也没事,到时候要用哪个就加哪个到工程里,主要是StartUp和User

StartUp如下:

技术分享图片

 

这些文件注意下载适合自己单片机的版本,如hd.s和md.s要分清。这些文件均与单片机启动时有关,文件的具体了解下面再讲,这里先讲流程

User如下:

技术分享图片

main.c自不用多说,下面四个文件也留到后面介绍

由于最后得编译烧录看下现象,用到个led,可以在Hardware中添加

以下操作只是作为笔记记下,所以只是简单写下。

打开软件,new project 选择路径到User即可,且把输出路径改为刚才新建的Obj,点击技术分享图片右边这个图标,将工程目录添加文件,如下

技术分享图片

led相应的端口控制用到gpio库,gpio库又依赖rcc库,因此lib里加入了这两个

 

点击左边这个图标,点击c/c++   设置define和include paths,后者就是把所有用到的.h文件的路径全部选择进去

前者如下:STM32F10X_HD,USE_STDPERIPH_DRIVER    其中STM32F10X_HD指明当前设备的存储容量为hd版,USE_STDPERIPH_DRIVER如果要使用官方提供的库函数则需定义(stm32f10x_conf.h   include了所有库文件,而stm32f10x.h最后有一句技术分享图片)

 然后在led和main都include "stm32f10x.h"   就可以开始写代码了,我随便写了点烧录下没什么问题

main的代码

#include "stm32f10x.h"
#include "led.h"

void delay(unsigned int cnt)
{
    unsigned int i,j;
    for(i=0;i<cnt;i++)
        for(j=0;j<5000;j++);
}
int main()
{
    LED_Init();
    while(1)
    {
        GPIO_SetBits(GPIOA,GPIO_Pin_8);//PA8??
        GPIO_ResetBits(GPIOD,GPIO_Pin_2);//PB2??
        delay(1000);
        GPIO_ResetBits(GPIOA,GPIO_Pin_8);
        GPIO_SetBits(GPIOD,GPIO_Pin_2);
        delay(1000);

    }
    return 0;
}

led的代码

#include "led.h"

void LED_Init(void)
{
    RCC->APB2ENR|=1<<2;    //ê1?üPORTAê±?ó            
    RCC->APB2ENR|=1<<5;    //ê1?üPORTDê±?ó            
    GPIOA->CRH&=0XFFFFFFF0; 
    GPIOA->CRH|=0X00000003;//PA8 í?íìê?3?        
    GPIOA->ODR|=1<<8;      //PA8 ê?3???
                                              
    GPIOD->CRL&=0XFFFFF0FF;
    GPIOD->CRL|=0X00000300;//PD.2í?íìê?3?
    GPIOD->ODR|=1<<2;      //PD.2ê?3??? 
}

下面仔细讲讲这些启动文件

 首先是startup_stm32f10x_hd.s,这应该是最先被执行的文件,下面讲讲其内容(参考:https://blog.csdn.net/wqx521/article/details/50925553)

先从头到尾了解

用到的指令

技术分享图片

Stack_Size      EQU     0x00000400;栈的大小

                AREA    STACK, NOINIT, READWRITE, ALIGN=3;分配名为STACK,不初始化,可读可写,8(2^3)字节对齐的1KB空间。
Stack_Mem       SPACE   Stack_Size;分配空间,栈为从高到低地址生长,即Stack_Mem到__initial_sp为最大栈顶和栈底,这里Stack_Mem为地址是我个人根据最后堆栈初始化的式子做的推测
__initial_sp 
; <h> Heap Configuration ; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> ; </h> Heap_Size EQU 0x00000200 AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base Heap_Mem SPACE Heap_Size;分配空间,堆为从低到高生长 __heap_limit PRESERVE8;指定当前文件的堆栈按照 8 字节对齐。 THUMB;表示后面指令兼容 THUMB 指令。THUBM 是ARM 以前的指令集,16bit,现在 Cortex-M 系列的都使用 THUMB-2 指令集,THUMB-2 是32 位的,兼容 16 位和 32 位的指令,是 THUMB 的超级。

 

                AREA    RESET, DATA, READONLY;定义一个名为RESET,可读的数据段。并声明 __Vectors、__Vectors_End 和__Vectors_Size 这三个标号可被外部的文件使用。 
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size        

 

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
........
__Vectors_End

向量表从 FLASH 的 0 地址开始放置,以 4 个字节为一个单位,地址 0 存放的是栈顶地址,0X04 存放的是复位程序的地址,以此类推。后面的都是一些中断服务程序的地址

__Vectors_Size  EQU  __Vectors_End - __Vectors   ;__Vectors 为向量表起始地址,__Vectors_End 为向量表结束地址,两个相减即可算出向量表大小。

 

                AREA    |.text|, CODE, READONLY;定义一个名为.text,可读的代码段
                
; Reset handler  这里为复位程序的内容,先调用SystemInit函数,再调用_main函数,注意此_main和c中的main函数是不同的,具体下面讲
Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  __main
                IMPORT  SystemInit
                LDR     R0, =SystemInit
                BLX     R0               
                LDR     R0, =__main
                BX      R0
                ENDP
                
; Dummy Exception Handlers (infinite loops which can be modified)

NMI_Handler     PROC
                EXPORT  NMI_Handler                [WEAK]
                B       .
                ENDP
........
SysTick_Handler PROC
                EXPORT  SysTick_Handler            [WEAK]
                B       .
                ENDP

 

Default_Handler PROC

                EXPORT  WWDG_IRQHandler            [WEAK];声明函数
                EXPORT  PVD_IRQHandler             [WEAK]
                EXPORT  TAMPER_IRQHandler          [WEAK]
                EXPORT  RTC_IRQHandler             [WEAK]
.........
                EXPORT  DMA2_Channel2_IRQHandler   [WEAK]
                EXPORT  DMA2_Channel3_IRQHandler   [WEAK]
                EXPORT  DMA2_Channel4_5_IRQHandler [WEAK]
WWDG_IRQHandler;定义空函数
PVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FLASH_IRQHandler
............
DMA2_Channel1_IRQHandler
DMA2_Channel2_IRQHandler
DMA2_Channel3_IRQHandler
DMA2_Channel4_5_IRQHandler

 

                 IF      :DEF:__MICROLIB
                
                 EXPORT  __initial_sp
                 EXPORT  __heap_base
                 EXPORT  __heap_limit
                
                 ELSE
                
                 IMPORT  __use_two_region_memory;使用双段模式--两区堆栈空间,堆和栈有各自的空间地址
                 EXPORT  __user_initial_stackheap
                 
__user_initial_stackheap

                 LDR     R0, =  Heap_Mem
                 LDR     R1, =(Stack_Mem + Stack_Size)
                 LDR     R2, = (Heap_Mem +  Heap_Size)
                 LDR     R3, = Stack_Mem
                 BX      LR

                 ALIGN

                 ENDIF

                 END

判断是否定义了__MICROLIB ,如果定义了则赋予标号__initial_sp(栈顶地址)、__heap_base(堆起始地址)、__heap_limit(堆结束地址)全局属性,可供外部文件调用。如果没有定义(实际的情况就是我们没定义__MICROLIB)则使用默认的 C 库,然后初始化用户堆栈大小,这部分有 C 库函数__main 来完成。

以上即为大致内容的了解,但是对于具体的执行流程还是有点乱,以下整体的讲讲

当STM32产生复位后,做的第一件事就是读取下列两个32位整数的值:

1、从地址0x0000,0000处取出MSP(主堆栈指针)的初始值放入MSP寄存器中;

2、从地址0x0000,0004处取出复位向量放入PC寄存器中,然后从PC中存取的地址出取指令并开始执行

也就是说,启动文件里最先被执行的是Reset_Handler函数,更具体的说,是执行SystemInit和_main函数,前者好说,可以直接goto看到内容,但是_main据我百度知,当编译器发现定义了main函数,那么就会自动创建__main,所以无法goto来查看内容,其具体内容较为复杂,这里暂时只说明,启动文件里那些堆栈的规划最后是在这个函数里得到执行的,执行完后跳转到main。

顺便一提,51单片机我记得启动后是先执行0地址跳转的,和这里不一样

 

 

 

其他文件:

stm32f10x_it.c:这是一个中断处理文件,_it结尾,就是interrupt(中断)的意思,说明这个是整个工程的中断处理函数,要是楼主没有用到中断的话就可以不添加进去,还有,也可以去掉,将中断函数添加到其它.c文件里也行

core_cm3.c:这个文件提供了一些汇编级函数实现,例如提供了中断屏蔽的汇编实现,因为你没使用这些函数所以删除它不会影响编译,例如你要做软复位函数时,你就会使用中断屏蔽在那个时候你删除它就会编译失败。

 待续



以上是关于stm32新建工程及启动过程了解的主要内容,如果未能解决你的问题,请参考以下文章

嵌入式 入门篇 - 第5章 STM32新建工程及配置

STM32开发-MDK新建工程及配置

STM32F0_新建软件工程详细过程

STM32如何新建工程+寄存器点亮LED灯

STM32CubeMX(01)基于HAL库点亮LED

UCOS2_STM32F1移植详细过程