STM32F407的时钟

文章目录

时钟源

时钟源用来为环形脉冲发生器提供频率稳定且电平匹配的方波时钟脉冲信号。它通常由石英 晶体振荡器和与非门组成的正反馈振荡电路组成,其输出送至环形脉冲发生器。

为什么 STM32 要有多个时钟源

F4开发指南P107
在这里插入图片描述

F407时钟地图

F4开发指南P108
在这里插入图片描述

F407的五个时钟源

HSI高速内部时钟源

High Speed Internal。RC 振荡器,频率为 16MHz。可以直接作为系统时钟或者用作 PLL输入。

PLL锁相环

  • PLL锁相环的作用: F4开发指南4.3.1,P109- 主 PLL 时钟第一个高速时钟输出 PLLP 的计算方法
  • PLL锁相环的配置: F4开发指南4.3.1,P109
    主PLL的计算方法:
    在这里插入图片描述
  • 先经过一个分频系数为 M 的分频器
  • 再经过倍频系数为 N 的倍频器
  • 再经过一个分频系数为 P(第一个输出 PLLP)或者 Q(第二个输出 PLLQ)的分频器
  • 最后才生成最终的主 PLL 时钟。

若我们的外部晶振选择 8MHz(HSE时钟源)。设置相应的分频器 M=8,倍频器倍频系数 N=336,分频器分频系数 P=2,那么主 PLL 生成的第一个输出高速时钟 PLLP:

PLL=8MHz * N/ (M*P)=8MHz* 336 /(8*2) = 168MHz

若SYSCLK时钟源为PLL,那么SYSCLK时钟为 168MHz。

LSE低速外部时钟源

Low Speed External 。接频率为 32.768kHz 的石英晶体。这个主要是 RTC 的时钟源。

LSI低速内部时钟源

Low Speed Internal。RC 振荡器,频率为 32kHz 左右。供独立看门狗和自动唤醒单元使用。

  • 看门狗时钟源只能是低速的 LSI 时钟。

HSE高速外部时钟源

High Speed External

  • 外部接一个4-16M的晶振,可接石英/陶瓷谐振器
  • 可以作为选择器1的输入;
  • 可以÷2分频之后作为选择器1的输入;
  • 经过选择器1之后可以作为选择器2的输入,HSI的2分频之后也作为选择器2的输入,之后进入PLL(锁相环)来倍频;
  • 也可以直接作为系统时钟的时钟源;

F4开发指南4.3.1,P108与P109

RTC时钟源

RTC 的时钟源可以选择 LSI,LSE,以及HSE 分频后的时钟,HSE 分频系数为 2~31。

外部晶振/陶瓷谐振器

  • 谐振器和负载电容要求必须尽可能地靠近振荡器的引脚,减少失真和起振时间。外部用户时钟必须使用占空比约为 50% (左右)外部时钟信号来驱动 OSC_IN 引脚,同时 OSC_OUT 引脚应保持为高阻态。
  • 外部时钟源
    在这里插入图片描述

MCO输出时钟

选择一个时钟信号输出到MCO引脚
F4开发指南P109C
输出时钟 MCO1 和 MCO2。
MCO1 是向芯片的 PA8 引脚输出时钟。它有四个时钟来源分别为:HSI,LSE,HSE 和 PLL 时钟。
MCO2 是向芯片的 PC9 输出时钟,它同样有四个时钟来源分为:HSE,PLL,SYSCLK 以及 PLLI2S
时钟。MCO 输出时钟频率最大不超过 100MHz。

系统时钟

SYSCLK 系统时钟来源有三个方面:HSI,HSE 和 PLL。
在我们实际应用中,因为对时钟速度要求都比较高我们才会选用 STM32F4 这种级别的处理器,所以一般情况下,都是采用 PLL 作为 SYSCLK时钟源。也就是上文计算的168M。

  • 以太网PTP时钟、AHB时钟、APB1低速时钟、APB2高速时钟都是来源于系统时钟。由系统时钟分频得到。
  • AHB最大时钟为168MHz, APB2高速时钟最大频率为84MHz,而APB1低速时钟最大频率为 42MHz。

以太网PTP时钟

直接使用系统时钟。

AHB预分频器

是Advanced High performance Bus的缩写,译作高级高性能总线,这是一种“系统总线”。

  • AHB主要用于高性能模块(如CPU、DMA和DSP等)之间的连接。
  • AHB 系统由主模块、从模块和基础结构(Infrastructure)3部分组成,整个AHB总线上的传输都由主模块发出,由从模块负责回应。
  • 该分频器有1,2,4,64,16,128,256,512等可以选择
    • 可以输出一个HCLOCK,最高72M
    • 可以进入APB1预分频器,产生PCLK1的时钟,最高36M,可以挂一些低速的外设
    • 可以进入APB2预分频器,产生PCLK2的时钟,最高72M,可以挂高速外设;2>1

APB预分频器

