STM32【H7】理论——综述、HAL库简述

文章目录

  • 1. STM32H7芯片简介
    • 1.1 STM32H7与STM32F1、F4系列芯片的区别
    • 1.2 硬件框图
    • 1.3 STM32H7各型号对比
    • 1.4 总线框图和时钟
    • 1.5 AXI总线
      • 1.5.1 AXI总线简介
      • 1.5.2 AXI总线优先级编程
    • 1.6 总线互联
    • 1.7 FLASH
    • 1.8 RAM
    • 1.9 电源系统
      • 1.9.1 系统上电启动
      • 1.9.2 低功耗模式
      • 1.9.3 电源去耦电容的选择
    • 1.10 复位系统
      • 1.10.1 硬件复位
      • 1.10.2 软件复位
    • 1.11 RCC 时钟系统
      • 1.11.1 STM32H7 RCC时钟简介
      • 1.11.2 HSE 和 LSE 硬件设计
  • 2. STM32H7的HAL库框架简介
    • 2.2 HAL 库的头文件`stm32h7xx_hal_conf.h`配置
    • 2.3 HAL 库的硬件初始化
    • 2.4 HAL 库的 DMA 处理思路
    • 2.5 HAL 库 API
      • 2.5.1 源文件 `stm32h7xx_hall.c`
        • 2.5.1.1 函数 `HAL_Init`
        • 2.5.1.2 函数 `HAL_DeInit`
        • 2.5.1.3 函数 `HAL_InitTick`
        • 2.5.1.4 Systick 的相关函数
        • 2.5.1.5 函数 `HAL_SYSCFG_VREFBUF_VoltageScalingConfig`
        • 2.5.1.6 STM32H7 自带电压基准相关函数
        • 2.5.1.7 函数 `HAL_SYSCFG_AnalogSwitchConfig`
        • 2.5.1.8 BOOST 的使能和禁止(用于 ADC)
        • 2.5.1.9 函数 `HAL_SYSCFG_CM7BootAddConfig`
        • 2.5.1.10 IO 补偿相关函数
      • 2.5.2 源文件 `stm32h7xx_hal_rcc.c`
        • 2.5.2.1 HCLK、PCLK时钟树
  • 3. STM32H7 启动过程详解
    • 3.1 不同 H7 系列芯片对应的启动文件
    • 3.2 启动文件分析
      • 3.2.1 复位序列
      • 3.2.2 BOOT 启动模式
  • 4. 其他

使用芯片:STM32H743XIH6 .
开发板:安富莱V7

1. STM32H7芯片简介


1.1 STM32H7与STM32F1、F4系列芯片的区别

  1. 相对于 F1、F4 系列,H7 最大的区别是 MPU 和 Cache 的配置,而外设的学习与应用大同小异。

其中,STM32H7 多出的一个 L1 Cache 一级缓存,在为低速存储器带来加速的同时,也为程序设计带来了一些问题,其中最为主要的是数据一致性问题。

  1. H7 携带了 DTCM 和 ITCM,ITCM 用于运行指令,即程序代码,DTCM 用于数据存取,特点是跟内核速度一样,而片上的其它 RAM 主频都是 200MHz .
  2. STM32H7 系列只有 HAL 库,没有再配套标准库。HAL 库的优点是配置方便,特别是配合图形开发工具 STM32CubeMX 时,缺点是源代码稍显臃肿,封装的有点多。而对于 F1,F4 系列,标准库和 HAL 库都有。
  3. STM32H7 的自带外设比较之前的任何 STM32 型号都要强劲,更换了 ADC,DMA,USART 等重要外设。如 ADC 换成了 3.6Msps 16 位分辨率,DMA 支持任意互联,USART 也支持波特率自适应。
  4. STM32F1 是 M3 内核,STM32F4 是 M4 内核,而 STM32H7 是 M7 内核,性能要比前两者要好一些。

1.2 硬件框图

下图的硬件框图可在芯片的官网中查看,若还需要更深入的了解,就去读官网介绍页面上的“Key Features”内容,再详细可翻查其数据手册;

1.3 STM32H7各型号对比

ST 有一个专门的文件,叫 STM32H7x3 MCUs High-performance line,它可在官网介绍页面中找到,对各个型号有作简单的对比,如引脚数、封装、FLASH 大小、RAM 大小以及是否带 HW CRYPTO硬件加密的区别;
STM32【H7】理论——综述、HAL库简述_第1张图片

1.4 总线框图和时钟

在芯片的数据手册中(搜索* Figure 1*)提供有芯片的总线框图,可方便查看每个总线时钟速度和该总线所挂的外设;

从上面的框图可知:
SYSCLK(Hz) = 400000000 (CPU Clock)
HCLK(Hz) = 200000000 (AXI and AHBs Clock)
AHB Prescaler = 2
D1 APB3 Prescaler = 2 (APB3 Clock 100MHz)
D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)
D2 APB2 Prescaler = 2 (APB2 Clock 100MHz)
D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)
因为 APB1 prescaler != 1, 所以 APB1 上的 TIMxCLK = APB1 x 2 = 200MHz;
因为 APB2 prescaler != 1, 所以 APB2 上的 TIMxCLK = APB2 x 2 = 200MHz;
APB4 上面的 TIMxCLK 没有分频,所以就是 100MHz;
APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1
APB2 定时器有 TIM1, TIM8 , TIM15, TIM16,TIM17
APB4 定时器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5

1.5 AXI总线

该总线主要用于高并发,AXI支持高频率、高性能的系统设计:

  1. 支持高带宽、低延迟设计;
  2. 提供高频操作,无需复杂的总线桥;
  3. 满足各种组件的接口需求;
  4. 适用于具有高初始访问延迟的内存控制器;
  5. 为互连架构的实现提供灵活性;
  6. 与现有的AHB、APB接口向后兼容;

除此之外AXI总线还有以下关键特性:

  1. 独立的地址、控制和数据线;
  2. 支持非字节对齐方式传输;
  3. 基于起始地址对其方式传输;
  4. 分开的读、写数据通道,且提供DMA传输;
  5. 支持发起多个地址;
  6. 支持无序传输;
  7. 允许添加寄存器,以提供时许收敛;

