找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 8536|回复: 1
收起左侧

学习笔记-STM32F10xx 时钟系统及代码分析

[复制链接]
ID:91350 发表于 2015-10-29 15:50 | 显示全部楼层 |阅读模式
 

19集     STM32 时钟

 


蓝色方框为时钟源:HSI RC、HSE OSC、PLL、LSE OSC、LSI RC

梯形紫色为选择器:就像一个切换开关。

可以从两步分析该时钟图:

1、从SYSCLK开始看,由谁可以提供时钟给SYSCLK,逐一分析。

2、从RTCCLK、IWDGCLK开始分析,由谁可以提供时钟给RTCCLK\IWDGCLK。

 

一、时钟源:内部时钟的精度都比较差(《STM32中文参考手册 V10》 ---- 57页)

HSI RC: 高速内部时钟(约8M),当被用作PLL时钟的输入时,系统时钟能得到的最大频率为64MHz

HSE OSC: 高速外部时钟,一般采用晶振(可选4~16M,一般选8M)

PLL: 锁相环(2~16倍),用于倍频时钟,可选来源 HSI 二分频、HSE、HSE二分频。

PLL的设置必须在其激活前完成,一旦PLL被激活,这些参数都无法被改动,如果发现PLL的    设置无效,就可能在设置时PLL已经完成激活,如果需要使用USB接口,PLL必须被设置为输    出48MHz或者72MHz时钟,用于提供48MHz的USBCLK时钟。

LSE OSC: 低速外部时钟,一般采用晶振(32.768KHz)用于提供RTC时钟

LSI RC: 低速内部时钟(约40KHz),用于提供独立看门口作为时钟源

 

二、SYSCLK:(最重要的时钟)

从上图中可以看到,SYSCLK为最重要的时钟,几乎大部分的外设时钟都是从SYSCLK得到时钟信号。所以可以从SYSCLK开始分析。

SYSCLK可以选择从HSI RC、PLL、HSE OSC取其中一个时钟信号,而PLL则可以从HSI RC的二分频、HSE OSC、HSE OSC 二分频取得时钟信号。

系统复位后,HSI为默认的系统时钟来源。在做时钟源切换时,目标时钟源稳定就绪后才会发生切换。

1、CSS 时钟安全系统:(当系统运行速度莫名其妙的下降、或者USB不工作,可以留意这里)

因STM32内部时钟是利用电容产生的时钟,并不是太稳定,所以一般项目中都会采用外部晶振通过HSE OSC提供稳定的时钟给系统,当外部晶振出现问题即HSE失效时,CSS就会自动将时钟源切换至HSI RC。

CSS可以通过软件被激活,一旦其被激活,CSS将在HSE振荡器启动延迟后被使能,并在HSE时钟关闭后关闭。

如果HSE时钟发生故障,HSE振荡器被自动关闭,时钟失效事件将被送到高级定时器(TIM1和TIM8)的刹车输入端,并产生时钟安全中断CSSI,允许软件完成营救操作。此CSSI中断连接到Cortex-M3的NMI中断。

一旦CSS被激活,并且HSE时钟出现故障,CSS中断就产生,并且NMI也自动产生。NMI将不断执行,直到CSS中断挂起位被清除。因此,在NMI的处理程序中必须通过设置时钟中断寄存器(RCC_CIR)里的CSSC位来清除CSS中断。

