第十章 STM32时钟系统

目录

10.1 时钟系统的时钟系统的基本概念和关键参数

10.1.1 时钟信号的作用和种类

10.1.2 时钟频率、周期、分频系数等关键参数的定义和计算方法。

10.2 RCC模块的基本原理和功能

10.2.1 什么是RCC

10.2.2 RCC的主要作用

10.2.3 RCC系统相关寄存器介绍

10.3 RCC时钟树分析

10.3.1 STM32时钟源

10.3.2 时钟树分析

10.4 系统时钟配置程序设计

10.4.1 使用HSE

10.4.2 使用HIS

10.4.3 实验要求

10.4.4 硬件设计

10.4.5 软件设计

10.4.6 下载验证

10.5 利用MCO对外提供时钟输出

10.5.1 实验要求

10.5.2 硬件设计

10.5.3 软件设计

10.5.4 下载验证


10.1 时钟系统的时钟系统的基本概念和关键参数

10.1.1 时钟信号的作用和种类

        时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的指令,时钟系统就是CPU的脉搏决定CPU速率,像人的心跳一样,只有有了心跳,人才能做其他的事情,而单片机有了时钟才能够运行执行指令才能够做其他的处理,在之前的实验中,我们配置相应GPIO端口时都对时钟进行了配置。

        STM32时钟信号的作用是驱动芯片内部各种时序电路的工作,包括CPU、存储器、外设等。它们可以同步各个模块之间的数据传输和操作,确保系统的稳定运行和正确功能实现。同时,时钟信号还可以提供时间基准,支持系统中所有计时和定时功能的实现。因此,正确配置和使用STM32时钟信号对于系统性能和功耗等方面都有重要影响。

        常见的STM32时钟信号种类包括以下几种:

        HCLK:总线时钟,即CPU和所有外设使用的主要时钟信号。

        PCLK1和PCLK2:低速外设时钟,分别用于连接到APB1和APB2总线的低速外设。

        SYSCLK:系统时钟,是STM32内部所有模块的主时钟信号,它通常由高速外部晶体振荡器或者内部RC震荡器提供。

        RTCCLK:实时时钟时钟,用于RTC模块的时钟信号,可以来源于LSE(低速外部晶体振荡器)或者LSI(内部低速时钟)。

        MCO:主时钟输出,可以连接到外部设备上,例如频率计或逻辑分析仪等。

10.1.2 时钟频率、周期、分频系数等关键参数的定义和计算方法。

        在STM32中,时钟频率、周期、分频系数等关键参数的定义如下:

        时钟频率(fclk):时钟信号每秒钟的振荡次数,即基础时钟频率。在STM32中,时钟频率通常指系统时钟(SYSCLK)的频率。

        SYSCLK频率计算方法:

        SYSCLK = fOSC / PLLM * PLLN / PLLP

        其中,fOSC是外部晶体振荡器的频率,PLLM和PLLN是PLL前置分频和反馈分频系数,PLLP是PLL输出分频系数。

        时钟周期(Tclk):时钟信号一个完整的周期所需要的时间,即1/fclk。

        时钟周期计算方法:Tclk = 1 / fclk

        分频系数(div):将时钟频率降低到特定频率的比例因子,通常用于控制外设模块的时钟频率。例如,APB1总线时钟的频率由SYSCLK和分频系数决定。

        分频系数计算方法:

        例如,如果要设置APB1总线时钟为42MHz,则可使用以下公式计算分频系数:

        div = SYSCLK / 42000000 - 1

        其中,SYSCLK是已知的系统时钟频率,42000000是目标APB1总线时钟频率。需要注意的是,STM32芯片支持多种时钟源和时钟分频方式,在使用时需要仔细阅读相关文档,并根据具体应用场景和需求进行选择和配置。

10.2 RCC模块的基本原理和功能