AXI总线框架如下
STM32【H7】理论——综述、HAL库简述_第2张图片
该图可方便识别总线所外挂的外设,共分为三个域:D1 DomainD2 DomainD3 Domain

  • D1 Domain:D1 域中的各个外设是挂在 64 位 AXI 总线组成 6*7 的矩阵上;

    • 6 个从接口端 ASIB1 到 ASIB6:外接的主控是 LTDC,DMA2D,MDMA,SDMMC1,AXIM 和 D2-to-D1 AHB 总线;
    • 7 个主接口端 AMIB1 到 AMIB7:外接的从设备是 AHB3 总线,Flash A,Flash B,FMC 总线,QSPI 和 AXI SRAM;另外 AHB3也是由 AXI 总线分支出来的,然后再由 AHB3 分支出 APB3 总线。
  • D2 Domain:D2 域的各个外设是挂在 32 位 AHB 总线组成 10*9 的矩阵上;

    • 10 个从接口:外接的主控是 D1-to-D2 AHB 总线,AHBP 总线,DMA1,DMA2,Ethernet MAC,SDMMC2,
      USB HS1 和 USB HS2;
    • 9 个主接口:外接的从设备是 SRAM1,SRMA2,SRAM3,AHB1,AHB2,APB1,APB2,D2-to-D1 AHB总线和 D2-to-D3 AHB 总线;
  • D3 Domain:D3 域的各个外设是挂在 32 位 AHB 总线组成 3*2 的矩阵上;

    • 3 个从接口:外接的主控 D1-to-D3 AHB 总线,D2-to-D3 AHB 总线和 BDMA;
    • 2 个主接口:外接的从设备是 AHB4,SRAM4 和 Bckp SRAM。另外 AHB4 也是这个总线矩阵分支出来的,然后再由 AHB4 分支出 APB4 总线;
  • 这三个域的互联

    • D1 域到 D2 域的 D1-to-D2 AHB bus:允许 D1 域中的主接口外设访问 D2 域里面的从接口外设。比如 D1 域里面的 DMA2D 访问 D2 域里面的 SRAM1;
    • D2 域到 D1 域的 D2-to-D1 AHB bus:允许 D2 域中的主接口外设访问 D1 域里面的从接口外设。比如 D2 域里面的 DMA2 访问 D1 域里面的 AXI SRAM;
    • D1 域到 D3 域的 D1-to-D3 AHB bus:允许 D1 域中的主接口外设访问 D3 域里面的从接口外设。比如 D1 域里面的 MDMA 访问 D3 域里面的 SRAM4;
    • D2 域到 D3 域的 D2-to-D3 AHB bus:允许 D2 域中的主接口外设访问 D3 域里面的从接口外设。比如 D2 域里面的 DMA2 访问 D3 域里面的 SRAM4;

1.5.1 AXI总线简介

关于AXI总线,ARM有详细的介绍文档《AXI4_specification》;
AXI总线的工作框图如下
STM32【H7】理论——综述、HAL库简述_第3张图片
通过上图可知,AXI 总线有 6 个从接口 ASIBs(AMBA slave interface blocks)和 7个主控接口 AMIBs(AMBA master interface blocks)

  • ASIBs:重点介绍最后一列读/写发起能力,如 IN5 连接的主控 DMA2D,支持的读发起能力是 2,写发起能力是 1,即存在两路读信号同时进行(因为 AXI 接口有一个 FIFO 的功能,可供同时进行),反映到 DMA2D 的实际应用中,就是 DMA2D 同时读取前景色和背景色的缓存区做 Alpha 融合之类的操作。写操作同理,DMA2D 的写发起能力仅支持一路。
    STM32【H7】理论——综述、HAL库简述_第4张图片
  • AMIBs:重点介绍最后一列读/写发起能力/总接收能力,这里多了一个总接收能力(Total acceptance),
    也就是读发起能力和写发起能力同时执行的情况。
    STM32【H7】理论——综述、HAL库简述_第5张图片

1.5.2 AXI总线优先级编程

由于存在多个 ASIB 从接口访问 AMIB 主控的问题,这就涉及到谁先谁后等问题。所以 AXI 总线矩阵就做了一个基于优先级的仲裁方案,每个 ASIB 接口支持读通道和写通道分别设置,优先级从 0 到 15。数值越大,优先级越高,默认情况都是优先级 0。如果有两个传输同时到达 AMIB 主控接口,那么优先级高的 ASIB 接口传输优先处理;如果优先级相同的话,根据 LUR 方案选择(least recently-used 最近最少使用情况)。

1.6 总线互联

STM32H7 的总线矩阵四通八达,但不是任意 Bus Master 总线主控端和 Bus Slave 设备端都可以相互通信的。

  • 黑色加粗字体是 64 位总线(ITCM,DTCM,Flash A,Flash,AXI SRAM,FMC 等),普通字体是32 位总线。
  • 访问通路(每个小方块里面的字符)
    任何有数字的表示有访问通路。
    短横杠“-”表示不可访问。
    有灰色阴影的表示有实用价值的访问通路。
  • 表格中具体数值所代表的含义
    D=direct
    1=via AXI bus matrix
    2=via AHB bus matrix in D2
    3=via AHB bus matrix in D3
    4=via AHB/APB bridge in D1
    5=via AHB/APB bridge in D2
    6=via AHB/APB bridge in D3
    7=via AHBS bus of Cortex-M7
    多个数值组合 = 互连路径以数字的顺序经过多个矩阵或/和桥。
  • 总线访问类型
    普通字体表示 32 位总线。
    斜体表示 32 位总线主机端/ 64 位总线从机端。
    粗体表示 64 位总线。
    当前要对这个图有个了解,后面章节讲解各个外设的时候要用到,比如DTCM和ITCM不支持DMA1,
    DMA2 和 BDMA,仅支持 MDMA。
    STM32【H7】理论——综述、HAL库简述_第6张图片

1.7 FLASH

  • 双 BANK,每个 BANK 的带宽都是 64bits,如下图所示:
    STM32【H7】理论——综述、HAL库简述_第7张图片

  • H7 中 Flash 的延迟和主频关系:H7 中已经没有 F1 和 F4 系列中的 ART Chrome 加速,通过 H7 中的 Cache加速即可。具体延迟数值和主频关系如下:
    STM32【H7】理论——综述、HAL库简述_第8张图片
    从上图可知,当延迟等待设置为 0 的时候,即无等待,单周期访问,速度可以做到70MHz。增加 1 个 Flash 周期后,访问速度可以做到 140MHz。当增加到 3 个或 4 个 Flash 周期后,最高速度可以做到 225MHz。

  • Flash 编程操作(写)最好以 256bits 为单位进行,应用中也可以小于 256bits,但是容易造成 ECC校验出问题,所以不推荐。Flash 读操作支持 64bits,32bits,16bits 和 8bits。

  • Flash 支持 ECC 校验,每 256bits 配 10bit 的 ECC 位,可以检测到 1 个 bit 并纠正或者检测 2 个 bit。随着芯片的制造工艺水平越高,带电粒子能产生的位翻转就越多,此时的 ECC 是必须要有的,一般可以纠正 1-2 个 bit。安全等级高的 Flash 类存储器和 RAM 类都是必须要带 ECC 的。

