STM32RCC(Reset and Clock Control)是STM32微控制器系列中的一个模块,它负责配置和管理系统时钟。
以下是一些STM32RCC的主要功能和作用:
1. 系统时钟配置:STM32微控制器通常具有多个时钟源可供选择,例如内部振荡器、外部晶体振荡器、外部时钟源等。STM32RCC模块允许用户根据需要配置系统时钟源,并进行时钟源切换。
2. 时钟使能控制:系统中的各个外设需要时钟信号来进行操作,STM32RCC模块提供了对各个外设时钟的使能控制。用户可以通过STM32RCC模块配置和启用相应的时钟,以激活具体的外设功能。
3. 时钟分频控制:STM32RCC模块允许用户对系统时钟进行分频,以调整系统时钟频率。这可以用来降低功耗、减小时钟信号传输距离引起的噪音等。
4. 时钟安全机制:STM32RCC模块还提供了一些时钟安全机制,如时钟失效检测、时钟频率监测等,旨在确保系统时钟的稳定性和可靠性。
总之,STM32RCC模块在STM32微控制器中负责配置和管理系统时钟,并提供了时钟源选择、时钟使能控制、时钟分频控制和时钟安全机制等功能。它是系统中确保时钟稳定和外设正常运行的重要模块。
我们知道当我们编写的程序启动的时候会先执行汇编文件,在汇编文件里面会调用SystemInit,这是固件库编辑的一个函数,它里面会把时钟初始化位72MHZ,当我们开始执行main函数的时候,系统已经给我们将系统时钟设置为了72MHZ。所以我们在学习点亮LED的时候并不需要设置系统时钟 。
时钟树如下图:
HSE:High Speed External Clock signal,即高速的外部时钟。从上图的OSC_OUT和OSC_IN端口进入,我们也可以通过原理图晶振电路模块查看。来源是无源晶振(4-16M),我们通常使用8M。如果要使用HSE这个时钟,需要RCC_CR时钟控制寄存器的位16:HSEON控制。RCC_CR时钟控制寄存器如下图:
当HSEON位置0时振荡器关闭,当置1时振荡器开启。因为起振需要一定的时间,所以就需要用到位17的HSERDY:外部高速时钟就绪标志
当这个位为0时表示振荡器没有就绪,为1时表示振荡器就绪。
HSI:High Speed Internal Clock signal,高速的内部时钟。HSI来源自芯片内部,大小也为8M,当HSE故障的时候,系统时钟会自动切换到HSI,直到HSE启动成功。同样HSI时钟的控制需要用到RCC_CR时钟控制器的HSION和HSIRDY两个位,作用和HSE的两个位的作用相同。
当HSE以8M通过HSE OSC进入,然后又两条路,一条路以8M继续进入下一步,另一条路将8M二分频为4M,这两条路的选择通过时钟配置寄存器RCC_CFGR的位17的PLLXTPRE控制
一般我们通过PLLXTPRE置0,不分频继续以8M传输到PLLSRC,PLLSRC也是 由RCC_CFGR寄存器控制
因为有两条路进入PLLSRC,一条是经过 PLLXTPRE的HSE,另一条则是被2分频的HSI,PLLSRC的作用就是控制是HSE还是HSI时钟作为PLL的输入时钟。
进入到PLL后通过PLLMUL乘上倍频因子(2-16),PLLMUL也是通过RCC_CFGR寄存器控制
最终PLLCLK这个时钟就被倍频为72MHZ。
系统时钟来源有三个,HSI、PLLCLK、HSE,通过 RCC_CFGR寄存器的SW位来控制
因为时钟切换需要时间,所以SWS位用于表示当前是哪个输入作为系统时钟。
系统时钟通过AHB总线分预频器进入到APB1和APB2总线的预分频器,这三个预分频器同样由RCC_CFGR寄存器控制
一般将AHB一倍频,APB1是低速总线最高36MHZ,所以要将72MHZ2分频为36MHZ,APB2是高速总线,最高为72MHZ,配置为1分频。
RTC时钟有单个来源,一个是HSE128分频后得到,一个是LSE,一个是内部低速的LSI (30-60MHZ)。
MCO:mircrocontroller clock output,微控制器时钟输出引脚。来源:PLLCLK/2、HSE、SHI、SYSCLK。控制:CRGR:MCO。
可以通过MCO这个引脚连接示波器查看频率是否和设置的值一致。
SetSysClockTo72(void)这个函数是用于将系统时钟设置为72MHZ。
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* 使能HSE */
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->ACR |= FLASH_ACR_PRFTBE;
/* 两个等待周期 */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
/* HCLK = SYSCLK = 72MHZ */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK = 72MHZ */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK = 36MHZ */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
/* 锁相环配置: PLLCLK = HSE * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
/* 使能锁相环 */
RCC->CR |= RCC_CR_PLLON;
/* 等待锁相环稳定 */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* 选择锁相环作为系统时钟 */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* 等待锁相环切换为系统时钟*/
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else
{ /* 如果HSE启动失败,用户可以在这里添加处理错误的代码 */
}
}
通过RCC->CR |= ((uint32_t)RCC_CR_HSEON)使HSE使能,然后通过do...while()循环等待 HSE就绪并作超时处理,当HSE成功使能后, HSEStatus值为1,while条件判断为假,跳出循环,然后再用一个if...else判断一下。然后剩下的过程通过一个if...else判断,如果HSE使能成功则程序继续执行,在else中添加处理错误的代码。
紧接着是FLASH相关的代码,因为我们代码是放在FLASH中缓存的,读取的时候是一条一条的读取,所以有一个预取缓冲区,取指的时候有一个等待时间,不能取得很快,等待的时间和频率是一样的,72MHZ要配置成两条指令之间取的时间要等待多少时间是有规定的。
通过闪存访问控制寄存器(FLASH_ACR)控制
这里用到了PRFTBE启动预取缓冲区,通过LATENCY设置时延。
接着配置AHB、APB1和APB2,然后配置锁相环 ,通过PLLXTPRE让HSE不分频,通过PLLMULL9将时钟倍频为72MHZ。然后使能锁相环,然后使用一个空白的while循环判断PLLRDY值,等待锁相环稳定,然后通过SW设置锁相环为系统时钟,再用一个whlie循环等待时钟切换,这样就系统时钟就配置为72MHZ。