• 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倍频后的二分频时钟

     

  • 相关阅读:
    [PHP] 适配器模式的日常使用
    [MySQL] timestamp和datetime的区别和大坑
    [PHP] foreach循环的引用赋值可能导致的问题
    [PHP] 邮件发送mail()函数失败问题 sendmail命令与postfix
    [日常] 腾讯云发送邮件失败问题
    [日常] 正则表达式 小括号() 中括号[] 大括号{}
    [Linux] awk与posix字符集
    [PHP] 工厂模式的日常使用
    [PHP] 抽象类abstract的回顾
    [日常]灵活的频率限制实现
  • 原文地址:https://www.cnblogs.com/xqzhao/p/3016873.html
Copyright © 2020-2023  润新知