ECC知识整理

1.8 RAM

1.9 电源系统

电源管理部分最繁琐的就是 CPU,D1,D2,D3 域的各种运行,待机,停机状态切换。

  • 常见电源表示简介:
引脚名(标识) 描述
VDD 用于 I/O 引脚和系统模拟部分供电,比如复位、电源管理、振荡器等。
VDDA 用于 ADC、DAC、运放、比较器和电压基准供电,这部分供电是独立的。
VREF+,VREF- 用于 ADC 和 DAC 的基准电压,当使能了 STM32H7 内部的电压基准, 将使用内部基准供 VREF+,VREF-。如果没有使能的话,通过外置电压基准提供。
VBAT 当 VDD 不供电的时候,由 VBAT 为备份域供电。
VDDLDO 电压稳压器供电。 VCAP 数字核心域供电。
VDD50USB USB 稳压器供电。
VDD33USB USB 稳压器输出。
VSS 所有电源和模拟稳压器的地端。

1.9.1 系统上电启动

STM32H7上电后的系统时序图如下:
STM32【H7】理论——综述、HAL库简述_第9张图片
主要看 Operating mode 部分,依次是 Power down –> Reset -> Wait Oscillator ->HW system init -> Run -> Wait ACTVOS RDY –> Run,即断电状态 -> 复位状态 -> 等待振荡器 HSI 就绪->硬件初始化 -> 运行 -> 等待 ACTVOS 位就绪 -> 正式运行。

  • 其详细些的执行流程如下:
  1. 当系统上电后,POR(Power on reset 上电复位)会检测 VDD 供电,当 VDD 大于 POR 设置的阀值时,将使能电压稳压器。
  2. 看 VCORE那条曲线,只要 VOSRDY 未就绪,就会一直处于复位状态。
  3. 一旦 VCORE 正常输出,系统将走出复位状态,内部高速 RC 振荡器 HSI 将使能。
  4. HSI 稳定后,将开始系统初始化,主要是 Flash 和可选字节的加载,这些都是由硬件完成的,CPU 也将以受限的方式运行(主要是指不允许对 RAM 进行写操作)。
  5. 软件程序初始化系统,包括供电配置。当供电配置完成后,等待 ACTVOSRDY 位置 1,完成置 1 后,CPU 就进入正常的运行的模式,允许读写 RAM 了。

1.9.2 低功耗模式

为了实现各种低功耗模式,CPU 和 D1,D2,D3 域支持的各种模式如下:

  • CPU 模式:
    • CRun:运行状态,CPU 和 CPU 子系统外设正常运行。
    • CSleep:休眠状态,CPU 时钟停止运行,CPU 子系统外设正常运行。
    • CStop:停止状态,CPU 和 CPU 子系统外设都停止运行。
  • D1 域模式:
    • DRun:运行状态,D1 域的总线矩阵正常运行,CPU 子系统运行在 CRun 或者 CSleep 模式。
    • DStop:停机状态,D1 域的总线矩阵时钟停止运行,CPU 子系统运行在 CStop 模式,PDDS_D1 位选择 DStop 模式。
    • DStandby:待机状态,D1 域的总线矩阵断电,CPU 子系统运行在 CStop 模式,PDDS_D1 位选择DStandby 模式。
  • D2 域模式:
    • DRun:运行状态,D2 域的总线矩阵正常运行,CPU 子系统在 D2 域中有分配的外设,CPU 子系统运行在 CRun 或者 CSleep 模式。
    • DStop:停机状态,D2 域的总线矩阵时钟停止运行,CPU 子系统没有在 D2 域分配外设,PDDS_D1位选择 DStop 模式。或者 CPU 子系统在 D2 域中有分配的外设,CPU 子系统运行在 CStop 模式,PDDS_D1 位选择 DStop 模式。
    • DStandby:待机状态,D2 域的总线矩阵断电,CPU 子系统没有在 D2 域分配外设,PDDS_D1 位选 择 DStandby 模式。或者 CPU 子系统在 D2 域中有分配的外设,CPU 子系统运行在 CStop 模式,PDDS_D1 位选择 DStandby 模式。
  • 系统/D3 域模式:
    • Run:运行状态,系统时钟和 D3 域总线矩阵时钟处于运行状态。CPU 子系统处于 CRun 和 CSleep模式,或者一个唤醒信号处于激活状态。
    • Stop:停止状态,系统时钟和 D3 域总线矩阵时钟处于停止状态,CPU 子系统处于 CStop 模式。所有的唤醒信号都处于非激活状态,并且至少某个域的一个 PDDS_Dn 位选择了 Stop 模式。
    • Standby:待机状态,系统处于断电状态,CPU 子系统处于 CStop 模式,所有的唤醒信号都处于非激活状态,并且所有域的所有 PDDS_Dn 位选择 Standby 模式。

1.9.3 电源去耦电容的选择

如下图,每个电源引脚 (VDD/VSS, VDDA/VSSA …)必须使用下述的滤波陶瓷电容去耦。这些电容必须尽量靠近芯片引脚,以确保器件正常工作。
STM32【H7】理论——综述、HAL库简述_第10张图片

1.10 复位系统

1.10.1 硬件复位

当系统由可靠的电源供电时,一旦通电,电源迅速地达到额定输出电压,一旦断电,电源迅速地下降到 0V,并且在接通的时候,电压不会降低。这时能够可靠地使用基于一个电容和一个电阻的低成本硬件复位。这种形式的复位电路称为阻容复位。

注:如果电源不够可靠,考虑到安全性,这种简单的阻容解决方案就不合适了。

  • 上电复位和手动复位系统
    STM32【H7】理论——综述、HAL库简述_第11张图片
  1. STM32 这款 CPU 的复位引脚是低电平有效,即 NRST 为低电平时,CPU 处于复位状态。
  2. 当系统上电瞬间,C114 电容两端电压可以认为是 0,CPU 处于复位状态。3.3V 电源通过 R173 给 C114 充电,当 C114 的电压升到 CPU 的高电平门槛电压时,CPU 退出复位状态转入运行状态。
  3. 在设计电路时,需要选择适当的 R 值和 C 值,以保证 NRST 低电平持续时间满足 CPU 复位最小脉宽的要求。
  4. 当按下 S4 轻触开关时,C114 两端被短路接地,可实现手动复位 CPU。

