1. 第一个工程 翻转引脚
编译下载运行此代码,会看到一个 LED灯(连至MCU的 PA5引脚)不停地闪烁。为了完成这个简单的功能,我们看到这个工程里包含了不少文件:
2. 文件分类解释
启动代码
ALIGN 变量或代码对齐。如:
EQU 给标号赋值。如:
DCD 分配1个或多个字(words)的内存空间。如:
AREA 定义一个代码或数据段(section),命名并指定属性。如:
AREA Func01, CODE, READONLY;
SPACE 保留一段空间并初始化为 0。如:
IMPORT 导入其它文件中的标号,以在当前文件中引用。如:
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
EXPORT 导出能被连接器(Linker)识别的标号。从ASM文件导出的标号可以在C中引用。
[WEAK] 如果在其它地方定义了相同的标号,则此处定义被覆盖。
PROC 定义一个函数的起始地址。
例子:
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B.
ENDP
定义堆和栈:
__initial_sp
Reset_Handler
复位向量,我们在上一篇文章已经讲到如何从复位向量一步一步执行到用户代码中的主程序main( )。
SysTick_Handler
需要注意的是 SysTick_Handler 这个中断处理函数在用户代码文件stm32f0xx_it.c
中进行了重定义,所以当 SysTick 中断发生时,实际会跳转到用户代码的中断处理函数,而不是跳到下图所示的汇编代码中断处理函数进入死循环。
再往下可以看到,对所有芯片级中断定义了一个共享的陷阱函数。用户在实际使用到某一个中断的时候,要在中断处理文件 stm32f0xx_it.c 中用相同的函数名定义,从而在中断发生时跳转到实际的中断处理函数。
在此文件的最下面的代码,是用来传递堆栈信息给库的:
M0 内核初始化
驱动
stm32f0xx_hal.c
此文件包含用户程序必须首先调用的 HAL_Init( ),它会使能数据和指令缓存,预取指令队列;配置系统滴答时钟产生 1ms 中断;调用 HAL_MspInit( )回调函数。
HAL_MspInit( )函数用来做系统级的初始化,配置某一模块相关的 时钟,引脚,DMA,中断等资源,但是在所有的例程中都没有实际用到此函数。可以先忽略。
RCC(Reset and Clock Controller)模块的驱动。一个模块为什么要两个驱动文件呢?前一个文件提供了基本的通用的功能驱动,后一个文件是扩展功能驱动,通常针对某一特定型号的芯片。如同我们吃饭需要餐具,_rcc.c 提供碗筷等常用必备工具,_rcc_ex 可能提供的就是酒杯,烛台等这些东西。
GPIO 模块的驱动。
BSP 板级支持包
stm32f0xx_nucleo.c
用户代码
stm32f0xx_it.c 中断处理
使用到哪个模块就在配置文件中打开使能该模块的宏定义。
stm32f0xx_hal_conf.h
然后第一步必须调用 HAL_Init( )。
第二步,如果希望系统时钟工作在默认内部时钟(8MHz HIS)以外的频率,则需要调用 SystemClock_Config( )。此函数又调用 HAL_RCC_ClockConfig( ) 完成新配置。
stm32f0xx_it.c 中的中断处理函数 SysTick_Handler( ) 很简单,每次进入就对滴答计时变量 uwTick 加1,其它 HAL 函数可以基于此变量计时。
参考资料:
Description of STM32F0 HAL and low-layer drivers
ARM Compiler armasm User Guide
STM32F030 Datasheet
STM32F030 Reference Manual
联系客服