Systemlnit时钟系统初始化函数剖析

本文详细解析了STM32微控制器中的SystemInit()函数,包括如何配置内部高速时钟HSI、设置预分频器及PLL参数,最终实现72MHz系统时钟的配置过程。

SystemInit():

 

在工程文件的system_stm32f10x.c中有如下代码:
这里将为大家逐个注释,让大家了解。

void SystemInit (void)
{

  RCC->CR |= (uint32_t)0x00000001;//把HSI时钟打开
   //HSION:内部高速时钟使能 (Internal high-speed clock enable) 由软件置’1’或清零。 
   //当从待机和停止模式返回或用作系统时钟的外部4-16MHz振荡器发生故障时,该位由硬件置’1’ 来启动内部8MHz的RC振荡器。
   //当内部8MHz振荡器被直接或间接地用作或被选择将要作为系 统时钟时,该位不能被清零。 
   //0:内部8MHz振荡器关闭; 
   //1:内部8MHz振荡器开启。

 //该部分是把相关位设置为0
  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000; 
#else
  RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */   
  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;
  /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;
  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
  RCC->CFGR &= (uint32_t)0xFF80FFFF;

#ifdef STM32F10X_CL  //判断宏定义标识符(如果是_HD,这段不执行)
  /* Reset PLL2ON and PLL3ON bits */
  RCC->CR &= (uint32_t)0xEBFFFFFF;
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x00FF0000;
  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;
  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;      
#else  //_HD执行该段
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */

//hd同样不执行该段    
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
  #ifdef DATA_IN_ExtSRAM
    SystemInit_ExtMemCtl(); 
  #endif /* DATA_IN_ExtSRAM */
#endif 

//
  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  /* Configure the Flash Latency cycles and enable prefetch buffer */
  SetSysClock();//重点※

#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif 
}

//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) */ 
}
COPY

SetSysClock():

这里选择讲解72MHz为例,该函数的步骤为:

  • 打开HSE时钟(3-8行)
  • 等待HSE时钟就绪(9-24行)
  • 设置flash和三个时钟,确定关系(26-51行)
  • 设置CFGR寄存器确定PLL指针来源和倍频系数(75-80行)
  • 把PLL时钟作为系统时钟的来源(84-97行)

最后有所使用寄存器和系统时钟图参考.

static void SetSysClockTo72(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

/* 使能 HSE 时钟*/    
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
  //在头文件中宏定义了RCC_CR_HSEON对应地址为0x000100000  即位十六——外部高速时钟使能

  /* 等待HSE时钟稳定 */
  do
  {
      //通过判断RCC_CR_HSERDY(外部高速时钟就绪标志,地址对应0x000200000,位17)= 1,则表示稳定
    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;//如果就绪了,则让HSEStatus=0x01
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }  

//判断就绪,然后继续执行
  if (HSEStatus == (uint32_t)0x01)
  {
  //由于cpu速度快于芯片flash很多,所以操作前要等待几个时钟
  //ACR寄存器:LATENCY:时延
    //这些位表示SYSCLK(系统时钟)周期与闪存访问时间的比例000:零等待状态,当0<sYSCLK≤24MHz
    //001:一个等待状态,当24MHz< sYSCLK ≤48MHz
    //010:两个等待状态,当48MHzACR |= FLASH_ACR_PRFTBE;
    /* 两个等待状态 */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; 

 //根据图解可知频率关系
    /* HCLK = SYSCLK 72MHz*/
    //实现则需要AHB预分频器要设置为1
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
    //RCC_CFGR_HPRE_DIV1对应地址0x00000000  不分频的位7:4为0xxx

    /* PCLK2 = HCLK 72MHz*/
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
    //RCC_CFGR_PPRE2_DIV1对应地址0x00000000  不分频的位13:11为0xx

    /*36MHz PCLK1 = HCLK/2 72MHz*/
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
    //RCC_CFGR_PPRE2_DIV1对应地址0x00000400  二分频的位10:8为100

#ifdef STM32F10X_CL   //判断为_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   //非_CL 执行 
    /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
                                        RCC_CFGR_PLLMULL));
                                        //直接将HSE作为PLL时钟的来源
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);//设置PLL 9倍频

#endif /* STM32F10X_CL */

    /* 使能 PLL时钟 */
    RCC->CR |= RCC_CR_PLLON;
    //RCC_CR_PLLON地址对应0x010000000  位24为1:使能PLL

    /* 等待 PLL时钟 稳定 */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }

    /* 选择 PLL时针作为系统时针来源 */
    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 */
  }
}COPY

寄存器和系统时钟图参考:

42行:位7:4
HPRE[3:0]:AHB预分频(AHB Prescaler)
由软件置’1’或清0’来控制AHB时钟的预分频系数。
0xxx: sYSCLK不分频
1000: sYSCLK 2分频 1100: sYSCLK64分频
1001: sYSCLK4分频 1101: sYSCLK 128分频
1010: sYSCLK8分频 1110: sYSCLK 256分频
1011:sYSCLK 16分频 1111:sYSCLK 512分频
注意:当AHB时钟的预分频系数大于1时,必须开启预取缓冲器。详见闪存读取(第2.3.3节).