Advanced Peripheral Bus,外围总线
在使用任何的外设之前,都要使相应的时钟使能位开启,否则就没法使用

  • APB1 上面连接的是低速外设,包括电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3 等等
  • APB2 上面连接的是高速外设包括 UART1、SPI1、Timer1、ADC1、ADC2、所有普通 IO 口(PA~PE)、第二功能 IO 口等。

CSS时钟监控系统

检测到HSE失败,就会切换到HSI

I2S时钟源

来源于 PLLI2S 或者映射到 I2S_CKIN 引脚的外部时钟。I2S 出于音质的考虑,对时钟精度要求很高。

内部以太网 MAC 时钟的来源

对于 MII 接口来说,必须向外部PHY 芯片提供 25Mhz 的时钟,这个时钟,可以由 PHY 芯片外接晶振,或者使用STM32F4 的 MCO 输出来提供。然后,PHY 芯片再给 STM32F4 提供ETH_MII_TX_CLK 和 ETH_MII_RX_CLK 时钟。
对于 RMII 接口来说,外部必须提供 50Mhz 的时钟驱动 PHY 和 STM32F4 的 ETH_RMII_REF_CLK,这个 50Mhz时钟可以来自 PHY、有源晶振或者 STM32F4 的 MCO
我们的开发板使用的是RMII 接 口 , 使 用 PHY 芯 片 提 供 50Mhz 时 钟 驱 动 STM32F4 的ETH_RMII_REF_CLK。

USB时钟

外部 PHY 提供的 USB OTG HS(60MHZ)时钟

系统时钟(D部分)

系统时钟的来源

系统时钟SYSCLK可来源于三个时钟源:
①、HSI振荡器时钟
②、HSE振荡器时钟
③、PLL时钟

系统时钟的设置

  • 在我们系统启动的时候都会调用SystemInit ,用来设置系统的整个系统和总线时钟。
  • SystemInit的执行过程:F4开发指南,4.3.2,P111
    • RCC 时钟配置寄存器 (RCC_CFGR)可以参考下文的RCC时钟控制->RCC寄存器
    • SetSysClock的详细内容与执行过程,F4开发指南4.3.2,P113
      在这里插入图片描述

F407系统初始化时钟的大小

在System_stm32f4xx.c文件,把PLL第一级分频系数M修改为8,这样达到主时钟频率为168MHz。

#define PLL_M 8
#define PLL_Q 7
#define PLL_N 336
#define PLL_P 2

所以我们的主 PLL 时钟为168MHz。
同时,我们要在stm32f4xx.h里面修改外部时钟HSE_VALUE值为8MHz

#if !defined   (HSE_V ALUE)  
  #define HSE_V ALUE    ((uint32_t)8000000) /*!< V alue of the External oscillator in Hz */ 
   
#endif /* HSE_V ALUE */ 

这里默认固件库配置的是 25000000,我们外部时钟为 8MHz,所以我们根据我们硬件情况修改为 8000000 即可。

系统时钟分频产生三种时钟。FCLK,HCLK,PCLK都称为系统时钟,但区别如下,

FCLK

  • 提供给CPU内核的时钟信号,CPU的主频就是指这个信号;

HCLK

  • 提供给高速总线AHB的时钟信号。是系统时钟经过AHB分频器而来。

PCLK

提供给低速总线APB的时钟信号,默认是45MHz;CAN 位时序寄存器(CAN_BTR)

SytemInit调用

SystemInit 是整个设置系统时钟的入口函数。这个函数对于我们使用 ST提供的 STM32F4 固件库的话,会在系统启动之后先执行 main 函数,然后再接着执行 SystemInit函数实现系统相关时钟的设置。这个过程设置是在启动文件 startup_stm32f40_41xxx.s 中间设置的。所以我们不需要再在 main()函数中调用 SystemInit()函数。

总结一下 SystemInit()函数中设置的系统时钟大小:
F4开发指南4.3.2,P113
在这里插入图片描述

RCC时钟使能寄存器

相关外设的时钟实际是在 RCC 相关寄存器中配置的。《STM32F4 中文参考手册》6.3 小节。

  • STM32F4 的外设在使用之前,必须对时钟进行使能

挂载位置的搜索

  • 要使能 GPIOA,我们只需要在 stm32f4xx_rcc.h 头文件里面搜索 GPIOA,就可以搜索到对应的时钟使能函数的第一个入口参数为RCC_AHB1Periph_GPIOA,从这个宏定义标识符一眼就可以看出,GPIOA 是挂载在 AHB1 下面。

RCC_AHB1PeriphClockCmd

RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState)