1.10.2 软件复位

除了上电和手动复位,程序设计设置中还经常要用到软件复位,即调用一条函数就可以实现复位功能。此函数已经由 CMSIS 软件包中的 core_cm7.h 文件提供,函数如下:

/**
 \brief System Reset
 \details Initiates a system reset request to reset the MCU.
*/
__STATIC_INLINE void __NVIC_SystemReset(void)
{
	 __DSB(); /* Ensure all outstanding memory accesses included
	 buffered write are completed before reset */
	 SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
	 (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
	 SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */
	 __DSB(); /* Ensure completion of memory access */
	 for(;;) /* wait until reset */
	 {
		 __NOP();
	 } 
 }

软件复位反映到实际硬件上,就是给硬件复位部分发一个复位信号:
STM32【H7】理论——综述、HAL库简述_第12张图片

1.11 RCC 时钟系统

1.11.1 STM32H7 RCC时钟简介

STM32H7 有六种时钟可供使用:

  1. HSI (High-speed internal oscillator) :HSI 是内部的高速 RC 振荡器,频率 64MHz,可被用于系统时钟。优势是低成本,无需外部时钟,快速启动(仅需几个微秒),缺点是精度差,即使经过校准。
  2. HSE (High-speed external oscillator):HSE 是外部的高速振荡器,通过外接时钟源,有源或者无源晶振驱动,时钟范围 4-48MHz。优势是精度高,缺点是增加成本。
  3. LSE (Low-speed external oscillator) :LSE 是外部的低速振荡器,通过外接时钟源,有源或者无源晶振驱动,一般接 32.768KHz,主要用于RTC 实时时钟。
  4. LSI (Low-speed internal oscillator) :LSI 是内部的低速 RC 振荡器,频率约是 32KHz,主要用于独立看门狗和自动唤醒,也可以用于 RTC实时时钟。
  5. CSI (Low-power internal oscillator) :CSI 是内部的低速振荡器,频率约是 4MHz,相比 64MHz 的 HSI,主要用于低功耗。
  6. HSI48 (High-speed 48 MHz internal oscillator) :HSI48 是内部高速振荡器,频率约是 48MHz,用于给特定的外设提供时钟,比如 USB。
  • 时钟树:
    STM32【H7】理论——综述、HAL库简述_第13张图片

1.11.2 HSE 和 LSE 硬件设计

  1. HSE 时钟:下面硬件电路用的 25MHz 晶振为 HSE 提供时钟:
    STM32【H7】理论——综述、HAL库简述_第14张图片

注:

  1. 晶振和负载电容需要尽可能近地靠近 H7 的晶振引脚,以减小输出失真和启动稳定时间。负载电容值必须根据选定的晶振进行调节。
  2. 对于图中 C15 和 C17,推荐使用高质量陶瓷电容,这种电容是设计用于需要高频率的场合,并且可以满足晶体或谐振器的需求。C15 和 C17 通常具有相同的值。
  • HSE 旁路时钟和外置晶振区别:当STM32H7采用的外置晶振模式时,高速外部 (HSE) 时钟可以使用一个 4 到 48MHz 的晶振 / 陶瓷谐振振荡器产生:
    STM32【H7】理论——综述、HAL库简述_第15张图片
    而 bypass 旁路的意思即不使用它、绕过它。具体到 HSE 旁路的话,使用用户直接提供 4-50MHz 的时钟源,可以使用有源晶振或者 FPGA 提供时钟等方式:
    STM32【H7】理论——综述、HAL库简述_第16张图片

  1. LSE 时钟:下面电路用 32768Hz 晶振为 LSE 提供时钟,STM32 的 LSE 晶振起振难(又称 RTC 起振)是老毛病了,选取晶振和配套电容比较讲究,最好按照 ST提供的厂家和配套电容选取:http://www.armbbs.cn/forum.php?mod=viewthread&tid=87673 .
    STM32【H7】理论——综述、HAL库简述_第17张图片

2. STM32H7的HAL库框架简介


HAL库是ST公司随 STM32Cube 软件推出的ST官方应用库,一般在STM32Cube 软件中可直接使用,也可以移植。

HAL 库为各种外设基本都配了三套 API:查询,中断和 DMA .

2.2 HAL 库的头文件stm32h7xx_hal_conf.h配置

HAL 库有一个专门的配置文件叫 stm32h7xx_hal_conf.h用于配置一些核心参数。
在这里插入图片描述

  1. 晶振频率配置:参数HSE_VALUE用于配置MCU使用的晶振频率,板子使用的实际晶振频率一定要与这个数值一致。如 V7 开发板的外置晶振是 25MHz,那么就配置宏定义为:#define HSE_VALUE ((uint32_t)25000000)

  2. 滴答定时器优先级配置:在调用基于此时间基准的延迟函数HAL_Delay()时会选用时间基准是滴答定时器还是通用定时器,一般都使用滴答定时器,将滴答定时器优先级调到最高即可。#define TICK_INT_PRIORITY ((uint32_t)0x0F)

  3. 实时系统使用:参数USE_RTOS用于配置MCU是否使用实时系统,用则配置为1,否则为0 .

  4. 使能UHS-I 模式:参数USE_SD_TRANSCEIVER用于使能UHS-I 模式,不使能时注释掉即可。

  5. 使能断言功能:参数USE_FULL_ASSERT用于使能断言assert功能,不使用时注释掉即可。当使能断言功能后 ,若断言功能被触发(传入参数不正确)将调用函数void assert_failed(uint8_t* file, uint32_t line),但HAL库仅定义了该函数而将函数实现的内容留给了用户,下面给出一个参考的函数方案。

/********************************************************************************
* 函 数 名: assert_failed
* 形 参:file : 源代码文件名称。关键字__FILE__表示源代码文件名。
* line :代码行号。关键字 __LINE__ 表示源代码行号
* 返 回 值: 无
********************************************************************************/
void assert_failed(uint8_t* file, uint32_t line)
{ 
/* 用户可以添加自己的代码报告源代码文件名和代码行号,比如将错误文件和行号打印到串口printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

/* 这是一个死循环,断言失败时程序会在此处死机,以便于用户查错 */
while (1){} 
}

ST 库函数使用了 C 编译器的断言功能,如果定义了 USE_FULL_ASSERT,那么所有的 ST 库函数将检查函数形参是否正确。如果不正确将调用 assert_failed() 函数,这个函数是一个死循环,便于用户检查代码,其中:

  • 关键字 __LINE__ 表示源代码行号。
  • 关键字__FILE__表示源代码文件名。

断言功能使能后将增大代码大小,推荐仅在调试时使能,在正式发布软件应禁止掉。

2.3 HAL 库的硬件初始化

下面简介硬件初始化函数bsp_Init() .

/********************************************************************************
*	函 数 名: bsp_Init
*	功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
*	形    参: 无
*	返 回 值: 无
********************************************************************************/
void bsp_Init(void)
{
    /* 配置MPU */
	MPU_Config();
	
	/* 使能L1 Cache */
	CPU_CACHE_Enable();

	/* 
       STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
	   - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
	   - 设置NVIV优先级分组为4。
	 */
	HAL_Init();

	/* 
       配置系统时钟到400MHz
       - 切换使用HSE。
       - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    */
	SystemClock_Config();

	/* 
	   Event Recorder:
	   - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
	   - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
	*/	
#if Enable_EventRecorder == 1  
	/* 初始化EventRecorder并开启 */
	EventRecorderInitialize(EventRecordAll, 1U);
	EventRecorderStart();
#endif

	bsp_InitKey();    	/* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
	bsp_InitTimer();  	/* 初始化滴答定时器 */
	
	bsp_InitUart();		/* 初始化串口 */
	bsp_InitExtIO();	/* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */	
	bsp_InitLed();    	/* 初始化LED */	
	bsp_InitExtSDRAM();	/* 初始化SDRAM */
	bsp_InitI2C();		/* 初始化I2C总线 */
	
	bsp_InitBMP085();
	bsp_InitBH1750();
	
	bsp_InitSPIBus();	/* 初始化SPI总线 */
	bsp_InitSFlash();	/* 识别串行flash W25Q64 */
	
	bsp_InitQSPI_W25Q256();	
	
	bsp_InitRTC();		/* 配置RTC */
	
	TOUCH_InitHard();
	LCD_InitHard();
}
  1. ,配置 MPU 和使能 Cache,对于 H7 而已,这两个函数要优先执行,因为 Flash 速度,SRAM 速度跟 CPU 和 TCM 有些差距的,所以要使能 Cache。而不同存储区的 Cache 策略要通过MPU 分别进行配置。
  2. 注意调用函数 HAL_Init 时,系统依然使用的 64MHz HSI 时钟。此函数会调用函数 HAL_InitTick,初始化滴答时钟中断为 1ms,并设置 NVIV 优先级分组为 4。同时它会开启滴答定时器中断,如果用户也要使用滴答定时器中断,建议对其终端服务函数配置如下:
/********************************************************************************
* 函 数 名: SysTick_Handler
* 功能说明: 系统嘀嗒定时器中断服务程序。启动文件中引用了该函数。
* 形 参: 无 * 返 回 值: 无
* 备 注:该函数定义在文件bsp_timer.C 中
********************************************************************************/
void SysTick_Handler(void)
{
	HAL_IncTick(); /* ST HAL 库的滴答定时中断服务程序 */
	if (g_ucEnableSystickISR == 0) /* 做了一个变量标志,调用了函数 bsp_InitTimer 才置位此变量 */
	{
		return;
	}
	SysTick_ISR(); /* 安富莱 bsp 库的滴答定时中断服务程序 */
}
  1. 通过函数SystemClock_Config()切换 HSI 到外部高速时钟 HSE,并配置系统时钟到 400MHz。调用这个函数会更新全局变量 SystemCoreClock,并重新配置函数 HAL_InitTick .

2.4 HAL 库的 DMA 处理思路

为了方便各种外设直接启动 DMA,HAL 库专门为支持 DMA 操作的外设都提供了对应的 DMA 函数,。比如串口的:

HAL_UART_Transmit_DMA()
HAL_UART_Receive_DMA()
HAL_UART_DMAPause()
HAL_UART_DMAResume()
HAL_UART_DMAStop()

特别注意一点,针对外设的 DMA 函数基本都有开启中断,如果用户使能此外设的 NVIC,使用
中务必别忘了写 DMA 的中断服务程序,比如使用 DMA1_Stream1的中断服务函数:

void DMA1_Stream1_IRQHandler(void)
{
	/* USER CODE BEGIN USART1_IRQn 0 */
	  此处可以添加用户代码
	/* USER CODE END USART1_IRQn 0 */
	 /* 参数是 DMA 句柄 */
	HAL_DMA_IRQHandler(&hdma_usart1_tx);
	/* USER CODE BEGIN USART1_IRQn 1 */
	  此处可以添加用户代码
	/* USER CODE END USART1_IRQn 1 */
}

如果要在 DMA 传输完成,半传输完成等中断里面执行功能,也是通过 HAL_DMA_IRQHandler 调用的各种回调函数里面实现,这些回调都是弱定义的,方便用户直接在其它文件里面重定义:

HAL_UART_TxHalfCpltCallback()
HAL_UART_TxCpltCallback()
HAL_UART_RxHalfCpltCallback()
HAL_UART_RxCpltCallback()

2.5 HAL 库 API

下面通过一个简单的初始化流程来了解 STM32H7 的工程模板所必备的库文件和 API:

  1. 第 1 步:系统上电复位,进入启动文件 startup_stm32h743xx.s,在这个文件里面执行复位中断服务程序。
    1. 在复位中断服务程序里面执行函数 SystemInit,此函数在文件 system_stm32h7xx.c 里面。
    2. 之后是调用编译器封装好的函数,比如用于 MDK 的启动文件是调用__main,最终进入到 main
      函数。
  2. 第 2 步:进入到 main 函数就可以开始用户应用程序编程了。在这个函数里面要做几个重要的初始化,依次是:
    1. MPU 初始化,需要用到库文件 stm32h7xx_hal_cortex.c 和 stm32h7xx_hal_cortex.h .
    2. Cache 初始化,需要用到 core_cm7.h 文件。
    3. HAL 库初始化函数 HAL_Init,需要用到文件 stm32h7xx_hal.c .
    4. 系统时钟初始化,需要用到库文件 stm32h7xx_hal_rcc.c .

前面的两步完成后,就可以开始做用户需要的按键、串口等方面的初始化和应用代码的实现了。

2.5.1 源文件 stm32h7xx_hall.c

基准电压大小配置,EXTI 配置,IO 补偿配置等都在这个文件里面设置。下面是一些注意事项:

  1. HAL 库中各个外设驱动里面的延迟实现是基于此文件提供的时间基准,而这个时间基准既可以使用滴答定时器实现也可以使用通用的定时器实现,默认情况下是用的滴答定时器。
  2. 函数 HAL_Init 里面会调用时间基准初始化函数 HAL_InitTick,而调用函数HAL_RCC_ClockConfig也会调用时间基准初始化函数 HAL_InitTick .
  3. 如果在中断服务程序里面调用延迟函数 HAL_Delay 要特别注意,因为这个函数的时间基准是基于滴答定时器或者其他通用定时器实现,实现方式是滴答定时器或者其他通用定时器里面做了个变量计数。如此一来,结果是显而易见的,如果其他中断服务程序调用了此函数,且中断优先级高于滴答定时器,会导致滴答定时器中断服务程序一直得不到执行,从而卡死在里面。所以滴答定时器的中断优先级一定要比它们高。

2.5.1.1 函数 HAL_Init

HAL_StatusTypeDef HAL_Init(void)
{
	/* 设置中断优先级分组 */
	 HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
	 /* 使用滴答定时器做为默认时基,配置为 1ms 滴答,另外系统上电后默认使用的 HIS 时钟 */
	 if(HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)
	 {
	 	return HAL_ERROR;
	 }
	 /* 初始化底层硬件 */
	 HAL_MspInit();
	 /* 返回函数状态 */
	 return HAL_OK;
}
  • 此函数用于初始化 HAL 库,此函数主要实现如下功能:

    1. 设置 NVIC 优先级分组是 4。
    2. 设置滴答定时器的每 1ms 中断一次。
    3. HAL 库不像之前的标准库,在系统启动函数 SystemInit 里面做了 RCC 初始化,HAL 库是没有做的,所以进入到 main 函数后,系统还在用内部高速时钟 HSI,对于 H7 来说,HSI 主频是 64MHz .
    4. 函数 HAL_Init 里面调用的 HAL_MspInit 一般在文件 stm32h7xx_hal_msp.c 里面做具体实现,主要用于底层初始化。当前此函数也在文件 stm32h7xx_hal.c 里面,只是做了弱定义。
  • 返回值:返回 HAL_ERROR 表示参数错误,HAL_OK 表示发送成功,HAL_BUSY 表示忙,正在使用中。

  • 注意事项

    1. 必须在 main 函数里面优先调用此函数。
    2. 用户务必保证每 1ms 进入一次滴答中断。

2.5.1.2 函数 HAL_DeInit

HAL_StatusTypeDef HAL_DeInit(void)
{
	/* 复位所有外设 */__set_PRIMASK
	 __HAL_RCC_AHB3_FORCE_RESET();
	 __HAL_RCC_AHB3_RELEASE_RESET();
	 /* 省略未写 */
	 __HAL_RCC_APB4_FORCE_RESET();
	 __HAL_RCC_APB4_RELEASE_RESET();
	/* 复位底层硬件初始化 */
	 HAL_MspDeInit();
	/* 返回值 */
	 return HAL_OK;
 }

此函数用于复位 HAL 库和滴答时钟:

  1. 复位了 AHB1,2,3,4 的时钟以及 APB1L,APB1H,APB2,3,4 的时钟。
  2. 函数HAL_DeInit里面调用的HAL_MspDeInit一般在文件stm32h7xx_hal_msp.c里面做具体实现,主要用于底层初始化,跟函数 HAL_Init 里面调用的 HAL_MspInit 是一对。当前此函数也在文件stm32h7xx_hal.c 里面,只是做了弱定义。

2.5.1.3 函数 HAL_InitTick

__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
	/* Configure the SysTick to have interrupt in 1ms time basis*/
	 if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
	 {
		 return HAL_ERROR;
	 }
	/* Configure the SysTick IRQ priority */
	 if (TickPriority < (1UL << __NVIC_PRIO_BITS))
	 {
		 HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
		 uwTickPrio = TickPriority;
	 }
	 else
	 {
	 	return HAL_ERROR;
	 }
	 /* Return function status */
	 return HAL_OK;
}
  • 此函数用于初始化滴答时钟,此函数相关问题如下:

    1. 此函数有个前缀__weak ,表示弱定义,用户可以重定义。
    2. 此函数用于初始化滴答时钟 1ms 中断一次,并且为滴答中断配置一个用户指定的优先级。
    3. 此函数由 HAL_Init 调用,或者任何其它地方调用函数 HAL_RCC_ClockConfig 配置 RCC 的时候也会调用 HAL_InitTick .
    4. 调用基于此函数实现的 HAL_Delay 要特别注意,因为这个函数的时间基准是基于滴答定时器或者其他通用定时器实现,实现方式是滴答定时器或者其他通用定时器里面做了个变量计数。如此一来,结果是显而易见的,如果其他中断服务程序调用了此函数,且中断优先级高于滴答定时器,会导致滴答定时器中断服务程序一直得不到执行,从而卡死在里面。所以滴答定时器的中断优先级一定要比它们高。
  • 输入形参: TickPriority 用于设置滴答定时器优先级。

  • 返回值:返回 HAL_ERROR 表示参数错误,HAL_OK 表示发送成功,HAL_BUSY 表示忙,正在使用中。

