6. MCO时钟输出
图 15-4 STM32F767 MCO时钟
MCO是microcontroller clock output的缩写,是微控制器时钟输出引脚,主要作用是可以对外提供时钟,相当于一个有源晶振。F767中有两个MCO,由PA8/PC9复用所得。MCO1所需的时钟源通过 RCC 时钟配置寄存器 (RCC_CFGR) 中的 MCO1PRE[2:0] 和 MCO1[1:0]位选择。MCO2所需的时钟源通过 RCC 时钟配置寄存器 (RCC_CFGR) 中的 MCO2PRE[2:0] 和 MCO2位选择。
15.3 配置系统时钟实验
15.3.1 使用HSE
一般情况下,我们都是使用HSE,然后HSE经过PLL倍频之后作为系统时钟。F767系统时钟最高为216 MHz,这个是官方推荐的最高的稳定时钟。
如果我们使用库函数编程,当程序来到main函数首先调用SystemClock_Config ()函数把系统时钟初始化成216 MHz,SystemClock_Config ()在文件:main.c中定义。如果我们想把系统时钟设置低一点或者超频的话,可以修改底层的库文件。
15.3.2 使用HSI
当HSE直接或者间接(HSE经过PLL倍频)的作为系统时钟的时候,如果HSE发生故障,不仅HSE会被关闭,连PLL也会被关闭,这个时候系统会自动切换HSI作为系统时钟,此时SYSCLK=HSI=16M,如果没有开启CSS和CSS中断的话,那么整个系统就只能在低速率运行,这是系统跟瘫痪没什么两样。
如果开启了CSS功能的话,那么可以当HSE故障时,在CSS中断里面采取补救措施,使用HSI,重新设置系统频率为216 MHz,让系统恢复正常使用。但这只是权宜之计,并非万全之策,最好的方法还是要采取相应的补救措施并报警,然后修复HSE。临时使用HSI只是为了把损失降低到最小,毕竟HSI较于HSE精度还是要低点。
F103系列中,使用HSI最大只能把系统设置为64M,并不能跟使用HSE一样把系统时钟设置为72M,究其原因是HSI在进入PLL倍频的时候必须2分频,导致PLL倍频因子调到最大也只能到64M,而HSE进入PLL倍频的时候则不用2分频。
在F767中,无论是使用HSI还是HSE都可以把系统时钟设置为216 MHz,因为HSE或者HSI在进入PLL倍频的时候都会被分频为1M之后再倍频。
还有一种情况是,有些用户不想用HSE,想用HSI,但是又不知道怎么用HSI来设置系统时钟,因为调用库函数都是使用HSE,下面我们给出个使用HSI配置系统时钟例子,起个抛砖引玉的作用。
15.3.3 硬件设计
1、RCC
2、LED一个
RCC是单片机内部资源,不需要外部威廉希尔官方网站
。通过LED闪烁的频率来直观的判断不同系统时钟频率对软件延时的效果。
15.3.4 软件设计
我们编写两个RCC驱动文件,bsp_clkconfig.h和bsp_clkconfig.c,用来存放RCC系统时钟配置函数。
1. 编程要点
1、开启HSE/HSI ,并等待 HSE/HSI 稳定
2、设置 AHB、APB2、APB1的预分频因子
3、设置PLL的时钟来源,设置VCO输入时钟 分频因子PLL_M,设置VCO输出时钟
倍频因子PLL_N,设置PLLCLK时钟分频因子PLL_P,设置OTG FS,SDIO,RNG
时钟分频因子 PLL_Q
4、开启PLL,并等待PLL稳定
5、把PLLCK切换为系统时钟SYSCLK
6、读取时钟切换状态位,确保PLLCLK被选为系统时钟
2. 代码分析
这里只讲解核心的部分代码,有些变量的设置,头文件的包含等并没有涉及到,完整的代码请参考本章配套的工程。
使用HSE配置系统时钟
代码 11 HSE作为系统时钟来源
1 /*
2 * 使用HSE时,设置系统时钟的步骤
3 * 1、开启HSE ,并等待 HSE 稳定
4 * 2、设置 AHB、APB2、APB1的预分频因子
5 * 3、设置PLL的时钟来源
6 * 设置VCO输入时钟 分频因子 m
7 * 设置VCO输出时钟 倍频因子 n
8 * 设置PLLCLK时钟分频因子 p
9 * 设置OTG FS,SDIO,RNG时钟分频因子 q
10 * 4、开启PLL,并等待PLL稳定
11 * 5、把PLLCK切换为系统时钟SYSCLK
12 * 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟
13 */
14
15 /*
16 * m: VCO输入时钟 分频因子,取值2~63
17 * n: VCO输出时钟 倍频因子,取值50~432
18 * p: PLLCLK时钟分频因子 ,取值2,4,6,8
19 * q: OTG FS,SDIO,RNG时钟分频因子,取值4~15
20 * 函数调用举例,使用HSE设置时钟
21 * SYSCLK=HCLK=216MHz,PCLK2=HCLK/2=108MHz,PCLK1=HCLK/4=54MHz
22 * HSE_SetSysClock(25, 432, 2, 9);
23 *
24 HSE作为时钟来源,经过PLL倍频作为系统时钟,这是通常
25 的做法
26 */
27 void HSE_SetSysClock(uint32_t m, uint32_t n, uint32_t p, uint32_t q)
28 {
29 RCC_ClkInitTypeDef RCC_ClkInitStruct;
30 RCC_OscInitTypeDef RCC_OscInitStruct;
31 HAL_StatusTypeDef ret = HAL_OK;
32
33 /*
34 使能HSE,配置HSE为PLL的时钟源,配置PLL的各种分频因
35 子M N P Q
36 * PLLCLK = HSE/M*N/P = 25M / 25 *432 / 2 = 216M
37 */
38 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
39 RCC_OscInitStruct.HSEState = RCC_HSE_ON;
40 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
41 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
42 RCC_OscInitStruct.PLL.PLLM = m;
43 RCC_OscInitStruct.PLL.PLLN = n;
44 RCC_OscInitStruct.PLL.PLLP = p;
45 RCC_OscInitStruct.PLL.PLLQ = q;
46
47 ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
48 if (ret != HAL_OK) {
49 while (1) {
50 ;
51 }
52 }
53
54 /* 激活 OverDrive 模式以达到216M频率 */
55 ret = HAL_PWREx_EnableOverDrive();
56 if (ret != HAL_OK) {
57 while (1) {
58 ;
59 }
60 }
61
62 /* 选择PLLCLK作为SYSCLK,并配置 HCLK, PCLK1 and PCLK2
63 的时钟分频因子
64 * SYSCLK = PLLCLK = 216M
65 * HCLK = SYSCLK / 1 = 216M
66 * PCLK2 = SYSCLK / 2 = 108M
67 * PCLK1 = SYSCLK / 4 = 54M
68 */
69 RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK |
70 RCC_CLOCKTYPE_HCLK |
71 RCC_CLOCKTYPE_PCLK1 |
72 RCC_CLOCKTYPE_PCLK2);
73 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
74 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
75 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
76 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
77
78 ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
79 if (ret != HAL_OK) {
80 while (1) {
81 ;
82 }
83 }
84 }
这个函数采用库函数编写, 代码理解参考注释即可。
HSE我们使用25M,参数m我们一般也设置为25,所以我们需要修改系统时钟的时候只需要修改参数n和p即可,SYSCLK=PLLCLK=HSE/m*n/p。
函数调用举例:HSE_SetSysClock(25, 400, 2, 7) 把系统时钟设置为200 MHz。HSE_SetSysClock(25, 432, 2, 9)把系统时钟设置为216M。
使用HSI配置系统时钟
1 /*
2 * 使用HSI时,设置系统时钟的步骤
3 * 1、开启HSI ,并等待 HSI 稳定
4 * 2、设置 AHB、APB2、APB1的预分频因子
5 * 3、设置PLL的时钟来源
6 * 设置VCO输入时钟 分频因子 m
7 * 设置VCO输出时钟 倍频因子 n
8 * 设置SYSCLK时钟分频因子 p
9 * 设置OTG FS,SDIO,RNG时钟分频因子 q
10 * 4、开启PLL,并等待PLL稳定
11 * 5、把PLLCK切换为系统时钟SYSCLK
12 * 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟
13 */
14
15 /*
16 * m: VCO输入时钟 分频因子,取值2~63
17 * n: VCO输出时钟 倍频因子,取值50~432
18 * p: PLLCLK时钟分频因子 ,取值2,4,6,8
19 * q: OTG FS,SDIO,RNG时钟分频因子,取值4~15
20 * 函数调用举例,使用HSI设置时钟
21 * SYSCLK=HCLK=216MHz,PCLK2=HCLK/2=108M,PCLK1=HCLK/4=54MHz
22 * HSI_SetSysClock(16, 432, 2, 7);
23
24 */
25 void HSI_SetSysClock(uint32_t m, uint32_t n, uint32_t p, uint32_t q)
26 {
27 RCC_ClkInitTypeDef RCC_ClkInitStruct;
28 RCC_OscInitTypeDef RCC_OscInitStruct;
29 HAL_StatusTypeDef ret = HAL_OK;
30
31 /*
32 使能HSE,配置HSE为PLL的时钟源,配置PLL的各种分频因
33 子M N P Q
34 * PLLCLK = HSE/M*N/P = 25M / 25 *432 / 2 = 216M
35 */
36 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
37 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
38 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
39 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
40 RCC_OscInitStruct.PLL.PLLM = m;
41 RCC_OscInitStruct.PLL.PLLN = n;
42 RCC_OscInitStruct.PLL.PLLP = p;
43 RCC_OscInitStruct.PLL.PLLQ = q;
44
45 ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
46 if (ret != HAL_OK) {
47 while (1) {
48 ;
49 }
50 }
51
52 /* 激活 OverDrive 模式以达到216M频率 */
53 ret = HAL_PWREx_EnableOverDrive();
54 if (ret != HAL_OK) {
55 while (1) {
56 ;
57 }
58 }
59
60 /* 选择PLLCLK作为SYSCLK,并配置 HCLK, PCLK1 and PCLK2
61 的时钟分频因子
62 * SYSCLK = PLLCLK = 216M
63 * HCLK = SYSCLK / 1 = 216M
64 * PCLK2 = SYSCLK / 2 = 108M
65 * PCLK1 = SYSCLK / 4 = 54M
66 */
67 RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK |
68 RCC_CLOCKTYPE_HCLK |
69 RCC_CLOCKTYPE_PCLK1 |
70 RCC_CLOCKTYPE_PCLK2);
71 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
72 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
73 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
74 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
75
76 ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
77 if (ret != HAL_OK) {
78 while (1) {
79 ;
80 }
81 }
82 }
这个函数采用库函数编写, 代码理解参考注释即可。函数有4个形参m、n、p、q,具体说明如下:
形参
形参说明
取值范围
m
VCO输入时钟 分频因子
2~63
n
VCO输出时钟 倍频因子
192~432
p
PLLCLK时钟分频因子
2/4/6/8
q
OTG FS,SDIO,RNG时钟分频因子
4~15
HSI为16M,参数m我们一般也设置为16,所以我们需要修改系统时钟的时候只需要修改参数n和p即可,SYSCLK=PLLCLK=HSI/m*n/p。
函数调用举例:HSI_SetSysClock(16, 400, 2, 7) 把系统时钟设置为200MHz,。HSI_SetSysClock(16, 432, 2, 9)把系统时钟设置为216MHz。
软件延时
1 void Delay(__IO uint32_t nCount)
2 {
3 for (; nCount != 0; nCount--);
4 }
软件延时函数,使用不同的系统时钟,延时时间不一样,可以通过LED闪烁的频率来判断。
MCO输出
在F767中,PA8/PC9可以复用为MCO1/2引脚,对外提供时钟输出,我们也可以用示波器监控该引脚的输出来判断我们的系统时钟是否设置正确。HAL库有现成的库函数HAL_RCC_MCOConfig,配置MCO,只需确定输出引脚,输出时钟源,以及分频系数就可以输出时钟使用非常方便。
主函数
在主函数中,可以调用HSE_SetSysClock()或者HSI_SetSysClock()这两个函数把系统时钟设置成各种常用的时钟,然后通过MCO引脚监控,或者通过LED闪烁的快慢体验不同的系统时钟对同一个软件延时函数的影响。
1 int main(void)
2 {
3
4 //
5 程序来到main函数之前,启动文件:statup_stm32f746xx.
6 s已经调用
7 // SystemInit()函数把系统时钟初始化成16MHZ
8 // SystemInit()在system_stm32f7xx.c中定义
9 // 如果用户想修改系统时钟,可自行编写程序修改
10 //
11 重新设置系统时钟,这时候可以选择使用HSE还是HSI
12
13 //系统时钟设置为216M,最高是250M
14 HSE_SetSysClock(25, 432, 2, 9);
15
16 // 使用HSI,配置系统时钟为216M
17 //HSI_SetSysClock(16, 50, 2, 9);
18
19 // LED 端口初始化
20 LED_GPIO_Config();
21
22 // MCO1 输出PLLCLK
23 HAL_RCC_MCOConfig(RCC_MCO1,RCC_MCO1SOURCE_PLLCLK, RCC_MCODIV_2);
24
25 // MCO2 输出SYSCLK
26 HAL_RCC_MCOConfig(RCC_MCO2,RCC_MCO2SOURCE_SYSCLK, RCC_MCODIV_2);
27
28 while (1) {
29 LED1( ON ); // 亮
30 Delay(0x0FFFFF);
31 LED1( OFF ); // 灭
32 Delay(0x0FFFFF);
33 }
34 }
15.3.5 下载验证
15.3.6 下载验证
把编译好的程序下载到开发板,可以看到设置不同的系统时钟时,LED闪烁的快慢不一样。更精确的数据我们可以用示波器监控MCO引脚看到。
6. MCO时钟输出
图 15-4 STM32F767 MCO时钟
MCO是microcontroller clock output的缩写,是微控制器时钟输出引脚,主要作用是可以对外提供时钟,相当于一个有源晶振。F767中有两个MCO,由PA8/PC9复用所得。MCO1所需的时钟源通过 RCC 时钟配置寄存器 (RCC_CFGR) 中的 MCO1PRE[2:0] 和 MCO1[1:0]位选择。MCO2所需的时钟源通过 RCC 时钟配置寄存器 (RCC_CFGR) 中的 MCO2PRE[2:0] 和 MCO2位选择。
15.3 配置系统时钟实验
15.3.1 使用HSE
一般情况下,我们都是使用HSE,然后HSE经过PLL倍频之后作为系统时钟。F767系统时钟最高为216 MHz,这个是官方推荐的最高的稳定时钟。
如果我们使用库函数编程,当程序来到main函数首先调用SystemClock_Config ()函数把系统时钟初始化成216 MHz,SystemClock_Config ()在文件:main.c中定义。如果我们想把系统时钟设置低一点或者超频的话,可以修改底层的库文件。
15.3.2 使用HSI
当HSE直接或者间接(HSE经过PLL倍频)的作为系统时钟的时候,如果HSE发生故障,不仅HSE会被关闭,连PLL也会被关闭,这个时候系统会自动切换HSI作为系统时钟,此时SYSCLK=HSI=16M,如果没有开启CSS和CSS中断的话,那么整个系统就只能在低速率运行,这是系统跟瘫痪没什么两样。
如果开启了CSS功能的话,那么可以当HSE故障时,在CSS中断里面采取补救措施,使用HSI,重新设置系统频率为216 MHz,让系统恢复正常使用。但这只是权宜之计,并非万全之策,最好的方法还是要采取相应的补救措施并报警,然后修复HSE。临时使用HSI只是为了把损失降低到最小,毕竟HSI较于HSE精度还是要低点。
F103系列中,使用HSI最大只能把系统设置为64M,并不能跟使用HSE一样把系统时钟设置为72M,究其原因是HSI在进入PLL倍频的时候必须2分频,导致PLL倍频因子调到最大也只能到64M,而HSE进入PLL倍频的时候则不用2分频。
在F767中,无论是使用HSI还是HSE都可以把系统时钟设置为216 MHz,因为HSE或者HSI在进入PLL倍频的时候都会被分频为1M之后再倍频。
还有一种情况是,有些用户不想用HSE,想用HSI,但是又不知道怎么用HSI来设置系统时钟,因为调用库函数都是使用HSE,下面我们给出个使用HSI配置系统时钟例子,起个抛砖引玉的作用。
15.3.3 硬件设计
1、RCC
2、LED一个
RCC是单片机内部资源,不需要外部威廉希尔官方网站
。通过LED闪烁的频率来直观的判断不同系统时钟频率对软件延时的效果。
15.3.4 软件设计
我们编写两个RCC驱动文件,bsp_clkconfig.h和bsp_clkconfig.c,用来存放RCC系统时钟配置函数。
1. 编程要点
1、开启HSE/HSI ,并等待 HSE/HSI 稳定
2、设置 AHB、APB2、APB1的预分频因子
3、设置PLL的时钟来源,设置VCO输入时钟 分频因子PLL_M,设置VCO输出时钟
倍频因子PLL_N,设置PLLCLK时钟分频因子PLL_P,设置OTG FS,SDIO,RNG
时钟分频因子 PLL_Q
4、开启PLL,并等待PLL稳定
5、把PLLCK切换为系统时钟SYSCLK
6、读取时钟切换状态位,确保PLLCLK被选为系统时钟
2. 代码分析
这里只讲解核心的部分代码,有些变量的设置,头文件的包含等并没有涉及到,完整的代码请参考本章配套的工程。
使用HSE配置系统时钟
代码 11 HSE作为系统时钟来源
1 /*
2 * 使用HSE时,设置系统时钟的步骤
3 * 1、开启HSE ,并等待 HSE 稳定
4 * 2、设置 AHB、APB2、APB1的预分频因子
5 * 3、设置PLL的时钟来源
6 * 设置VCO输入时钟 分频因子 m
7 * 设置VCO输出时钟 倍频因子 n
8 * 设置PLLCLK时钟分频因子 p
9 * 设置OTG FS,SDIO,RNG时钟分频因子 q
10 * 4、开启PLL,并等待PLL稳定
11 * 5、把PLLCK切换为系统时钟SYSCLK
12 * 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟
13 */
14
15 /*
16 * m: VCO输入时钟 分频因子,取值2~63
17 * n: VCO输出时钟 倍频因子,取值50~432
18 * p: PLLCLK时钟分频因子 ,取值2,4,6,8
19 * q: OTG FS,SDIO,RNG时钟分频因子,取值4~15
20 * 函数调用举例,使用HSE设置时钟
21 * SYSCLK=HCLK=216MHz,PCLK2=HCLK/2=108MHz,PCLK1=HCLK/4=54MHz
22 * HSE_SetSysClock(25, 432, 2, 9);
23 *
24 HSE作为时钟来源,经过PLL倍频作为系统时钟,这是通常
25 的做法
26 */
27 void HSE_SetSysClock(uint32_t m, uint32_t n, uint32_t p, uint32_t q)
28 {
29 RCC_ClkInitTypeDef RCC_ClkInitStruct;
30 RCC_OscInitTypeDef RCC_OscInitStruct;
31 HAL_StatusTypeDef ret = HAL_OK;
32
33 /*
34 使能HSE,配置HSE为PLL的时钟源,配置PLL的各种分频因
35 子M N P Q
36 * PLLCLK = HSE/M*N/P = 25M / 25 *432 / 2 = 216M
37 */
38 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
39 RCC_OscInitStruct.HSEState = RCC_HSE_ON;
40 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
41 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
42 RCC_OscInitStruct.PLL.PLLM = m;
43 RCC_OscInitStruct.PLL.PLLN = n;
44 RCC_OscInitStruct.PLL.PLLP = p;
45 RCC_OscInitStruct.PLL.PLLQ = q;
46
47 ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
48 if (ret != HAL_OK) {
49 while (1) {
50 ;
51 }
52 }
53
54 /* 激活 OverDrive 模式以达到216M频率 */
55 ret = HAL_PWREx_EnableOverDrive();
56 if (ret != HAL_OK) {
57 while (1) {
58 ;
59 }
60 }
61
62 /* 选择PLLCLK作为SYSCLK,并配置 HCLK, PCLK1 and PCLK2
63 的时钟分频因子
64 * SYSCLK = PLLCLK = 216M
65 * HCLK = SYSCLK / 1 = 216M
66 * PCLK2 = SYSCLK / 2 = 108M
67 * PCLK1 = SYSCLK / 4 = 54M
68 */
69 RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK |
70 RCC_CLOCKTYPE_HCLK |
71 RCC_CLOCKTYPE_PCLK1 |
72 RCC_CLOCKTYPE_PCLK2);
73 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
74 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
75 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
76 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
77
78 ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
79 if (ret != HAL_OK) {
80 while (1) {
81 ;
82 }
83 }
84 }
这个函数采用库函数编写, 代码理解参考注释即可。
HSE我们使用25M,参数m我们一般也设置为25,所以我们需要修改系统时钟的时候只需要修改参数n和p即可,SYSCLK=PLLCLK=HSE/m*n/p。
函数调用举例:HSE_SetSysClock(25, 400, 2, 7) 把系统时钟设置为200 MHz。HSE_SetSysClock(25, 432, 2, 9)把系统时钟设置为216M。
使用HSI配置系统时钟
1 /*
2 * 使用HSI时,设置系统时钟的步骤
3 * 1、开启HSI ,并等待 HSI 稳定
4 * 2、设置 AHB、APB2、APB1的预分频因子
5 * 3、设置PLL的时钟来源
6 * 设置VCO输入时钟 分频因子 m
7 * 设置VCO输出时钟 倍频因子 n
8 * 设置SYSCLK时钟分频因子 p
9 * 设置OTG FS,SDIO,RNG时钟分频因子 q
10 * 4、开启PLL,并等待PLL稳定
11 * 5、把PLLCK切换为系统时钟SYSCLK
12 * 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟
13 */
14
15 /*
16 * m: VCO输入时钟 分频因子,取值2~63
17 * n: VCO输出时钟 倍频因子,取值50~432
18 * p: PLLCLK时钟分频因子 ,取值2,4,6,8
19 * q: OTG FS,SDIO,RNG时钟分频因子,取值4~15
20 * 函数调用举例,使用HSI设置时钟
21 * SYSCLK=HCLK=216MHz,PCLK2=HCLK/2=108M,PCLK1=HCLK/4=54MHz
22 * HSI_SetSysClock(16, 432, 2, 7);
23
24 */
25 void HSI_SetSysClock(uint32_t m, uint32_t n, uint32_t p, uint32_t q)
26 {
27 RCC_ClkInitTypeDef RCC_ClkInitStruct;
28 RCC_OscInitTypeDef RCC_OscInitStruct;
29 HAL_StatusTypeDef ret = HAL_OK;
30
31 /*
32 使能HSE,配置HSE为PLL的时钟源,配置PLL的各种分频因
33 子M N P Q
34 * PLLCLK = HSE/M*N/P = 25M / 25 *432 / 2 = 216M
35 */
36 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
37 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
38 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
39 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
40 RCC_OscInitStruct.PLL.PLLM = m;
41 RCC_OscInitStruct.PLL.PLLN = n;
42 RCC_OscInitStruct.PLL.PLLP = p;
43 RCC_OscInitStruct.PLL.PLLQ = q;
44
45 ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
46 if (ret != HAL_OK) {
47 while (1) {
48 ;
49 }
50 }
51
52 /* 激活 OverDrive 模式以达到216M频率 */
53 ret = HAL_PWREx_EnableOverDrive();
54 if (ret != HAL_OK) {
55 while (1) {
56 ;
57 }
58 }
59
60 /* 选择PLLCLK作为SYSCLK,并配置 HCLK, PCLK1 and PCLK2
61 的时钟分频因子
62 * SYSCLK = PLLCLK = 216M
63 * HCLK = SYSCLK / 1 = 216M
64 * PCLK2 = SYSCLK / 2 = 108M
65 * PCLK1 = SYSCLK / 4 = 54M
66 */
67 RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK |
68 RCC_CLOCKTYPE_HCLK |
69 RCC_CLOCKTYPE_PCLK1 |
70 RCC_CLOCKTYPE_PCLK2);
71 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
72 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
73 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
74 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
75
76 ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
77 if (ret != HAL_OK) {
78 while (1) {
79 ;
80 }
81 }
82 }
这个函数采用库函数编写, 代码理解参考注释即可。函数有4个形参m、n、p、q,具体说明如下:
形参
形参说明
取值范围
m
VCO输入时钟 分频因子
2~63
n
VCO输出时钟 倍频因子
192~432
p
PLLCLK时钟分频因子
2/4/6/8
q
OTG FS,SDIO,RNG时钟分频因子
4~15
HSI为16M,参数m我们一般也设置为16,所以我们需要修改系统时钟的时候只需要修改参数n和p即可,SYSCLK=PLLCLK=HSI/m*n/p。
函数调用举例:HSI_SetSysClock(16, 400, 2, 7) 把系统时钟设置为200MHz,。HSI_SetSysClock(16, 432, 2, 9)把系统时钟设置为216MHz。
软件延时
1 void Delay(__IO uint32_t nCount)
2 {
3 for (; nCount != 0; nCount--);
4 }
软件延时函数,使用不同的系统时钟,延时时间不一样,可以通过LED闪烁的频率来判断。
MCO输出
在F767中,PA8/PC9可以复用为MCO1/2引脚,对外提供时钟输出,我们也可以用示波器监控该引脚的输出来判断我们的系统时钟是否设置正确。HAL库有现成的库函数HAL_RCC_MCOConfig,配置MCO,只需确定输出引脚,输出时钟源,以及分频系数就可以输出时钟使用非常方便。
主函数
在主函数中,可以调用HSE_SetSysClock()或者HSI_SetSysClock()这两个函数把系统时钟设置成各种常用的时钟,然后通过MCO引脚监控,或者通过LED闪烁的快慢体验不同的系统时钟对同一个软件延时函数的影响。
1 int main(void)
2 {
3
4 //
5 程序来到main函数之前,启动文件:statup_stm32f746xx.
6 s已经调用
7 // SystemInit()函数把系统时钟初始化成16MHZ
8 // SystemInit()在system_stm32f7xx.c中定义
9 // 如果用户想修改系统时钟,可自行编写程序修改
10 //
11 重新设置系统时钟,这时候可以选择使用HSE还是HSI
12
13 //系统时钟设置为216M,最高是250M
14 HSE_SetSysClock(25, 432, 2, 9);
15
16 // 使用HSI,配置系统时钟为216M
17 //HSI_SetSysClock(16, 50, 2, 9);
18
19 // LED 端口初始化
20 LED_GPIO_Config();
21
22 // MCO1 输出PLLCLK
23 HAL_RCC_MCOConfig(RCC_MCO1,RCC_MCO1SOURCE_PLLCLK, RCC_MCODIV_2);
24
25 // MCO2 输出SYSCLK
26 HAL_RCC_MCOConfig(RCC_MCO2,RCC_MCO2SOURCE_SYSCLK, RCC_MCODIV_2);
27
28 while (1) {
29 LED1( ON ); // 亮
30 Delay(0x0FFFFF);
31 LED1( OFF ); // 灭
32 Delay(0x0FFFFF);
33 }
34 }
15.3.5 下载验证
15.3.6 下载验证
把编译好的程序下载到开发板,可以看到设置不同的系统时钟时,LED闪烁的快慢不一样。更精确的数据我们可以用示波器监控MCO引脚看到。
举报