STM32-仿真调试时的SystemInit陷阱

STM32-仿真调试时的SystemInit陷阱


我在开始STM32的仿真调试时,遇到一个问题,就是调试时程序一直停在SystemInit()中的等待晶振中,怎么也出不来。

SystemInit()前面部分的代码,都能走过,就是在执行到最后一个函数时出问题了。
最后一个函数是:SetSysClock(); 
执行到下面这个循环之后,出不来了:
  /* 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));

这里,我就有疑问了:
1,我希望的是直接进main函数,那么,这个SystemInit()函数是从哪里来的?
2,为什么会进入死循环?

我全工程搜索“SystemInit”,发现在startup_stm32f0xx.s中有这样的代码:
        IMPORT  __main
        IMPORT  SystemInit  
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP
看来,系统是先执行SystemInit,然后才执行main的啊。

接下来是第二个问题,为什么进入死循环?
看看注释:/* Wait till HSE is ready and if Time out is reached exit */
等待HSE准备就绪且超时时间到达。超时时间且不去管它,这个HSE是什么?

HSE(High Speed External Clock signal),高速外部时钟信号,是接外部时钟源的。
相应的还有HSI(High Speed Internal Clock signal),高速内部时钟信号,是stm32芯片自带的。

看到这个概念,我就明白问题所在了:是我用的板子,没有接外部晶振啊!
所以,等待HSE准备就绪,这是永远不能达成的条件啊。
所以,这里需要修改一下,不再等待HSE了,其实是不使用HSE了,而是修改为使用HSI。

当我准备修改文件的时候,发现了一个问题,我居然修改不了这个文件!
敲了字母,它不出现在代码中!?
上网一查,原来是system_stm32f0xx.c这个文件是只读的。
好吧,从windows的文件夹中找到文件,查看属性,
见下图:

STM32-仿真调试时的SystemInit陷阱_第1张图片

去掉“只读”即可。


不依赖于HSE,使用HSI,我修改后的代码如下:

/**
  * @brief  Configures the System clock frequency, AHB/APBx prescalers and Flash
  *         settings.
  * @note   This function should be called only once the RCC clock configuration
  *         is reset to the default reset state (done in SystemInit() function).
  * @param  None
  * @retval None
  */
static void SetSysClock(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* SYSCLK, HCLK, PCLK 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));

    
//    RCC_HSEConfig(RCC_HSE_OFF);//外部晶振关闭!
    RCC->CR |= ((uint32_t)RCC_CR_HSION);//使用内部晶振
    
    
  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  {
    HSEStatus = (uint32_t)0x01;
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }  

  if (HSEStatus == (uint32_t)0x01)
  {
    /* Enable Prefetch Buffer and set Flash Latency */
    FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
 
    /* HCLK = SYSCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
      
    /* PCLK = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1;

    /* PLL configuration = 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_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLMULL6);
            
    /* 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)RCC_CFGR_SWS_PLL)
    {
    }
  }
  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 */
        
          //设置系统时钟8MHz
                RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI);
                while(0x00 != RCC_GetSYSCLKSource());//等待设置成功       8--PLL  4--HSE   0--
HSI

                RCC_HCLKConfig(RCC_SYSCLK_Div1);//HCLK 8MHz
                RCC_PCLKConfig(RCC_HCLK_Div1);//PLCK 8MHz
        
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_SYSCFG,ENABLE);
                RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 | RCC_AHBPeriph_GPIOB,ENABLE);    
        
                RCC_ADCCLKConfig(RCC_ADCCLK_PCLK_Div4);//ADC1时钟频率 2MHz
  }  
    
}

这样修改之后,再进入在线调试,果然走过了SystemInit(),然后进入了main()。
这样,就解决了在线调试总是进不来main()的问题了。

不过,我还是有个疑问:为什么,这样的代码,在调试时有问题,而在全速运行的时候就没有问题呢?

再次仔细查看这段代码:
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;
    StartUpCounter++;  
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
其实并不是一个死循环,跳出的条件有两个:HSE准备好了,或者超时。
由于我的板子没有接外接晶振,第一个条件是不能达到的,那么,第二个条件其实是可以达到的啊,为什么我会以为是个死循环呢?

让我们来看看 HSE_STARTUP_TIMEOUT 是个什么值吧:

查看定义,是这样的:
#define HSE_STARTUP_TIMEOUT   ((uint16_t)0x0500) /*!< Time out for HSE start up */

其实,不是死循环,只是循环次数值太大(1280=0x500),单步调试,不能点击走这么多的循环次数(另外,在这里,想进行断点执行跳过循环也不管用,不清楚是什么原因,是因为还没有到执行到main()吗?若有知道原因的高手,请指点,谢谢!)。

这样,我就考虑到了有几个办法解决这个问题了:

1,改小HSE_STARTUP_TIMEOUT,例如:1        
    评估:危险!我们尽量不要去修改厂家提供的宏。万一以后需要用HSE呢?另外还要考虑这个值是否有其它地方的调用。
2,调试时,修改StartUpCounter变量值,为4ff,则很快达到0x500,跳出循环。
    评估:可行,但是比较麻烦,每次运行都需要修改一次。
    若不想修改任何代码,这倒也是一个选择。
3,像前文说的那样,修改SystemInit,默认选择HSI。
    评估:可行。不过,代码修改量比较大。或许我们还有更好的选择?
4,修改startup_stm32f0xx.s,不执行SystemInit了
如下修改:
        IMPORT  __main
;        IMPORT  SystemInit  
;                 LDR     R0, =SystemInit
;                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP
    实测,可行。修改时注意,这个文件也是只读的,需要去掉只读属性后才能修改代码。
    改动量较小。不过风险可不小,因为我还不能准确评估去掉 SystemInit 那部分代码的影响。
    可行的原因分析:系统复位后,HSI振荡器默认被选为系统时钟。
5,去掉SystemIit() 中对 SetSysClock() 的调用;
    实测,可行。
    改动最较小,只是把那句调用代码注释掉即可。且通过分析SetSysClock()函数,可以知道,若没有启用HSE,则相当于没有执行任何有效操作。可以说,对于使用HSI的情况,逻辑上没有任何差别。
    
最终,我采用了第5种修改方法,调试运行,一切正常。

 

你可能感兴趣的:(单片机)