10.2.1 什么是RCC

        在STM32中,RCC代表复位和时钟控制单元(Reset and Clock Control),它是一个重要的内部模块。RCC模块主要负责控制外设和处理器的时钟源以及重置系统。在STM32中,每个外设都需要一个时钟信号来驱动其工作,因此RCC模块非常重要,可以确保系统运行稳定可靠,并提供必要的时钟信号支持外设和CPU的正常工作。

        RCC模块可以选择不同的时钟源,例如内部RC振荡器、外部晶体振荡器或PLL锁相环等作为系统时钟源,并根据需要配置不同的时钟分频系数,以满足不同外设和处理器的时钟需求。同时,RCC模块还可以为每个外设配置独立的时钟源和分频系数,以满足不同外设的时钟需求。

        此外,RCC模块还可以控制处理器和外设的硬件重置信号,例如通过软件复位、WWDG看门狗定时器溢出等方式触发重置操作。同时,RCC模块还可以配置处理器和外设的时钟初始化状态,以满足特定应用的需求。

        总之,RCC是STM32中非常重要的一个模块,能够确保系统运行稳定可靠,并提供必要的时钟信号支持外设和CPU的正常工作。

10.2.2 RCC的主要作用

        提供系统时钟源:RCC模块可以选择不同的时钟源,例如内部RC振荡器、外部晶体振荡器或PLL锁相环等作为系统时钟源。通过配置时钟分频系数,RCC模块可以将时钟信号提供给处理器和各种外设,确保它们的正常工作。

        控制外设时钟源:每个外设都需要一个时钟信号来驱动其工作,RCC模块可以为每个外设配置独立的时钟源和分频系数,以满足不同外设的时钟需求。

        管理硬件复位操作:RCC模块可以控制处理器和外设的硬件重置信号,例如通过软件复位、WWDG看门狗定时器溢出等方式触发重置操作。

        管理时钟初始化状态:在系统启动时,RCC模块可以配置处理器和外设的时钟初始化状态,以确保它们处于正确的工作状态。

10.2.3 RCC系统相关寄存器介绍

10.2.3.1 RCC_CR寄存器

第十章 STM32时钟系统_第1张图片

第十章 STM32时钟系统_第2张图片

         以下是RCC_CR寄存器中一些重要的位域:

        HSEON:外部高速晶体振荡器(HSE)使能位。该位为1表示启用外部高速晶体振荡器作为时钟源。

        HSERDY:外部高速晶体振荡器就绪位。该位为1表示外部高速晶体振荡器已经稳定,并且可以用作时钟源。

        PLLON:PLL(Phase Locked Loop)使能位。该位为1表示启用PLL作为时钟源。

        PLLRDY:PLL就绪位。该位为1表示PLL已经稳定,并且可以用作时钟源。

        CSSON:时钟安全系统(Clock Security System)使能位。该位为1表示启用时钟安全系统,以确保时钟源的稳定性。

        RTCEN:RTC(Real Time Clock)时钟使能位。该位为1表示启用RTC时钟。

        RTCSEL[1:0]:RTC时钟源选择位。这两个位用于选择RTC时钟源,例如使用LSE(Low         Speed External)晶体振荡器或LSI(Low Speed Internal)RC振荡器等。

        总之,通过对RCC_CR寄存器中各位域的配置,可以实现不同的时钟源选择和控制硬件复位等功能。

10.2.3.2 RCC_CFGR寄存器第十章 STM32时钟系统_第3张图片

第十章 STM32时钟系统_第4张图片 第十章 STM32时钟系统_第5张图片第十章 STM32时钟系统_第6张图片

         以下是RCC_CFGR寄存器中一些重要的位域:

        SW[1:0]:系统时钟源选择位。这两个位用于选择系统时钟源,例如使用HSI、HSE或PLL作为时钟源。

        SWS[1:0]:系统时钟源状态位。这两个位用于指示当前系统时钟源的状态,例如正在使用HSI、HSE或PLL作为时钟源。

        HPRE[3:0]:AHB总线分频系数。该位域用于控制AHB总线时钟的分频系数,以满足不同外设对时钟信号精度和稳定性的要求。

        PPRE1[2:0]:APB1总线分频系数。该位域用于控制APB1总线时钟的分频系数,以满足不同外设对时钟信号精度和稳定性的要求。

        PPRE2[2:0]:APB2总线分频系数。该位域用于控制APB2总线时钟的分频系数,以满足不同外设对时钟信号精度和稳定性的要求。

        PLLSRC:PLL输入时钟源选择位。该位用于选择PLL输入时钟源,可以是HSE或者HSI/2。

        PLLMUL[3:0]: PLL倍频系数。该位用于配置PLL的倍频系数,即使得输出频率为输入频率的n倍(n=2~16)。

        通过对RCC_CFGR寄存器中各位域的配置,可以实现不同的时钟源选择、时钟分频等功能,以满足各种应用场景下处理器和外设对时钟信号精度和稳定性的要求。