如果HSE振荡器被直接或间接地作为系统时钟,时钟故障将导致系统时钟自动切换到HSI振荡器,同时外部HSE振荡器被关闭。在时钟失效时,如果HSE振荡器时钟是用做系统时钟的PLL的输入时钟,PLL也将关闭。(问题来了,那么USBCLK是不是也就失效了吧?

2、每个时钟都可以独立开启或关闭,这样可以关闭不需要的时钟从而降低功耗。

从下图可以看出,每次需要使用某个外设时都要开启相应的外设时钟。

(参考STM32中文参考手册 V10 56页)

 

三、RTCCLK(RTC时钟)

RTCCLK可以选择从HSE OSC的128分频、LSE OSC、LSI RC中取时钟信号。

这里没有CSS,那如果采用HSE的时钟信号而其又失效,岂不是RTC也就失效了?

 

四、IWDGCLK(看门狗时钟)

看门口时钟只能从 LSI RC中取得时钟源。

 

五、MCO 输出(PA8引脚)

STM32还可以对外输出时钟,时钟源可以从SYSCLK、HSI、HSE、PLLCLK的二分频取得信号。

 

六、重要的时钟

SYSCLK系统时钟

AHB总线时钟(高速)

APB1总线时钟(低速):速度最高36MHz 连接低速外设

APB2总线时钟(高速):速度最高72MHz 连接高速外设

PLL时钟:用于倍频

 

七、寄存器相关(具体参阅 《STM32中文参考手册 V10》 60~75页)

 

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;

 

0x4002 1000 - 0x4002 13FF 复位和时钟控制(RCC)

#define PERIPH_BASE           ((uint32_t)0x40000000)

#define AHBPERIPH_BASE       (PERIPH_BASE + 0x20000)

#define RCC_BASE              (AHBPERIPH_BASE + 0x1000)

#define RCC                       ((RCC_TypeDef *) RCC_BASE)

 

八、库函数类型(system_stm32f10x.c -- SystemInit() 函数中通过直接配置寄存器配置,并未采用库函数)

1、时钟使能配置:

      RCC_LSEConfig() 、RCC_HSEConfig()、

      RCC_HSICmd() 、 RCC_LSICmd() 、 RCC_PLLCmd() …

2、时钟源相关配置:

      RCC_PLLConfig ()、 RCC_SYSCLKConfig() 、

     RCC_RTCCLKConfig() 

3、分频系数选择配置:

      RCC_HCLKConfig() 、 RCC_PCLK1Config() 、 RCC_PCLK2Config()…

4、外设时钟使能:

      RCC_APB1PeriphClockCmd():  //APB1线上外设时钟使能

     RCC_APB2PeriphClockCmd();  //APB2线上外设时钟使能

     RCC_AHBPeriphClockCmd();   //AHB线上外设时钟使能

5.  其他外设时钟配置:

     RCC_ADCCLKConfig ();  RCC_RTCCLKConfig();

6、状态参数获取参数:

     RCC_GetClocksFreq();

     RCC_GetSYSCLKSource();

     RCC_GetFlagStatus()

7、RCC中断相关函数 :

     RCC_ITConfig() 、 RCC_GetITStatus() 、 RCC_ClearITPendingBit()…

 

20集     SystemInit()

 

SystemInit()函数在 startup_stm32f10x_hd.s 文件里面先于main函数调用。

也就是说我们在写mian函数时,不必去调用SystemInit()函数。

 

一、代码分析 (需要参考 《STM32中文参考手册 V10.pdf、《STM32F10xxx闪存编程参考手册.pdf》)

void SystemInit (void)

{

  // 开启HSI内部8MHz高速时钟 1

  RCC->CR |= (uint32_t)0x00000001;       

 

  // 设置HSI做为系统时钟 1:0

  // SYSCLK不分频 7:4

  // APB1\APB2 不分频 13:8

  // ADC预分频 PCLK2 2分频后作为ADC时钟 15:14

  // MCO引脚不输出时钟 26:24

  RCC->CFGR &= (uint32_t)0xF8FF0000;   

 

  // HSE振荡器关闭 16

  // CSS时钟监视关闭 19

  // PLL关闭 24

  RCC->CR &= (uint32_t)0xFEF6FFFF;

 

  // 设置外部4-16MHz振荡器没有旁路 18

  RCC->CR &= (uint32_t)0xFFFBFFFF;

 

  // 设置HSI振荡器时钟经2分频后作为PLL输入时钟 16

  // 设置HSE不分频 17

  // 设置PLL 2倍频输出 21:18

  // 设置USB预分频 PLL时钟1.5倍分频作为USB时钟 22

  RCC->CFGR &= (uint32_t)0xFF80FFFF;

 

  // 设置LSI、LSE、HSI、HSE、PLL就绪中断关闭 12:8

  // 清除LSI、LSE、HSI、HSE、PLL就绪中断标志位 20:16

  // 清楚CSSF时钟安全系统中断标志位 23

  RCC->CIR = 0x009F0000;

 

  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */

  /* Configure the Flash Latency cycles and enable prefetch buffer */

  SetSysClock();

 

#ifdef VECT_TAB_SRAM

  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */

#else

  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH.*/

#endif

}

 

#define SYSCLK_FREQ_72MHz  72000000

static void SetSysClock(void)

{

#ifdef SYSCLK_FREQ_HSE

  SetSysClockToHSE();

#elif defined SYSCLK_FREQ_24MHz

  SetSysClockTo24();

#elif defined SYSCLK_FREQ_36MHz

  SetSysClockTo36();

#elif defined SYSCLK_FREQ_48MHz

  SetSysClockTo48();

#elif defined SYSCLK_FREQ_56MHz

  SetSysClockTo56(); 

#elif defined SYSCLK_FREQ_72MHz        // <--- 该宏被定义

  SetSysClockTo72();                // <--- 该函数被调用

#endif

}

 

#define  RCC_CR_HSEON         ((uint32_t)0x00010000)

#define  RCC_CR_HSERDY        ((uint32_t)0x00020000)

#define HSE_STARTUP_TIMEOUT   ((uint16_t)0x0500)

 

#define  FLASH_ACR_PRFTBE                     ((uint8_t)0x10)

#define  FLASH_ACR_LATENCY                   ((uint8_t)0x03)

#define  FLASH_ACR_LATENCY_2                 ((uint8_t)0x02)

#define  RCC_CFGR_HPRE_DIV1                  ((uint32_t)0x00000000)

#define  RCC_CFGR_PPRE2_DIV1                 ((uint32_t)0x00000000)

#define  RCC_CFGR_PPRE1_DIV2                 ((uint32_t)0x00000400)

#define  RCC_CFGR_PLLSRC                     ((uint32_t)0x00010000)

#define  RCC_CFGR_PLLXTPRE                   ((uint32_t)0x00020000) 

#define  RCC_CFGR_PLLMULL                    ((uint32_t)0x003C0000)

#define  RCC_CFGR_PLLSRC_HSE                 ((uint32_t)0x00010000)

#define  RCC_CFGR_PLLMULL9                   ((uint32_t)0x001C0000)

#define  RCC_CR_PLLON                        ((uint32_t)0x01000000)

#define  RCC_CR_PLLRDY                       ((uint32_t)0x02000000)

#define  RCC_CFGR_SW                         ((uint32_t)0x00000003)

#define  RCC_CFGR_SW_PLL                     ((uint32_t)0x00000002)

#define  RCC_CFGR_SWS                        ((uint32_t)0x0000000C)

 

typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;

 

/* 要设置 72MHz 需要使用HSE外部时钟 */

static void SetSysClockTo72(void)

{

  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

  // 开启HSE时钟 16

  RCC->CR |= ((uint32_t)RCC_CR_HSEON);

  // 在一定的时间内等待 HSE 时钟稳定

  do

  {

    HSEStatus = RCC->CR & RCC_CR_HSERDY;

    StartUpCounter++; 

  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

  if ((RCC->CR & RCC_CR_HSERDY) != RESET)

  {

    HSEStatus = (uint32_t)0x01;        // HSE时钟就绪

  }

  else

  {

    HSEStatus = (uint32_t)0x00;        // HSE时钟未就绪

  } 

  // 如果HSE时钟就绪

  if (HSEStatus == (uint32_t)0x01)

  {

    // 启用预取缓冲区 4

    FLASH->ACR |= FLASH_ACR_PRFTBE; 

    // 48MHz < SYSCLK <= 72MHz 设置两个等待周期 2:0

    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);

    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;   

    /*** SYSCLK -> HCLK -> PLCK1\PCLK2 */

    /* HCLK = SYSCLK */

    // 设置AHB分频系数为 1 即不分频,等于SYSCLK的频率 即72MHz

    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;    

    /* PCLK2 = HCLK */

    // 设置APB2分频系数为 1 即不分频,等于HCLK的频率 即72MHz

    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;   

    /* PCLK1 = HCLK/2 */

    // 设置APB1分频系数为 2 即二分频,等于HCLK/2的频率 即36MHz

    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

    /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */

    // 这里主要是清零操作 21:16

    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));

    // 设置HSE作为PLL输入时钟 16

    // 设置PLL 9 倍频输出 (HSE为8M晶振输入:SYSCLK = 8MHz * 9 = 72MHz)

    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);

    /* Enable PLL */

    // 开启PLL

    RCC->CR |= RCC_CR_PLLON;

    /* Wait till PLL is ready */

    // 等待PLL就绪标志位就绪 25

    while((RCC->CR & RCC_CR_PLLRDY) == 0)

    {

    }   

    /* Select PLL as system clock source */

    // 清零操作

    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));

    // 设置PLL为SYSCLK时钟源

    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;   

    /* Wait till PLL is used as system clock source */

    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)

    {

    }

  }

  else    // HSE未就绪

  { /* If HSE fails to start-up, the application will have wrong clock

         configuration. User can add here some code to deal with this error */

  }

}

 

