01 什么是时钟
单片机如果要正常运行,时钟信号是必不可少的。作为CPU的脉搏,时钟的快慢决定了CPU的运行速率,执行指令的速度。一般时钟源会被分频器或倍频器分成多种频率的时钟,以满足系统的不同应用。
那么时钟信号是怎么产生的呢?
## 晶振
### 无源晶振
无源晶振是有2个引脚的无极性元件,需要结合外部时钟电路组成一个振荡器才能产生振荡信号。而MCU可以使用无源晶振的原因,是因为其内部有集成构成振荡器的电路。
> 特点:无源晶振信号质量和精度较差,要匹配外部电容和滤波电阻,输出的波形一般都为正弦波。
### 有源晶振
有源晶振有4只引脚,是一个完整的振荡器,内部除了石英晶体外,还有晶体管和阻容元件等。
> 特点:有源晶振不需要MCU的内部时钟电路,信号稳定,质量较好,输出波形一般为方波。
02 APM32的时钟系统
下面主要以F407系列为例来讲解。
### MSI
多速率内部振荡器,可由软件配置产生不同频率的时钟。
### HSIAPM32内部高速内部时钟,是一个RC振荡器。
> 下表只列出各系列典型型号的时钟频率,不同型号,可能有多或少的多种频率的HSI。
### HSE高速外部时钟,可接石英/陶瓷谐振器,或者接外部的时钟源,不同系列型号支持不同的输入频率范围。
### LSI
APM32内部低速内部时钟,是一个RC振荡器。
> 下表只列出各系列典型型号的时钟频率,不同型号,可能有多或少的多种频率的LSI。
### LSE
低速外部时钟,不同系列型号支持不同的输入频率范围。一般接入32.768KHz的时钟。
## PLL(Phase Locked Loop)锁相环
1.当输出信号的频率与输入信号的频率相等时,输出电压与输入电压保持固定的相位差值,即输出电压与输入电压的相位被锁住,这就是锁相环名称的由来。
2.PLL是通过比较外部信号的相位和由压控晶振(VCXO)的相位来实现同步的,而在比较的过程中,锁相环电路会不断根据外部信号的相位来调整本地晶振的时钟相位,直到两个信号的相位同步。
### APM32F4的PLL
* PLL1由 HSE 或 HSI 振荡器提供时钟信号,并具有两个不同的输出时钟。
1.输出高速系统时钟(Max:168MHz)。
2.输出USB OTG FS的时钟(48MHz)、随机数发生器的时钟 (≤48 MHz) 和 SDIO 时钟 (≤48 MHz)。
* PLL2专用于生成精确时钟,从而在I2S接口实现高品质的音频性能。
#### 基本结构外部时钟源经PLL源选择开关后作为PLLIN输入到PLL中,经分频和倍频后分别输出到后端系统时钟选择开关、USB OTG FS时钟及I2S时钟。
#### 分倍频系数
#### PLL时钟的计算
1.分频和倍频系数是有取值范围的;
2.VCO的输入和输出频率也有推荐取值,以便限制PLL抖动。
## 选择开关
## 时钟分频器
时钟分频器对时钟进行分频以满足应用需求,除了存在于上述PLL中之外,整个时钟系统中到处都可见其身影。有外设的APB1、APB2分频器,RTC时钟源分频器,MCO时钟输出分频器等等。
## 总线架构
## APM32系统时钟
### AHB
AHB(Advanced High performanceBus)高级高性能总线连接APB与总线矩阵,桥接APB1和APB2,主要用于高性能模块或外设的应用,AHB的时钟叫做HCLK。
### FCLK
FCLK(Free Running Clock)自由运行时钟,为CPU内核提供时钟信号。
### AHB1/2
APB(Advanced PeripheralBus)高级外设总线连接各种外设并和AHB桥接,APB1/2的时钟叫做PCLK1/2。
## 时钟信号输出
APM32可以通过选择开关和分频器把时钟信号输出到MCO引脚上,供外部使用。F4xx支持MCO1(PA8)和MCO2(PC9)两路时钟信号输出。
## 时钟安全系统
时钟安全系统CSS可通过软件激活。当激活后,时钟监测器将在 HSE 振荡器启动延迟后使能,并在此路振荡器停止时被关闭。如果 HSE 时钟发生故障,此路振荡器将自动禁止,并且一个时钟故障事件将发送到高级控制定时器TIM1和TIM8 的断路输入端,同时还将生成一个中断来向用户通知此故障(时钟安全系统中断,CSSI),以使 MCU 能够执行对应操作。
> CSSI 与 Cortex™-M4F NMI(不可屏蔽 中断)异常向量相链接。
03 时钟配置方法
void SystemInit(void)
{
/** FPU settings */
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); //!< set CP10 and CP11 Full Access
#endif
/** Reset the RCM clock configuration to the default reset state */
/** Set HSIEN bit */
RCM->CTRL_B.HSIEN = BIT_SET;
/** Reset CFG register */
RCM->CFG = 0x00000000;
/** Reset HSEEN, CSSEN and PLL1EN bits */
RCM->CTRL &= (uint32_t)0xFEF6FFFF;
/** Reset PLL1CFG register */
RCM->PLL1CFG = 0x24003010;
/** Reset HSEBCFG bit */
RCM->CTRL &= (uint32_t)0xFFFBFFFF;
/** Disable all interrupts */
RCM->INT = 0x00000000;
#if defined(DATA_IN_ExtSRAM)
SystemInit_ExtSRAM();
#endif /** DATA_IN_ExtSRAM */
SystemClockConfig();
/** Configure the Vector Table location add offset address */
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /** Vector Table Relocation in Internal SRAM */
#else
SCB->VTOR = FMC_BASE | VECT_TAB_OFFSET; /** Vector Table Relocation in Internal FLASH */
#endif
}
要注意几个宏定义,宏定义的值要根据所设计板卡的外部晶振及需求配置来定义。
#define HSE_VALUE ((uint32_t)8000000)
#define HSI_VALUE ((uint32_t)16000000)
#define PLL_A 336
#define PLL_B 8
#define PLL_C 2
#define PLL_D 7
SystemClockConfig函数将系统时钟重设为168MHz。
static void SystemClockConfig(void)
{
__IO uint32_t i;
RCM->CTRL_B.HSEEN = BIT_SET;
for (i = 0; i < HSE_STARTUP_TIMEOUT; i++)
{
if (RCM->CTRL_B.HSERDY**)
{
break;
}
}
if (RCM->CTRL_B.HSERDY**)
{
/** Select regulator voltage output Scale 1 mode */
RCM->APB1CLKEN_B.PMUEN = BIT_SET;
PMU->CTRL_B.VOSSEL = BIT_SET;
/** HCLK = SYSCLK / 1*/
RCM->CFG_B.AHBPSC = 0x0000;
/** PCLK2 = HCLK / 2*/
RCM->CFG_B.APB2PSC = 0x04;
/** PCLK1 = HCLK / 4*/
RCM->CFG_B.APB1PSC = 0x05;
/** Configure the main PLL */
RCM->PLL1CFG = PLL_B | (PLL_A << 6) | (((PLL_C >> 1) -1) << 16) |(PLL_D << 24);
RCM->PLL1CFG_B.PLL1CLKS = 0x01;
/** Enable the main PLL */
RCM->CTRL_B.PLL1EN = BIT_SET;
/** Wait till the main PLL is ready */
while (RCM->CTRL_B.PLL1RDY** == 0)
{
}
/** Configure Flash prefetch, Instruction cache, Data cache and wait state */
FMC->ACCTRL = 0x05 | BIT8 | BIT9 | BIT10;
/** Select the main PLL as system clock source */
RCM->CFG_B.SCLKSEL = RESET;
RCM->CFG_B.SCLKSEL = 0x02;
/** Wait till the main PLL is used as system clock source */
while ((RCM->CFG_B.SCLKSWSTS) != 0x02)
{
}
}
else
{
/** If HSE fails to start-up, the application will have wrong clock configuration. */
}
}
PLL值的配置大家可以根据PLL章节的介绍来配置PLL相关寄存器,这里不再赘述。