此函数由 HAL_Init 调用,无需用户操作,除非需要重定义。

2.5.1.4 Systick 的相关函数

调用了函数 HAL_Init 后,Systick 相关的函数就可以使用了。这些函数如下:

__weak void HAL_IncTick(void)
__weak uint32_t HAL_GetTick(void)
uint32_t HAL_GetTickPrio(void)
HAL_StatusTypeDef HAL_SetTickFreq(HAL_TickFreqTypeDef Freq)
HAL_TickFreqTypeDef HAL_GetTickFreq(void)
__weak void HAL_Delay(uint32_t Delay)
__weak void HAL_SuspendTick(void)
__weak void HAL_ResumeTick(void)

函数有个前缀__weak ,表示弱定义,用户可以重定义

  1. 函数 HAL_IncTick 在滴答定时器中断里面被调用,实现一个简单的计数功能,因为一般滴答定时器中断都是配置的 1ms,所以计数全局变量 uwTick 每毫秒加 1 .
  2. 函数 HAL_GetTick 用于获取全局变量 uwTick 当前的计数。
  3. 函数 HAL_GetTickPrio 用于获取滴答时钟优先级。
  4. 函数 HAL_SetTickFreq 和 HAL_GetTickFreq 是一对,前者用于设置滴答中断频率,后者用于获取滴答中断频率。
  5. 函数 HAL_Delay 用于阻塞式延迟,默认单位是 ms .
  6. 函数 HAL_SuspendTick 和 HAL_ResumeTick 是一对,前者用于挂起滴答定时器,后者用于恢复。