二、总结

    SystemInit():

        设置SYSCLK、APB1、APB2不分频,MCO不输出时钟

        SYSCLK由PLL提供时钟、PLL则由HSI经2分频后提供时钟

        设置USB时钟为PLL输出时钟的1.5倍

        关闭所有时钟源的中断、清除所有时钟源的中断标志位

    SetSysClockTo72():

        该函数首先是开启HSE时钟,然后在一定的时间等待其稳定。

        稳定之后,设置FLASH开启预取缓冲区、设置两个等待周期。

        接着设置AHB、APB2、APB1分配系数来配置HCLK、PCLK2、PCLK1的时钟频率。

        再设置HSE作为PLL时钟源,并设置PLL为9倍频,再开启PLL,等待PLL就绪。

 

三、注意

SystemCoreClock 在实际写代码中可以通过这个变量来获得当前所设置的系统时钟频率值

system_stm32f10x.h:

extern uint32_t SystemCoreClock;

system_stm32f10x.c:

#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)

/* #define SYSCLK_FREQ_HSE    HSE_VALUE */

 #define SYSCLK_FREQ_24MHz  24000000

#else

/* #define SYSCLK_FREQ_HSE    HSE_VALUE */

/* #define SYSCLK_FREQ_24MHz  24000000 */

/* #define SYSCLK_FREQ_36MHz  36000000 */