10.2.3.2 RCC_CIR寄存器第十章 STM32时钟系统_第7张图片第十章 STM32时钟系统_第8张图片

第十章 STM32时钟系统_第9张图片

第十章 STM32时钟系统_第10张图片

         以下是RCC_CIR寄存器中一些重要的位域:

        HSIRDYF:HSI稳定中断标志位。该位为1表示HSI稳定,并且可以用作时钟源。

        HSERDYF:HSE稳定中断标志位。该位为1表示外部高速晶体振荡器已经稳定,并且可以用作时钟源。

        PLLRDYF:PLL稳定中断标志位。该位为1表示PLL已经稳定,并且可以用作时钟源。

        CSSF:时钟安全系统触发中断标志位。该位为1表示时钟安全系统检测到时钟源异常,并且已经触发中断。

        HSITRIM[7:0]:HSI内部校准值。这八个位用于调整HSI的频率,以提高时钟精度。

        通过对RCC_CIR寄存器中各位域的操作,可以实现检测和清除时钟中断信号,以确保时钟源的稳定性和可靠性。

10.3 RCC时钟树分析

10.3.1 STM32时钟源

        STM32对于系统时钟提供多种选择,在STM32中有五个时钟源。HISHSE,LSI,LSE,PLL,他们都可以作为系统时钟的来源。

        系统时钟的选择是在启动时进行的,复位时内部8MHz的RC振荡器被选为默认的CPU始终最后可以选择外部的具有失效监控的4~16MHz的时钟,当检测到外部时钟失效时,它将被隔离,系统时钟将自动切换到内部的RC振荡器。

        HSI(High Speed Internal)内部RC振荡器:HSI是STM32内置的高速RC振荡器,频率通常为8MHz或16MHz,具有开机时间短、稳定性高等优点。

        HSE(High Speed External)外部晶体振荡器:HSE是一种外接的高速晶体振荡器,其频率通常为4MHz~26MHz,具有精度高、稳定性好等优点。

        LSI(Low Speed Internal)内部RC振荡器:LSI是STM32内置的低速RC振荡器,频率通常为40kHz,用于提供低功耗的时钟信号。

        LSE(Low Speed External)外部晶体振荡器:LSE是一种外接的低速晶体振荡器,其频率通常为32.768kHz,用于提供低功耗的时钟信号。

        PLL(Phase-Locked Loop)锁相环:PLL是一种基于反馈控制的电路,在输入信号的基础上产生具有倍频效应的输出信号。通过对PLL输入时钟源和倍频

10.3.2 时钟树分析

第十章 STM32时钟系统_第11张图片

 10.3.2.1 HSE高速外部时钟

        HSE外部高速时钟对应图中①部分,该信号可以在芯片部接有源品振或无源晶报提供,频率可以是4~16 MHz不等,使用有源晶振时时钟从OSC_IN引脚进入OSC_OUT悬空。

        振时时钟从OSC_IN,OSC_OUT进入,并且要配谐振电容,HSE最常使用的就是8M的无源晶振。当确定PLL时钟来源的时候,HSE可以不频成者2分频,这个由时钟配置寄存器CFGR的位17:PLLXTPRE来设置,从①经过选择后的时钟信号,传递到②部分。(PLLXTPRE可理解为可用程序控制的“单刀双掷”开关)

10.3.2.2 PLL时钟源

        ②部分的名称是PLL时钟源,PLL时钟的来源有两个,一个是HSE,也就是①过程产生的信号另一个是HSI/2.(也就是高速内部RC振荡器经2分频后传递过来的信号具体选择哪一路,则由时钟配置寄存号CFGR的位16:PLLSRC设置.HSI是内部高速的时钟信号.频率为8MHz。根据温度和环境的情况频率会有漂称,在某些交用环境下可能会存在较大的误差,一般不作为PLL的时钟来源,②信号送出后么送到③PLLMUL时钟倍频。

10.3.2.3 PLLMUL时钟倍频器

        ③部分对应的就是PLLMU时钟倍频器,是用来产生PLL时钟PUCLK的电路。②中信号在经过③中通过锁相环倍频后.输出一路名为PLLCLK的时钟信号,具体要通过设置PLL的倍频因子,可以对PLL的时钟源进行信频,具体设置成多少可由时钟配置寄存器CFGR的位12——位18:PLLMUL[3:0]设置

10.3.2.4 SYSCLK时钟信号的来源

        在③中裁取了PLLCk信号后,紧接着被传道到④处系统时钟选择电路,从图中能够看出SYSCLK系统时钟来源可以是:HSE,HIS,PLLCLK,具体角选择那个时钟由时钟配置寄存器 CFGR的位1-0:SWL:0]设置,从③出来的信号,被分成两条路一条路通过USB预分频器合频用作USB设备的时钟信号,另一路则经④选择后作为SYSCLK系统时钟.送到⑤部分。