46行:位13:11
PPRE2[2:0]:高速APB预分频(APB2)(APB high-speed prescaler (APB2))由软件置’1’或清’0’来控制高速APB2时钟(PCLK2)的预分频系数。
0xx:HCLK不分频
100:HCLK 2分频
101:HCLK 4分频
110:HCLK 8分频
111:HCLK 16分频

50行:位10:8
PPRE1[2:0]低速APB预分频(APB1) (APB low-speed prescaler (APB1))
由软件置’1’或清’0’来控制低速APB1时钟(PCLK1)的预分频系数。
警告:软件必须保证APB1时钟频率不超过36MHz。
0xx:HCLK不分频
100:HCLK 2分频
101:HCLK 4分频
110:HCLK 8分频
111:HCLK 16分频

本文转载自: Systemlnit时钟系统初始化函数剖析 – 布尔博客

<think>好的,用户想了解OV7670摄像头模块为什么需要系统时钟初始化,以及其作用。首先,我需要回忆一下摄像头模块的基本工作原理。OV7670是一种图像传感器,常用于嵌入式系统中,比如FPGA或微控制器项目。初始化寄存器是配置其工作模式的关键步骤,而系统时钟可能涉及传感器内部的时序控制。 用户提到的参考文档提到有160多个寄存器需要配置,这说明时钟配置可能是其中之一。系统时钟初始化可能关系到图像采集的同步、数据传输速率以及传感器的整体稳定性。比如,时钟频率不正确可能导致图像失真、数据丢失或者通信错误。另外,不同的应用场景可能需要不同的时钟频率,比如分辨率调整或帧率变化,因此初始化时设置正确的时钟参数至关重要。 接下来,我需要确定系统时钟在OV7670中的具体作用。查阅数据手册,OV7670的系统时钟(可能由外部晶振提供)是整个传感器工作的基础,负责时序生成、像素输出速率等。初始化时设置寄存器中的时钟分频系数或倍频参数,可以调整内部时钟频率,从而影响图像输出的帧率和曝光时间。如果时钟配置错误,可能导致图像不同步或传感器无法正常工作。 此外,系统时钟初始化还可能涉及与主控制器(如FPGA或MCU)的接口时序匹配。例如,I2C配置寄存器的通信需要正确的时钟信号,否则寄存器写入会失败,导致初始化不完全。因此,系统时钟初始化不仅是传感器内部工作的需要,也是确保与外部系统正常通信的关键步骤。 用户可能还关心如何具体进行时钟初始化,比如需要配置哪些寄存器,设置哪些参数。例如,时钟预分频器寄存器(如CLKRC)可能用于调整内部时钟频率,而其他寄存器可能控制输出数据的格式和时序。正确的时钟配置可以优化图像质量,减少噪声,并确保数据稳定传输。 最后,总结系统时钟初始化的原因和作用:确保传感器内部时序正确,匹配外部主控设备,调整帧率和分辨率,保证数据稳定传输,避免图像异常。这些都需要在初始化过程中通过配置相关寄存器来实现。</think>OV7670摄像头模块的系统时钟初始化是其正常工作的重要前提,主要作用及原因如下: ### 一、系统时钟初始化的核心作用 1. **时序基准建立** - 为图像传感器提供精确的时钟信号$f_{clk}$,控制像素采样频率和行/帧同步信号生成。典型时钟频率范围为6-48MHz,直接影响输出帧率:$$f_{ps} = \frac{f_{clk}}{(H_{total} \times V_{total})}$$ 其中$H_{total}$为行像素总数,$V_{total}$为帧行总数[^1] 2. **接口同步保障** - 通过设置CLKRC寄存器(地址0x11)调整时钟分频系数,确保: ```c // 示例寄存器配置(分频系数=2) i2c_write(0x11, 0x80 | 0x01); // 内部时钟分频使能 ``` - 使输出像素时钟(PCLK)与主控设备的采集时序严格同步,避免数据错位 ### 二、初始化必要性分析 1. **硬件依赖性** - 摄像头模块默认使用外部XCLK输入(典型24MHz),但实际应用中需根据主控设备接口特性调整: - FPGA方案:可能直接使用板载晶振 - MCU方案:需通过PWM或专用时钟引脚生成 2. **功耗优化** - 通过降低时钟频率可减少动态功耗,如: $$P_{dynamic} = C \times V^2 \times f_{clk}$$ 适用于电池供电场景 ### 三、典型初始化流程 1. 硬件电路确认XCLK输入质量(幅度1.8-3.3V,上升时间<5ns) 2. 配置时钟控制寄存器: ```c // 设置内部时钟分频(CLKRC寄存器) i2c_write(0x11, 0x8F); // 使用自动分频模式 // 配置像素时钟极性(COM10寄存器) i2c_write(0x15, 0x02); // PCLK上升沿有效 ``` 3. 通过VSYNC/HSYNC信号验证时序关系
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值