2.5.1.5 函数 HAL_SYSCFG_VREFBUF_VoltageScalingConfig

void HAL_SYSCFG_VREFBUF_VoltageScalingConfig(uint32_t VoltageScaling)

此函数用于配置 STM32H7 内部电压基准大小:

  1. 当形参 VoltageScaling = SYSCFG_VREFBUF_VOLTAGE_SCALE0 时
    输出基准是 2.048 V,条件是 VDDA >= 2.4V .
  2. 当形参 VoltageScaling = SYSCFG_VREFBUF_VOLTAGE_SCALE1 时
    输出基准是 2.5 V,条件是 VDDA >= 2.8V .
  3. 当形参 VoltageScaling = SYSCFG_VREFBUF_VOLTAGE_SCALE2 时
    输出基准是 1.5 V,条件是 VDDA >= 1.8V .
  4. 当形参 VoltageScaling = SYSCFG_VREFBUF_VOLTAGE_SCALE3 时
    输出基准是 1.8 V,条件是 VDDA >= 2.1V .

2.5.1.6 STM32H7 自带电压基准相关函数

void HAL_SYSCFG_VREFBUF_HighImpedanceConfig(uint32_t Mode)
void HAL_SYSCFG_VREFBUF_TrimmingConfig(uint32_t TrimmingValue)
HAL_StatusTypeDef HAL_SYSCFG_EnableVREFBUF(void)
void HAL_SYSCFG_DisableVREFBUF(void)
  1. 函数 HAL_SYSCFG_VREFBUF_HighImpedanceConfig此函数用于配置 STM32H7 内部电压基准是否在芯片内部与 VREF+引脚接通。
    1. 形参为 SYSCFG_VREFBUF_HIGH_IMPEDANCE_DISABLE 时,表示导通。
    2. 形参为 SYSCFG_VREFBUF_HIGH_IMPEDANCE_ENABLE 时,表示高阻,即不导通。
  2. 函数 HAL_SYSCFG_VREFBUF_TrimmingConfig此函数用于内部电压基准的校准调节。
  3. 函数 HAL_SYSCFG_EnableVREFBUFHAL_SYSCFG_DisableVREFBUF 是一对,分别用于内部电压参考基准的禁止和使能。