10.3.2.5 AHB总线时钟HCLK

        ⑤部分是AHB总线时钟HCLK也就是所有外围设备的时钟信号的总集了,系统时钟SYSCLK经过⑤中的AHB预分频器分频后得到APB总线时钟,即HCLK,具体的分频因子的设置由CFGR的位7-4:HPRE[3:0]设置.片上大部分外设的时钟都是经过HCLK分频得到,至于AHB总线上的外设时钟设置为多少,得等到我们使用的时候才设置,经⑤中出来的信号HCLK会被送到各个外设中去,其中APB1总线时钟HCLK1、APB2上用中HCLK2为两条主要线路

10.3.2.6 APB1总线时钟HCLK1

        ⑥ABl总线用钟 HCLK1由HCLK经过低速APB预分频器得到,具体合频因子由CFGR的位13-11:PPRE1[2:0]决定,HCLK1属于低速总线时钟,最高为36MHz片上的低速外设就挂截到这条总线上、比知USART2/3/4/5,SPI2/3,I2C1/2。

10.3.2.7 APB2总线时钟HCLK2

        ⑦APB2时钟RCLK2

        APB2总线时钟和APB1总线时钟相同都需要HCLK经相应总线预分频器得到,具体分频因子由AFGR的位13-11:PPRE2[2:0]决定.HCLK2属于高速时钟,片上高速外设均接载到这条外设上,如全部的GPIO,USARTI,SPI1等,PCLK2最高可设置为72MHz。

10.3.2.8 时钟配置例程

        配置例程:HSE不分频,选择HSE作为PLL的时钟来源,PLLMUL[3:0]设置的倍频子我们选择9倍频,选择PLLIK做为SYSCLK胜钟信号源,设置AHB预分频器为1分频,APB1总线预分频器设量为2分频,APB2总线颜分频器设置为1分频

static void SetSysClockTo72(void)
{
	__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
	/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/    
	/* ①使能HSE,并等待SE稳定 */    
	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;
	}
	else
	{
		HSEStatus = (uint32_t)0x00;
	}  

	/*HSE启动成功,程序继续执行*/
	if (HSEStatus == (uint32_t)0x01)
	{
		/* 使能Flash预存缓冲区 */
		FLASH->ACR |= FLASH_ACR_PRFTBE;

		/* 设置SYSCLK周期与访问Flash访问时间的比例,这里都是2 */
		FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
		FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    

		/*设置AHB,APB2,APB1,预分频因子
		/* HCLK = SYSCLK */
		RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
      
		/* PCLK2 = HCLK */
		RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
    
		/* PCLK1 = HCLK/2 */
		RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
        
		/*设置PLL时钟来源,设置PLL倍频因子,PLLCLK = HSE * 9 = 72MHz*/
		RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2
								|RCC_CFGR2_PLL2MUL
								|RCC_CFGR2_PREDIV1
								|RCC_CFGR2_PREDIV1SRC));
		RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 
								|RCC_CFGR2_PLL2MUL8 
								|RCC_CFGR2_PREDIV1SRC_PLL2 
								|RCC_CFGR2_PREDIV1_DIV5);
		/* 使能PLL时钟 */
		RCC->CR |= RCC_CR_PLLON;

		/* 等待PLL时钟稳定 */
		while((RCC->CR & RCC_CR_PLLRDY) == 0)
		{
		}
    
		/* 选择PLL作为系统时钟来源 */
		RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
		RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    

		/* 读取时钟切换状态位,确保PLLCLK被选作系统时钟 */
		while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
		{
		}
	}
	else
	{ 
		/* 如果HSE启动失败,用户可以在这里添加错误代码 */
	}
}

         注:static 关键字:当一个源程序由多个源文件组成时,C语言根据函数能否被其他源文件的函数数调用,将函数分为内部函数种外部函数,内部函数称静态函数。

