STM32时钟安全机制(CSS)研究及实现

本文探讨了STM32在外部晶振短路时如何利用内部RC振荡器确保系统运行,并在外部晶振恢复后切换回外部晶振的方法。通过KEIL开发环境,介绍了不依赖额外头文件的独立移植函数,强调了系统时钟配置和时钟频率设置的重要性。
AI助手已提取文章相关产品:

时钟对于MCU而言就像脉搏一样,在恶劣情况下如果出现外部晶振短路的情况会导致MCU无法正常运行。如果MCU应用于安全生产时,有可能造成安全事故。为了应对这一突发状况,ST公司生产的STM32采用双时钟机制,在外部晶振被短路的情况下,转而使用内部RC振荡器作为自己时钟心跳保证系统正常运行。然而RC振荡器并不是非常的准确,我们希望能够在外部晶振恢复正常的情况下继续使用外部晶振。本文主要研究内容是在外部晶振被短路的情况下,转而使用内部HSI。当外部晶振恢复正常后,重新使用外部晶振。



根据以上理论研究可知,当晶振被短路以后,进入NMI中断。这个中断不需要开启,而且等级很好。
		设计方案:开启CSS功能-设置RCC中断等级
                                              |
                                              |
                                              |
	                                      NMI中断中-------打开HSE--开启HSE中断就绪中断
	                                      |
	                                      |
	                                      RCC中断-------检测HSE就绪状态----设置HSE作为时钟----关闭HSE就绪中断----清楚状态位
	                                      

 /**************************RCC中断处理函数**************************/
//当HSE恢复正常时进入该函数

void RCC_IRQHandler(void)
	{
	
		
		if(RESET != RCC_GetITStatus(RCC_IT_HSERDY))											      //如果HSE就绪
		{
			
		#if defined (DBGUG_RCC_IRQHandler)
			
			printf("i'm in IRQ\n");																  //串口输出进入RCC中断
			
		#endif	
		
			Set_SysClockToHSE();																  //设置系统时钟为HSE
			RCC_ITConfig(RCC_IT_HSERDY, DISABLE); 									              //关闭HSE就绪中断 
			RCC_ClearITPendingBit(RCC_IT_HSERDY);   											  // 清除HSE就绪中断标志位
			

		}
		
		
	} 
/*************************NMI中断函数入口**************************/
//当外部晶振被短路不能起振后进入NMI中断函数


 void NMI_Handler(void)
 {
	 
	 if (RCC_GetITStatus(RCC_IT_CSS) != RESET) 								                  //HSE、PLl已经被禁止(但是PLL设置未变) 
	 {
		 
	 #if defined(DEBUG_NMI_Handler)
		 
		 printf("i'm in NMI\n");                                                              //串口输出进入NMI中断
		 
	 #endif
	 
		 RCC_HSEConfig(RCC_HSE_ON);           									              // 使能HSE
		 RCC_ITConfig(RCC_IT_HSERDY, ENABLE); 									              // 使能HSE就绪中断 
		 
		 RCC_ClearITPendingBit(RCC_IT_CSS);   									              // 清除时钟安全系统的中断挂起位
		
	 }

	 
}


/*************************************************************
****函 数 名:CSS_Init
****功    能:初始化CSS(时钟安全)
****入口参数:无
****返 回 值:无
****说    明:无
**************************************************************/

void CSS_Init(void)
{
	//设置HSE就绪中断的优先级
	//NVIC 配置
	NVIC_InitTypeDef NVIC_InitStructure;
	
	NVIC_InitStructure.NVIC_IRQChannel = RCC_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;					  	      //抢占优先级0
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;								  	  //子优先级0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;									      //IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);													      //根据指定的参数初始化VIC寄存器
	
	RCC_ClockSecuritySystemCmd(ENABLE);   //启动时钟安全系统CSS
}

/*************************************************************
****函 数 名:Set_SysClockToHSE
****功    能:设置系统时钟为HSE
****入口参数:无
****返 回 值:
****说    明:无
**************************************************************/	 
	
	
static void Set_SysClockToHSE(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)
  {

#if !defined STM32F10X_LD_VL && !defined STM32F10X_MD_VL && !defined STM32F10X_HD_VL
    /* Enable Prefetch Buffer */
    FLASH->ACR |= FLASH_ACR_PRFTBE;

    /* Flash 0 wait state */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);

#ifndef STM32F10X_CL
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0;
#else
    if (HSE_VALUE <= 24000000)
	{
      FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0;
	}
	else
	{
      FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_1;
	}
#endif /* STM32F10X_CL */
#endif
 
    /* 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_DIV1;
    
    /* Select HSE as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_HSE;    

    /* Wait till HSE is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x04)
    {
    }
  }
  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 */
  }  
}

/*************************************************/
/*************************************************/


开发环境使用KEIL,库函数版本为3.5
1.本函数不依赖其他头文件,可以独立移植,所需要的头文件文件已经包含在文件中。
注意:在头文件中有包含串口头文件,使用时请将#include "usart.h"中的usart.h替换成
工程中串口文件命
2.该功能使用前提为:将系统时钟设置成HSE(外部晶振,8M),在移植时请将官方提供的
system_stm32f10x.c system_stm32f10x.h替换成本工程提供的文件,并根据宏定义修改系统时钟为HSE(默认为HSE)
3.设置后系统时钟为HSE(假定8MHz)后,AHB为8MHz,APB1为4MHz,APB2为8MHz
注意:APB1下的定时器仍未8MHz


文件下载地址:

http://download.csdn.net/detail/wandermen/8779101

您可能感兴趣的与本文相关内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值