RCC_AHB1Periph

  *            @arg RCC_AHB1Periph_GPIOA:       GPIOA clock
  *            @arg RCC_AHB1Periph_GPIOB:       GPIOB clock 
  *            @arg RCC_AHB1Periph_GPIOC:       GPIOC clock
  *            @arg RCC_AHB1Periph_GPIOD:       GPIOD clock
  *            @arg RCC_AHB1Periph_GPIOE:       GPIOE clock
  *            @arg RCC_AHB1Periph_GPIOF:       GPIOF clock
  *            @arg RCC_AHB1Periph_GPIOG:       GPIOG clock
  *            @arg RCC_AHB1Periph_GPIOG:       GPIOG clock
  *            @arg RCC_AHB1Periph_GPIOI:       GPIOI clock
  *            @arg RCC_AHB1Periph_GPIOJ:       GPIOJ clock (STM32F42xxx/43xxx devices) 
  *            @arg RCC_AHB1Periph_GPIOK:       GPIOK clock (STM32F42xxx/43xxx devices)  
  *            @arg RCC_AHB1Periph_CRC:         CRC clock
  *            @arg RCC_AHB1Periph_BKPSRAM:     BKPSRAM interface clock
  *            @arg RCC_AHB1Periph_CCMDATARAMEN CCM data RAM interface clock
  *            @arg RCC_AHB1Periph_DMA1:        DMA1 clock
  *            @arg RCC_AHB1Periph_DMA2:        DMA2 clock
  *            @arg RCC_AHB1Periph_DMA2D:       DMA2D clock (STM32F429xx/439xx devices)  
  *            @arg RCC_AHB1Periph_ETH_MAC:     Ethernet MAC clock
  *            @arg RCC_AHB1Periph_ETH_MAC_Tx:  Ethernet Transmission clock
  *            @arg RCC_AHB1Periph_ETH_MAC_Rx:  Ethernet Reception clock
  *            @arg RCC_AHB1Periph_ETH_MAC_PTP: Ethernet PTP clock
  *            @arg RCC_AHB1Periph_OTG_HS:      USB OTG HS clock
  *            @arg RCC_AHB1Periph_OTG_HS_ULPI: USB OTG HS ULPI clock

NewState

ENABLE/DISABLE;

RCC_AHB2PeriphClockCmd

RCC_AHB2PeriphClockCmd(uint32_t RCC_AHB2Periph, FunctionalState NewState)

RCC_AHB2Periph

  *            @arg RCC_AHB2Periph_DCMI:   DCMI clock
  *            @arg RCC_AHB2Periph_CRYP:   CRYP clock
  *            @arg RCC_AHB2Periph_HASH:   HASH clock
  *            @arg RCC_AHB2Periph_RNG:    RNG clock
  *            @arg RCC_AHB2Periph_OTG_FS: USB OTG FS clock

RCC_AHB3PeriphClockCmd

RCC_AHB3PeriphClockCmd(uint32_t RCC_AHB3Periph, FunctionalState NewState)

RCC_AHB3Periph

RCC_AHB3Periph_FSMC

RCC_AHB3Periph_FMC (STM32F42xxx/43xxx devices)  

RCC_APB1PeriphClockCmd

RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState)

RCC_APB1Periph

  *            @arg RCC_APB1Periph_TIM2:   TIM2 clock
  *            @arg RCC_APB1Periph_TIM3:   TIM3 clock
  *            @arg RCC_APB1Periph_TIM4:   TIM4 clock
  *            @arg RCC_APB1Periph_TIM5:   TIM5 clock
  *            @arg RCC_APB1Periph_TIM6:   TIM6 clock
  *            @arg RCC_APB1Periph_TIM7:   TIM7 clock
  *            @arg RCC_APB1Periph_TIM12:  TIM12 clock
  *            @arg RCC_APB1Periph_TIM13:  TIM13 clock
  *            @arg RCC_APB1Periph_TIM14:  TIM14 clock
  *            @arg RCC_APB1Periph_WWDG:   WWDG clock
  *            @arg RCC_APB1Periph_SPI2:   SPI2 clock
  *            @arg RCC_APB1Periph_SPI3:   SPI3 clock
  *            @arg RCC_APB1Periph_USART2: USART2 clock
  *            @arg RCC_APB1Periph_USART3: USART3 clock
  *            @arg RCC_APB1Periph_UART4:  UART4 clock
  *            @arg RCC_APB1Periph_UART5:  UART5 clock
  *            @arg RCC_APB1Periph_I2C1:   I2C1 clock
  *            @arg RCC_APB1Periph_I2C2:   I2C2 clock
  *            @arg RCC_APB1Periph_I2C3:   I2C3 clock
  *            @arg RCC_APB1Periph_CAN1:   CAN1 clock
  *            @arg RCC_APB1Periph_CAN2:   CAN2 clock
  *            @arg RCC_APB1Periph_PWR:    PWR clock
  *            @arg RCC_APB1Periph_DAC:    DAC clock
  *            @arg RCC_APB1Periph_UART7:  UART7 clock
  *            @arg RCC_APB1Periph_UART8:  UART8 clock

RCC_APB2PeriphClockCmd

