layout: post

tags: [STM32]

comments: true

为什么要了解时钟树?

最近项目开发的时候,外部时钟源是16MHz,结果配置错了系统时钟,STM32F103的系统时钟频率最高为72MHz,错误地配置到了144MHz,但是AHB总线时钟又正确地配置到72MHz,最终导致了以下几种情况:

  • 程序会意外跑飞,然后进入Hard fault,甚至不知道发生了什么,就出现这样的错误;
  • 数据意外错误,int32负数乘法的时候,第30位数据异常;

    以上的问题,较难排查,而且给人一种芯片不稳定的错觉,对的,没错,这就是超频的代价,高速行驶的汽车,更容易翻车,所以,对于芯片的时钟配置有一个全面的了解,有着弥足轻重的作用。

树的根



先看一下这张从网上扒来的图,说到树,不由自主想到了二叉树,设备树,森林里的树,而时钟树,也不例外,和这些树一样,都有根,有叶子,做一下简单的类比;

是树汲取营养的地方,是树赖以生存的一个部位;

而时钟树的根就是时钟输入源,用于产生系统的时钟节拍,即系统时钟

系统时钟源

  • 高速内部时钟 HSI8MHz;
  • 高速外部时钟 HSE4MHz25MHz;
  • 锁相环时钟 PLL;

    常用的配置方案例如:选用外部时钟HSE,频率为8MHz,经过锁相环时钟,PLL进行9倍频,则系统时钟频率:

SysClock = 8MHz*9 = 72Mhz

或者使用外部时钟HSE,频率为16MHz,则经过锁相环时钟,PLL2分频,然后9倍频,则系统时钟频率:

SysClock = 16MHz/2*9 = 72Mhz

可见,PLL时钟源的使用很灵活,可以灵活运用;

标准库的时钟配置

STM32标准库的启动文件中可以发现,在main()运行前,已经运行了SystemInit()函数,代码如下,IDE是MDK,代码如下,具体可以参考

Reset_Handler    PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP

标准库是默认按照外部高速时钟频率为8MHz进行配置的,SystemInit()函数原型位于system_stm32f10x.c,具体的调用关系如下,具体源码可以参考标准库;

    SystemInit()
-->SetSysClock()
-->SetSysClockTo72()

在函数SetSysClock()通过宏定义选择系统所需要配置的时钟频率;

static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
SetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHz
SetSysClockTo72();
#endif
/* If none of the define above is enabled, the HSI is used as System clock
source (default after reset) */
}

函数SetSysClockTo72默认定义系统时钟为72MHz

#define SYSCLK_FREQ_72MHz   72000000

初始化后系统的状态:

时钟 频率
SYSCLK 72MHz
AHB 72MHz
PCLK1 36MHz
PCLK2 72MHz
PLL 72MHz

外部时钟源16M

SetSysClockTo72

如果外部时钟源的频率是16M,需要进行哪些修改?

根据源码可以知,最终在SetSysClock中进行修改即可,因为要配置到72M的系统时钟频率,则直接进入函数SetSysClockTo72()进行修改;

SetSysClockTo72源码如下;或者跳过源码直接看patch文件;

/**
* @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2
* and PCLK1 prescalers.
* @note This function should be used only after reset.
* @param None
* @retval None
*/
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0; /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* Enable HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON); /* Wait till HSE is ready and if Time out is reached exit */
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;
} if (HSEStatus == (uint32_t)0x01)
{
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE; /* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; /* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; /* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; /* PCLK1 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; #ifdef STM32F10X_CL
/* Configure PLLs ------------------------------------------------------*/
/* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
/* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |
RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |
RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); /* Enable PLL2 */
RCC->CR |= RCC_CR_PLL2ON;
/* Wait till PLL2 is ready */
while((RCC->CR & RCC_CR_PLL2RDY) == 0)
{
} /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_PLLMULL9);
#else
/* PLL configuration: 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);
#endif /* STM32F10X_CL */ /* Enable PLL */
RCC->CR |= RCC_CR_PLLON; /* Wait till PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
} /* Select PLL as system clock source */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; /* Wait till PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
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 */
}
}
#endif

patch

需要修改两个地方:

  • stm32f10x.hHSE_VALUE的值;
  • system_stm32f10x.cSetSysClockTo72(void)函数的PLL配置;

    patch如下所示;
diff --git a/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/stm32f10x.h b/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/stm32f10x.h
index 8bf7624..e0ad316 100644
--- a/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/stm32f10x.h
+++ b/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/stm32f10x.h
@@ -116,7 +116,9 @@
#ifdef STM32F10X_CL
#define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
#else
- #define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
+// #define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */ diff --git a/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c b/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c
index 71efc85..4ff040a 100644
--- a/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c
+++ b/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c
@@ -1053,7 +1053,7 @@ static void SetSysClockTo72(void)
/* PLL configuration: 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->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9 | RCC_CFGR_PLLXTPRE_HSE_Div2);
#endif /* STM32F10X_CL */ /* Enable PLL */

这样就完成了基本配置的修改。

其他细节

  • RTC的时钟来源:LSE, LSI,LSE的128分频
  • 独立看门狗IWDGCLK的时钟来源:LSI
  • APB1总线的时钟,最大到36M
  • APB2总线的时钟,最大到72M
  • APB,APB1,APB2为外设提供时钟。