10.3.2.9 其他时钟

 USB prescaler

        A部分:USB时钟是由PLLCLK经过USB预分频器得到,具体的由时钟配置寄存器CFGR的位22:USBPRE配置,USB的最高时钟是48M根据分频因子反推过来PLLCLK只能是48M或72M,一般我们设置PLLCLK为72M,USBCLK=48M,USB对时钟要求比较高.所以PLLCLK只能由HSE倍频得D到,不能能使用HSI倍频。

Cortex System timer

        B部部分:Gortex系统时钟.Corte系统时钟由HCk8分频得到,即9M.Grteh系统时钟用来驱动内核的系统定时器。SysTick,SysTick一般用于操作系统的时钟节拍也可用做普通的定时。

ADC prescaler

        C部分:ADC时钟.ADC时钟由PCLK2经过ADC预分频器得到,具体配置由CFGR案存器的位15-14:ADCPRE[1:0]决定,但奇怪的是ADC并没有1分频选项。ADC最高只能是14M。如果采样周期设置成最短的1.5个周期的话ADC的转换时间可达到最短的lus,如果真要达到最短的较换时间lus的话,那ADC的时钟就得是14M.那反推PLCLK2的时钟只能是28M、56M、84M、112M鉴于PCLK2最高是72M所以只能取28M和56M。

RTCCLK

        D部分:RTC时钟,独立看门狗时钟,RTC时钟可由HSE/128分频得到,也可由低速外部时钟LSE提供,频率为32.768KHz.也可由低速内部时钟HSI提供,具体选那一个由备份域控制寄存器BDOR的位9-8:RTCSEL[1:0]配置.独立看门拘的时钟由LSI提供,且只能,最由LSI提供.一般取40KHz.

MCO

        E部分:MCO时钟输出,MCO是microcontroller clock output的缩写,是微控利器时钟输出引脚,在STM32 F1系列中,由PA8复用所得,主要作用是可以对外提供时钟,相于一个有源晶振,MCO时钟来源可以是PLLCLK/2、HIS、HSE、SYSCLK具体选哪一个由CFGR的位26-24:MCO[2:0]决定,除了对外提供时钟外,我们还可以通过波器监控MCO引脚的时钟输出,来验证我们的系统时钟配置是否正确。

10.4 系统时钟配置程序设计

10.4.1 使用HSE

        一般情况下,我们都是使用 HSE,然后 HSE 经过 PLL 倍频之后作为系统时钟。通常的配置是:HSE=8M, PLL 的倍频因子为: 9,系统时钟就设置成:SYSCLK = 8M * 9 = 72M。

        使用HSE,系统时钟 SYSCLK 最高是 128M。我们使用的库函数就是这么干的,当程序来到 main 函数之前,启动文件: statup_stm32f10x_hd.s 已经调用 SystemInit() 函数把系统时钟初始化成 72MHZ, SystemInit()在库文件: system_stm32f10x.c 中定义。如果我们想把系统时钟设置低一点或者超频的话,可以修改底层的库文件,但是为了维持库的完整性,我们可以根据时钟树的流程自行写一个。

10.4.2 使用HIS

        当 HSE 故障的时候,如果 PLL 的时钟来源是 HSE,那么当 HSE 故障的时候,不仅 HSE 不能使用,连 PLL 也会被关闭,这个时候系统会自动切换 HSI 作为系统时钟,此时 SYSCLK=HSI=8M,如果没有开启 CSS 和 CSS 中断的话,那么整个系统就只能在低速率运行,这是系统跟瘫痪没什么两样。如果开启了 CSS 功能的话,那么可以当 HSE 故障时,在 CSS 中断里面采取补救措施,使用 HSI,并把系统时钟设置为更高的频率,最高是 64M, 64M 的频率足够一般的外设使用,如:ADC、 SPI、 I2C 等。但是这里就又有一个问题了,原来 SYSCLK=72M,现在因为故障改成 64M,那么那些外设的时钟肯定被改变了,那么外设工作就会被打乱,那我们是不是在设置 HSI 时钟的时候,也重新调整外设总线的分频因子,即 AHB, APB2 和 APB1 的分频因子,使外设的时钟达到跟 HSE 没有故障之前一样。但是这个也不是最保障的办法,毕竟不能一直使用 HSI,所以当HSE 故障时还是要采取报警措施。

        还有一种情况是,有些用户不想用 HSE,想用 HSI,但是又不知道怎么用 HSI 来设置系统时钟,因为调用库函数都是使用 HSE,下面我们给出个使用 HSI 配置系统时钟例子,起个抛砖引玉的作用。