2.5.1.7 函数 HAL_SYSCFG_AnalogSwitchConfig

void HAL_SYSCFG_AnalogSwitchConfig(uint32_t SYSCFG_AnalogSwitch , uint32_t SYSCFG_SwitchState )

引脚 PA0,PA1,PC2,PC3 用于 ADC 时,还有一组对应的可选引脚 PA0_C,PA1_C,PC2_C 和PC3_C . 此函数的作用就是切换可选引脚。关于这个问题的详情可看此贴:http://www.armbbs.cn/forum.php?mod=viewthread&tid=87707

2.5.1.8 BOOST 的使能和禁止(用于 ADC)

void HAL_SYSCFG_EnableBOOST(void)
void HAL_SYSCFG_DisableBOOST(void)

这两个函数用于使能或者禁止 Booster。如果使能了 booster 的话,在供电电压低于 2.7V 时,可以减少模拟开关总的谐波失真,这样的话,模拟开关的性能跟正常供电电压时的全范围测量一样。

2.5.1.9 函数 HAL_SYSCFG_CM7BootAddConfig

void HAL_SYSCFG_CM7BootAddConfig(uint32_t BootRegister, uint32_t BootAddress)

用于配置 BOOT = 0 或者 BOOT = 1 时的启动地址。

2.5.1.10 IO 补偿相关函数

void HAL_EnableCompensationCell(void)
void HAL_DisableCompensationCell(void)
void HAL_SYSCFG_EnableIOSpeedOptimize(void)
void HAL_SYSCFG_DisableIOSpeedOptimize(void)
void HAL_SYSCFG_CompensationCodeSelect(uint32_t SYSCFG_CompCode)
void HAL_SYSCFG_CompensationCodeConfig(uint32_t SYSCFG_PMOSCode, uint32_t SYSCFG_NMOSCode )
  • 函数 HAL_EnableCompensationCellHAL_DisableCompensationCell分别用于使能或者禁止 IO 补偿,只有在供电电压范围是 2.4V 到 3.6V 之间时,使用此功能才有意义。
  • 函数 HAL_SYSCFG_EnableIOSpeedOptimizeHAL_SYSCFG_DisableIOSpeedOptimize分别用于优化 IO 速度或者禁止优化,不过仅在供电电压低于 2.5V 时可用,高于 2.5V 不可以使用,另外使用这个功能的前提是用户使能了 PRODUCT_BELOW_25V(是可选字节配置选项里面的一个 bit)。
  • 函数 HAL_SYSCFG_CompensationCodeSelectIO 补偿单元的选择,函数形参可以是SYSCFG_CELL_CODE,即寄存器 SYSCFG_CCVR,也可以是SYSCFG_REGISTER_CODE,即寄存器 SYSCFG_CCCR .
  • 函数 HAL_SYSCFG_CompensationCodeConfig用于设置 GPIO 内部构造中 NMOS 和 PMOS 的补偿值,两个形参的范围都是 0-15。根据用户调用函数HAL_SYSCFG_CompensationCodeSelect 选择的寄存器,这里仅有一个形参的设置是有效的。

2.5.2 源文件 stm32h7xx_hal_rcc.c

这个文件主要是实现内部和外部时钟(HSE、HSI、LSE、CSI、LSI、HSI48、PLL、CSS、MCO)以及总线时钟(SYSCLK、AHB3、 AHB1、AHB2、AHB4、APB3、APB1L、APB1H、APB2、 APB4)的配置。

  • 注意事项:
    • 系统上电复位后,通过内部高速时钟 HSI 运行(主频 64MHz),Flash 工作在等待周期,所有外设除了 SRAM、Flash、JTAG 和 PWR,时钟都是关闭的。关于SRAM的详情看此贴:http://www.armbbs.cn/forum.php?mod=viewthread&tid=87784 .
    • AHB 和 APB 总线无分频,所有挂载这两类总线上的外设都是以 HSI 频率运行。
    • 所有的 GPIO 都是模拟模式,除了 JTAG 相关的几个引脚。
    • 系统上电复位后,用户需要完成以下工作:  选择用于驱动系统时钟的时钟源。
    • 配置系统时钟频率和 Flash 设置。
    • 配置分频器。
    • 使能外设时钟。
    • 配置外设时钟源,部分外设的时钟可以不来自系统时钟,此时通过配置寄存器RCC_D1CCIPR、RCC_D2CCIP1R、RCC_D2CCIP2R 和 RCC_D3CCIPR 实现。

  • 使能了外设时钟后,不能立即操作对应的寄存器,要加延迟。不同外设延迟不同:
    • 如果是 AHB 的外设,使能了时钟后,需要等待 2 个 AHB 时钟周期才可以操作这个外设的寄存器。
    • 如果是 APB 的外设,使能了时钟后,需要等待 2 个 APB 时钟周期才可以操作这个外设的寄存器。

