STM32启动代码分析、简化、实战
本文通过对STM32的官方固件库
STM32F10x_StdPeriph_Lib_V3.5.0
里的MDK启动文件分析,简化部分不需要的代码,
并从繁杂的固件库里,精炼出一个类似于“hello world”的入门实战小程序——点亮一个LED。该工程仅仅包含一个启动文件和一个有main函数的C文件。
本文初衷:不用固件库建立自己的工程!
实验软件:Keil uVision4
实验硬件:神舟IV号开发板
芯片型号:STM32F107VC
STM32启动代码分析、简化、实战
汇编基础:
1.伪指令:EQU
语法格式:名称EQU表达式{,类型}
EQU伪指令用于为程序中的常量、标号等定义一个等效的字符名称,类似于C语言的#define。其中EQU可以用“*”代替。
名称为EQU伪指令定义的字符名称,当表达式为32位的常量时,可以指定表达式的数据类型,可以有一下三种类型:
CODE16、CODE32和DA TA
2.伪指令:AREA
语法格式:AREA段名{,属性1}{,属性2}……
AREA命令指示汇编程序汇编一个新的代码段或数据段。段是独立的、指定的、不可见的代码或数据块,它们由链接程序处理。
段名:可以为段选择任何段名。但是,以一个数字开始的名称必须包含在竖杠号内,否则会产生一个缺失段名错误。例如,|1_DataArea|。
有些名称是习惯性的名称。例如:|.text|用于表示由C编译程序产生的代码段,或用于以某种方式与C库关联的代码段。
属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号分隔。常用的属性如下:——CODE属性:用于定义代码段,默认为READONLY。
——DA TA属性:用于定义数据段,默认为READWRITE。
——READONLY属性:指定本段为只读,代码段默认为READONLY。
——READWRITE属性:指定本段为可读可写,数据段的默认属性为READWRITE。——ALIGN属性:使用方式为ALIGN表达式。在默认时,ELF(可执行连接文件)的代码段和数据段是按字对齐的,表达式的取值范围为0~31,相应的对齐方式为2表达式次方。——NOINIT属性:表示数据段是未初始化的或初始化为零。其只包含零初始化的空间保留命令SPACE或DCB、DCD、DCDU、DCQ、DCQU、DCW或DCWU。可以决定在链接时AREA是未初始化的还是零初始化的。
一个汇编语言程序至少要包含一个段,当程序太长时,也可以将程序分为多个代码段和数据段。
3.伪指令:SPACE用于分配一片连续的存储单元
启动代码分析:
定义栈段,不初始化
栈名:STACK
大小:Stack_Size
只分配空间不做初始化或者初始化为0:NOINIT
可读可写:READWRITE:
按8字节对齐:ALIGN=3
栈顶地址:__initial_sp
Stack_Size EQU0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
定义堆段,不初始化
堆名:HEAP
大小:Heap_Size
只分配空间不做初始化或者初始化为0:NOINIT
可读可写:READWRITE:
按8字节对齐:ALIGN=3
堆起始地址:__heap_base
堆终止地址:__heap_limit
Heap_Size EQU0x00000200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
4.伪指令:EXPORT
语法格式:EXPORT标号{[WEAK]}
EXPORT伪指令用于在程序中声明一个全局的标号,该标号可在其他的文件中引用。EXPORT可用GLOBAL代替。标号在程序中区分大小写,[WEAK]选项声明其他的同名标号优先于该标号被引用。
5.伪指令:DCD
语法格式:DCD(或DCDU)表达式
DCD(或DCDU)伪指令用于分配一片连续的字存储单元并用伪指令中指定的表达式初始化。其中,表达式可以为程序标号或数字表达式。DCD也可用“&”代替。
用DCD分配的字存储单元是字对齐的,而用DCDU分配的字存储单元并不严格字对齐。
PRESERVE8 ;指示编译器8字节对齐(keil编译器时需要加上)
THUMB ;指示编译器为THUMB指令
定义复位段(中断向量表),并初始化
段名:RESET
大小:__V ectors_Size
数据段:DA TA
只读:READONLY
按字节对齐:默认ALIGN
向量表起始地址:__V ectors
向量表终止地址:__V ectors_End
AREA RESET, DA TA, READONLY
EXPORT __V ectors
EXPORT __V ectors_End
EXPORT __V ectors_Size
__V ectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD WWDG_IRQHandler ; Window Watchdog
DCD PVD_IRQHandler ; PVD through EXTI Line detect
DCD TAMPER_IRQHandler ; Tamper
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2
DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3
DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4
DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5
DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6
DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7
DCD ADC1_2_IRQHandler ; ADC1 and ADC2
DCD CAN1_TX_IRQHandler ; CAN1 TX
DCD CAN1_RX0_IRQHandler ; CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; EXTI Line 9..5
DCD TIM1_BRK_IRQHandler ; TIM1 Break
DCD TIM1_UP_IRQHandler ; TIM1 Update
DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation
DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare
DCD TIM2_IRQHandler ; TIM2
DCD TIM3_IRQHandler ; TIM3
DCD TIM4_IRQHandler ; TIM4
DCD I2C1_EV_IRQHandler ; I2C1 Event
DCD I2C1_ER_IRQHandler ; I2C1 Error
DCD I2C2_EV_IRQHandler ; I2C2 Event
DCD I2C2_ER_IRQHandler ; I2C1 Error
DCD SPI1_IRQHandler ; SPI1
DCD SPI2_IRQHandler ; SPI2
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD USART3_IRQHandler ; USART3
DCD EXTI15_10_IRQHandler ; EXTI Line 15..10
DCD RTCAlarm_IRQHandler ; RTC alarm through EXTI line
DCD OTG_FS_WKUP_IRQHandler ; USB OTG FS
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD TIM5_IRQHandler ; TIM5
DCD SPI3_IRQHandler ; SPI3
DCD UART4_IRQHandler ; UART4
DCD UART5_IRQHandler ; UART5
DCD TIM6_IRQHandler ; TIM6
DCD TIM7_IRQHandler ; TIM7
DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1
DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2
DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3
DCD DMA2_Channel4_IRQHandler ; DMA2 Channel4
DCD DMA2_Channel5_IRQHandler ; DMA2 Channel5
DCD ETH_IRQHandler ; Ethernet
DCD ETH_WKUP_IRQHandler ; Ethernet Wakeup through EXTI line
DCD CAN2_TX_IRQHandler ; CAN2 TX
DCD CAN2_RX0_IRQHandler ; CAN2 RX0
DCD CAN2_RX1_IRQHandler ; CAN2 RX1
DCD CAN2_SCE_IRQHandler ; CAN2 SCE
DCD OTG_FS_IRQHandler ; USB OTG FS
__V ectors_End
__V ectors_Size EQU__V ectors_End - __V ectors
6.过程定义伪指令:PROC、ENDP
语法格式:<过程名> PROC [类型]
……
RET
<过程名> ENDP
过程就是子程序。一个过程可以被其它程序所调用(用CALL指令),过程的最后一条指令一
般是返回指令(RET)。
7.伪指令:IMPORT
语法格式:IMPORT 标号{[WEAK]}
IMPORT伪指令用于通知编译器要使用的标号在其他源文件中定义,但是在当前源文件中引用,而且无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的标号表中。
标号在程序中区分大小写,[WEAK]选项表示当所有的源文件都没有定义这样的一个标号时,编译器也不给出错误信息,在多数情况下将该标号置为0,若该标号为B或BL指令引用,则将B或BL指令置为NOP操作。
8.伪指令:LDR
语法格式:LDR{执行条件,如EQ、NE等} register,=expr/label_expr
大范围的地址读取伪指令LDR用于加载32位的立即数或一个地址值到指定寄存器,在汇编编译源程序时,LDR伪指令被编译器替换成一条合适的指令。若加载的常数未超出MOV 或MVN的范围,则使用MOV或MVN指令代替LDR伪指令,否则汇编器将常量放入字池,并使用一条程序相对偏移的LDR指令从文字池读出常量。
9.Thumb跳转指令:B、BL、BX
语法格式:B{执行条件,如EQ、NE等} label
带链接BL{执行条件,如EQ、NE等} label
带状态切换BX{执行条件,如EQ、NE等} label
定义代码段(异常处理函数),并初始化
段名:.text
代码段:CODE
只读:READONLY
按字节对齐:默认ALIGN
代码段起始地址:Reset_Handler
AREA|.text|, CODE, READONLY
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT__main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
10.内置变量:{PC} 或“.”当前指令地址
11.汇编语句格式规范:
ARM汇编中,所有标号必须在一行的顶格书写,其后面不要添加“:”,而所有指令均不能顶格书写。
ARM汇编器对标识符大小写敏感,书写标号及指令时字母大小写要一致,在ARM汇编程序中,一个ARM指令、伪指令、寄存器名可以全部为大写字母,也可以全部为小写字母,但不要大小写混合使用。
注释使用“;”,注释内容由“;”开始到此行结束,注释可以在一行的顶格书写。
源程序中允许有空行,适当地插入空行可以提高源程序代码的可读性。如果单行太长,可以
使用字符“\”将其分行,“\”后不能有任何字符,包括空格和制表符等。对于变量的设置,常量的定义,其标识符必须在一行的顶格书写。
异常处理函数(续1)
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B.
ENDP
HardFault_Handler PROC
EXPORT HardFault_Handler [WEAK]
B.
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
PROC
EXPORT UsageFault_Handler [WEAK]
B .
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B .
ENDP
DebugMon_Handler\
PROC
EXPORT DebugMon_Handler [WEAK]
B .
ENDP
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
异常处理函数(续2)
这个默认的异常处理函数处理所有外部中断
Default_Handler PROC
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMPER_IRQHandler [WEAK]
EXPORT RTC_IRQHandler [WEAK]
EXPORT FLASH_IRQHandler [WEAK]
EXPORT RCC_IRQHandler [WEAK]
EXPORT EXTI0_IRQHandler [WEAK]
EXPORT EXTI1_IRQHandler [WEAK]
EXPORT EXTI2_IRQHandler [WEAK]
EXPORT EXTI3_IRQHandler [WEAK]
EXPORT EXTI4_IRQHandler [WEAK]
EXPORT DMA1_Channel1_IRQHandler [WEAK]
EXPORT DMA1_Channel2_IRQHandler [WEAK]
EXPORT DMA1_Channel3_IRQHandler [WEAK]
EXPORT DMA1_Channel4_IRQHandler [WEAK]
EXPORT DMA1_Channel5_IRQHandler [WEAK]
EXPORT DMA1_Channel6_IRQHandler [WEAK]
EXPORT DMA1_Channel7_IRQHandler [WEAK]
EXPORT ADC1_2_IRQHandler [WEAK]
EXPORT CAN1_TX_IRQHandler [WEAK]
EXPORT CAN1_RX0_IRQHandler [WEAK]
EXPORT CAN1_RX1_IRQHandler [WEAK]
EXPORT CAN1_SCE_IRQHandler [WEAK]
EXPORT EXTI9_5_IRQHandler [WEAK]
EXPORT TIM1_BRK_IRQHandler [WEAK]
EXPORT TIM1_UP_IRQHandler [WEAK]
EXPORT TIM1_TRG_COM_IRQHandler [WEAK]
EXPORT TIM1_CC_IRQHandler [WEAK]
EXPORT TIM2_IRQHandler [WEAK]
EXPORT TIM3_IRQHandler [WEAK]
EXPORT TIM4_IRQHandler [WEAK]
EXPORT I2C1_EV_IRQHandler [WEAK]
EXPORT I2C1_ER_IRQHandler [WEAK]
EXPORT I2C2_EV_IRQHandler [WEAK]
EXPORT I2C2_ER_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT USART1_IRQHandler [WEAK]
EXPORT USART2_IRQHandler [WEAK]
EXPORT USART3_IRQHandler [WEAK]
EXPORT EXTI15_10_IRQHandler [WEAK]
EXPORT RTCAlarm_IRQHandler [WEAK]
EXPORT OTG_FS_WKUP_IRQHandler [WEAK]
EXPORT TIM5_IRQHandler [WEAK]
EXPORT SPI3_IRQHandler [WEAK]
EXPORT UART4_IRQHandler [WEAK]
EXPORT UART5_IRQHandler [WEAK]
EXPORT TIM6_IRQHandler [WEAK]
EXPORT TIM7_IRQHandler [WEAK]
EXPORT DMA2_Channel1_IRQHandler [WEAK]
EXPORT DMA2_Channel2_IRQHandler [WEAK]
EXPORT DMA2_Channel3_IRQHandler [WEAK]
EXPORT DMA2_Channel4_IRQHandler [WEAK]
EXPORT DMA2_Channel5_IRQHandler [WEAK]
EXPORT ETH_IRQHandler [WEAK]
EXPORT ETH_WKUP_IRQHandler [WEAK]
EXPORT CAN2_TX_IRQHandler [WEAK]
EXPORT CAN2_RX0_IRQHandler [WEAK]
EXPORT CAN2_RX1_IRQHandler [WEAK]
EXPORT CAN2_SCE_IRQHandler [WEAK]
EXPORT OTG_FS_IRQHandler [WEAK]
;下面的全部异常处理函数标号都对应同一个地址,这个地址也是Default_Handler的地址WWDG_IRQHandler
PVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_IRQHandler
DMA1_Channel3_IRQHandler
DMA1_Channel4_IRQHandler
DMA1_Channel5_IRQHandler
DMA1_Channel6_IRQHandler
DMA1_Channel7_IRQHandler
ADC1_2_IRQHandler
CAN1_TX_IRQHandler
CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler EXTI9_5_IRQHandler
TIM1_BRK_IRQHandler
TIM1_UP_IRQHandler
TIM1_TRG_COM_IRQHandler TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler USART2_IRQHandler USART3_IRQHandler
EXTI15_10_IRQHandler RTCAlarm_IRQHandler
OTG_FS_WKUP_IRQHandler TIM5_IRQHandler
SPI3_IRQHandler
UART4_IRQHandler
UART5_IRQHandler
TIM6_IRQHandler
TIM7_IRQHandler
DMA2_Channel1_IRQHandler DMA2_Channel2_IRQHandler DMA2_Channel3_IRQHandler DMA2_Channel4_IRQHandler DMA2_Channel5_IRQHandler ETH_IRQHandler
ETH_WKUP_IRQHandler CAN2_TX_IRQHandler
CAN2_RX0_IRQHandler
CAN2_RX1_IRQHandler
CAN2_SCE_IRQHandler OTG_FS_IRQHandler
B . ENDP
初始化堆栈段
由于前面只是定义了堆栈段并没有初始化,这里对堆栈段进行初始化。就像定义了:int a; 初始化a = 1;也可以像代码段一样定义的同时就初始化:int b = 2;
下面代码中有个__MICROLIB,对应后面MDK截图的Use MicroLIB,如果选了勾选了Use MicroLIB,IF就为真,否则为假。
ALIGN
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
+++++++++++启动代码分析到此结束++++++++++++++
简化后的启动代码
1.去掉外部中断,去掉__MICROLIB判断,如果需要可以手动添加
2.去掉SystemInit,系统初始化可以在main函数里进行
3.去掉[WEAK],如果用户自己想定义内部中断服务程序可以加上[WEAK]
;定义栈
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
;定义堆
Heap_Size EQU 0x00000200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
;MDK关键字
PRESERVE8
THUMB
;定义并初始化中断向量表
AREA RESET, DA TA, READONLY
EXPORT __V ectors
EXPORT __V ectors_End
EXPORT __V ectors_Size
__V ectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
__V ectors_End
__V ectors_Size EQU __V ectors_End - __V ectors
;定义并初始化代码段,程序正真的入口
AREA |.text|, CODE, READONLY
Reset_Handler PROC
;__main为运行时库提供的函数,会调用下面定义的__user_initial_stackheap,初始化堆栈。
IMPORT __main
LDR R0, =__main
BX R0 ;进入main
ENDP
;异常服务程序,对应同一个死循环的地址
NMI_Handler
HardFault_Handler
MemManage_Handler
BusFault_Handler
UsageFault_Handler
SVC_Handler
DebugMon_Handler
PendSV_Handler
SysTick_Handler PROC
B .
ENDP
;初始化堆栈
ALIGN
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
END
;*************************END OF FILE**********************************
启动代码实战(点亮一个LED)1.打开Keil,新建工程,命名LED保存。
2.选择芯片型号STM32F107VC,点击OK
3.是否要默认启动代码,点击否
4.新建启动文件,保存为start_up.s,并添加到工程中。
5.新建C程序文件,保存为main.c,并添加到工作中
6.拷贝启动代码到start_up.s中,并保存。
7.拷贝C程序到main.c中,并保存。(C程序在最后)
8.勾选Create HEX File
9.编译链接,产生hex文件。
10.下载到开发板上运行。
11.运行结果
/*************************************************************************/
/* 本程序精炼自STM32官方固件库,留下必要的代码,并做了详细说明*/
/*************************************************************************/
/* 定义时钟结构体*/
typedef struct
{
volatile unsigned int CR;
volatile unsigned int CFGR;
volatile unsigned int CIR;
volatile unsigned int APB2RSTR;
volatile unsigned int APB1RSTR;
volatile unsigned int AHBENR;
volatile unsigned int APB2ENR;
volatile unsigned int APB1ENR;
volatile unsigned int BDCR;
volatile unsigned int CSR;
volatile unsigned int AHBRSTR;
volatile unsigned int CFGR2;
} RCC_TypeDef;
/* 定义时钟结构体的指针变量*/
#define RCC ((RCC_TypeDef *)0x40021000)
/* memory mapped structure for System Control Block (SCB) */
typedef struct
{
volatile unsigned int CPUID;
volatile unsigned int ICSR;
volatile unsigned int VTOR; /* 存放中断向量表的起始地址的寄存器*/ volatile unsigned int AIRCR;
volatile unsigned int SCR;
volatile unsigned int CCR;
volatile unsigned int SHP[12];
volatile unsigned int SHCSR;
volatile unsigned int CFSR;
volatile unsigned int HFSR;
volatile unsigned int DFSR;
volatile unsigned int MMFAR;
volatile unsigned int BFAR;
volatile unsigned int AFSR;
volatile unsigned int DFR;
volatile unsigned int ADR;
volatile unsigned int MMFR[4];
volatile unsigned int ISAR[5];
} SCB_Type;
#define SCB ((SCB_Type *)0xE000ED00) /* 定义对应结构体指针变量*/
/* 定义GPIO结构体*/
typedef struct
{
volatile unsigned int CRL; /*配置寄存器GPIOx_CRL,偏移地址00*/
volatile unsigned int CRH; /*配置寄存器GPIOx_CRH,偏移地址04*/
volatile unsigned int IDR; /*数据寄存器GPIOx_IDR,偏移地址08 */
volatile unsigned int ODR; /*数据寄存器GPIOx_ODR,偏移地址0C*/
volatile unsigned int BSRR; /*置位寄存器GPIOx_BSRR,偏移地址10*/
volatile unsigned int BRR; /*复位寄存器GPIOx_BRR,偏移地址14*/
volatile unsigned int LCKR; /*锁定寄存器GPIOx_LCKR,偏移地址18*/
} GPIO_TypeDef;
/* 定义GPIO结构体指针变量*/
#define GPIOD ((GPIO_TypeDef *)0x40011400)
/*************************************************************************/ /* main函数*/ /*************************************************************************/ int main(void)
{
/* 初始化系统时钟*/
RCC->CR |= (unsigned int)0x00000001;
RCC->CFGR &= (unsigned int)0xF8FF0000;
RCC->CR &= (unsigned int)0xFEF6FFFF;
RCC->CR &= (unsigned int)0xFFFBFFFF;
RCC->CFGR &= (unsigned int)0xFF80FFFF;
RCC->CR &= (unsigned int)0xEBFFFFFF;
RCC->CIR = 0x00FF0000;
RCC->CFGR2 = 0x00000000;
/* V ector Table Relocation in Internal SRAM.*/
SCB->VTOR = ((unsigned int)0x20000000);
/* 打开GPIOD口时钟*/
RCC->APB2ENR |= 0x20;
/* 设置GPIOD第2脚为推挽输出模式,速率为50MHz */
GPIOD->CRL &= 0xFFFFF0FF;
GPIOD->CRL |= 0x00000300;
/* 点亮PD2口的LED */
GPIOD->BRR &= 0xFFFFFFFB;
while (1)
{
}
}
/**************************** *****END OF FILE**********************/