10.4.3 实验要求

        通过LED灯的闪烁频率直观的判断不同系统时钟对软件延时的效果。

10.4.4 硬件设计

第十章 STM32时钟系统_第12张图片

         RCC 是单片机内部资源,不需要外部电路。通过 LED 闪烁的频率来直观的判断不同系统时钟频率对软件延时的效果。

10.4.5 软件设计

        我们在点亮LED灯项目的基础上添加clkconfig文件里面包含两个 RCC 驱动文件, bsp_clkconfig.h 和 bsp_clkconfig.c,用来存放 RCC 系统时钟配置函数。

10.4.5.1 编程思路

        ①开启HSE/HIS,并等待HSE/HIS稳定。

        ②设置AHB,APB2,APB1时钟分频因子。

        ③设置PLL的时钟来源和倍频因子,各种频率主要就是在这里设置。

        ④开启PLL并等待PLL稳定。

        ⑤把PLLCLK切换为系统时钟SYSCLK。

        ⑥读取时钟切换状态位,确保PLLCLK被选为系统时钟。

10.4.5.2 代码分析

端口宏定义文件

#ifndef _BSP_CLKCONFIG_H
#define _BSP_CLKCONFIG_H

#include "stm32f10x.h"

void HSE_SetSysCLK(uint32_t RCC_PLLMul_x);
void HSI_SetSysCLK(uint32_t RCC_PLLMul_x);
 
#endif /* _BSP_CLKCONFIG_H */

         由于RCC 是单片机内部资源,不需要外部电路。所以在这个文件里不需要对相关端口进行声明。

时钟配置函数文件

#include "bsp_clkconfig.h"

void HSE_SetSysCLK(uint32_t RCC_PLLMul_x)
{
	uint32_t HSEStatus = 0;
	//复位所有RCC寄存器
	RCC_DeInit();
	
	//使能HSE
	RCC_HSEConfig(RCC_HSE_ON);
	
	//等待HSE启动成功
	HSEStatus = RCC_WaitForHSEStartUp();
	
	//判断HSE是否启动成功
	if(HSEStatus == SUCCESS)
	{
		//启动成功,程序继续执行
		//使能Flash预存取缓冲区
		FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
		
		//设置SYSCLK周期与Flash访问时间的比例
		FLASH_SetLatency(FLASH_Latency_2);
		
		//设置AHB,APB1,APB2预分频因子
		RCC_HCLKConfig(RCC_SYSCLK_Div1);
		RCC_PCLK1Config(RCC_HCLK_Div2);
		RCC_PCLK2Config(RCC_HCLK_Div1);
		
		//设置PLL时钟来源,设置PLL倍频因子(配置PLLCLK = HSE * RCC_PLLMul_x)
		RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_x);
		
		//使能PLL
		RCC_PLLCmd(ENABLE);
		
		//等待PLL稳定
		while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET)
		{
		}
		
		//选择PLL作为系统时钟来源
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
		
		//读取始终切换状态位,确保PLLCLK被选为系统时钟
		while (RCC_GetSYSCLKSource() != 0x08);
		{
		}
	}
	else
	{
		//启动失败
	}
}

void HSI_SetSysCLK(uint32_t RCC_PLLMul_x)
{
	uint32_t HSIStatus = 0;
	//复位所有RCC寄存器
	RCC_DeInit();
	
	//使能HSI
	RCC_HSICmd(ENABLE);
	
	//等待HSI启动稳定,并作超时处理
	HSIStatus = RCC->CR & RCC_CR_HSIRDY;
	
	//判断HSE是否启动成功
	if(HSIStatus == RCC_CR_HSIRDY)
	{
		//启动成功,程序继续执行
		//使能Flash预存取缓冲区
		FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
		
		//设置SYSCLK周期与Flash访问时间的比例
		FLASH_SetLatency(FLASH_Latency_2);
		
		//设置AHB,APB1,APB2预分频因子
		RCC_HCLKConfig(RCC_SYSCLK_Div1);
		RCC_PCLK1Config(RCC_HCLK_Div2);
		RCC_PCLK2Config(RCC_HCLK_Div1);
		
		//设置PLL时钟来源,设置PLL倍频因子(配置PLLCLK = HSE * RCC_PLLMul_x)
		RCC_PLLConfig(RCC_PLLSource_HSI_Div2,RCC_PLLMul_x);
		
		//使能PLL
		RCC_PLLCmd(ENABLE);
		
		//等待PLL稳定
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
		{
		}
		
		//选择PLL作为系统时钟来源
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
		
		//读取始终切换状态位,确保PLLCLK被选为系统时钟
		while (RCC_GetSYSCLKSource() != 0x08);
		{
		}
	}
	else
	{
		//启动失败
	}
}

        void HSE_SetSysCLK(uint32_t RCC_PLLMul_x)函数实现了选择HSE作为系统时钟,void HSI_SetSysCLK(uint32_t RCC_PLLMul_x)函数则实现了使用HIS作为系统时钟,两个函数都需要一个参数,这个参数主要是让用户自己选择PLL倍频系数。