2.5.2.1 HCLK、PCLK时钟树

STM32【H7】理论——综述、HAL库简述_第18张图片
由上图可知:

  1. HCLK1,2,3,4 对应的是 AHB 总线 AHB1,AHB2,AHB3 和 AHB4 时钟。
  2. PCLK1、2、3、4 对应的是 APB 总线 APB1,APB2,APB3 和 APB4 时钟。

  • 内部和外部时钟配置:
    • HSI (high-speed internal):高速内部 RC 振荡器,可以直接或者通过 PLL 倍频后做系统时钟源。缺点是精度差些,即使经过校准。
    • CSI (Low-power internal oscillator):低功耗内部 RC 振荡器,作用同 HSI,不过主要用于低功耗。
    • LSI (low-speed internal):低速内部时钟,主要用于独立看门狗和 RTC 的时钟源。
    • HSE (high-speed external):高速外部晶振,可接 4 - 48MHz 的晶振,可以直接或者通过 PLL 倍频后做系统时钟源,也可以做 RTC 的是时钟源。
    • LSE (low-speed external):低速外部晶振,主要用于 RTC。
    • CSS (Clock security system):时钟安全系统,一旦使能后,如果 HSE 启动失败(不管是直接作为系统时钟源还是通过 PLL 输出后做系统时钟源),系统时钟将切换到 HSI。如果使能了中断的话,将进入不可屏蔽中断 NMI。
    • MCO1 (micro controller clock output):可以在 PA8 引脚输出HSI,LSE,HSE,PLL1(PLL1_Q)或 HSI48 时钟。
    • **MCO2 (micro controller clock output):**可以在 PC9 引脚输出HSE,PLL2(PLL2_P),SYSCLK, LSI, CSI 或 PLL1(PLL1_P) 时钟。
    • PLL 锁相环:时钟输入来自 HSI , HSE 或者 CSI,主锁相环 PLL1,用于给 CPU 和部分外设提供时钟。专用锁相环 PLL2 和 PLL3,用于给部分外设提供时钟。

STM32【H7】理论——综述、HAL库简述_第19张图片

3. STM32H7 启动过程详解


这里的启动过程是指从 CPU 上电复位执行第 1 条指令开始(汇编文件)到进入 C 程序 main()函数入口之间的部分。

相比 F1,F4 的启动方式,H7 的启动方式更灵活些,只需一个 boot 引脚即可。但是一个引脚只能区分出两个状态,为了解决这个问题,H7 专门配套了两个 option bytes 选项字节来解决此问题。如此以来就可以方便设置各种存储器地址了。详见节3.2.2 BOOT 启动模式

3.1 不同 H7 系列芯片对应的启动文件

在工程路径 \Libraries\CMSIS\Device\ST\STM32H7xx\Source\Templates\arm 下,有Keil下的芯片启动文件。
STM32【H7】理论——综述、HAL库简述_第20张图片
不同型号的启动文件区别如下:
STM32【H7】理论——综述、HAL库简述_第21张图片

3.2 启动文件分析

启动文件是后缀为.s 的汇编语言文本文件。他主要完成如下工作:

  • 设置堆栈指针 SP = __initial_sp。
  • 设置 PC 指针 = Reset_Handler。
  • 设置中断向量表。
  • 配置系统时钟。
  • 配置外部 SRAM/SDRAM 用于程序变量等数据存储(这是可选的)。
  • 跳转到 C 库中的 __main ,最终会调用用户程序的 main()函数。
    Cortex-M 内核处理器复位后,处于线程模式,指令权限是特权级别(最高级别),堆栈设置为使用主堆栈 MSP。

3.2.1 复位序列

硬件复位之后,CPU 内的时序逻辑电路首先完成如下两个工作(程序代码下载到内部 flash 为例,flash首地址 0x0800 0000)

  • 将 0x08000000 位置存放的堆栈栈顶地址存放到 SP 中(MSP)。
  • 将 0x08000004 位置存放的向量地址装入 PC 程序计数器。

CPU 从 PC 寄存器指向的物理地址取出第 1 条指令开始执行程序,也就是开始执行复位中断服务程序 Reset_Handler。
STM32【H7】理论——综述、HAL库简述_第22张图片

复位中断服务程序会调用SystemInit()函数来配置系统时钟、配置FMC总线上的外部SRAM/SDRAM,然后跳转到 C 库中__main 函数。由 C 库中的__main 函数完成用户程序的初始化工作(比如:变量赋初值等),最后由__main 函数调用用户写的 main()函数开始执行 C 程序。

3.2.2 BOOT 启动模式

BOOT 引脚 BOOT 模式选择(BOOT 地址,选项字节) BOOT 区域
0 BOOT_ADD0[15:0] 启动地址由 BOOT_ADD0 定义,默认值是 0x0800,对应Flash 首地址 0x0800 0000。
1 BOOT_ADD1[15:0] 启动地址由 BOOT_ADD1 定义,默认值是 0x1FF0,对应系 统 bootloader 的首地址 0x1FF0 0000

BOOT_ADD0BOOT_ADD1 对应 32 位地址的高 16 位。通过这两个选项字节,所有 0x0000 00000x3FFF 0000 的存储器地址都可以设置,包括:

  • 所有 Flash 地址空间。
  • 所有 RAM 地址空间,ITCM,DTCM 和 SRAM。
    设置了选项字节后,掉电不会丢失,下次上电或者复位后,会根据 BOOT 引脚状态从 BOOT_ADD0, 或 BOOT_ADD1 所设置的地址进行启动。

使用 BOOT 功能,注意以下几个问题:

  • 如果用户不慎,设置的地址范围不在有效的存储器地址,那么 BOOT = 0 时,会从 Flash 首地址0x0800 0000 启动,BOOT = 1 时,会从 ITCM 首地址 0x0000 0000 启动。
  • 如果用户使能了 Flash Level 2 保护,那么只能从 Flash 地址空间进行启动。

作为对比,下面补充 F1,F4 的启动方式由 BOOT0 和 BOOT1 引脚共同决定:
STM32【H7】理论——综述、HAL库简述_第23张图片

4. 其他


你可能感兴趣的:(#,STM32,H7,stm32,安富莱,mcu)