文章目录
- 嵌入式开发常识汇总
- 1、嵌入式Linux和stm32之间的区别和联系
- 2、stm32程序下载方式
- 3、Keil5安装芯片包
- 4、芯片封装种类
- 5、STM32命名
- 6、数据手册和参考手册
- 7、什么是寄存器、寄存器映射和内存映射
- 8、芯片引脚顺序
- 9、stm32芯片里有什么
- 10、存储器空间的划分
- 11、如何理解寄存器说明
- 12、如何操作寄存器的某一位
- 13、Doxygen注释规范
- 14、stm32位带操作
- 15、最小系统
- STM32F407芯片学习
- 1、stm32单片机启动流程
- stm32启动模式选择
- 启动流程
- 2、新建一个工程
- 1、本地目录设置
- 2、Keil新建工程
- 3、编译项目,看有无错误
- 3、GPIO寄存器分析和使用
- 1、GPIO寄存器
- 2、GPIO四种模式怎样配置及使用场景
- 3、 使用GPIO点亮LED灯
- 4、按键检测
- 4、启动文件
- 1、启动文件干了什么
- 2、如何选择启动文件
- 3、启动文件分析
- 5、配置时钟
- 1、相关名词
- 2、时钟树
- 3、系统时钟APB、AHB总线时钟分析
- 4、时钟与复位寄存器RCC
- 5、其他时钟
- 6、系统如何设置的时钟
嵌入式开发常识汇总
1、嵌入式Linux和stm32之间的区别和联系
1)嵌入式MPU:Miroc Processor Unit 微处理器单元,一般指性能更高的芯片,可以运行Linux+ARM架构,一般跑Linux或者Android系统,可以移植Qt。
2)嵌入式MCU:Micro Control Unit 微控制器单元,常见的有stm32、51、ESP8266等,芯片资源较少,性能较低。
3)嵌入式DSP:Digital Signal Procrssor :专门用于数字信号处理的芯片。
MPU和MCU都是芯片,只不过性能和资源不同,在MCU比如stm32Fxx系列的单片机性能较低,不能支持Linux这些较大的系统运行,只能运行FreeRTOS等实时系统,这些系统内核较为简单,运行起来并不需要太多的资源。在MCU上可以使用emXGUI、emWIN等图形框架。在MPU上可以运行Linux、Android等系统,界面开发一般选用Qt。在stm系列中有也有性能强大的芯片可以支持Linux系统。所以,MCU和MPU并不是以芯片公司或名称区分,只看芯片的性能。
2、stm32程序下载方式
1)串口下载:此方法需要USB转TTL下载器。stm32从系统存储器启动,boot0=1,boot1=0,需要串口转TTl,电脑安装CH340驱动,使用串口下载助手,将USB 转 TTL下载器(CH340)的TXD、RXD、GND与开发板的RXD、TXD、GND连接好,下载器另一头插电脑,下载文件即可(工具软件:FlyMcu)
2)STLINK:此方法需要STLINK下载器。电脑安装STLINK驱动,将ST-Link V2的SWDIO、GND、SWCLK、3.3V接到开发板的DIO、GND、CLK、3.3引脚上,有些板的丝印标法不同,但都能对应的看出来,然后将ST-Link插电脑上,在Keil软件上设置好Debug选项,直接下载。此方法下载程序到stm32 的Flash,所以boot引脚和串口下载不同。
3、Keil5安装芯片包
为了可以支持对相应芯片的开发,在安装好Keil软件之后还需要自己安装芯片包,否则在新建工程时找不到对应芯片。keil软件官方提供各种芯片所需要的包。地址:http://www.keil.com/dd2/pack/
4、芯片封装种类
参考文档:https://blog.csdn.net/ffdia/article/details/116222777
5、STM32命名
6、数据手册和参考手册
数据手册主要用于芯片选型和设计原理图时参考,参考手册主要用于在编程的时候查阅外设的功能和寄存器说明
7、什么是寄存器、寄存器映射和内存映射
STM32有32根地址线,理论上其寻址空间为2的32次方,所以STM32的有效地址最多有4G个,每个地址代表1B,STM32的存储地址就有4GB。
然而上述都是理论上的,STM32可以使用的存储空间远远没有4GB。
映射的意思就是将理论上存在的地址对应到具体的设备,如:0x1234 5678 ~ 0x2234 5678 ==>flash,在硬件上将对应的线路连接至Flash,这样在访问这些地址时就是在访问Flash设备,通过指针对这些地址读写就是对Flash的读写。
内存映射(存储器映射): 将理论上的地址对应到具体的外设上。
寄存器映射 :给对应的内存地址取个别名,便于记忆和访问内存地址 例如:define REGISTER 0x1111 2222
寄存器:为不同内存地址取的别名,别名与内存映射的外设有关,如上面的REGISTER,若定义 (uint_16*)REGISTER,则REGISTER寄存器就代表 0x1111 2222 往后的16个地址,对寄存器的操作就是对这16个地址操作,一般使用定义结构体,这样只需要将结构体首地址对应到寄存器基地址,结构体中的成员就可以按照对应的基地址往后占位,操作结构体成员,就是在对基地址之后的地址操作。
在存储器 Block2 这块区域,设计的是片上外设,它们以四个字节为一个单元,共 32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。
8、芯片引脚顺序
一般在芯片的一角有一个缺口或者小圆点,此处为1脚,引脚按逆时针排序。
9、stm32芯片里有什么
1)ContexM内核====>相当于CPU,由ARM公司设计
2)片上外设====>ISP、USART、ADC、DAC、FSMC、Flash、RAM等,这些由ST厂家设计
3)系统总线矩阵==>连接外设和内核,由ST厂家设计。
10、存储器空间的划分
存储器空间被ARM公司打开划分为8块(block0~block7),每块大小为512MB。最重要就是Block0 ~ 2 这三块,Block0 为内部Flash,用作程序存放,Block1为内部RAM,Block2为外设地址,ST公司将Block2划分为AHB、APB,APB又划分为APB1、APB2,AHB又划分为AHB1、AHB2.
11、如何理解寄存器说明
12、如何操作寄存器的某一位
使用逻辑或或逻辑与,例如操作寄存器32位reg的第六位
置1:( * reg)|=( 0x01<<6);
置0:( * reg)&=~(0x01<<6)
13、Doxygen注释规范
14、stm32位带操作
1)位带区:正常的存储空间,可以正常访问,但是不能以位(1Bty为8位)访问。
2)位带别名区:正常的存储空间,可以正常访问,以32位访问,最低位的操作会一一对应到位带区的一位
3)位带区和位带别名区的联系
4)位带操作:对位带区的某一位A的操作转化为对位带别名区的32位B的操作,B的最低位的值会写入A。
5)stm32f407的位带区:一个是 SRAM 区的最低 1MB 空间,另一个是外设区最低1MB 空间,所以这两个位带区分别有32MB的对应位带别名区
6)位带区与对应位带别名区的地址的转换:不同单片机不同,百度搜索。
15、最小系统
最小系统应该包括电源电路、晶振电路、下载电路、复位电路及BOOT启动电路,这一样芯片才可以被使用
STM32F407芯片学习
1、stm32单片机启动流程
stm32启动模式选择
复位后,在 SYSCLK 的第四个上升沿锁存 BOOT 引脚的值。BOOT0 为专用引脚,而 BOOT1 则与 GPIO 引脚共用。一旦完成对 BOOT1 的采样,相应 GPIO 引脚即进入空闲状态,可用于其它用途。BOOT0与BOOT1引脚的不同值指向了三种启动方式:
从主Flash启动。主Flash指的是STM32的内置Flash。选择该启动模式后,内置Flash的起始地址将被重映射到0x00000000地址,代码将在该处开始执行。一般我们使用JTAG或者SWD模式下载调试程序时,就是下载到这里面,重启后也直接从这启动。
从系统存储器启动。系统储存器指的是STM32的内置ROM,选择该启动模式后,内置ROM的起始地址将被重映射到0x00000000地址,代码在此处开始运行。ROM中有一段出厂预置的代码,这段代码起到一个桥的作用,允许外部通过UART/CAN或USB等将代码写入STM32的内置Flash中。这段代码也被称为ISP(In System Programing)代码,这种烧录代码的方式也被称为ISP烧录。关于ISP、ICP和IAP之间的区别将在后续章节中介绍。
从嵌入式SRAM中启动。显然,该方法是在STM32的内置SRAM中启动,选择该启动模式后,内置SRAM的起始地址将被重映射到0x00000000地址,代码在此处开始运行。这种模式由于烧录程序过程中不需要擦写Flash,因此速度较快,适合调试,但是掉电丢失。
原文链接
启动模式只决定程序烧录的位置,加载完程序之后会有一个重映射(映射到0x00000000地址位置);真正产生复位信号的时候,CPU还是从开始位置执行。
值得注意的是STM32上电复位以后,代码区都是从0x00000000开始的,三种启动模式只是将各自存储空间的地址映射到0x00000000中。
启动流程
1)进入启动文件(厂家提供)
2)设置内存地址
3)设置向量表
4)设置时钟
5)初始化寄存器
6)设置堆栈
7)设置各指针值
8)进去main函数
以上操作在启动文件中的顺序可以参考启动文件中的汇编代码。
2、新建一个工程
1、本地目录设置
1)在本地新建项目文件夹
2)新建子目录用于存放对应文件
3)将厂家提供的文件放至对应新建目录
CMSIS下的文件
启动文件
寄存器配置文件、系统初始化文件
内核外设文件,拷贝至Lib
下图stm32f4xx_conf.h为系统资源管理文件,用以管理外设头文件,存放之User,设置默认宏USE_STDPERIPH_DRIVER启用该文件。
2、Keil新建工程
1)创建项目
2)设置项目目录
3)向项目目录添加文件
4)设置项目,使用C库文件,在使用串口时可以使用printf函数。
默认宏
设置USE_STDPERIPH_DRIVER,STM32F40XX这两个宏之后,在编译程序时会根据宏添加所需要的文件。
设置Debug,选择要使用的调试器,如STLINK等
设置调试器选项
3、编译项目,看有无错误
根据错误添加头文件路径或头文件
再次编译,出现错误,f407没该外设,项目中删除该外设的文件
再次编译,报错
TimingDelay_Decrement();函数在系统SysTick的中断服务函数中调用,但是并没有实现此函数,所以报错,该函数应该提供的内容可以是操作全局变量计时。先在还没有定义此函数,注释即可。再次编译。
未定义SystemInit函数,该函数在启动文件中被引用,但是并没有实现,可以在其他地方实现该函数,后者在启动文件中删除SystemInit函数相关语句。在使stm32f4xx_it.c中实现该函数。再次编译。
使用MDK5.XX以后的版本,用户不需要写上面的预定义,因为在选择相应器件以后,编译器已经将相应的头文件加入了。取消默认宏,再次编译。
无错误,无告警。下载程序至开发板成功
至此,新建项目完成。
3、GPIO寄存器分析和使用
1、GPIO寄存器
参考手册中又详细内容
1)端口模式寄存器 GPIOx_MODER:配置端口模式,有四种:输入、输出、复用模拟
2) 端口输出类型寄存器 GPIOx_OTYPER:输出模式时需要配置,推挽或开漏
开漏输出,通过一个NMOS管和上拉电阻实现
推挽输出,通过两个mos管实现
3)GPIO 端口输出速度寄存器 GPIOx_OSPEEDR:配置端口电平转换速度,即IO口输出速度
4) 端口上拉/下拉寄存器 GPIOx_PUPDR:无上拉下拉、上拉、下拉、保留。
当GPIO处于output模式,一般选择no pull,引脚能够正确地输出输出高电平低电平信号
当GPIO处于input模式,需要根据默认的输入值来确定配置模式,如果默认输入的值为1时,最好配置为pull up,默认值为0,则选择下拉。
所以上下拉就是选择在默认情况下gpio端口的电平高低。
5)端口输入数据寄存器 GPIOx_IDR:端口输入的数据,即电平高低,该寄存器只能读,低16位有效。
6)端口输出数据寄存器 GPIOx_ODR:端口输出寄存器,低16位有效,可读可写,用以获取或设置端口的输出电平。
7) 端口置位/复位寄存器 GPIOx_BSRR:只能写,高16位BRy(复位),这些位置1,则对应端口复位。低16位BSy(置位),这些位置1,则对应端口置位。复位时,复位位复位值,置位值与复位值相反。对ODR操作。同时对BSx和BRx操作,BSx优先级更高,在对BSSR写后会自动清零。
8)端口配置锁定寄存器 GPIOx_LCKR:可读可写,低17位有效。由于锁定某GPIO端口的配置,每一位锁定某个寄存器的配置。锁定后在复位之前无法更改该端口位的值。
LCKR[16] = 0,未锁定,可以对LCKR[15:0]操作;
LCKR[15:0]:对应一个GPIO端口位,对应位置1,则锁定配置。
9)复用功能低位寄存器 GPIOx_AFRL:四位一组,确定一个端口的复用功能(0~7位端口)
10)复用功能高位寄存器 GPIOx_AFRL:四位一组,确定一个端口的复用功能(8~15位端口)
2、GPIO四种模式怎样配置及使用场景
1)输出模式:此模式需要配置推挽或开漏,对于是否上下拉或者悬空其实没啥影响,输出速度按情况分析。
2)输入模式:此模式需要指定上拉或是下拉,用以确定端口空闲(即外部无输入)时端口电平状态,输入速度按情况分析。
3)模拟模式:模拟输入用作ADC电压采集,模拟输出用作DAC数模转换。配置为模拟模式。
4)复用模式:配置为复用模式,配置复用寄存器即可
3、 使用GPIO点亮LED灯
1)确定LED的端口和连接方式
共阳极连接,GPIO默认输出高电平,则灯灭,控制使出低电平则灯亮,连接端口位PF6、7、8(红、蓝、绿)
2)配置GPIOF6、7、8位上拉、推挽、高速模式(对于LED并不重要)
3)写头文件和C文件
问题记录:
问题1:写好后,LED无反应
原因:再新建工程时编译报错未定义SystemInit函数,所以自己在stm32f4xx_it.c中自己写了一个空函数,实际上该函数在system_stm32f4xx.c中,但新建文件时并没有将该文件加入工程,SystemInit函数中设置了时钟等系统资源,将此函数变为空函数则系统时钟等系统资源未初始化,所以代码无法执行。
解决:将system_stm32f4xx.c文件加入项目,删除自己写的空函数
问题2:初始化GPIO后,LED点亮.
原因:ODR复位值为0,LED为共阳极连接,所以导致电压一侧高,GPIO端口处电压低,导致电流流过LED。
解决:初始化GPIO后将将端口置位。
代码
#include "bsp_led.h"
void BSP_LEDInit(void)
{
RCC_AHB1PeriphClockCmd(LED_RAD_CLK,ENABLE);
RCC_AHB1PeriphClockCmd(LED_BLUE_CLK,ENABLE);
RCC_AHB1PeriphClockCmd(LED_GREE_CLK,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = LED_RAD_PIN;
GPIO_InitStruct.GPIO_Mode = LED_RAD_Mode;
GPIO_InitStruct.GPIO_Speed = LED_RAD_Speed;
GPIO_InitStruct.GPIO_OType = LED_RAD_OType;
GPIO_InitStruct.GPIO_PuPd = LED_BLUE_PuPd;
GPIO_Init(LED_RAD_PORT,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = LED_GREE_PIN;
GPIO_InitStruct.GPIO_Mode = LED_GREE_Mode;
GPIO_InitStruct.GPIO_Speed = LED_GREE_Speed;
GPIO_InitStruct.GPIO_OType = LED_GREE_OType;
GPIO_InitStruct.GPIO_PuPd = LED_BLUE_PuPd;
GPIO_Init(LED_GREE_PORT,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = LED_BLUE_PIN;
GPIO_InitStruct.GPIO_Mode = LED_BLUE_Mode;
GPIO_InitStruct.GPIO_Speed = LED_BLUE_Speed;
GPIO_InitStruct.GPIO_OType = LED_BLUE_OType;
GPIO_InitStruct.GPIO_PuPd = LED_BLUE_PuPd;
GPIO_Init(LED_BLUE_PORT,&GPIO_InitStruct);
LED_SetSatus(LED_RAD,DISABLE);
LED_SetSatus(LED_GREE,DISABLE);
LED_SetSatus(LED_BLUE,DISABLE);
}
void LED_SetSatus(LEDx led,FunctionalState status)
{
if(status)
{
GPIO_ResetBits(GPIOF,(uint32_t)(0x01<<led));
}
else{
GPIO_SetBits(GPIOF,(uint32_t)(0x01<<led));
}
}
头文件
#ifndef __BSP_LED_H
#define __BSP_LED_H
#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#define LED_RAD_PORT GPIOF
#define LED_RAD_PIN GPIO_Pin_6
#define LED_RAD_Mode GPIO_Mode_OUT
#define LED_RAD_Speed GPIO_Low_Speed
#define LED_RAD_OType GPIO_OType_PP
#define LED_RAD_PuPd GPIO_PuPd_NOPULL
#define LED_RAD_CLK RCC_AHB1Periph_GPIOF
#define LED_GREE_PORT GPIOF
#define LED_GREE_PIN GPIO_Pin_7
#define LED_GREE_Mode GPIO_Mode_OUT
#define LED_GREE_Speed GPIO_Low_Speed
#define LED_GREE_OType GPIO_OType_PP
#define LED_GREE_PuPd GPIO_PuPd_UP
#define LED_GREE_CLK RCC_AHB1Periph_GPIOF
#define LED_BLUE_PORT GPIOF
#define LED_BLUE_PIN GPIO_Pin_8
#define LED_BLUE_Mode GPIO_Mode_OUT
#define LED_BLUE_Speed GPIO_Low_Speed
#define LED_BLUE_OType GPIO_OType_PP
#define LED_BLUE_PuPd GPIO_PuPd_UP //猜想:设置为下拉,初始化后LED亮
#define LED_BLUE_CLK RCC_AHB1Periph_GPIOF
typedef enum
{
LED_RAD = 6,
LED_GREE = 7,
LED_BLUE = 8
}LEDx;
void BSP_LEDInit(void);
void LED_SetSatus(LEDx led,FunctionalState status);
#endif /*__BSP_LED_H*/
在头文件中将端口参数重定义是为了以后移植方便
4、按键检测
1)确定硬件连接:
两个按键分别连接到了PA0和PC13,按键没有按下时外部为3.3V,按下后电路接地为0V,所以只要检测按键连接的端口是否为低电平即可确定按键是否按下,而且外部电路确定,配置为上拉、下拉、或者浮空并没有影响(但是若是端口用于模拟通信时,上下拉就有作用了,应为模拟通信时用电平高低确认通信线路是否空闲)
2)配置PA0、PC13为输入模式,浮空状态,快速模式
3)问题记录:无
4)C代码及头文件
头文件
#ifndef __BSP_KEY_H
#define __BSP_KEY_H
#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#define KEY_1_PORT GPIOA
#define KEY_1_PIN GPIO_Pin_0
#define KEY_1_Mode GPIO_Mode_IN
#define KEY_1_Speed GPIO_Fast_Speed
#define KEY_1_OType GPIO_OType_PP
#define KEY_1_PuPd GPIO_PuPd_NOPULL
#define KEY_1_CLK RCC_AHB1Periph_GPIOA
#define KEY_2_PORT GPIOC
#define KEY_2_PIN GPIO_Pin_13
#define KEY_2_Mode GPIO_Mode_IN
#define KEY_2_Speed GPIO_Fast_Speed
#define KEY_2_OType GPIO_OType_PP
#define KEY_2_PuPd GPIO_PuPd_NOPULL
#define KEY_2_CLK RCC_AHB1Periph_GPIOC
typedef enum
{
KEY_OFF = 0,
KEY_ON = !KEY_OFF
}KEY_Status;
void BSP_KEYInit(void);
KEY_Status KEY_Scan(GPIO_TypeDef* KEY_x_PORT,uint16_t KEY_x_PIN);
#endif /*__BSP_KEY_H*/
C文件
#include "bsp_key.h"
void BSP_KEYInit(void)
{
RCC_AHB1PeriphClockCmd(KEY_1_CLK|KEY_2_CLK,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = KEY_1_PIN;
GPIO_InitStruct.GPIO_Mode = KEY_1_Mode;
GPIO_InitStruct.GPIO_Speed = KEY_1_Speed;
GPIO_InitStruct.GPIO_OType = KEY_1_OType;
GPIO_InitStruct.GPIO_PuPd = KEY_1_PuPd;
GPIO_Init(KEY_1_PORT,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = KEY_2_PIN;
GPIO_InitStruct.GPIO_Mode = KEY_2_Mode;
GPIO_InitStruct.GPIO_Speed = KEY_2_Speed;
GPIO_InitStruct.GPIO_OType = KEY_2_OType;
GPIO_InitStruct.GPIO_PuPd = KEY_2_PuPd;
GPIO_Init(KEY_2_PORT,&GPIO_InitStruct);
}
KEY_Status KEY_Scan(GPIO_TypeDef* KEY_x_PORT,uint16_t KEY_x_PIN)
{
if(GPIO_ReadInputDataBit(KEY_x_PORT, KEY_x_PIN) == 0)
{
while(GPIO_ReadInputDataBit(KEY_x_PORT, KEY_x_PIN) == 0);
return KEY_ON;
}
return KEY_OFF;
}
4、启动文件
1、启动文件干了什么
- 初始化堆栈指针 SP=_initial_sp
- 初始化 PC 指针 =Reset_Handler
- 初始化中断向量表
- 配置系统时钟
- 调用 C 库函数 _main 初始化用户堆栈,从而最终调用 main 函数,进去我们写得函数
2、如何选择启动文件
1)stm32不同系列官方会提供不同的固件库包
2)根据提供的启动文件按照芯片类型选择,如野火霸天虎V2,使用stm32f407系列芯片,选用后缀相同的文件即可,有些芯片同系列内部falsh大小不同,还需要根据内部flash大小选择,如stm32f1系列
3、启动文件分析
启动文件由汇编语言编写,在汇编语言中注释用** ;**,其中的指令均为ARM指令,常见的ARM指令如下
;******************** (C) COPYRIGHT 2016 STMicroelectronics ********************
;* File Name : startup_stm32f40_41xxx.s
;* Author : MCD Application Team
;* @version : V1.8.0
;* @date : 09-November-2016
;* Description : STM32F40xxx/41xxx devices vector table for MDK-ARM toolchain.
;* This module performs:
;* - Set the initial SP
;* - Set the initial PC == Reset_Handler
;* - Set the vector table entries with the exceptions ISR address
;* - Configure the system clock and the external SRAM mounted on
;* STM324xG-EVAL board to be used as data memory (optional,
;* to be enabled by user)
;* - Branches to __main in the C library (which eventually
;* calls main()).
;* After Reset the CortexM4 processor is in Thread mode,
;* priority is Privileged, and the Stack is set to Main.
;* <<< Use Configuration Wizard in Context Menu >>>
;*******************************************************************************
;
; Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
; You may not use this file except in compliance with the License.
; You may obtain a copy of the License at:
;
; http://www.st.com/software_license_agreement_liberty_v2
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
;*******************************************************************************
; Amount of memory (in bytes) allocated for Stack
; Tailor this value to your application needs
; <h> Stack Configuration
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
;在文件开头定义了栈的大小0x00000400 0x400 = 0100 0000 0000 = 2^10 = 1K 栈大小为 1KB
Stack_Size EQU 0x00000400
;告诉汇编器汇编一个新的代码段或者数据段。STACK 表示段名,这个可以任意命名;NOINIT 表示不初始化;READWRITE 表示可读可写,ALIGN=3,表示按照 ;2^3 对齐,即 8 字节对齐。
AREA STACK, NOINIT, READWRITE, ALIGN=3
;分配栈空间,单位为字节,大小为Stack_Size= 0x00000400
Stack_Mem SPACE Stack_Size
;标号 __initial_sp 紧挨着 SPACE 语句放置,表示栈的结束地址,即栈顶地址,栈是由高向低生长的
__initial_sp
;栈的作用是用于局部变量,函数调用,函数形参等的开销,栈的大小不能超过内部 SRAM 的大小(栈空间在SRAM内),如果编写的程序比较大,定义的局部变量很多,那么就需要修改栈的大小。
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
;定义堆的大小
Heap_Size EQU 0x00000200 = 0010 0000 0000 = 2^9 = 512B
;开始一个新的代码段,不初始化,可读可写,以2的3次方即8字节对齐
AREA HEAP, NOINIT, READWRITE, ALIGN=3
;堆的起始地址
__heap_base
;分配堆空间
Heap_Mem SPACE Heap_Size
;堆的结束地址,这里和栈不同,因为栈是逆向生长所以只制定了大小和起始地址,即使栈使用的过多也会在最小地址处,不会越限而栈是正向生长所以指定了大 ;小,结束地址和起始地址防止空间越限
__heap_limit
;指定当前文件的堆栈按照 8 字节对齐
PRESERVE8
;表示后面指令兼容 THUMB 指令
THUMB
; Vector Table Mapped to Address 0 at Reset
;开启一个新的代码段RESET,数据段,只读,并声明了三个全局变量__Vectors __Vectors_End __Vectors_Size
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
;上面声明的全局变量__Vectors,即向量表,将各个中断服务函数的地址写入向量表
;;__Vectors向量表开始地址,DCD为分配空间指令分配四字节大小,DCD __initial_sp 表示分配四字节地址并初始化,内容为__initial_sp,栈顶地址
__Vectors 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 detection
DCD TAMP_STAMP_IRQHandler ; Tamper and TimeStamps through the EXTI line
DCD RTC_WKUP_IRQHandler ; RTC Wakeup through the EXTI line
DCD FLASH_IRQHandler ; FLASH
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line0
DCD EXTI1_IRQHandler ; EXTI Line1
DCD EXTI2_IRQHandler ; EXTI Line2
DCD EXTI3_IRQHandler ; EXTI Line3
DCD EXTI4_IRQHandler ; EXTI Line4
DCD DMA1_Stream0_IRQHandler ; DMA1 Stream 0
DCD DMA1_Stream1_IRQHandler ; DMA1 Stream 1
DCD DMA1_Stream2_IRQHandler ; DMA1 Stream 2
DCD DMA1_Stream3_IRQHandler ; DMA1 Stream 3
DCD DMA1_Stream4_IRQHandler ; DMA1 Stream 4
DCD DMA1_Stream5_IRQHandler ; DMA1 Stream 5
DCD DMA1_Stream6_IRQHandler ; DMA1 Stream 6
DCD ADC_IRQHandler ; ADC1, ADC2 and ADC3s
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 ; External Line[9:5]s
DCD TIM1_BRK_TIM9_IRQHandler ; TIM1 Break and TIM9
DCD TIM1_UP_TIM10_IRQHandler ; TIM1 Update and TIM10
DCD TIM1_TRG_COM_TIM11_IRQHandler ; TIM1 Trigger and Commutation and TIM11
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 ; I2C2 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 ; External Line[15:10]s
DCD RTC_Alarm_IRQHandler ; RTC Alarm (A and B) through EXTI Line
DCD OTG_FS_WKUP_IRQHandler ; USB OTG FS Wakeup through EXTI line
DCD TIM8_BRK_TIM12_IRQHandler ; TIM8 Break and TIM12
DCD TIM8_UP_TIM13_IRQHandler ; TIM8 Update and TIM13
DCD TIM8_TRG_COM_TIM14_IRQHandler ; TIM8 Trigger and Commutation and TIM14
DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare
DCD DMA1_Stream7_IRQHandler ; DMA1 Stream7
DCD FSMC_IRQHandler ; FSMC
DCD SDIO_IRQHandler ; SDIO
DCD TIM5_IRQHandler ; TIM5
DCD SPI3_IRQHandler ; SPI3
DCD UART4_IRQHandler ; UART4
DCD UART5_IRQHandler ; UART5
DCD TIM6_DAC_IRQHandler ; TIM6 and DAC1&2 underrun errors
DCD TIM7_IRQHandler ; TIM7
DCD DMA2_Stream0_IRQHandler ; DMA2 Stream 0
DCD DMA2_Stream1_IRQHandler ; DMA2 Stream 1
DCD DMA2_Stream2_IRQHandler ; DMA2 Stream 2
DCD DMA2_Stream3_IRQHandler ; DMA2 Stream 3
DCD DMA2_Stream4_IRQHandler ; DMA2 Stream 4
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
DCD DMA2_Stream5_IRQHandler ; DMA2 Stream 5
DCD DMA2_Stream6_IRQHandler ; DMA2 Stream 6
DCD DMA2_Stream7_IRQHandler ; DMA2 Stream 7
DCD USART6_IRQHandler ; USART6
DCD I2C3_EV_IRQHandler ; I2C3 event
DCD I2C3_ER_IRQHandler ; I2C3 error
DCD OTG_HS_EP1_OUT_IRQHandler ; USB OTG HS End Point 1 Out
DCD OTG_HS_EP1_IN_IRQHandler ; USB OTG HS End Point 1 In
DCD OTG_HS_WKUP_IRQHandler ; USB OTG HS Wakeup through EXTI
DCD OTG_HS_IRQHandler ; USB OTG HS
DCD DCMI_IRQHandler ; DCMI
DCD CRYP_IRQHandler ; CRYP crypto
DCD HASH_RNG_IRQHandler ; Hash and Rng
DCD FPU_IRQHandler ; FPU
;向量表结束地址
__Vectors_End
;计算栈大小,综上来看向量表就是一个数组,元素大小为4字节,数组下标即为异常编号,元素内容为中断服务函数名,即中断服务函数入口
__Vectors_Size EQU __Vectors_End - __Vectors
;定义一个名称为.text 的代码段,可读。
AREA |.text|, CODE, READONLY
; Reset handler
;PROC(定义一个子函数)复位函数
Reset_Handler PROC
;声明全局变量Reset_Handler,并且是弱引用,即若是用户在其他地方实现该函数则使用用户定义的 Reset_Handler 函数
EXPORT Reset_Handler [WEAK]
;引用外部函数 SystemInit,__main
;SystemInit() 是一个标准的库函数,在 system_stm32f4xx.c 这个库文件总定义。主要作用是配置系统时钟.
IMPORT SystemInit
;__main 是一个标准的 C 库函数,主要作用是初始化用户堆栈,最终调用 main 函数.
IMPORT __main
LDR R0, =SystemInit
BLX R0
;若修改为 LDR R0, =user_main,则主函数为user_main而不是main
LDR R0, =__main
BX R0
;定义子程序
ENDP
; Dummy Exception Handlers (infinite loops which can be modified)
;定义子函数NMI_Handler(不可屏蔽中断)
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
Default_Handler PROC
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMP_STAMP_IRQHandler [WEAK]
EXPORT RTC_WKUP_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_Stream0_IRQHandler [WEAK]
EXPORT DMA1_Stream1_IRQHandler [WEAK]
EXPORT DMA1_Stream2_IRQHandler [WEAK]
EXPORT DMA1_Stream3_IRQHandler [WEAK]
EXPORT DMA1_Stream4_IRQHandler [WEAK]
EXPORT DMA1_Stream5_IRQHandler [WEAK]
EXPORT DMA1_Stream6_IRQHandler [WEAK]
EXPORT ADC_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_TIM9_IRQHandler [WEAK]
EXPORT TIM1_UP_TIM10_IRQHandler [WEAK]
EXPORT TIM1_TRG_COM_TIM11_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 RTC_Alarm_IRQHandler [WEAK]
EXPORT OTG_FS_WKUP_IRQHandler [WEAK]
EXPORT TIM8_BRK_TIM12_IRQHandler [WEAK]
EXPORT TIM8_UP_TIM13_IRQHandler [WEAK]
EXPORT TIM8_TRG_COM_TIM14_IRQHandler [WEAK]
EXPORT TIM8_CC_IRQHandler [WEAK]
EXPORT DMA1_Stream7_IRQHandler [WEAK]
EXPORT FSMC_IRQHandler [WEAK]
EXPORT SDIO_IRQHandler [WEAK]
EXPORT TIM5_IRQHandler [WEAK]
EXPORT SPI3_IRQHandler [WEAK]
EXPORT UART4_IRQHandler [WEAK]
EXPORT UART5_IRQHandler [WEAK]
EXPORT TIM6_DAC_IRQHandler [WEAK]
EXPORT TIM7_IRQHandler [WEAK]
EXPORT DMA2_Stream0_IRQHandler [WEAK]
EXPORT DMA2_Stream1_IRQHandler [WEAK]
EXPORT DMA2_Stream2_IRQHandler [WEAK]
EXPORT DMA2_Stream3_IRQHandler [WEAK]
EXPORT DMA2_Stream4_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]
EXPORT DMA2_Stream5_IRQHandler [WEAK]
EXPORT DMA2_Stream6_IRQHandler [WEAK]
EXPORT DMA2_Stream7_IRQHandler [WEAK]
EXPORT USART6_IRQHandler [WEAK]
EXPORT I2C3_EV_IRQHandler [WEAK]
EXPORT I2C3_ER_IRQHandler [WEAK]
EXPORT OTG_HS_EP1_OUT_IRQHandler [WEAK]
EXPORT OTG_HS_EP1_IN_IRQHandler [WEAK]
EXPORT OTG_HS_WKUP_IRQHandler [WEAK]
EXPORT OTG_HS_IRQHandler [WEAK]
EXPORT DCMI_IRQHandler [WEAK]
EXPORT CRYP_IRQHandler [WEAK]
EXPORT HASH_RNG_IRQHandler [WEAK]
EXPORT FPU_IRQHandler [WEAK]
WWDG_IRQHandler
PVD_IRQHandler
TAMP_STAMP_IRQHandler
RTC_WKUP_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA1_Stream0_IRQHandler
DMA1_Stream1_IRQHandler
DMA1_Stream2_IRQHandler
DMA1_Stream3_IRQHandler
DMA1_Stream4_IRQHandler
DMA1_Stream5_IRQHandler
DMA1_Stream6_IRQHandler
ADC_IRQHandler
CAN1_TX_IRQHandler
CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_TIM9_IRQHandler
TIM1_UP_TIM10_IRQHandler
TIM1_TRG_COM_TIM11_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
RTC_Alarm_IRQHandler
OTG_FS_WKUP_IRQHandler
TIM8_BRK_TIM12_IRQHandler
TIM8_UP_TIM13_IRQHandler
TIM8_TRG_COM_TIM14_IRQHandler
TIM8_CC_IRQHandler
DMA1_Stream7_IRQHandler
FSMC_IRQHandler
SDIO_IRQHandler
TIM5_IRQHandler
SPI3_IRQHandler
UART4_IRQHandler
UART5_IRQHandler
TIM6_DAC_IRQHandler
TIM7_IRQHandler
DMA2_Stream0_IRQHandler
DMA2_Stream1_IRQHandler
DMA2_Stream2_IRQHandler
DMA2_Stream3_IRQHandler
DMA2_Stream4_IRQHandler
ETH_IRQHandler
ETH_WKUP_IRQHandler
CAN2_TX_IRQHandler
CAN2_RX0_IRQHandler
CAN2_RX1_IRQHandler
CAN2_SCE_IRQHandler
OTG_FS_IRQHandler
DMA2_Stream5_IRQHandler
DMA2_Stream6_IRQHandler
DMA2_Stream7_IRQHandler
USART6_IRQHandler
I2C3_EV_IRQHandler
I2C3_ER_IRQHandler
OTG_HS_EP1_OUT_IRQHandler
OTG_HS_EP1_IN_IRQHandler
OTG_HS_WKUP_IRQHandler
OTG_HS_IRQHandler
DCMI_IRQHandler
CRYP_IRQHandler
HASH_RNG_IRQHandler
FPU_IRQHandler
B . ;B:跳转到一个标号。这里跳转到一个‘.’,即表示无线循环。,所以若开启了中断又没有自己实现中断服务函数,中断就会跳 ;转到启动文件中定义的中断服务函数,陷入死循环,导致程序卡死
ENDP
;编译器指令,使地址对齐,后面不带参数默认为四字节对齐
ALIGN
;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
;; 用户栈和堆初始化, 由 C 库函数_main 来完成
IF :DEF:__MICROLIB DEF ;DEF:__MICROLIB 这个宏在 KEIL 里面开启,在配置里勾选使用微库
;若使用微库,则声明一下变量变量为全局变量,__initial_sp __heap_base __heap_limit在前面堆栈定义时声明过,不过并不 :是全局的
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
;若没有使用微库,则引用外部函数__use_two_region_memory(自己实现),并声明全局变量__user_initial_stackheap
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
__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
;************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE*****
5、配置时钟
1、相关名词
1)RCC:reset clock control 复位和时钟控制器
2)SYSCLK:系统时钟
3)HSE: 高速外部时钟信号,由有源或者无源晶振提供信号(野火霸天虎开发板为25M)
4)HSI:高速的内部时钟时钟信号 ,作为备选时钟,在外部晶振故障时提供时钟信号
5) PLL:锁相环,主要作用是对时钟进行倍频,然后把时钟输出到各个功能部件,PLL 有两个,一个是主PLL,另外一个是专用的 PLLI2S,他们均由 HSE 或者 HSI 提供时钟输入信号。主 PLL 有两路的时钟输出,第一个输出时钟 PLLCLK 用于系统时钟,二个输出用于 USB OTG FS 的时钟(48M)、RNG 和 SDIO 时钟(<=48M)。专用的 PLLI2S 用于生成精确时钟,给 I2S 提供时钟。HSE故障时PLL也会关闭,并使用HSI直到HSE恢复。SYSCLK=HSI=16M,如果没有开启 CSS 和 CSS 中断的话,那么整个系统就只能在低速率运行,这是系统跟瘫痪没什么两样。如果开启了 CSS 功能的话,那么可以当 HSE 故障时,在 CSS 中断里面采取补救措施,使用 HSI,重新设置系统频率为 168M,让系统恢复正常使用。
6)分频因子和倍频因子:将输入的时钟信号按因子成倍放大或成倍减小(指时钟频率:如分频因子25,输入时钟频率25M,分频后时钟信号频率就变为1M)后输出。
7)HCLK:AHB总线时钟
8)PCLK1:APB1总线时钟,属于低速总线(最大42M)
9)PCLK2:APB2总线时钟,属于高速总线
2、时钟树
3、系统时钟APB、AHB总线时钟分析
4、时钟与复位寄存器RCC
RCC寄存器较多,使用时参考 参考手册
5、其他时钟
- RTC 时钟
- 独立看门狗时钟
- I2S 时钟
- PHY 以太网时钟:F407 要想实现以太网功能,除了有本身内置的 MAC 之外,还需要外接一个 PHY 芯片
- USB PHY 时钟:用做USB高速传输
- MCO 时钟输出:相当于一个有源晶振,可以为外部提供时钟信号
6、系统如何设置的时钟
当程序来到 main 函数之前,启动文件:startup_stm32f40xxx.s 已经调用 SystemInit() 函数,在SystemInit() 函数中调用d SetSysClock()函数,通过对相关rcc寄存器的操作,设置M、P、Q、N等因子,把系统时钟初始化成 168MHZ,HCLK=SYSCLK=168M,PCLK2 = HCLK /2= 84M,PCLK1= HCLK/4 = 42M。SystemInit() 在库文件:system_stm32f4xx.c 中定义。