STM32 时钟树配置快速入门的更多相关文章

  1. STM32—时钟树(结合系统时钟函数理解)

    时钟树的概念: 我们可以把MCU的运行比作人体的运行一样,人最重要的是什么?是心跳! 心脏的周期性收缩将血液泵向身体各处.心脏对于人体好比时钟对于MCU,微控制器(MCU)的运行要靠周期性的时钟脉冲来 ...

  2. STM32入门系列-STM32时钟系统,STM32时钟树

    时钟对于单片机来说是非常重要的,它为单片机工作提供一个稳定的机器周期从而使系统能够正常运行.时钟系统犹如人的心脏,一旦有问题整个系统就崩溃.我们知道STM32属于高级单片机,其内部有很多的外设,但不是 ...

  3. CodeIgniter框架——数据库类(配置+快速入门)

    CodeIgniter用户指南——数据库类 数据库配置 入门:用法举例 连接数据库 查询 生成查询结果 查询辅助函数 Active Record 类 事务 表格元数据 字段元数据 自定义函数调用 查询 ...

  4. Linux下时钟框架实践---一款芯片的时钟树配置

    关键词:时钟.PLL.Mux.Divider.Gate.clk_summary等. 时钟和电源是各种设备的基础设施,整个时钟框架可以抽象为几种基本的元器件:负责提供晶振 Linux内核提供了良好的CC ...

  5. STM32 TIM1高级定时器配置快速入门

    layout: post tags: [STM32] comments: true 文章目录 layout: post tags: [STM32] comments: true 重点内容 时基单元 计 ...

  6. STM32 TIM 多通道互补PWM波形输出配置快速入门

    platform:stm32f10xxx lib:STM32F10x_StdPeriph_Lib_V3.5.0 前言 在做三相逆变的时候,需要软件生成SVPWM波形,具体的算法需要产生三对互补的PWM ...

  7. 【python】pycharm常用配置快速入门。

    俗话说,工欲善其事必先利其器.当我们想从事一门新的语言的时候,最重要的是熟悉其常用的编辑器的配置.刚好这两天在学习python,网上看到一篇比较好的文章,转载过来自己学习一下.感谢:https://s ...

  8. STM32时钟树

    STM32的时钟系统 相较于51单片机,stm32的时钟系统可以说是非常复杂了,我们现在看下面的一张图: 上图说明了时钟的走向,是从左至右的从时钟源一步步的分配给外设时钟.需要注意的是,上图左侧一共有 ...

  9. Prometheus入门教程(三):Grafana 图表配置快速入门

    文章首发于[陈树义]公众号,点击跳转到原文:https://mp.weixin.qq.com/s/sA0nYevO8yz6QLRz03qJSw 前面我们使用 Prometheus + Grafana ...

随机推荐

  1. 【简单了解系列】从基础的使用来深挖HashMap

    HashMap定义 说的专业一点,HashMap是常用的用于存储key-value键值对数据的一个集合,底层是基于对Map的接口实现.每一个键值对又叫Entry,这些Entry分散的存储在一个由数组和 ...

  2. 哈密顿绕行世界问题 HDU2181

    题目大意都比较简单,用vector存一下图,然后爆搜就可以了. #include<bits/stdc++.h> using namespace std; ; vector<]; bo ...

  3. I - Red and Black DFS

    There is a rectangular room, covered with square tiles. Each tile is colored either red or black. A ...

  4. Fiddler抓包过滤

    1.User Fiters启用 2.Action Action:Run Filterset now是否运行,Load Filterset加载,Save Filterset保存: 3.Hosts过滤 Z ...

  5. 【山外笔记-SVN命令】svnlook命令详解

    本文打印版问文件下载地址 [山外笔记-SVN命令]svnlook命令详解-打印版.pdf 一.命令简介 svnlook是检验Subversion版本库不同方面的命令行工具,不会对版本库有任何修改,只是 ...

  6. Canvas(3)---绘制饼状图

    Canvas(3)---绘制饼状图 有关canvas之前有写过两篇文章 1.Canvas(1)---概述+简单示例 2.Canvas(2)---绘制折线图 在绘制饼状图之前,我们先要理解什么是圆弧,如 ...

  7. kubernetes的Statefulset介绍

    StatefulSet是一种给Pod提供唯一标志的控制器,他可以保证部署和扩展的顺序. Pod一致性 包含次序(启动和停止次序).网络一致性.此一致性和Pod相关.与被调度到哪个Node节点无关. 稳 ...

  8. 移植madplay到ARM板

    一.环境和软件介绍 1.主机环境:Ubuntu16.04 2.交叉编译器: gcc version 4.4.1 (Sourcery G++ Lite 2009q3-67) 3.ARM板:kernel: ...

  9. java学习(第一篇)

    Java 简介 Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计语言和Java平台的总称.由James Gosling和同事们共同研发,并在1995年正式 ...

  10. Java中的小数运算与精度损失

    float.double类型的问题 我们都知道,计算机是使用二进制存储数据的.而平常生活中,大多数情况下我们都是使用的十进制,因此计算机显示给我们看的内容大多数也是十进制的,这就使得很多时候数据需要在 ...