void RCC_APB2PeriphClockCmd(u32 RCC_APB2Periph, FunctionalState NewState)

RCC_APB2Periph

  *            @arg RCC_APB2Periph_TIM1:   TIM1 clock
  *            @arg RCC_APB2Periph_TIM8:   TIM8 clock
  *            @arg RCC_APB2Periph_USART1: USART1 clock
  *            @arg RCC_APB2Periph_USART6: USART6 clock
  *            @arg RCC_APB2Periph_ADC1:   ADC1 clock
  *            @arg RCC_APB2Periph_ADC2:   ADC2 clock
  *            @arg RCC_APB2Periph_ADC3:   ADC3 clock
  *            @arg RCC_APB2Periph_SDIO:   SDIO clock
  *            @arg RCC_APB2Periph_SPI1:   SPI1 clock
  *            @arg RCC_APB2Periph_SPI4:   SPI4 clock
  *            @arg RCC_APB2Periph_SYSCFG: SYSCFG clock
  *            @arg RCC_APB2Periph_TIM9:   TIM9 clock
  *            @arg RCC_APB2Periph_TIM10:  TIM10 clock
  *            @arg RCC_APB2Periph_TIM11:  TIM11 clock
  *            @arg RCC_APB2Periph_SPI5:   SPI5 clock
  *            @arg RCC_APB2Periph_SPI6:   SPI6 clock
  *            @arg RCC_APB2Periph_SAI1:   SAI1 clock (STM32F42xxx/43xxx devices) 
  *            @arg RCC_APB2Periph_LTDC:   LTDC clock (STM32F429xx/439xx devices) 

时钟源配置

时钟源使能函数

void RCC_HSICmd(FunctionalState NewState);
void RCC_LSICmd(FunctionalState NewState);
void RCC_PLLCmd(FunctionalState NewState);
void RCC_PLLI2SCmd(FunctionalState NewState);
void RCC_PLLSAICmd(FunctionalState NewState);
void RCC_RTCCLKCmd(FunctionalState NewState);

这些函数是用来使能相应的时钟源。

RCC_PLLCmd(ENABLE);

我们要使能相应的时钟源,调用对应的函数即可。

时钟源配置函数

void RCC_LSEConfig(uint8_t RCC_LSE);
void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource);
void RCC_HCLKConfig(uint32_t RCC_SYSCLK);
void RCC_PCLK1Config(uint32_t RCC_HCLK);
void RCC_PCLK2Config(uint32_t RCC_HCLK);
void RCC_RTCCLKConfig(uint32_t RCC_RTCCLKSource);
void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t PLLM,
uint32_t PLLN, uint32_t PLLP, uint32_t PLLQ);

设置系统时钟源为 HSI:RCC_HCLKConfig(RCC_SYSCLKSource_HSI);//配置时钟源为 HSI
设置 APB1 总线时钟为 HCLK 的 2 分频:RCC_PCLK1Config(RCC_HCLK_Div2);

外设复位函数

void RCC_AHB1PeriphResetCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState);
void RCC_AHB2PeriphResetCmd(uint32_t RCC_AHB2Periph, FunctionalState NewState);
void RCC_AHB3PeriphResetCmd(uint32_t RCC_AHB3Periph, FunctionalState NewState);
void RCC_APB1PeriphResetCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

复位对应的外

RCC时钟控制

何为RCC

RCC,Reset and Clock Control(复位和时钟控制),在绝大部分MCU芯片中都包含复位和时钟控制模块,也是MCU重要的组成部分。
主要用来设置系统时钟 SYSCLK 、设置 AHB 分频因子(决定 HCLK 等于多少)、设置 APB2 分频因子(决定 PCLK2 等于多少)、设置 APB1 分频因子(决定 PCLK1 等于多少)、设置各个外设的分频因子;控制 AHB 、 APB2 和 APB1 这三条总线时钟的开启、控制每个外设的时钟的开启。

RCC寄存器

AHB1

在这里插入图片描述

AHB2

在这里插入图片描述

APB1 外设时钟使能寄存器(RCC_APB1ENR)

在这里插入图片描述

APB2 外设时钟使能寄存器(RCC_APB2ENR)

在这里插入图片描述用于 STM32F405xx/07xx 和 STM32F415xx/17xx 。

在这里插入图片描述

CR寄存器

可以使能HSI、HSE、CSS、PLL,使能之后才能被打开和使用。使能之后不能立马稳定,所以需要一个标志位判断时钟是否稳定,叫做就绪标志位。

CFGR寄存器

F4中文参考手册,6.3.3,P118
设置时钟源的选择和分频系数。
在这里插入图片描述

PPRE2,APB2分频

在这里插入图片描述

PPRE1,APB1分频

在这里插入图片描述

HPRE,AHB分频

在这里插入图片描述

F407外设时钟使能:

官方库提供了五个打开 GPIO 和外设时钟的函数分别为:

void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState);
void RCC_AHB2PeriphClockCmd(uint32_t RCC_AHB2Periph, FunctionalState NewState);
void RCC_AHB3PeriphClockCmd(uint32_t RCC_AHB3Periph, FunctionalState NewState);
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

AHB1
在这里插入图片描述

AHB2
在这里插入图片描述

AHB3
在这里插入图片描述

APB1
在这里插入图片描述

APB2
在这里插入图片描述

其他外设时钟配置:

 RCC_ADCCLKConfig ()
  RCC_RTCCLKConfig();

状态参数获取参数:

RCC_GetClocksFreq();
RCC_GetSYSCLKSource();
RCC_GetFlagStatus()

RCC中断相关函数 :

 RCC_ITConfig()
  RCC_GetITStatus() 
   RCC_ClearITPendingBit()

RCC配置结构体

typedef struct
{
  __IO uint32_t CR;       //HSI,HSE,CSS,PLL等的使能和就绪标志位 
  __IO uint32_t CFGR;     //PLL等的时钟源选择,分频系数设定
  __IO uint32_t CIR;      // 清除/使能 时钟就绪中断
  __IO uint32_t APB2RSTR;  //APB2线上外设复位寄存器
  __IO uint32_t APB1RSTR;   //APB1线上外设复位寄存器
  __IO uint32_t AHBENR;    //DMA,SDIO等时钟使能
  __IO uint32_t APB2ENR;   //APB2线上外设时钟使能
  __IO uint32_t APB1ENR;   //APB1线上外设时钟使能
  __IO uint32_t BDCR;        //备份域控制寄存器
  __IO uint32_t CSR;           //控制状态寄存器
} RCC_TypeDef;

RTC时钟

RTC时钟介绍

  • 全称为:实时时钟Real time clock ,一个独立的定时器。本质上是一个掉电后还可以继续运行的定时器。
  • 时钟来源:F4开发指南297
    • STM32F4的 RTC时钟源 RTCCLK 通过时钟控制器,可以从 LSE时钟、 LSI时钟以及HSE时钟三者中选择。
    • 一般我们选择 LSE,即外部 32.768Khz 晶振作为时钟源(RTCCLK)
    • RTC 时钟核心,要求提供 1Hz 的时钟
  • RTC的四种功能:F4开发指南20.1,P297
    • STM32F4 的 RTC 日历时间(RTC_TR)和日期(RTC_DR)寄存器,用于存储时间和日期
    • 这些时间与日期可以通过与 PCLK1(APB1 时钟)同步的影子寄存器来访问,也可以通过时间和日期寄存器直接访问,这样可避免等待同步的持续时间。
  • 可编程闹钟
    • STM32F4 提供两个可编程闹钟:闹钟 A(ALARM_A)和闹钟 B(ALARM_B)。
    • 通过 RTC_CR寄存器的 ALRAE 和 ALRBE 位置 1 来使能可编程闹钟功能。
    • 当日历的亚秒、秒、分、小时、日期分别与闹钟寄存器 RTC_ALRMASSR/RTC_ALRMAR 和 RTC_ALRMBSSR/RTC_ALRMBR中的值匹配时,则可以产生闹钟(需要适当配置)。
  • 周期性自动唤醒
    • STM32F4 的 RTC 不带秒钟中断了,但是多了一个周期性自动唤醒功能。
    • 周期性唤醒功能,由一个 16 位可编程自动重载递减计数器(RTC_WUTR)生成,可用于周期性中断/唤醒。
    • 可以通过 RTC_CR 寄存器中的 WUTE 位设置使能此唤醒功能
  • 取消RTC模块写保护:F4开发指南20.1,P296

RTC时钟框图及解析

在这里插入图片描述

RTC 由两个主要部分组成
第一部分(APB1 接口)用来和 APB1 总线相连。
此单元还包含一组 16 位寄存器,可通过 APB1 总线对其进行读写操作。
APB1 接口由 APB1 总线时钟驱动,用来与 APB1 总线连接。
另一部分(RTC 核心)由一组可编程计数器组成,分成两个主要模块。
第一个模块是 RTC 的预分频模块,它可编程产生 1 秒的 RTC 时间基准 TR_CLK。
RTC 的预分频模块包含了一个 20位的可编程分频器(RTC 预分频器)。
如果在 RTC_CR 寄存器中设置了相应的允许位,则在每个TR_CLK 周期中 RTC 产生一个中断(秒中断)。
第二个模块是一个 32 位的可编程计数器(RTC_CNT),可被初始化为当前的系统时间,一个 32 位的时钟计数器,按秒钟计算,可以记录 4294967296 秒,约合 136 年左右。
RTC 还有一个闹钟寄存器 RTC_ALR,用于产生闹钟。系统时间按 TR_CLK 周期累加并与存储在 RTC_ALR 寄存器中的可编程时间相比较,如果 RTC_CR 控制寄存器中设置了相应允许位,比较匹配时(即:RTC_CNT=RTC_ALR 时)将产生一个闹钟中断,从而实现闹钟功能。

