STM32F100C8的SystemInit

ba5ag.kai at gmail.com 2014-04-25

Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c

这是startup.s里调用的第一个函数SystemInit所在的地方。我们这样来编译它:

CC = $(GNUARM)/bin/arm-none-eabi-gcc
DEFS = -DSTM32F10X_MD_VL
FLAG = -mcpu=cortex-m3 -mthumb -Wall
system_stm32f10x.o: $(STM)/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c
        $(CC) $(INC) $(DEFS) $(FLAG) -c $<

我们来看看这个.c里面有什么。

#include "stm32f10x.h"

暂时还不清楚它具体为什么要用到这个头文件,先看下去。

#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
#define SYSCLK_FREQ_24MHz  24000000

既然我们-D了STM32F10X_MD_VL,那么这个宏SYSCLK_FREQ_24MHz就会被定义,显然后面会有用。

#define VECT_TAB_OFFSET  0x0

中断向量表起始地址。这个宏在我们这块芯片的代码中没有用到。

#elif defined SYSCLK_FREQ_24MHz
  uint32_t SystemCoreClock         = SYSCLK_FREQ_24MHz; 

看出来了吧,因为我们-D了STM32F10X_MD_VL,那么就有了宏SYSCLK_FREQ_24MHz,于是SystemCoreClock这个全局变量就有了24000000的值。这个全局变量是开放的,所以在你的应用程序里是可以使用的,以此可以知道当前的芯片的时钟频率。

__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};

这是AHB外设总线上的预分频器的分频表。这是用在下面一个叫做SystemCoreClockUpdate的函数里的。这个SystemCoreClockUpdate函数是用来根据时钟类寄存器的值重新刷新SystemCoreClock这个全局变量的。

接下来就是这个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 */
  RCC->CFGR &= (uint32_t)0xF0FF0000;

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

  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;      

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

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

它调用了SetSysClock(),这个函数就在它下面(中间隔了另一个函数),这个函数由一堆的条件编译,最后走去调用具体和芯片相关的一个时钟配置函数,我们的F100C8是SetSysClockTo24

  __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)
  {
    /* 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;

    /*  PLL configuration:  = (HSE / 2) * 6 = 24 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_Div2 | 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)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 */
  } 
}

这两个函数都是通过读写RCC组的寄存器来做基础的时钟配置的,具体要读懂每一次读写,需要去看详细的芯片数据手册了。我们先暂且放过,但是,这里的RCC是什么? 在stm32f10x.h里(这就是为什么它要include这个头文件),我们找到了:

#define RCC                 ((RCC_TypeDef *) RCC_BASE)

而这个RCC_BASE:

#define RCC_BASE              (AHBPERIPH_BASE + 0x1000)

而这个AHBPERIPH_BASE

#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)

而这个PERIPH_BASE

#define PERIPH_BASE           ((uint32_t)0x40000000)

好嘛,绕了一大圈,RCC是RCC组寄存器的第一个寄存器的地址0x40021000,那么RCC_TypeDef是什么?

typedef struct
{
  __IO uint32_t CR;
  __IO uint32_t CFGR;
  __IO uint32_t CIR;
  __IO uint32_t APB2RSTR;
  __IO uint32_t APB1RSTR;
  __IO uint32_t AHBENR;
  __IO uint32_t APB2ENR;
  __IO uint32_t APB1ENR;
  __IO uint32_t BDCR;
  __IO uint32_t CSR;

  uint32_t RESERVED0;
  __IO uint32_t CFGR2;
} RCC_TypeDef;

看到了吧,就是这一组寄存器,所以,SystemInit的第一句:

RCC->CR |= (uint32_t)0x00000001;

就是CR寄存器或上了1。