硬件设计
时钟源构成
STM32中有5个最重要的时钟源,分别为HSI ,HSE ,LSI ,LSE 和PLL。其中PLL实际分为两个时钟源,分别为主PLL和专用PLL。五种时钟源有一下两种分类方式:
分类方法\类别 | 高速时钟源/外部时钟源 | 低速时钟源/内部时钟源 |
---|
时钟频率 | HSI HSE PLL | LSI LSE | 来源 | HSE LSE | HSI LSI PLL |
时钟源详细信息
名称 | 频率 | 功能 |
---|
LSI低速内部时钟 | 32kHz左右 | 独立看门狗、自动唤醒单元 | LSE低速外部时钟 | 32.768kHz | RTC时钟源 | HSI高速内部时钟 | 16MHz | 直接作为系统时钟、PPL输入 | HSE高速外部时钟 | 4MHz~26MHz | 直接作为系统时钟、PPL输入 |
PLL为锁相环倍频输出。STM32F4有两个PLL:
- 主PLL(PPL)由HSE或者HSI提供时钟信号,并具有两个不同的输出时钟。第一个输出PLLP用于生成高速的系统时钟(最高168MHz);第二个输出PLLQ用于生成USB OTG FS的时钟(48MHz)、随机数发生器的时钟和SDIO时钟。
- 专用PPL(PPLI2S)用于生成精准时钟,从而在I2S接口实现高品质音频性能
锁相环是一种利用反馈控制原理实现相位和频率同步的技术,作用是将电路输出的时钟与其外部的参考时钟保持同步。
主PLL时钟输出PLLP的计算方法
主PLL时钟的时钟源需要先经过一个分频系数为M的分频器,然后经过倍频系数为N的倍频器,之后经过一个分频系数为P(输出PLLP)或者Q(输出PLLQ)的分频器分频后,生成最终的主PLL时钟。
P
L
L
=
F
R
E
Q
?
N
P
?
Q
PLL=\frac{FREQ*N}{P*Q}
PLL=P?QFREQ?N? 其中FREQ为时钟源时钟频率。若时钟频率为8MHz,设置分频器M=8,倍频器倍频系数N=336,分频器分频系数P=2,那么主PPL生成的高速时钟PLLP为168MHz。
若选择HSE作为PLL时钟源,SYSCLK时钟源为PLL,那么SYSCLK时钟为168MHz。
系统及外设时钟
看门狗时钟
看门狗时钟源只能是低速的LSI时钟。
RTC时钟源
RTC时钟源可以选择LSI、LSE以及HSE分频后的时钟。HSE分频系数为2~31。
STM32F4输出时钟MCO1、MCO2
MCO1是向芯片的PA8引脚输出时钟。它的时钟来源为HSI、LSE、HSE和PLL时钟。
MCO2是向芯片的PC9引脚输出时钟。它的时钟来源为HSE、PLL、SYSCLK以及PLLI2S时钟。
MCO输出时钟频率最大不超过100MHz。
系统时钟SYSCLK
SYSCLK有三个时钟来源HSI、HSE和PLL。在实际应用中,一般采用PLL作为SYSCLK时钟源。
以太网PTP时钟、AHB时钟、APB1低速时钟、APB2高速时钟
四个时钟的时钟源均为SYSCLK系统时钟。其中以太网PTP时钟使用系统时钟,AHB、APB1和APB2由SYSCLK分频而来。AHB最大时钟频率为168MHz,APB1最大时钟频率为42MHz,APB2最大时钟频率为84MHz。
I2S时钟源
I2S时钟源源于PLLI2S或者映射到I2S_CKIN引脚为外部时钟。处于音质考虑,I2S对时钟精度要求很高。
STM32F4内部以太网MAC时钟
USB OTG HS(60MHz)是中欧
由外部PHY提供。
Cortex系统定时器Systick时钟
Systick时钟源可以是AHB时钟HCLK或HCLK的8分频。
软件设计
系统启动后,程序会先执行HAL库定义的SystemInit函数:
void SystemInit(void)
{
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));
#endif
RCC->CR |= (uint32_t)0x00000001;
RCC->CFGR = 0x00000000;
RCC->CR &= (uint32_t)0xFEF6FFFF;
RCC->PLLCFGR = 0x24003010;
RCC->CR &= (uint32_t)0xFFFBFFFF;
RCC->CIR = 0x00000000;
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
SystemInit_ExtMemCtl();
#endif
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET;
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
#endif
}
SystemInit主要做了一下四个方面的工作:
- FPU(浮点运算单元)设置
- 复位RCC时钟,配置为默认复位值(默认开启了HSI)
- 外部储存器配置
- 中断向量表地址配置
SystemInit函数并没有进行时钟的配置,因此需要自行编写时钟初始化函数Clock_Init:
void Clock_Init(uint32_t plln, uint32_t pllm, uint32_t pllp, uint32_t pllq)
{
HAL_StatusTypeDef ret = HAL_OK;
RCC_OscInitTypeDef RCC_OscConfig;
RCC_ClkInitTypeDef RCC_ClkConfig;
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscConfig.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscConfig.HSEState = RCC_HSE_ON;
RCC_OscConfig.PLL.PLLState = RCC_PLL_ON;
RCC_OscConfig.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscConfig.PLL.PLLM = pllm;
RCC_OscConfig.PLL.PLLN = plln;
RCC_OscConfig.PLL.PLLP = pllp;
RCC_OscConfig.PLL.PLLQ = pllq;
ret = HAL_RCC_OscConfig(&RCC_OscConfig);
if(ret != HAL_OK) while(1);
RCC_ClkConfig.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
RCC_ClkConfig.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkConfig.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkConfig.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkCOnfig.APB2CLKDivider = RCC_HCLK_DIV2;
ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_5);
if(ret != HAL_OK) while(1);
if (HAL_GetREVID() == 0x1001)
{
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
}
}
Clock_Init的作用是进行时钟系统配置,不仅配置了PLL相关参数确定SYSCLK值,还配置了AHB、APB1和APB2的分频系数,也就是确定了HCLK、PCLK1和PCLK2的时钟。
使用HAL库配置STM32F407时钟系统的一般步骤:
- 使能PWR时钟:调用函数__HAL_RCC_PWR_CLK_ENABLE();
- 设置调压器输出电压级别:调用函数__HAL_PWR_VOLTAGESCALING_CONFIG();
- 选择是否开始Over-Driver功能:调用函数__HAL_PWREx_EnableOverDrive();
- 配置时钟源相关参数:嗲用函数__HAL_RCC_OscConfig();
- 配置系统时钟源以及AHB、APB1和APB2的分频系数:调用函数HAL_RCC_ClockConfig()。
步骤4和步骤5是时钟系统配置的关键步骤。对于步骤4,我们嗲调用HAL_RCC_OscCOnfig()函数进行时钟的配置。该函数在stm32f4xx_hal_rcc.h中声明,在stm32f4xx_hal_rcc.c中定义。其只有一个入口参数,为结构体RCC_OscInitTypeDef类型指针,其定义为:
typedef struct
{
uint32_t OscillatorType;
uint32_t HSEState;
uint32_t LSEState;
uint32_t HSIState;
uint32_t HSICalibrationValue;
uint32_t LSIState;
RCC_PLLInitTypeDef PLL;
}RCC_OscInitTypeDef;
对于这个结构体,前几个参数用来选择配置的振荡器类型。例如我们要开启HSE,那么我们会设置OscillatorType成员的值为RCC_OSCILLATORTYPE_HSE,然后配置HSEState的值为RCC_HSE_ON来开启HSE。对于时钟源HSI、LSI和LSE配置方法类似。这个结构体中另一个重要的成员为RCC_PLLInitTypeDef PLL,它的作用是配置PLL相关参数,其定义为:
typedef struct
{
uint32_t PLLState;
uint32_t PLLSource;
uint32_t PLLM;
uint32_t PLLN;
uint32_t PLLP;
uint32_t PLLQ;
}RCC_PLLInitTypeDef;
这个结构体是用来配置PLL时钟源以及相关分频倍频参数。
在配置好RCC_OscInitTypeDef结构体后,可以通过HAL_RCC_OscConfig()函数初始化时钟系统。而设置好PLL的时钟频率后,需要使用HAL_RCC_ClockConfig()函数来配置时钟频率。这个函数有两个入口参数:第一个入口参数为RCC_ClkInitTypeDef结构体指针类型参数,用来设置SYSCLK时钟源以及AHB、APB1和APB2的分频系数;第二个参数FLatency用于配置FLASH延迟。
RCC_ClkInitTypeDef结构体中有如下几个参数:ClockType、SYSCLKSource、AHBCLKDivider、APB1CLKDivider和APB2CLKDivider,分别为时钟类型、SYSCLK时钟源、AHB分频系数、APB1分频系数以及APB2分频系数。
|