二轮平衡小车(3):SystemInit函数

发布于:2025-08-02 ⋅ 阅读:(14) ⋅ 点赞:(0)

二轮平衡小车(3):SystemInit函数

1、SystemInit函数

主控是STM32F103C8,工程中宏定义STM32F10X_MD

/**
  * @brief  Setup the microcontroller system
  *         Initialize the Embedded Flash Interface, the PLL and update the 
  *         SystemCoreClock variable.
  * @note   This function should be used only after reset.
  * @param  None
  * @retval None
  */
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 
}

2、使用软件调试

首先,打开Keil魔术棒工具将Xtal改为8Mhz(这边用软件调试,也可以用Stlink硬件调试,stlink会有点点卡),
在这里插入图片描述

将对应的DLL文件和参数修改,参数改为自己的芯片型号!!!

将对应的DLL文件和参数修改,参数改为自己的芯片型号!!!

将对应的DLL文件和参数修改,参数改为自己的芯片型号!!!

在这里插入图片描述
先编译,不然没法打断点,进入调试,复位,在此处打断点
在这里插入图片描述
点击菜单栏,view->system viewer->rcc,用于查看RCC寄存器的值
在这里插入图片描述
点开寄存器中的+号,可以查看具体的值,开始单步调试

在这里插入图片描述

/* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;

根据注释,这一句语句打开HSI时钟,对应的位(其实还是复位值),但也确实置为了1。查看手册对应寄存器对应位的描述,打开了内置的8MhzRC振荡器。
在这里插入图片描述
在这里插入图片描述
接下来,执行这句话,应为使用的不是互联型产品

  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000;

其实也没改变复位值
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这句话对应各个位的意义是:
高四位保留,不管
MCO脚无时钟源(1)
在这里插入图片描述
OTGFSPRE这个位只有互联型产品有,其余类型产品保留,不管
在这里插入图片描述
锁相环倍频因子,保留(2)
在这里插入图片描述
PLLXTPRE这个位比较有意思,它是PREDIV1(3)的最低有效位,对应还有一个RCC->CFGR2的寄存器中有一个相同的位,所以修改CFGR2也会修改这一位,而且如果CFGR2中[3:1]没有被置位,那么这一位进一步控制,PREDIVE(3)是否对输入时钟进行二分频,1的时候分频。
从RCC中可以看到,STM32F103C8并没有CFGR2这个寄存器,那么这一位就控制是否对输入时钟进行分频,这个时候是不分频。
在这里插入图片描述
在这里插入图片描述
PLLSRC(4) 锁相环的来源,HSI内部RC振荡器二分频作为锁相环输入时钟
在这里插入图片描述
ADCPRE(5)ADC时钟的预分频器,PCLK2(6)二分频
在这里插入图片描述
PPRE2 (7) APB2高速总线的预分频器,不分频
在这里插入图片描述

PPRE1(8)APB1低速总线的预分频器,不分频
在这里插入图片描述
HPRE(9)AHB总线预分频器,不分频
在这里插入图片描述
SWS(10)系统时钟选择位,选择内部RC振荡器为系统时钟
在这里插入图片描述
具体,如下图所示
在这里插入图片描述
接下来的几句话,其实都没改变复位值,CFGR寄存器对应位的分析还是和上面一样,CR寄存器的分析,和注释一样,关闭对应的时钟源,只保留内部的RC振荡器

  /* 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;

在这里插入图片描述
接下来执行这句话,Clock interrupt register这个寄存器CIR用来控制中断,所有位都是0,所有中断都被清除。

RCC->CIR = 0x009F0000;

在这里插入图片描述
在这里插入图片描述
接下来才是重中之重

3、SetSysClock()

继续点击单步调试
在这里插入图片描述
我们发现,它直接跳到这个函数,根据宏定义只有定义了SYSCLK_FREQ_72MHZ才会调用这个函数,打开搜索ctrl+f
在这里插入图片描述
点击Find All
在这里插入图片描述
一共有这么多处使用了这个宏定义,去看看
在这里插入图片描述
第一处有这样的语句,我们定义的是MD不在第一个ifdefine语句中,那么就调用#define SYSCLK_FREQ_72MHz 72000000这句话

#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

第二处,根据上面的定义,执行这句话#elif defined SYSCLK_FREQ_72MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz; 这里面的SystemCoreClock是一个全局变量,用来存储当前系统的内核时钟频率。

#ifdef SYSCLK_FREQ_HSE
  uint32_t SystemCoreClock         = SYSCLK_FREQ_HSE;        /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_24MHz
  uint32_t SystemCoreClock         = SYSCLK_FREQ_24MHz;        /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_36MHz
  uint32_t SystemCoreClock         = SYSCLK_FREQ_36MHz;        /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_48MHz
  uint32_t SystemCoreClock         = SYSCLK_FREQ_48MHz;        /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_56MHz
  uint32_t SystemCoreClock         = SYSCLK_FREQ_56MHz;        /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_72MHz
  uint32_t SystemCoreClock         = SYSCLK_FREQ_72MHz;        /*!< System Clock Frequency (Core Clock) */
