zoukankan      html  css  js  c++  java
  • STM32F10x芯片的时钟

    1、介绍

                STM32F10x芯片的时钟控制主要包括以下几个方面知识:时钟源的选择(HSEHISPLL)、系统时钟频率的配置、总线(AHBAPB2APB1)时钟的配置、总线(AHBAPB2APB1)设备时钟的使能/除能、总线(AHBAPB2APB1)设备的复位

    2、系统时钟框图

    STM32F10x可以用三种不同的时钟源来驱动系统时钟(SYSCLK):HSI振荡器时钟;HSE外部时钟和PLL时钟。他们之间的关系如附件所示(时钟树)。

    从时钟树中可以看出一下几点:

    l         系统时钟的来源可以是HSI振荡器时钟、PLL时钟、或者HSE时钟;且系统总线时钟最大值为72MHz,AHB和APB2总线最大频率也是72MHz,APB1总线最大允许频率是36MHz;而Cortex-M3核的自由运行时钟是FCLK(来源于AHB总线);

    l         stm32f10x芯片总共有四个时钟源:HSE、LSE(外部时钟信号);HIS、LSI(内部时钟信号),芯片内的其他所有时钟都是通过如上四个时钟源分频得来;

    l         RTC的时钟来源可以是HSE外部时钟128分频之后的时钟、或者LSE外部时钟(32.768kHz)或者内部LSI振荡器时钟;

    l         IWDG的时钟来源必须是内部LSI振荡器时钟;

    l         MCO引脚的时钟输出源的来源有:PLL时钟的2分频、内部HIS时钟、外部HSE时钟以及系统时钟。

    l         PLL时钟的来源可以是HIS振荡器时钟或者HSE外部提供的时钟;

    l         USB外设是直接使用PLL输出时钟(如果使用USB外设,HSE和PLL时钟都必须使能,且系统时钟必须是48MHz或者72MHz);AHB总线的时钟输入源的是系统时钟;APB1和APB2的时钟来源是AHB;

    l         始终安全系统(CSS必须由HSE提供时钟源;若CSS激活且HSE时钟出现故障,则引发CSS中断,同时产生NMI(NMI中断是不可屏蔽的),NMI将被不断执行,知道CSS中断挂起位被清除;

    l         定时器时钟要么等于总线时钟,要么等于总线时钟频率的两倍,这取决于总线分频系数的值是否为1;

    l         当HIS被用于作为PLL时钟输入时,系统时钟能得到的最大频率是64MHz;

    l         Cortex-M3内核的自由运行时间是FCLK。

    3、时钟寄存器描述

    l         时钟控制寄存器:RCC_CR

    l         时钟配置寄存器:RCC_CFGR

    l         时钟中断寄存器:RCC_CIR

    l         APB2外设复位寄存器:RCC_APB2RSTR

    l         APB1外设复位寄存器:RCC_APB1RSTR

    l         AHB外设时钟使能寄存器:RCC_AHBENR

    l         APB2外设时钟使能寄存器:RCC_APB2ENR

    l         APB1外设时钟使能寄存器:RCC_APB1ENR

    l         备份域控制寄存器:RCC_BDCR

    l         控制/状态寄存器:RCC_CSR

     

     

    4、时钟控制主要按照以下五步进行控制

    l         系统复位后,HSI振荡器被选为系统时钟;

    l         调用RCC_DeInit()函数将外设RCC寄存器重置为缺省值;

    l         选择系统时钟

    ?         若选择HSE做系统时钟:先调用RCC_HSEConfig()使能HSE,然后调用RCC_WaitForHSEStartUp()函数等待HSE起震,最后调用RCC_GetFlagStatus()函数获取HSE晶振状态,查看HIE晶振是否就绪;;

    ?         若选择HSI做系统时钟:首先调用RCC_AdjustHSICalibrationValue()函数调整内部高速晶振校准值(也可以不用,使用系统预留值),然后调用RCC_HSICmd()函数使能HSI,最后调用RCC_GetFlagStatus()函数获取HSI晶振状态,查看HIS晶振是否就绪;

    ?         若要使用PLL做系统时钟,如前面两步将HSE和HIS设定好之后,调用RCC_PLLConig()选择PLL时钟源并设定倍频系数,最后调用RCC_PLLCmd()使能PLL,最后调用RCC_GetFlagStatus()函数获取PLL晶振状态,查看PLL是否就绪;。

    l         最后,在以上时钟配置就绪之后,调用RCC_SYSCLKConfig()函数选择系统时钟输入源:HSE/HIS/PLL。

    至此,系统时钟设定完成,可以调用RCC_GetSYSCLKSource()函数来获取当前系统时钟是使用的哪个时钟(检测设置是否成功):0x010:HIS;x040:HSE;x08:PLL。

    l         然后是总线时钟设置:设置AHB总线时钟:调用RCC_HCLKConfig()函数;设置APB1总线时钟:调用RCC_PCLK1Config()函数;设置APB2总线时钟:调用RCC_PCLK2Config()函数。其中AHB总线时钟来源于SYSCLK总线时钟,APB1和APB2总线时钟来源于AHB总线时钟。注意:这三个时钟的设置可以在系统时钟、PLLHSEHIS启动之前设置,也可以在他们之后设置,但习惯在PLL配置之前。

    l         最后是根据应用需要配置各总线上的外围设备,启动/停用外围设备的函数有:RCC_AHBPeriphClockCmd();RCC_APB2PeriphClockCmd();RCC_APB1PeriphClockCmd();复位总线上的设备函数:RCC_APB2PeriphResetCmd();RCC_APB1PeriphResetCmd();具体可以查看RCC固件库。

    注意:使能外设时钟的函数必须在调用外设初始化函数XXX_Init()函数之前,否则可能会导致对应外设初始化失败,编译器却不会因此报错。

    5、时钟控制例子

    void SetSysClockToHSE(void)

    {

        ErrorStatus HSEStartUpStatus;

        RCC_DeInit();  

        RCC_HSEConfig(RCC_HSE_ON);

        HSEStartUpStatus = RCC_WaitForHSEStartUp();

        if(SUCCESS == HSEStartUpStatus)

        {

            FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

     

            FLASH_SetLatency(FLASH_Latency_0);

            RCC_HCLKConfig(RCC_SYSCLK_Div1);

            RCC_PCLK2Config(RCC_HCLK_Div1);

            RCC_PCLK1Config(RCC_HCLK_Div1);

            RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);

            while(0x04 != RCC_GetSYSCLKSource())

            {

            }

        }

        else

        {

        }

    }

    void SetSysClockTo20(void)

    {

        ErrorStatus HSEStartUpStatus;

        RCC_DeInit();

        RCC_HSEConfig(RCC_HSE_ON);

        HSEStartUpStatus = RCC_WaitForHSEStartUp();

        if(SUCCESS == HSEStartUpStatus)

        {

            FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

            FLASH_SetLatency(FLASH_Latency_0);

            RCC_HCLKConfig(RCC_SYSCLK_Div1);

            RCC_PCLK2Config(RCC_HCLK_Div1);

            RCC_PCLK1Config(RCC_HCLK_Div1);

            RCC_PLLConfig(RCC_PLLSource_HSE_Div2,RCC_PLLMul_5);

            RCC_PLLCmd(ENABLE);

            while(RESET == RCC_GetFlagStatus(RCC_FLAG_PLLRDY))

            {

            }

            RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

            while(0x08 != RCC_GetSYSCLKSource())

            {

            }

        }

        else

        {

        }

    }

    void SetSysClockTo36(void)

    {

        ErrorStatus HSEStartUpStatus; 

        RCC_DeInit();

        RCC_HSEConfig(RCC_HSE_ON);

        HSEStartUpStatus = RCC_WaitForHSEStartUp();

        if(SUCCESS == HSEStartUpStatus)

        {

            FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

            FLASH_SetLatency(FLASH_Latency_1);

            RCC_HCLKConfig(RCC_SYSCLK_Div1);

            RCC_PCLK2Config(RCC_HCLK_Div1);

            RCC_PCLK1Config(RCC_HCLK_Div1);

            RCC_PLLConfig(RCC_PLLSource_HSE_Div2,RCC_PLLMul_9);

            RCC_PLLCmd(ENABLE);

            while(RESET == RCC_GetFlagStatus(RCC_FLAG_PLLRDY))

            {

            }

            RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

            while(0x08 != RCC_GetSYSCLKSource())

            {

            }

        }

        else

        {

        }

    }

    void SetSysClockTo48(void)

    {

        ErrorStatus HSEStartUpStatus; 

        RCC_DeInit();

        RCC_HSEConfig(RCC_HSE_ON);

        HSEStartUpStatus = RCC_WaitForHSEStartUp();

        if(SUCCESS == HSEStartUpStatus)

        {

            FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

            FLASH_SetLatency(FLASH_Latency_1);

            RCC_HCLKConfig(RCC_SYSCLK_Div1);

            RCC_PCLK2Config(RCC_HCLK_Div1);

            RCC_PCLK1Config(RCC_HCLK_Div2);

            RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_6);

            RCC_PLLCmd(ENABLE);

            while(RESET == RCC_GetFlagStatus(RCC_FLAG_PLLRDY))

            {

            }

            RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

            while(0x08 != RCC_GetSYSCLKSource())

            {

            }

        }

        else

        {

        }

    }

    void SetSysClockTo72(void)

    {

        ErrorStatus HSEStartUpStatus;

        RCC_DeInit();

        RCC_HSEConfig(RCC_HSE_ON);

        HSEStartUpStatus = RCC_WaitForHSEStartUp();

        if(SUCCESS == HSEStartUpStatus)

        {

            FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

            FLASH_SetLatency(FLASH_Latency_2);

            RCC_HCLKConfig(RCC_SYSCLK_Div1);

            RCC_PCLK2Config(RCC_HCLK_Div1);

            RCC_PCLK1Config(RCC_HCLK_Div2);

            RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);

            RCC_PLLCmd(ENABLE);

            while(RESET == RCC_GetFlagStatus(RCC_FLAG_PLLRDY))

            {

            }

            RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

            while(0x08 != RCC_GetSYSCLKSource())

            {

            }

        }

        else

        {

        }

    }    

     

    6、系统时钟安全系统CSS

           在实际应用中,经常出现由于晶体振荡器在运行中失去作用,造成微处理器的时钟源丢失,从而出现死机的现象,导致系统出错。为避免发声这种严重错误,STM32F10X系列芯片提供了一个时钟安全系统CCS机制。如下图:

           时钟安全系统被激活后,时钟监控器将实时监控外部高速振荡器;如果HSE时钟发生故障,外部振荡器自动被关闭,产生时钟安全中断,此中断被连接到Cortex-M3的NVIC中断;与此同时CSS将内部RC振荡器(HSI)切换为STM32的系统时钟源。

           注意:一旦CSS被激活,当HSE时钟出现故障产生CSS中断,同时自动产生NMI,NMI将不断执行,直到CSS中断挂起位被清除。因此在NMI的处理程序中,必须通过设置时钟中断寄存器RCC_CIR中的CSSC位(软件置1清除)来清除CSS中断。(其实RCC的其他各时钟源的就绪中断标志,也都需要通过软件置1来清除,只是CSS是NMI中断(不可屏蔽中断),其他中断需要设置相应位允许中断,并要在NVIC中打开RCC的中断通道)

    7、系统时钟安全系统CSS应用

           启动时钟安全系统CCS:

    RCC_ClockSecuritySystemCmd(ENABLE);

           编写NMI中断处理函数:

    void NMI_Handler(void)

    {

           if(RESET != RCC_GetITStatus(RCC_IT_CSS))

           {             /* HSEPLL已经被禁止,但PLL设置未变 */

                  ……/* 客户添加相应的系统保护代码处理 */

                  /* 下面添加HSE恢复后的预设代码 */

                  RCC_HSEConfig(RCC_HSE_ON);

                  RCC_ITConfig(RCC_IT_HSERDY,ENABLE);

                  RCC_ITConfig(RCC_IT_PLLRDY,ENABLE);

                  RCC_ClearITPendingBit(RCC_IT_CSS);

           /* 至此一旦HSE时钟恢复,将发生HSERDY中断,在RCC中断处理程序中,可以将系统时钟设置到以前的状态 */

    }

    }

           编写RCC中断处理函数:

    void RCC_IRQHandler(void)

    {

    if(RESET != RCC_GetITStatus(RCC_IT_HSERDY))

    {

    /* 添加相应处理 */

           RCC_ClearITPendingBit(RCC_IT_HSERDY);

    }

    if(RESET != RCC_GetITStatus(RCC_IT_PLLRDY))

    {

    /* 添加相应处理 */

           RCC_ClearITPendingBit(RCC_IT_PLLRDY);

    }

    }

    8、输出芯片内部时钟

           STM32F10x芯片支持将内部时钟通过PA.8输出,但是必须注意GPIO输出管脚最大响应频率为50MHz,如果超过这个频率,输出的波形将会失真。应用实例如下:

    首先配置端口PA.8

    GPIO_InitTypeDef GPIO_InitStructure;   

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_Init(GPIOA,&GPIO_InitStructure);

    然后调用函数RCC_MCOConfig(RCC_MCO)选择要输出的内部时钟:RCC_MCO可以是:

    RCC_MCO_NoClock——无时钟输出

    RCC_MCO_SYSCLK——输出系统时钟

    RCC_MCO_HSI——输出内部高速8MHzRC振荡器时钟

    RCC_MCO_HSE——输出外部时钟信号

    RCC_MCO_PLLCLK_Div2——输出PLL倍频后的二分频时钟

     

  • 相关阅读:
    Leetcode 16.25 LRU缓存 哈希表与双向链表的组合
    Leetcode437 路径总和 III 双递归与前缀和
    leetcode 0404 二叉树检查平衡性 DFS
    Leetcode 1219 黄金矿工 暴力回溯
    Leetcode1218 最长定差子序列 哈希表优化DP
    Leetcode 91 解码方法
    Leetcode 129 求根到叶子节点数字之和 DFS优化
    Leetcode 125 验证回文串 双指针
    Docker安装Mysql记录
    vmware虚拟机---Liunx配置静态IP
  • 原文地址:https://www.cnblogs.com/xqzhao/p/3016873.html
Copyright © 2011-2022 走看看