主函数

#include "stm32f10x.h"
#include "bsp_led.h" 
#include "bsp_clkconfig.h"

#define SOFT_DELAY	delay(0XFFFFF)

void delay(uint32_t count);

int main(void)
{
	LED_GPIO_Config();
	
	// 重新设置系统时钟,根据需要修改,一般设置最高为72MH
	// SYSCLK = 8M * RCC_PLLMul_x, x:[2,3,...16]
	//HSE_SetSysCLK(RCC_PLLMul_16);
	HSI_SetSysCLK(RCC_PLLMul_16);
	
	while(1)
	{
		LED3_ON;
		SOFT_DELAY;
		
		LED3_OFF;
		SOFT_DELAY;
	}
}

void delay(uint32_t count)
{
	for(;count != 0;count--);
}

10.4.6 下载验证

        观察开发板上得LED灯的闪烁频不同。

10.5 利用MCO对外提供时钟输出

10.5.1 实验要求

        在 STM32F103 系列中, PA8 可以复用为 MCO 引脚,对外提供时钟输出,我们也可以用示波器监控该引脚的输出来判断我们的系统时钟是否设置正确。

10.5.2 硬件设计

        将单片机的PA8脚接到示波器的测量通道进行测量。

10.5.3 软件设计

10.5.3.1 编程思路

        在F1系列中MCO引脚只有一个,即PA8,在F4系列中,MCO引脚会有两个。在编程时对PA8端口配置为复用推挽输出,然后对PA8端口进行初始化即可实现此功能。

10.5.3.2 代码分析

端口宏定义文件

#ifndef _BSP_MCO_H
#define _BSP_MCO_H

#include "stm32f10x.h"

void MCO_GPIO_Config(void);

#endif /* _BSP_MCO_H */

配置函数文件

#include "bsp_mco.h"

void MCO_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	// 开启GPIOA的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	// 选择GPIO8引脚
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
	
	//设置为复用功能推挽输出
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	
	//设置IO的翻转速率为50M
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	// 初始化GPIOA8
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}

主函数

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_clkconfig.h"
#include "bsp_mco.h"

#define SOFT_DELAY	delay(0XFFFFF)
void delay(uint32_t count); 
int main(void)
{
	LED_GPIO_Config();
	
	// 重新设置系统时钟,根据需要修改,一般设置最高为72MH
	// SYSCLK = 8M * RCC_PLLMul_x, x:[2,3,...16]
	//HSE_SetSysCLK(RCC_PLLMul_16);
	HSI_SetSysCLK(RCC_PLLMul_16);
	
	// 设置MCO引脚输出时钟,用示波器即可在PA8测量到输出的时钟信号,
	// 我们可以把PLLCLK/2作为MCO引脚的时钟来检测系统时钟是否配置准确
	// MCO引脚输出可以是HSE,HSI,PLLCLK/2,SYSCLK	
	//RCC_MCOConfig(RCC_MCO_HSE);	             	        
	//RCC_MCOConfig(RCC_MCO_HSI);	                   
	//RCC_MCOConfig(RCC_MCO_PLLCLK_Div2);    	
	RCC_MCOConfig(RCC_MCO_SYSCLK);	
	MCO_GPIO_Config();
	
	while(1)
	{
		LED3_ON;
		SOFT_DELAY;
		
		LED3_OFF;
		SOFT_DELAY;
	}
}

void delay(uint32_t count)
{
	for(;count != 0;count--);
}

10.5.4 下载验证

        使用示波器能够观察输出对应的波形。

谢谢阅读!

你可能感兴趣的:(STM32开发基础,stm32,单片机,嵌入式硬件)