嵌入式芯片一些硬件问题总结
本文档是自己学习总结文档,有些乱,勿怪
1、STM32F407时钟和PLL
接下来,看程序,程序开始
1 startup_stm32f40_41xxx.s 1 Reset_Handler PROC 2 EXPORT Reset_Handler [WEAK] 3 IMPORT SystemInit 4 IMPORT __main 5 6 LDR R0, =SystemInit 7 BLX R0 8 LDR R0, =__main 9 BX R0 10 ENDP
在进入main函数之前,系统调用了SystemInit函数.
2 system_stm32f4xx.c
在SystemInit里截取一段程序 1 /* Configure the System clock source, PLL Multiplier and Divider factors, 2 AHB/APBx prescalers and Flash settings ----------------------------------*/ 3 SetSysClock();
注释:配置系统时钟源,....
进入SetSysClock(); 1 static void SetSysClock(void) 2 { 3 .... 4 5 /* HCLK = SYSCLK / 1*/ 6 RCC->CFGR |= RCC_CFGR_HPRE_DIV1; 7 8 9 #if defined (STM32F40_41xxx) || defined (STM32F427_437xx) || defined (STM32F429_439xx) 10 /* PCLK2 = HCLK / 2* 11 RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; 12 13 /* PCLK1 = HCLK / 4*/ 14 RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; 15 #endif /* STM32F40_41xxx || STM32F427_437x || STM32F429_439xx */ 16 .... 17 /* Configure the main PLL */ 18 RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | 19 (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24); 20 21 /* Enable the main PLL */ 22 RCC->CR |= RCC_CR_PLLON; 23 }
可以知道HCLK = SYSCLK/1,PCLK2 = HCLK / 2,PCLK1 = HCLK / 4。接下来就和M,P,Q有关了
这些值在前边有define,以我的工程为例 1 .... 2 #if defined (STM32F40_41xxx) || defined (STM32F427_437xx) || defined (STM32F429_439xx) || defined (STM32F401xx) 3 /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */ 4 #define PLL_M 8 5 #else /* STM32F411xE */ 6 ...... 7 /* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */ 8 #define PLL_Q 7 9 10 #if defined (STM32F40_41xxx) 11 #define PLL_N 336 12 /* SYSCLK = PLL_VCO / PLL_P */ 13 #define PLL_P 2 14 #endif /* STM32F40_41xxx */ 15 ....
至此就差一项还不知道了,就是外部晶振频率,它在stm32f4xx.h中有定义, #if !defined (HSE_VALUE) #define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */ #endif /* HSE_VALUE */ /** * @brief In the following line adjust the External High Speed oscillator (HSE) Startup Timeout value */ #if !defined (HSE_STARTUP_TIMEOUT) #define HSE_STARTUP_TIMEOUT ((uint16_t)0x05000) /*!< Time out for HSE start up */ #endif /* HSE_STARTUP_TIMEOUT */ #if !defined (HSI_VALUE) #define HSI_VALUE ((uint32_t)16000000) /*!< Value of the Internal oscillator in Hz*/ #endif /* HSI_VALUE */
1、系统时钟SYSCLK
在STM32F407中,除了一些特定的时钟(例如,USB OTG FS时钟,I2S时钟)外,系统所有外设的时钟均是通过SYSCLK来提供的。也就是说我们经常用到的外设时钟,都是通过SYSCLK分频得到的。下面是和SYSCLK相关的时钟树的图。
由图中可以看到,SYSCLK的来源主要有三个。
l HSI 振荡器时钟
l HSE 振荡器时钟
l 主 PLL (PLL) 时钟
下面分别来看下系统时钟SYSCLK的三个来源。
(1) 高速外部时钟HSE(4M~26M)
高速外部时钟是芯片内部的HSE振荡器产生的。高速外部信号(HSE)有两个时钟源
l HSE外部晶振,这种方式是通过在OSC_IN和OSC_OUT接入外部晶振实现的。在这种模式下,因为晶振起振需要一定的时间,因此可以通过RCC 时钟控制寄存器(RCC_CR) 中的HSERDY 标志指示高速外部振荡器是否稳定。
l HSE外部时钟,这种方式是指通过OSC_IN引脚输入一个外部时钟源,这个时钟源是外部产生的现成的时钟信号。在这种模式下,必须使用占空比大约为50%的外部时钟信号来驱动OSC_IN引脚。
(2) 高速内部时钟HSI
STM32F407内部自带一个16M的内部晶振,这个晶振产生的时钟信号可以直接用作系统时钟SYSCLK。也可以作为PLL的输入。内部晶振的优点是成本低,启动速度也比外部晶振要快,但是内部晶振的精度比外部晶振要差。可以通过 RCC 时钟控制寄存器 (RCC_CR) 中的HSIRDY 标志来判断HSI RC时钟信号是否稳定。
(3) 主PLL时钟
STM32F407具有两个PLL,用于产生不同的时钟信号。这里主要来讨论主PLL时钟。主PLL时钟的时钟源有两个信号,分别是上边提到的HIS信号和HSE信号。PLL通过把这两个信号倍频,分频等达到更高频率的时钟信号。一般来说,由于HIS和HSE的时钟频率较低,SYSCLK一般都不会选择HIS时钟和HSE时钟作为系统时钟,而主PLL产生的时钟一般会用作系统时钟。下面我们来看下主PLL的内部结构框图。
由图可以看出PLL的时钟源主要有两个:HSE时钟信号和HIS时钟信号。其中PLL_M,PLL_N,PLL_P,PLL_Q是一个分频器,而PLL_M是一个倍频器。假设图中经过PLL_N之后的时钟信号为PLL_VCO,PLL使用外部时钟信号HSE作为时钟源。那么有如下关系。
PLL_VCO = (HSE / PLL_M)* PLL_N
SYSCLK = PLL_VCO/ PLL_P
所以,如果外部晶振的频率已经确定了,那么确定PLL_M,PLL_N,PLL_P的系数就可以得到对应频率的系统时钟SYSCLK,其中PLL_M,PLL_N,PLL_P可以有多种组合,只要满足相应的条件即可。例如,假设外部晶振为8M,对STM32F407来说,系统时钟的最大频率为168M,那么久可以分别设置PLL_M = 8,PLL_N = 336,PLL_P = 2。来产生168M的时钟。即SYSCLK = ( 8 /8 ) * 336 / 2 = 168M。
通过配置主PLL,我们就得到了系统的主时钟SYSCLK,下一步我们就可以通过这个时钟来分频得到各个外设的时钟。
2、AHBx时钟
经过AHBx预分频器,可以得到AHBx外设时钟。一般来说AHBx分频器的值为1,也就是说AHBx的时钟和系统时钟相等。AHBx上边所挂的外设可以到RCC和AHBx外设相关的寄存器去看,在使能AHBx上的某个外设之前,必须使能该外设时钟。
3、APB1和APB2时钟
经过APB1预分频器和APB2预分频器可以得到APB1和APB2外设时钟。在STM32F407里边,APB1的最大时钟为42M,APB2的最大时钟为84M。同样可以在RCC的寄存器下边观测APB1和APB2上边所挂的外设。使用某个外设之前也要使能相应的时钟。
4、system_stm32f4xx.c
在stm32里边system_stm32f4xx.c是整个系统的时钟配置文件,上边三个部分的所有时钟配置都是在这个源文件中实现。ST官方有一个很强大的时钟配置工具stm32f4xx_Clock_Configuration_V1.0.0.xls,这个工具的界面是在excel里边实现的,功能很强大,通过图形化界面配置时钟的各个寄存器,配置完成之后可以由该工具自动生成system_stm32f4xx.c文件。该工具的界面如下所示
通过这个图形化配置界面用户可以直接得到满足自己要求的时钟配置文件。下边来分析一下system_stm32f4xx.c文件的具体内容。
system_stm32f4xx.c主要包含三个重要的东西:
(1) SystemInit()函数:这个函数主要用来配置时钟,这个函数可以用来设置上边讲到的时钟配置过程中的时钟源选择和时钟系数选择。经过这个函数以后系统的时钟就配置完成了。
(2) SystemCoreClock变量:这个变量的值就是经过配置之后SYSCLK的值
(3) SystemCoreClockUpdate()函数:这个函数用来更新SystemCoreClock变量,在时钟配置完成之后,需要调用这个函数来更新SystemCoreClock变量。
其中SystemInit()函数中,前边是RCC寄存器的初始化,SystemInit()函数会调用SetSysClock函数,这个函数是设置时钟的主要函数。下边主要来分析一下这个函数。这里选择主PLL作为系统时钟SYSCLK来源,选择HSE时钟作为PLL的时钟来源。SetSysClok()源码如下。 static void SetSysClock(void) { /******************************************************************************/ /* PLL (clocked by HSE) used as System clock source */ /******************************************************************************/ __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CR |= ((uint32_t)RCC_CR_HSEON); //使能HSE时钟
do { HSEStatus = RCC->CR & RCC_CR_HSERDY; StartUpCounter++; } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); //等待HSE时钟信号稳定
if ((RCC->CR & RCC_CR_HSERDY) != RESET) { HSEStatus = (uint32_t)0x01; //表示HSE启动成功 } else { HSEStatus = (uint32_t)0x00; //表示HSE启动失败 }
if (HSEStatus == (uint32_t)0x01) { /* Enable high performance mode, System frequency up to 168 MHz */ RCC->APB1ENR |= RCC_APB1ENR_PWREN; //使能电源接口时钟 PWR->CR |= PWR_CR_PMODE; //调压器输出电压级别选择
/* HCLK = SYSCLK / 1*/ RCC->CFGR |= RCC_CFGR_HPRE_DIV1; //设置AHBx预分频器的值,经过这个分频器设置得到 AHBx=SYSCLK / RCC_CFGR_HPRE_DIV1
/* PCLK2 = HCLK / 2*/ RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; //设置APB2预分频器的值,经过这个分频器设置得到 APB2 = AHBx / RCC_CFGR_PPRE2_DIV2
/* PCLK1 = HCLK / 4*/ RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //设置APB1预分频器的值,经过这个分频器设置得到 APB1 = AHBx / RCC_CFGR_PPRE1_DIV4
/* Configure the main PLL */ RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | //设置主PLL的时钟来源,这里设置为HSE. 设置主PLL分频器的值,也就是PLL_M,PLL_N,PLL_P,PLL_Q. (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
/* Enable the main PLL */ RCC->CR |= RCC_CR_PLLON; //启动主PLL
/* Wait till the main PLL is ready */ while((RCC->CR & RCC_CR_PLLRDY) == 0) //等待主PLL时钟就绪 { }
/* Configure Flash prefetch, Instruction cache, Data cache and wait state */ FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
/* Select the main PLL as system clock source */ RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); //选择PLL作为系统时钟SYSCLK来源 RCC->CFGR |= RCC_CFGR_SW_PLL;
/* Wait till the main PLL is used as system clock source */ while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL); //等待PLL成为主时钟来源 { } } else { /* If HSE fails to start-up, the application will have wrong clock configuration. User can add here some code to deal with this error */ }
}
经过以上配置,系统的时钟就配置完成了。然后用户可以调用SystemCoreClockUpdate()函数来更新SystemCoreClock变量,SystemCoreClockUpdate()函数实际上就是根据HSE的值以及上边配置过程中设置的系数重新计算出来一个SystemCoreClock的值,相当于读取上边配置好寄存器里边的值,再把系统时钟计算一遍。但是在更新之前,必须正确设置HSE_VALUE这个宏定义的值,一般在stm32f4xx.h文件中会默认为25M,用户需要根据自己的需要来修改这个值,我这边晶振是8M。所以HSE_VALUE得值修改为8M。否则会导致计算出来的SystemCoreClock不正确。这个8M是因为我们的主频一般是168M,一般面对的程序时钟频率也是在8M上的PLL分频和倍频上获得的。
5、系统时钟输出功能
STM32F407允许用户把时钟分频后通过MCO1引脚和MCO2引脚输出,用户可以将时钟用作他用或者用来测量时钟信号。
(1) MCO1
用户可以通过配置分频器的值(1-5)向MCO1引脚输出四个不同的时钟源:HSI时钟,LSE时钟,HSE时钟,PLL时钟
(2) MCO2
用户可以通过配置分频器的值(1-5)向MCO2引脚输出四个不同的时钟源:HSE时钟,PLL时钟,系统时钟SYSCLK,PLLI2S时钟。
ST官方给的固件库里边的工程模板就是将时钟进行输出的。
2、MX6U cortexA7时钟晶振PLL
1 系统时钟来源
打开 I.MX6U-ALPHA 开发板原理图,
从图 可以看出 I.MX6U-ALPHA 开发板的系统时钟来源于两部分:32.768KHz 和24MHz 的晶振,其中 32.768KHz 晶振是 I.MX6U 的 RTC 时钟源,24MHz 晶振是 I.MX6U 内核和其它外设的时钟源,也是我们重点要分析的。
2、 7 路 PLL 时钟源
I.MX6U 的外设有很多,不同的外设时钟源不同,NXP 将这些外设的时钟源进行了分组,
一共有 7 组,这 7 组时钟源都是从 24MHz 晶振 PLL 而来的,因此也叫做 7 组 PLL,这 7 组 PLL结构如图
图中展示了 7 个 PLL 的关系,我们依次来看一下这 7 个 PLL 都是什么做什么的:
①、 ARM_PLL(PLL1),此路 PLL 是供 ARM 内核使用的,ARM 内核时钟就是由此 PLL
生成的,此 PLL 通过编程的方式最高可倍频到 1.3GHz。
②、528_PLL(PLL2),此路 PLL 也叫做 System_PLL,此路 PLL 是固定的 22 倍频,不可编
程修改。因此,此路 PLL 时钟=24MHz * 22 = 528MHz,这也是为什么此 PLL 叫做 528_PLL 的
原因。此 PLL 分出了 4 路 PFD,分别为:PLL2_PFD0~PLL2_PFD3,这 4 路 PFD 和 528_PLL
共同作为其它很多外设的根时钟源。通常 528_PLL 和这 4 路 PFD 是 I.MX6U 内部系统总线的时钟源,比如内处理逻辑单元、DDR 接口、NAND/NOR 接口等等。
③、USB1_PLL(PLL3),此路 PLL 主要用于 USBPHY,此 PLL 也有四路 PFD,为:PLL3_PFD0~PLL3_PFD3,USB1_PLL 是固定的 20 倍频,因此 USB1_PLL=24MHz *20=480MHz。USB1_PLL虽然主要用于USB1PHY,但是其和四路PFD同样也可以作为其他外设的根时钟源。
④、USB2_PLL(PLL7,没有写错!就是 PLL7,虽然序号标为 4,但是实际是 PLL7),看名字就知道此路PLL是给USB2PHY使用的。同样的,此路PLL固定为20倍频,因此也是480MHz。
⑤、ENET_PLL(PLL6),此路 PLL 固定为 20+5/6 倍频,因此 ENET_PLL=24MHz * (20+5/6)= 500MHz。此路 PLL 用于生成网络所需的时钟,可以在此 PLL 的基础上生成 25/50/100/125MHz的网络时钟。
⑥、VIDEO_PLL(PLL5),此路 PLL 用于显示相关的外设,比如 LCD,此路 PLL 的倍频可以调整,PLL 的输出范围在 650MHz~1300MHz。此路 PLL 在最终输出的时候还可以进行分频,可选 1/2/4/8/16 分频。
⑦、AUDIO_PLL(PLL4),此路 PLL 用于音频相关的外设,此路 PLL 的倍频可以调整,PLL的输出范围同样也是 650MHz~1300MHz,此路 PLL 在最终输出的时候也可以进行分频,可选1/2/4 分频。