#else /*!< HSI Selected as System Clock source */
  uint32_t SystemCoreClock         = HSI_VALUE;        /*!< System Clock Frequency (Core Clock) */
#endif

第三处,就调用了static void SetSysClockTo72(void);这个函数。

#ifdef SYSCLK_FREQ_HSE
  static void SetSysClockToHSE(void);
#elif defined SYSCLK_FREQ_24MHz
  static void SetSysClockTo24(void);
#elif defined SYSCLK_FREQ_36MHz
  static void SetSysClockTo36(void);
#elif defined SYSCLK_FREQ_48MHz
  static void SetSysClockTo48(void);
#elif defined SYSCLK_FREQ_56MHz
  static void SetSysClockTo56(void);  
#elif defined SYSCLK_FREQ_72MHz
  static void SetSysClockTo72(void);
#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) */ 
}

最后一处,通过配置RCC寄存器使得系统时钟为72MHZ

#elif defined SYSCLK_FREQ_72MHz
/**
  * @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;

4、static void SetSysClockTo72(void)

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;

定义的这两个变量,我们右击将它们添加到watch1中观察它们的值

__IO uint32_t StartUpCounter = 0, HSEStatus = 0;

在这里插入图片描述
此时,我们先记录一下CR,CFGR,FLASH->ACR寄存器的值
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
查看注释,我们可以大概知道这个函数用来配置SYSCLK、HCLK、PCLK2和PCLK1

/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/    
  /* Enable HSE */    
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);

这句话用来使能外部晶振,RCC_CR_HSEON的值为,第16位置为1,使能外部晶振。

#define  RCC_CR_HSEON                        ((uint32_t)0x00010000) 

在这里插入图片描述
接着进入一个dowhile循环,根据注释是用来判断,外部晶振的状态。RCC_CR_HSERDY的定义如下。

/* 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));
#define  RCC_CR_HSERDY                       ((uint32_t)0x00020000)

在这里插入图片描述
通过判断位的值和超时时间来判断外部晶振是否准备好。
如果准备好了,那么HSEStatus变量的值为1

  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  {
    HSEStatus = (uint32_t)0x01;
  }

接下来执行这段语句,

  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;

首先使能Flash的预缓冲区,提高系统的整体性能。
在这里插入图片描述
在这里插入图片描述
这两句话,FLASH_ACR_LATENCY=0x03,取反就是1111 1100,

    /* Flash 2 wait state */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
#define  FLASH_ACR_LATENCY                   ((uint8_t)0x03)               /*!< LATENCY[2:0] bits (Latency) */
#define  FLASH_ACR_LATENCY_0                 ((uint8_t)0x00)               /*!< Bit 0 */
#define  FLASH_ACR_LATENCY_1                 ((uint8_t)0x01)               /*!< Bit 0 */
#define  FLASH_ACR_LATENCY_2                 ((uint8_t)0x02)

其实主要就是操作,这三位,表示系统时钟与闪存访问时间的比值,这里设置为2,之前系统时钟为72Mhz这里配置比例为2,表示FLASH需要两个周期来响应一个访问请求。在这里插入图片描述
最后

/* 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;
#define  RCC_CFGR_HPRE_DIV1                  ((uint32_t)0x00000000) 
#define  RCC_CFGR_PPRE2_DIV1                 ((uint32_t)0x00000000)
#define  RCC_CFGR_PPRE1_DIV2                 ((uint32_t)0x00000400)

表面上,这三句话只有最后一句话起作用,其实第一句话配置了AHB总线的分频器为1,第二句话配置了APB2的分频器为1,最后一句话配置APB1总线时钟配置为HCLK的二分频,主要是PPRE1的三个位。
在这里插入图片描述
接着,这两句话根据注释应该是吧锁相环时钟配置为外部晶振的9倍频。

    /*  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);
#define  RCC_CFGR_PLLSRC                     ((uint32_t)0x00010000)
#define  RCC_CFGR_PLLXTPRE                   ((uint32_t)0x00020000)
#define  RCC_CFGR_PLLMULL                    ((uint32_t)0x003C0000)

在这里插入图片描述
在这里插入图片描述
之后使能锁相环,

/* Enable PLL */
    RCC->CR |= RCC_CR_PLLON;

#define  RCC_CR_PLLON                        ((uint32_t)0x01000000) 

在这里插入图片描述
在这里插入图片描述
判断锁相环准备好了没

    /* 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;   
#define  RCC_CFGR_SW                         ((uint32_t)0x00000003)
#define  RCC_CFGR_SW_PLL                     ((uint32_t)0x00000002)

在这里插入图片描述
最后等待使用锁相环作为系统时钟源。

    /* Wait till PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
    {
    }

看一下时钟树
在这里插入图片描述
最后的最后,将中断向量表映射到FLASH中。

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

5、最后进入main函数

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到