软件是通过 APB1 接口访问 RTC 的预分频值、计数器值和闹钟值的
但RTC 内核完全独立于 RTC APB1 接口
相关可读寄存器只在 RTC APB1 时钟进行重新同步的 RTC 时钟的上升沿被更新,RTC 标志也是如此。
这就意味着,如果 APB1 接口刚刚被开启之后,在第一次的内部寄存器更新之前,从 APB1 上读取的 RTC 寄存器值可能被破坏了(通常读到 0)。
因此,若在读取 RTC 寄存器曾经被禁止的 RTC APB1 接口,软件首先必须等待 RTC_CRL 寄存器的 RSF位(寄存器同步标志位,bit3)被硬件置 1。

RTC的寄存器

RTC 的控制寄存器RTC_CR

RTC总共有 2 个控制寄存器 RTC_CRH 和 RTC_CRL,该寄存器用来控制中断的,若要用到秒钟中断,该寄存器必须设置最低位为 1,以允许秒钟中断。

RTC_CRL 寄存器

第 0 位是秒钟标志位,我们在进入 RTC中断的时候,通过判断这位来决定是不是发生了秒钟中断。
然后必须通过软件将该位清零(写0)。
第 1 位是闹钟标志位,当 RTC_CNT 的值等于 RTC_ALR 的值时,此位将由硬件置 1(可以判断此位是否为 1 来判定是否产生了闹钟),如果设置了闹钟中断(ALRIE=1),则将产生 RTC闹钟中断,该位也必须软件写 0 清除。
第 3 位为寄存器同步标志位,我们在修改控制寄存器 RTC_CRH/CRL 之前,必须先判断该位,是否已经同步了,如果没有则等待同步,在没同步的情况下修改 RTC_CRH/CRL 的值是不行的。
第 4 位为配置标位,在软件修改 RTC_CNT/RTC_ALR/RTC_PRL 的值的时候,必须先软件置位该位,以允许进入配置模式。
第 5 位为 RTC 操作位,该位由硬件操作,软件只读。通过该位可以判断上次对 RTC 寄存器的操作是否完成,如果没有,我们必须等待上一次操作结束才能开始下一次操作。

RTC 预分频装载寄存器

2 个寄存器组成,RTC_PRLH 和RTC_PRLL。
这两个寄存器用来配置 RTC 时钟的分频数的。
比如我们使用外部 32.768K 的晶振作为时钟的输入频率,那么我们要设置这两个寄存器的值为 32767,以得到一秒钟的计数频率。
RTC_PRLH 的各位描述
在这里插入图片描述

RTC_PRLH 只有低四位有效,用来存储 PRL 的 19~16 位。而 PRL的前 16 位,存放在 RTC_PRLL 里面,寄存器 RTC_PRLL 的各位描述
在这里插入图片描述

RTC 预分频器余数寄存器

该寄存器也有 2 个寄存器组成 RTC_DIVH 和 RTC_DIVL
这两个寄存器的作用就是用来获得比秒钟更为准确的时钟,
比如可以得到 0.1 秒,或者 0.01 秒等。
该寄存器的值自减的,用于保存还需要多少时钟周期获得一个秒信号。
在一次秒钟更新后,由硬件重新装载。这两个寄存器和 RTC 预分频装载寄存器的各位是一样的

RTC 计数器寄存器 RTC_CNT

该寄存器由 2 个 16位的寄存器组成 RTC_CNTH 和 RTC_CNTL,总共 32 位,用来记录秒钟值(TR_CLK=1Hz 的
情况下)。一般我们设置时间,就是设置 RTC_CNTH/RTC_CNTL 寄存器的值。
假定我们以 1970年为起始时间,那么当 RTC_CNTH=RTC_CNTL=0 的时候,就代表 1970 年 1 月 1 日 0 时 0 分,
这样就可以很方便的根据 RTC_CNT 的值计算当前时间了。
反过来,如果要设置时间,则只需要将当前时间的年份减去 1970,然后剩下的时间换算成秒钟,写入 RTC_CNT 即可完成时间设置。

闹钟寄存器 RTC_ALR

该寄存器同 RTC_CNT 一样,也是由 2 个 16位的寄存器组 RTC_ALRH 和 RTC_ALRL 组成,总共 32 位,用来记录闹钟时刻,实际上,RTC_ALR 就是一个用于同 RTC_CNT 比较的寄存器,当 RTC_CNT=RTC_ALR 的时候,就说明闹钟时间到了,需要闹铃。
因此 RTC_ALR 的设置和读取完全同 RTC_CNT 一模一样。
假定我们设置RTC_CNTH=RTC_CNTL=0,然后设置RTC_ALRH=0且RTC_ALRL=30,然后启动RTC,
那么 30 秒钟后,ALRF 将为 1,表示有闹钟产生,如果开启了闹钟中断(ALRIE=1),那么将产生闹钟中断,这就是 STM32F1 的 RTC 闹钟原理。

