STM32F4 | STM32F4 总线架构 | STM32F4时钟系统 | IO 引脚复用器和映射 | NVIC 中断优先级管理 | HAL 库中寄存器地址名称映射分析

本文深入探讨了STM32F4的总线架构,包括多层总线矩阵及其互联的主控和被控总线。接着详细解析了STM32F4的时钟系统,阐述了时钟源、时钟树配置、时钟使能和NVIC中断优先级管理。此外,还介绍了IO引脚复用器和映射,以及HAL库中寄存器地址名称的映射分析。
摘要由CSDN通过智能技术生成

一、STM32F4 总线架构

  STM32F29 的总线架构图如下所示:
在这里插入图片描述
主系统由 32 位多层 AHB 总线矩阵构成。总线矩阵用于主控总线之间的访问仲裁管理。仲裁采取循环调度算法。总线矩阵可实现以下部分互联:

  • 八条主控总线是:

    • Cortex-M4 内核 I 总线, D 总线和 S 总线;
    • DMA1 存储器总线, DMA2 存储器总线;
    • DMA2 外设总线;
    • 以太网 DMA 总线;
    • USB OTG HS DMA 总线;
  • 七条被控总线:

    • 内部 FLASH ICode 总线;
    • 内部 FLASH DCode 总线;
    • 主要内部 SRAM1(112KB)
    • 辅助内部 SRAM2(16KB);
    • 辅助内部 SRAM3(64KB) (仅适用 STM32F42xx 和 STM32F43xx 系列器件);
    • AHB1 外设 和 AHB2 外设;
    • FSMC

下面我们具体讲解一下图中几个总线的知识。

  • I 总线(S0):此总线用于将 Cortex-M4 内核的指令总线连接到总线矩阵。内核通过此总线获取指令。此总线访问的对象是包括代码的存储器。
  • D 总线(S1):此总线用于将 Cortex-M4 数据总线和 64KB CCM 数据 RAM 连接到总线矩阵。内核通过此总线进行立即数加载和调试访问。
  • S 总线(S2):此总线用于将 Cortex-M4 内核的系统总线连接到总线矩阵。此总线用于访问位于外设或 SRAM 中的数据。
  • DMA 存储器总线(S3,S4):此总线用于将 DMA 存储器总线主接口连接到总线矩阵。DMA 通过此总线来执行存储器数据的传入和传出。
  • DMA 外设总线:此总线用于将 DMA 外设主总线接口连接到总线矩阵。DMA 通过此总线访问 AHB 外设或执行存储器之间的数据传输。
  • 以太网 DMA 总线:此总线用于将以太网 DMA 主接口连接到总线矩阵。以太网 DMA通过此总线向存储器存取数据。
  • USB OTG HS DMA 总线(S7):此总线用于将 USB OTG HS DMA 主接口连接到总线矩阵。USB OTG HS DMA 通过此总线向存储器加载/存储数据。

二、STM32F4时钟系统

1.STM32F429 时钟树概述

  STM32F429的时钟系统比较复杂,不像简单的51单片机一个系统时钟就可以解决一切。于是有人要问,采用一个系统时钟不是很简单吗?为什么 STM32 要有多个时钟源呢? 因为首先 STM32 本身非常复杂,外设非常的多,但是并不是所有外设都需要系统时钟这么高的频率,比如看门狗以及 RTC 只需要几十 k 的时钟即可。同一个电路,时钟越快功耗越大,同时抗电磁干扰能力也会越弱,所以对于较为复杂的 MCU 一般都是采取多时钟源的方法来解决这些问题。
  首先,看看 STM32F429 的时钟系统图。
在这里插入图片描述
STM32F429 中,有 5 个最重要的时钟源,为 HSIHSELSILSEPLL。其中 PLL实际是分为三个时钟源,分别为主 PLLI2S 部分专用 PLLI2SSAI 部分专用 PLLSAI。从时钟频率来分可以分为高速时钟源和低速时钟源,在这 5 个中 HSIHSE 以及 PLL 是高速时钟,LSILSE 是低速时钟。从来源可分为外部时钟源和内部时钟源,外部时钟源就是从外部通过接晶振的方式获取时钟源,其中 HSELSE 是外部时钟源,其他的是内部时钟源。
  按图中红圈标示的顺序,STM32F429 的这 5 个时钟源介绍如下:

  • LSI 是低速内部时钟,RC 振荡器,频率为 32kHz 左右。供独立看门狗和自动唤醒单元使用
  • LSE 是低速外部时钟,接频率为 32.768kHz 的石英晶体。这个主要是 RTC 的时钟源
  • HSE 是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为 4MHz~26MHz
    STM32F4开发板接的是 25M 的晶振。HSE 也可以直接做为系统时钟或者 PLL 输入
  • HSI 是高速内部时钟,RC 振荡器,频率为 16MHz。可以直接作为系统时钟或者用作 PLL输入
  • PLL 为锁相环倍频输出。STM32F4 有三个 PLL:
    • PLL(PLL)HSE 或者 HSI 提供时钟信号,并具有两个不同的输出时钟。
      第一个输出 PLLP 用于生成高速的系统时钟(最高 180MHz)
      第二个输出 PLLQ 为 48M 时钟,用于 USB OTG FS 时钟,随机数发生器的时钟和 SDIO时钟。
    • 第一个专用 PLL(PLLI2S)用于生成精确时钟,在 I2SSAI1 上实现高品质音频性能。其中,N 是用于 PLLI2S vco 的倍频系数,其取值范围是:192~432RI2S 时钟的分频系数,其取值范围是:2~7QSAI 时钟分频系数,其取值范围是:2~15P 没用到。
    • 第二个专用 PLL(PLLSAI)同样用于生成精确时钟,用于 SAI1 输入时钟,同时还为 LCD_TFT接口提供精确时钟。其中,N 是用于 PLLSAI vco 的倍频系数,其取值范围是:192~432QSAI 时钟分频系数,其取值范围是:2~15RLTDC 时钟的分频系数,其取值范围是:2~7P 没用到。

  这里我们着重看看主 PLL 时钟第一个高速时钟输出 PLLP 的计算方法。STM32F429PLL时钟图如下:
在这里插入图片描述
PLL 时钟的时钟源要先经过一个分频系数为 M 的分频器,然后经过倍频系数为 N 的倍频器出来之后还需要经过一个分频系数为 P(第一个输出 PLLP)或者 Q(第二个输出 PLLQ)的分频器分频之后,最后才生成最终的主 PLL 时钟。
  例如我们的外部晶振选择 25MHz,同时我们设置相应的分频器 M=25,倍频器倍频系数 N=360,分频器分频系数 P=2,那么主 PLL 生成的第一个输出高速时钟 PLLP 为:
P L L = 25 M H z ∗ N / ( M ∗ P ) = 25 M H z ∗ 360 / ( 25 ∗ 2 ) = 180 M H z PLL=25MHz * N/ (M*P)=25MHz* 360 /(25*2) = 180MHz PLL=25MHzN/(MP)=25MHz360/(252)=180MHz
如果我们选择HSEPLL时钟源,同时SYSCLK时钟源为PLL,那么SYSCLK时钟为 180MHz。对于我们后面的实验都是采用这样的配置。
总结:

  1. 系统时钟SYSCLK可来源于三个时钟源:

    • HSI振荡器时钟
    • HSE振荡器时钟
    • PLL时钟
  2. STM32F4时钟信号输出MCO1(PA8)MCO2(PC9)

    • MCO1:用户可以配置预分频器(1~5)向MCO1引脚PA8输出4个不同的时钟源:
      HIS
      LSE
      HSE
      PLL
    • MCO2:用户可以配置预分频器(1~5)向MCO2引脚PC9输出4个不同的时钟源:
      HSE
      PLL
      SYSCLK
      PLLI2S
  3. 注意:任何一个外设在使用前,必须首先使能其相应的时钟

2.STM32F429 时钟初始化配置

  不管是在STM32F4xx还是在STM32F7xx的启动文件startup_stm32f429xx.s中,都有下面一段代码:

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

这一段代码的作用是:引导芯片启动之后,先执行 SystemInit 函数,然后再执行main函数。
  在系统启动之后,程序会先执行 HAL 库定义的 SystemInit 函数,进行系统一些初始化配置。那么我们先来看看 SystemInit 函数(在system_stm32f4xx.c中定义):

void SystemInit(void)
{
   
	/* FPU 设置------------------------------------------------------------*/
	#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
	SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
	#endif
	/* 复位 RCC 时钟配置为默认配置-----------*/
	RCC->CR |= (uint32_t)0x00000001;//打开 HSION 位
	RCC->CFGR = 0x00000000;//复位 CFGR 寄存器
	RCC->CR &= (uint32_t)0xFEF6FFFF;//复位 HSEON, CSSON and PLLON 位
	RCC->PLLCFGR = 0x24003010; //复位寄存器 PLLCFGR
	RCC->CR &= (uint32_t)0xFFFBFFFF;//复位 HSEBYP 位
	RCC->CIR = 0x00000000;//关闭所有中断
	
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
	SystemInit_ExtMemCtl(); 
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */

 /* 配置中断向量表地址=基地址+偏移地址 ------------------*/
#ifdef VECT_TAB_SRAM
	SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; 
#else
	SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; 
#endif
}

从上面代码可以看出,SystemInit 主要做了如下四个方面工作:

  • FPU 设置
  • 复位 RCC 时钟配置为默认复位值(默认开始了 HIS
  • 外部存储器配置
  • 配置中断向量表地址配置

  HAL 库的 SystemInit 函数并没有像标准库的 SystemInit 函数一样进行时钟的初始化配置。HAL库的 SystemInit 函数除了打开 HSI 之外,没有任何时钟相关配置,所以,使用 HAL 库我们必须编写自己的时钟配置函数。首先,我们打开工程模板看看我们在工程 SYSTEM 分组下面定义的 sys.c文件中的时钟初始化函数 Stm32_Clock_Init 的内容:

//时钟系统配置函数
//Fvco=Fs*(plln/pllm);
//SYSCLK=Fvco/pllp=Fs*(plln/(pllm*pllp));
//Fusb=Fvco/pllq=Fs*(plln/(pllm*pllq));

//Fvco:VCO频率
//SYSCLK:系统时钟频率
//Fusb:USB,SDIO,RNG等的时钟频率
//Fs:PLL输入时钟频率,可以是HSI,HSE等. 
//plln:主PLL倍频系数(PLL倍频),取值范围:64~432.
//pllm:主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.
//pllp:系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)
//pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.

//外部晶振为25M的时候,推荐值:plln=360,pllm=25,pllp=2,pllq=8.
//得到:Fvco=25*(360/25)=360Mhz
//     SYSCLK=360/2=180Mhz
//     Fusb=360/8=45Mhz
//返回值:0,成功;1,失败
void Stm32_Clock_Init(u32 plln,u32 pllm,u32 pllp,u32 pllq)
{
   
    HAL_StatusTypeDef ret = HAL_OK;
    RCC_OscInitTypeDef RCC_OscInitStructure; 
    RCC_ClkInitTypeDef RCC_ClkInitStructure;
    
    __HAL_RCC_PWR_CLK_ENABLE(); 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值