STM32外部晶振和内部晶振切换

STM32外部晶振和内部晶振切换


以前做STM32的项目都是移植的正点原子官方的Demo,晶振是默认使用外部晶振8MHz,倍频到72MHz来实际运用的,实际上大家为了麻烦也都是用的这种方法。
最近有几个项目都没有外部晶振,是使用内部晶振,虽然精度相对来说没有外部的高,但是项目也没有使用到很精确时钟的地方。下面来分析一下怎么在正点原子官方Demo的基础上修改成内部晶振。

首先要明确的是内部晶振精度相对来说没有外部晶振高,而且不能像外部晶振一样倍频到很高,注意官方的说明,内部晶振是不能超过最大值,好像是64MHz晶振,感兴趣的话可以做一下实验。

我们先来看下,晶振是怎么启动的—>打开启动文件(汇编语言)

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

是先初始化晶振,然后再运行main函数。

然后我们看到SystemInit函数

void SystemInit (void)
{
  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;

  /* 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
  /* 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
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */
    
#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函数----在此之前我们先修改一个宏定义,修改为48MHz

#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
 #define SYSCLK_FREQ_24MHz  24000000
#else
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz  24000000 */ 
/* #define SYSCLK_FREQ_36MHz  36000000 */
#define SYSCLK_FREQ_48MHz  48000000 
/* #define SYSCLK_FREQ_56MHz  56000000 */
//#define SYSCLK_FREQ_72MHz  72000000
#endif
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) */ 
}

然后顺势进入SetSysClockTo48函数

static void SetSysClockTo48(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 1 wait state */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_1;    
 
    /* 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 * 6 = 48 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_PLLMULL6); 
#else    
    /*  PLL configuration: PLLCLK = HSE * 6 = 48 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_PLLMULL6);
#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 */
	  
	//如果外部晶振启动失败,则初始化内部晶振
	  SetSysClock_HSI_To48();
  } 
}

你们可以看到,本身如果外部晶振启动失败的话,是直接出去,但SystemInit函数一开始就使能了内部晶振,因为一开始,系统就是靠内部晶振起来的,所以如果外部晶振初始化失败,直接对内部晶振惊醒配置就好了,这里给出一段配置内部晶振倍频到48MHz的参考程序,如果想配置成其他的频率对应着修改参数就可以了。。。
如下:

void SetSysClock_HSI_To48(void)
{
	// Reset HSEON bit
	RCC->CR &= ~((uint32_t)RCC_CR_HSEON);//实际发现不关掉外部晶振很容易进入硬件错误中断
	 
	// Set HSION bit 
	RCC->CR |= (uint32_t)0x00000001;

	// select HSI/2 as PLL source = 8MHz / 2 = 4MHz
	RCC->CFGR |= (uint32_t)RCC_CFGR_PLLSRC_HSI_Div2;        

	//PLLCLK = 4MHz * 12 = 48M
	RCC->CFGR |= (uint32_t)RCC_CFGR_PLLMULL12;
			
	// HCLK = SYSCLK/1     
	RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
			 
	/* 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)
	{
		
	}
}

网上有一个解决办法,是直接在SystemInit函数里面修改对应的代码,虽然最终也能使用,但个人认为这样对代码移植起来比较麻烦,代码的可读性也不强,个人认为,对代码的修改最好不要直接一通修改。
再一个,我这样修改之后有个比较好的地方就是,如果外部晶振接上能直接用外部晶振,如果没接我可以用内部晶振,两不误。
当然内外部晶振使用时分别对应多少主时钟频率,还需要自己去设置,这里只是提供一种方法和思路。

最后再提一个最终频率的查看方法:

RCC_ClocksTypeDef get_rcc_clock;//获取系统时钟数据

int main(void)
{	
	RCC_GetClocksFreq(&get_rcc_clock);//查看时钟频率

可以通过debug里面的watch选项观察结构体里面的值

  • 14
    点赞
  • 96
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
STM单片机含有一些IO引脚用于外部时钟源的接入,其中一个重要的引脚是OSC (振荡器)。OSC引脚用于连接外部晶振或者陶瓷振荡器,以提供给单片机一个稳定的时钟信号。单片机内部的时钟发生器可以利用这个时钟信号来产生各种不同的时钟频率,从而满足不同应用的需求。 在STM单片机设计中,OSC引脚通常会设计成多功能IO引脚,可以根据特定的配置进行复用。这意味着,当OSC引脚没有被用作外部时钟源时,它可以用作普通GPIO引脚来实现其他功能。这样设计的好处是节省了硬件引脚资源,提高了系统的灵活性和可扩展性。 复用OSC引脚可以通过软件配置来实现。通常在系统初始化时,会通过编程将OSC引脚配置为时钟输入模式,从而将外部时钟源连接到单片机内部的时钟系统。当不需要外部时钟源时,可以将OSC引脚重新配置为普通GPIO引脚,实现其他功能的输入或输出。 需要注意的是,复用OSC引脚在设计中需要谨慎考虑。一方面,如果引脚长时间保持为时钟源输入模式,可能会导致没有外部时钟源时系统无法正常工作;另一方面,如果将OSC引脚配置为普通GPIO引脚时,需要确保与其他引脚的复用冲突或信号干扰问题。 综上所述,STM单片机的OSC引脚可以通过软件配置为复用IO引脚,实现其他功能的输入或输出。这一设计能够节省硬件引脚资源,提高系统的灵活性和可扩展性,但在使用时需要注意配置的合理性和引脚冲突的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值