备份区域控制寄存器RCC_BDCR

在这里插入图片描述

RTC 的时钟源选择及使能设置都是通过这个寄存器来实现的,所以我们在 RTC 操作之前先要通过这个寄存器选择 RTC 的时钟源,然后才能开始其他的操作。

RTC 配置步骤

使能电源时钟

RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//使能 PWR 时钟

PWR:F4中文参考手册6.3.15,P139,Bit28

取消备份区写保护

PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问

PWR_BackupAccessCmd:F1固件库使用手册14.2.2,P189

开启外部低速振荡器

RCC_LSEConfig(RCC_LSE_ON);// 开启外部低速振荡器

F1固件库使用手册15.2.16,P204,注意参数不完全一致。

选择 RTC 时钟,并使能

RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设选择 LSE 作为 RTC 时钟
RCC_RTCCLKCmd(ENABLE); //使能 RTC 时钟

RCC_RTCCLKConfig F1固件库使用手册15.2.18,P205
在这里插入图片描述

初始化 RTC,设置 RTC 的分频

ErrorStatus RTC_Init(RTC_InitTypeDef* RTC_InitStruct);
 
typedef struct
{
uint32_t RTC_HourFormat;
uint32_t RTC_AsynchPrediv;
uint32_t RTC_SynchPrediv;
}RTC_InitTypeDef;
  • RTC_HourFormat 如果设置为 24 小时格式参数值可选择 RTC_HourFormat_24,12 小时格式,参数值可以选择 RTC_HourFormat_24。 F4中文参考手册23.6.3控制寄存器CR,P589,Bit6

    • 位 6 FMT:小时格式 (Hour format)
      0:24 小时/天格式
      1:AM/PM 小时格式
  • 参数 RTC_AsynchPrediv 用来设置 RTC 的异步预分频系数,也就是设置 RTC_PRER 预分频器寄存器的 PREDIV_A 相关位。同时,因为异步预分频系数是 7 位,所以最大值为 0x7F,不能超过这个值。
    在这里插入图片描述

  • 参数 RTC_SynchPrediv 用来设置 RTC 的同步预分频系数,也就是设置 RTC_PRER 寄存器的 PREDIV_S 相关位。同时,因为同步预分频系数也是 15 位,所以最大值为 0x7FFF,不能超过这个值。

要想明白同步和异步要达到的目的,可以参考F4中文参考手册23.3.1,P574。

同步和异步通道的区别在于同步发过去消息会发生阻塞,直到返回值才继续运行。

设置 RTC 的时间


ErrorStatus RTC_SetTime(uint32_t RTC_Format, RTC_TimeTypeDef* RTC_TimeStruct);

typedef struct
{
uint8_t RTC_Hours;
uint8_t RTC_Minutes;
uint8_t RTC_Seconds;
uint8_t RTC_H12;
}RTC_TimeTypeDef;

在这里插入图片描述
最后一个参数的取值范围:
在这里插入图片描述

别用来设置 RTC 时间参数的小时,分钟,秒钟,以及 AM/PM 符号

设置 RTC 的日期

 ErrorStatus RTC_SetDate(uint32_t RTC_Format, RTC_DateTypeDef* RTC_DateStruct);
 typedef struct
{
uint8_t RTC_WeekDay;
uint8_t RTC_Month;
uint8_t RTC_Date;
uint8_t RTC_Year;
}RTC_DateTypeDef;

设置日期的星期几,月份,日期,年份。

获取 RTC 当前日期和时间

获取当前 RTC 时间的函数为:

void RTC_GetTime(uint32_t RTC_Format, RTC_TimeTypeDef* RTC_TimeStruct);

获取当前 RTC 日期的函数为:

void RTC_GetDate(uint32_t RTC_Format, RTC_DateTypeDef* RTC_DateStruct);

最终代码

u8 My_RTC_Init(void)
{
RTC_InitTypeDef RTC_InitStructure;
u16 retry=0X1FFF;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//使能 PWR 时钟
PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
if(RTC_ReadBackupRegister(RTC_BKP_DR0)!=0x5050)//是否第一次配置?
{
RCC_LSEConfig(RCC_LSE_ON);//LSE 开启
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
//检查指定的 RCC 标志位设置与否,等待低速晶振就绪
{ retry++;
delay_ms(10);
}
if(retry==0)return 1; //LSE 开启失败.
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //选择 LSE 作为 RTC 时钟
RCC_RTCCLKCmd(ENABLE); //使能 RTC 时钟
RTC_InitStructure.RTC_AsynchPrediv = 0x7F;//RTC 异步分频系数(1~0X7F)
RTC_InitStructure.RTC_SynchPrediv = 0xFF;//RTC 同步分频系数(0~7FFF)
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;//24 小时格式
RTC_Init(&RTC_InitStructure);//初始化 RTC 参数
RTC_Set_Time(23,59,56,RTC_H12_AM); //设置时间
RTC_Set_Date(14,5,5,1); //设置日期
RTC_WriteBackupRegister(RTC_BKP_DR0,0x5050); //标记已经初始化过了
}
return 0;
}