/* #define SYSCLK_FREQ_48MHz  48000000 */

/* #define SYSCLK_FREQ_56MHz  56000000 */

#define SYSCLK_FREQ_72MHz  72000000

#endif

 

#ifdef SYSCLK_FREQ_HSE

  uint32_t SystemCoreClock         = SYSCLK_FREQ_HSE;  

#elif defined SYSCLK_FREQ_24MHz

  uint32_t SystemCoreClock         = SYSCLK_FREQ_24MHz;

#elif defined SYSCLK_FREQ_36MHz

  uint32_t SystemCoreClock         = SYSCLK_FREQ_36MHz;

#elif defined SYSCLK_FREQ_48MHz

  uint32_t SystemCoreClock         = SYSCLK_FREQ_48MHz;

#elif defined SYSCLK_FREQ_56MHz

  uint32_t SystemCoreClock         = SYSCLK_FREQ_56MHz;

#elif defined SYSCLK_FREQ_72MHz

  uint32_t SystemCoreClock         = SYSCLK_FREQ_72MHz;

#else /*!< HSI Selected as System Clock source */

  uint32_t SystemCoreClock         = HSI_VALUE;       

#endif   

 


回复

使用道具 举报

ID:83257 发表于 2019-2-15 10:20 | 显示全部楼层
不错,讲得还蛮详细的
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表