窗口看门狗时钟

介绍

  • 窗口看门狗(WWDG Window Watch Dog Gou)通常被用来监测由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障
  • 之所以称为窗口就是因为其喂狗时间是一个有上下限的范围内(窗口),你可以通过设定相关寄存器,设定其上限时间(下限固定)。喂狗的时间不能过早也不能过晚。
  • 喂狗就是窗口看门狗的计数器T[6:0]被更新。
    在这里插入图片描述

窗口看门狗的必要性

  • 对于一般的看门狗,程序可以在它产生复位前的任意时刻刷新看门狗,但这有一个隐患,有可能程序跑乱了又跑回到正常的地方,或跑乱的程序正好执行了刷新看门狗操作,这样的情况下一般的看门狗就检测不出来了;
  • 如果使用窗口看门狗,程序员可以根据程序正常执行的时间设置刷新看门狗的一个时间窗口,保证不会提前刷新看门狗也不会滞后刷新看门狗,这样可以检测出程序没有按照正常的路径运行非正常地跳过了某些程序段的情况

复位原理

W[6:0]即是 WWDG->CFR 的低七位。T[6:0]就是窗口看门狗的计数器,而 W[6:0]则是窗口看门狗的上窗口,下窗口值是固定的(0X40)。

  • 递减计数器的值在 T6 位(WWDG->CR 的第六位)如果在变成 0 前不被刷新,看门狗电路在达到预置的时间周期时,会产生一个 MCU 复位。
  • 在递减计数器达到窗口配置寄存器(WWDG->CFR)数值之前,如果 7 位的递减计数器数值(在控制寄存器中)被刷新,也就是CFR寄存器被写入, 那么也将产生一个 MCU 复位。
    在这里插入图片描述

窗口看门狗超时时间

窗口看门狗的超时公式如下:
Twwdg=(4096×2^WDGTB×(T[5:0]+1)) /Fpclk1;
其中:
Twwdg:WWDG 超时时间(单位为 ms)
Fpclk1:APB1 的时钟频率(单位为 Khz)
WDGTB:WWDG 的预分频系数
T[5:0]:窗口看门狗的计数器低 6 位
在这里插入图片描述

窗口看门狗的寄存器

控制寄存器(WWDG_CR)

在这里插入图片描述

T6-0 看门狗计数器值

WWDG_CR 只有低八位有效,T[6:0]用来存储看门狗的计数器值,随时更新的,每个窗口看门狗计数周期(4096×2^ WDGTB)减 1。当该计数器的值从 0X40 变为 0X3F 的时候,将产生看门狗复位。

WDGA 看门狗激活位

看门狗的激活位,该位由软件置 1,以启动看门狗,并且一定要注意的是该位一旦设置,就只能在硬件复位后才能清零了。

配置寄存器(WWDG_CFR)

中文参考手册19.6.2
在这里插入图片描述

9 EWI early wakeup interrupt

如果启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),它可以用于喂狗以避免WWDG复位。

  • 提前唤醒中断,也就是在快要产生复位的前一段时间(T[6:0]=0X40)来提醒我们,需要进行喂狗了,否则将复位!
  • 一般用该位来设置中断,当窗口看门狗的计数器值减到 0X40 的时候,如果该位设置,并开启了中断,则会产生中断,我们可以在中断里面向 WWDG_CR 重新写入计数器的值,来达到喂狗的目的。
  • 进入中断后,必须在不大于 1 个窗口看门狗计数周期的时间(在 PCLK1 频率为 36M 且 WDGTB 为 0 的条件下,该时间为 113us)内重新写 WWDG_CR,否则,看门狗将产生复位!
8-7 WDGTB 预分频

在这里插入图片描述

6-0 W[6:0] 7位窗口值

在这里插入图片描述

状态寄存器(WWDG_SR)

用来记录当前是否有提前唤醒的标志。该寄存器仅有位 0 有效,其他都是保留位。
当计数器值达到 40h 时,此位由硬件置 1。它必须通过软件写 0 来清除。对此位写 1 无效。即使中断未被使能,在计数器的值达到 0X40的时候,此位也会被置 1。

使用步骤

使能看门狗时钟

RCC_APB1PeriphClockCmd();

设置分频系数

WWDG_SetPrescaler();

设置上窗口值

WWDG_SetWindowValue();

开启提前唤醒中断并分组(可选)

WWDG_EnableIT();
NVIC_Init();

使能看门狗

WWDG_Enable();

喂狗

WWDG_SetCounter();

编写中断服务函数

WWDG_IRQHandler();

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万码无虫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值