【STM32】RCC时钟模块(使用HAL库)

news2024/9/30 23:26:17

https://gitee.com/linhir-linhir/stm32-f103-c8/blob/master/STM32%E6%9C%80%E6%96%B0%E5%9B%BA%E4%BB%B6%E5%BA%93v3.5/Libraries/STM32F10x_StdPeriph_Driver/inc/stm32f10x_rcc.h

STM32最新固件库v3.5/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c · 林何/STM32F103C8 - 码云 - 开源中国 (gitee.com)

1.宏定义

1.宏定义的位置

如果这个宏定义只能在.c文件中使用,则应该在.c文件中定义

如果这个宏定义既可以在.c或者.h文件中使用,则应该在.h中定义

2.位带:RCC_OFFSET

因为我们STM32是32位的寄存器,所以如果我们只想要操作寄存器其中的一位,所以我们可以使用位移操作

/* ------------ RCC registers bit address in the alias region ----------- */ 
/*
	RCC_OFFSET:等价于RCC的基地址和外设寄存器之差
*/
/*!< PERIPH_BAS--》 Peripheral base address in the alias region */

#define RCC_OFFSET                (RCC_BASE - PERIPH_BASE)

3.第一个寄存器:CR

1.HSION

/* --- CR Register ---*/

/* Alias word address of HSION bit */
//这个寄存器相对于基地址的位置
#define CR_OFFSET                 (RCC_OFFSET + 0x00)
//操作HSION这一位相对于整个CR寄存器的偏移量
#define HSION_BitNumber           0x00
//CR寄存器中的HSION中的位带
//PERIPH_BB_BASE:位带访问区的基地址
#define CR_HSION_BB               (PERIPH_BB_BASE + (CR_OFFSET * 32) + (HSION_BitNumber * 4))

如果想要对其进行设置,就直接给CR_HSION_BB赋值

2.PLLON

/* Alias word address of PLLON bit */
#define PLLON_BitNumber           0x18
#define CR_PLLON_BB               (PERIPH_BB_BASE + (CR_OFFSET * 32) + (PLLON_BitNumber * 4))

4.RCC registers bit mask

Reset:进行位与置0

Set:进行位或置1

/* ---------------------- RCC registers bit mask ------------------------ */

/* CR register bit mask */
#define CR_HSEBYP_Reset           ((uint32_t)0xFFFBFFFF)
#define CR_HSEBYP_Set             ((uint32_t)0x00040000)
#define CR_HSEON_Reset            ((uint32_t)0xFFFEFFFF)
#define CR_HSEON_Set              ((uint32_t)0x00010000)
#define CR_HSITRIM_Mask           ((uint32_t)0xFFFFFF07)

2.全局变量

定义了预分配处理器

1.static

c语言中static关键字用法详解_static在c语言中的用法-CSDN博客

2.volatile

这个变量跟某一个寄存器的值进行绑定,寄存器里面有一个值是硬件可以改动的值

C语言丨深入理解volatile关键字-腾讯云开发者社区-腾讯云 (tencent.com)

3.uint

uint8:表示unsigned short

uint16:表示unsigned char

uint32:表示unsigned int

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

3.函数

1.RCC_DeInit

/**
  * @brief  Resets the RCC clock configuration to the default reset state.
  * @param  None
  * @retval None
  */
void RCC_DeInit(void)
{
  /* Set HSION bit */
//将CR这个位写为1
  RCC->CR |= (uint32_t)0x00000001;

  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000;
#else//非CL的芯片使用
  RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */   
  
  /* Reset HSEON, CSSON and PLLON bits */
//HSEON, CSSON and PLLON:将这几位置0
  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 */

}

2.RCC_HSEConfig

可以关闭时钟,所以平时我们不操纵它

这个HSEConfig实际效果:控制CPU是使用

                外部晶振+内部振动电路        VS        外部时钟

1.assert:断言

assert机制是c语言用来判断一个东西是对的还是错的,如果是对的直接忽略过去,如果是错的就以某一种方式告诉我们(warrning error)让我们去修改。

/* Exported macro ------------------------------------------------------------*/
#ifdef  USE_FULL_ASSERT
/**
  * @brief  The assert_param macro is used for function's parameters check.
  * @param  expr: If expr is false, it calls assert_failed function which reports 
  *         the name of the source file and the source line number of the call 
  *         that failed. If expr is true, it returns no value.
  * @retval None
  */
  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
  void assert_failed(uint8_t* file, uint32_t line);
#else
  #define assert_param(expr) ((void)0)
#endif /* USE_FULL_ASSERT */

这个函数要用户自己去实现

void assert_failed(uint8_t* file, uint32_t line)
{ 
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */

    
    //用户用自己的方法去报错一个断言错误
    //用户可以用#error灯方法来在编译时报错(前提是断言表达式必须在预处理时就能有结果)
    //更常见的方式是用户在运行时报错,用printf打印调试信息
    //
  while (1)
  {
  }
}

2.判断用户输入的参数是否正确

3.代码理解

/**
  * @brief  Configures the External High Speed oscillator (HSE).
  * @note   HSE can not be stopped if it is used directly or through the PLL as system clock.
  * @param  RCC_HSE: specifies the new state of the HSE.
  *   This parameter can be one of the following values:
  *     @arg RCC_HSE_OFF: HSE oscillator OFF
  *     @arg RCC_HSE_ON: HSE oscillator ON
  *     @arg RCC_HSE_Bypass: HSE oscillator bypassed with external clock
  * @retval None
  */
void RCC_HSEConfig(uint32_t RCC_HSE)
{
  /* Check the parameters */
  assert_param(IS_RCC_HSE(RCC_HSE));
  /* Reset HSEON and HSEBYP bits before configuring the HSE ------------------*/
  /* Reset HSEON bit */
  RCC->CR &= CR_HSEON_Reset;
  /* Reset HSEBYP bit */
  RCC->CR &= CR_HSEBYP_Reset;
  /* Configure HSE (RCC_HSE_OFF is already covered by the code section above) */
  switch(RCC_HSE)
  {
    case RCC_HSE_ON:
      /* Set HSEON bit */
      RCC->CR |= CR_HSEON_Set;
      break;
      
    case RCC_HSE_Bypass:
      /* Set HSEBYP and HSEON bits */
      RCC->CR |= CR_HSEBYP_Set | CR_HSEON_Set;
      break;
      
    default://RCC_HSE_OFF
      break;
  }
}

3.RCC_WaitForHSEStartUp(等待HSE)

1.ErrorStatus

判断是否成功

一般:0:表示失败

           1:表示成功

2.计数值加上volatile

因为这个变量是我们来进行判断是否超时的局部变量,所以每当我们调用这个函数的时候,应该将这个计数值清0,所以这里才使用

3.代码理解

/**
  * @brief  Waits for HSE start-up.
  * @param  None
  * @retval An ErrorStatus enumuration value:
  * - SUCCESS: HSE oscillator is stable and ready to use
  * - ERROR: HSE oscillator not yet ready
  */
ErrorStatus RCC_WaitForHSEStartUp(void)
{
  __IO uint32_t StartUpCounter = 0;
  ErrorStatus status = ERROR;
  FlagStatus HSEStatus = RESET;
  
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
  //读取寄存器的值
    HSEStatus = RCC_GetFlagStatus(RCC_FLAG_HSERDY);
    StartUpCounter++;  //读取是否超时的
  } while((StartUpCounter != HSE_STARTUP_TIMEOUT) && (HSEStatus == RESET));

  //这里判断是防止超时
  if (RCC_GetFlagStatus(RCC_FLAG_HSERDY) != RESET)
  {
    status = SUCCESS;
  }
  else
  {
  //超时
    status = ERROR;
  }  
  return (status);
}

4.RCC_GetFlagStatus(获取bit位状态)

1)确定这个RCC-FLAG在哪一个寄存器上

2)确定这个RCC_FLAG在寄存器是哪一个位上

1.返回值进行状态判断

2.输入参数

3.IS_RCC_FLAG

4.判断当前是在哪一个寄存器中

右移5位是想要判断第5位是1还是2还是3,然后进行判断是哪一个寄存器

  /* Get the RCC register index */
  //将输入的标志位右移5位
  tmp = RCC_FLAG >> 5;
  //判断要访问哪一个寄存器
  if (tmp == 1)               /* The flag to check is in CR register */
  {
    statusreg = RCC->CR;
  }
  else if (tmp == 2)          /* The flag to check is in BDCR register */
  {
    statusreg = RCC->BDCR;
  }
  else                       /* The flag to check is in CSR register */
  {
    statusreg = RCC->CSR;
  }

5.判断在寄存器的哪一个位上

  /* Get the flag position */
  /**
	FLAG_Mask:0x1f
  */
  tmp = RCC_FLAG & FLAG_Mask;
  if ((statusreg & ((uint32_t)1 << tmp)) != (uint32_t)RESET)
  {
    bitstatus = SET;
  }
  else
  {
    bitstatus = RESET;
  }

6.代码理解

/**
  * @brief  Checks whether the specified RCC flag is set or not.
  * @param  RCC_FLAG: specifies the flag to check.
  *   
  *   For @b STM32_Connectivity_line_devices, this parameter can be one of the
  *   following values:
  *     @arg RCC_FLAG_HSIRDY: HSI oscillator clock ready
  *     @arg RCC_FLAG_HSERDY: HSE oscillator clock ready
  *     @arg RCC_FLAG_PLLRDY: PLL clock ready
  *     @arg RCC_FLAG_PLL2RDY: PLL2 clock ready      
  *     @arg RCC_FLAG_PLL3RDY: PLL3 clock ready                           
  *     @arg RCC_FLAG_LSERDY: LSE oscillator clock ready
  *     @arg RCC_FLAG_LSIRDY: LSI oscillator clock ready
  *     @arg RCC_FLAG_PINRST: Pin reset
  *     @arg RCC_FLAG_PORRST: POR/PDR reset
  *     @arg RCC_FLAG_SFTRST: Software reset
  *     @arg RCC_FLAG_IWDGRST: Independent Watchdog reset
  *     @arg RCC_FLAG_WWDGRST: Window Watchdog reset
  *     @arg RCC_FLAG_LPWRRST: Low Power reset
  * 
  *   For @b other_STM32_devices, this parameter can be one of the following values:        
  *     @arg RCC_FLAG_HSIRDY: HSI oscillator clock ready
  *     @arg RCC_FLAG_HSERDY: HSE oscillator clock ready
  *     @arg RCC_FLAG_PLLRDY: PLL clock ready
  *     @arg RCC_FLAG_LSERDY: LSE oscillator clock ready
  *     @arg RCC_FLAG_LSIRDY: LSI oscillator clock ready
  *     @arg RCC_FLAG_PINRST: Pin reset
  *     @arg RCC_FLAG_PORRST: POR/PDR reset
  *     @arg RCC_FLAG_SFTRST: Software reset
  *     @arg RCC_FLAG_IWDGRST: Independent Watchdog reset
  *     @arg RCC_FLAG_WWDGRST: Window Watchdog reset
  *     @arg RCC_FLAG_LPWRRST: Low Power reset
  *   
  * @retval The new state of RCC_FLAG (SET or RESET).
  */
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG)
{
  uint32_t tmp = 0;
  uint32_t statusreg = 0;
  FlagStatus bitstatus = RESET;
  /* Check the parameters */
  assert_param(IS_RCC_FLAG(RCC_FLAG));

  /* Get the RCC register index */
  //将输入的标志位右移5位
  tmp = RCC_FLAG >> 5;
  //判断要访问哪一个寄存器
  if (tmp == 1)               /* The flag to check is in CR register */
  {
    statusreg = RCC->CR;
  }
  else if (tmp == 2)          /* The flag to check is in BDCR register */
  {
    statusreg = RCC->BDCR;
  }
  else                       /* The flag to check is in CSR register */
  {
    statusreg = RCC->CSR;
  }

  /* Get the flag position */
  /**
	FLAG_Mask:0x1f:1 1111
  */
  //这里我们可以得出应该将“1”移动几个bit
  //比如我们此时选中的RCC_FLAG=RCC->CR的HSERDY(此位对应bit17)
  //则此时tmp=11 0001    & 1 1111=1 0001(对应十进制17) 
  tmp = RCC_FLAG & FLAG_Mask;
	//RESET表示置位:表示数值“0”
  if ((statusreg & ((uint32_t)1 << tmp)) != (uint32_t)RESET)
  {
  //此时进入,表示该位已经被设置为1
    bitstatus = SET;
  }
  else
  {
    bitstatus = RESET;
  }

  /* Return the flag status */
  return bitstatus;
}

5.RCC_HSICmd(设置内部晶振状态)

发送命令的

/**
  * @brief  Enables or disables the Internal High Speed oscillator (HSI).
  * @note   HSI can not be stopped if it is used directly or through the PLL as system clock.
  * @param  NewState: new state of the HSI. This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void RCC_HSICmd(FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  *(__IO uint32_t *) CR_HSION_BB = (uint32_t)NewState;
}

这里使用解引用的方式,因为我们这里只需要操纵一位

6.RCC_PLLConfig(设置时钟频率)

在使用这个PLL之前一定一定是没有使用PLL,才可以调用这个函数

配置PLL的时钟源倍频

1.参数:时钟倍频

2.参数:时钟PLL倍频系数

3.代码理解

/**
  * @brief  Configures the PLL clock source and multiplication factor.
  * @note   This function must be used only when the PLL is disabled.
  * @param  RCC_PLLSource: specifies the PLL entry clock source.
  *   For @b STM32_Connectivity_line_devices or @b STM32_Value_line_devices, 
  *   this parameter can be one of the following values:
  *     @arg RCC_PLLSource_HSI_Div2: HSI oscillator clock divided by 2 selected as PLL clock entry
  *     @arg RCC_PLLSource_PREDIV1: PREDIV1 clock selected as PLL clock entry
  *   For @b other_STM32_devices, this parameter can be one of the following values:
  *     @arg RCC_PLLSource_HSI_Div2: HSI oscillator clock divided by 2 selected as PLL clock entry
  *     @arg RCC_PLLSource_HSE_Div1: HSE oscillator clock selected as PLL clock entry
  *     @arg RCC_PLLSource_HSE_Div2: HSE oscillator clock divided by 2 selected as PLL clock entry 
  * @param  RCC_PLLMul: specifies the PLL multiplication factor.
  *   For @b STM32_Connectivity_line_devices, this parameter can be RCC_PLLMul_x where x:{[4,9], 6_5}
  *   For @b other_STM32_devices, this parameter can be RCC_PLLMul_x where x:[2,16]  
  * @retval None
  */
void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t RCC_PLLMul)
{
  uint32_t tmpreg = 0;

  /* Check the parameters */
  assert_param(IS_RCC_PLL_SOURCE(RCC_PLLSource));
  assert_param(IS_RCC_PLL_MUL(RCC_PLLMul));

  tmpreg = RCC->CFGR;//我们要操纵的2个参数都在CFGR
  /* Clear PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */
  tmpreg &= CFGR_PLL_Mask;//清零
  /* Set the PLL configuration bits */
  tmpreg |= RCC_PLLSource | RCC_PLLMul;//置1
  /* Store the new value */
  RCC->CFGR = tmpreg;
}

7.RCC_PREDIV1Config(与F1无关,此处不看)

7.RCC_AHBPeriphClockCmd(外设时钟复位)

外设复位


/**
  * @brief  Enables or disables the AHB peripheral clock.
  * @param  RCC_AHBPeriph: specifies the AHB peripheral to gates its clock.
  *   
  *   For @b STM32_Connectivity_line_devices, this parameter can be any combination
  *   of the following values:        
  *     @arg RCC_AHBPeriph_DMA1
  *     @arg RCC_AHBPeriph_DMA2
  *     @arg RCC_AHBPeriph_SRAM
  *     @arg RCC_AHBPeriph_FLITF
  *     @arg RCC_AHBPeriph_CRC
  *     @arg RCC_AHBPeriph_OTG_FS    
  *     @arg RCC_AHBPeriph_ETH_MAC   
  *     @arg RCC_AHBPeriph_ETH_MAC_Tx
  *     @arg RCC_AHBPeriph_ETH_MAC_Rx
  * 
  *   For @b other_STM32_devices, this parameter can be any combination of the 
  *   following values:        
  *     @arg RCC_AHBPeriph_DMA1
  *     @arg RCC_AHBPeriph_DMA2
  *     @arg RCC_AHBPeriph_SRAM
  *     @arg RCC_AHBPeriph_FLITF
  *     @arg RCC_AHBPeriph_CRC
  *     @arg RCC_AHBPeriph_FSMC
  *     @arg RCC_AHBPeriph_SDIO
  *   
  * @note SRAM and FLITF clock can be disabled only during sleep mode.
  * @param  NewState: new state of the specified peripheral clock.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_RCC_AHB_PERIPH(RCC_AHBPeriph));
  assert_param(IS_FUNCTIONAL_STATE(NewState));

  if (NewState != DISABLE)
  {
    RCC->AHBENR |= RCC_AHBPeriph;
  }
  else
  {
    RCC->AHBENR &= ~RCC_AHBPeriph;
  }
}

8.RCC_HCLKConfig(AHB频率)

配置AHB clock

/**
  * @brief  Configures the AHB clock (HCLK).
  * @param  RCC_SYSCLK: defines the AHB clock divider. This clock is derived from 
  *   the system clock (SYSCLK).
  *   This parameter can be one of the following values:
  *     @arg RCC_SYSCLK_Div1: AHB clock = SYSCLK
  *     @arg RCC_SYSCLK_Div2: AHB clock = SYSCLK/2
  *     @arg RCC_SYSCLK_Div4: AHB clock = SYSCLK/4
  *     @arg RCC_SYSCLK_Div8: AHB clock = SYSCLK/8
  *     @arg RCC_SYSCLK_Div16: AHB clock = SYSCLK/16
  *     @arg RCC_SYSCLK_Div64: AHB clock = SYSCLK/64
  *     @arg RCC_SYSCLK_Div128: AHB clock = SYSCLK/128
  *     @arg RCC_SYSCLK_Div256: AHB clock = SYSCLK/256
  *     @arg RCC_SYSCLK_Div512: AHB clock = SYSCLK/512
  * @retval None
  */
void RCC_HCLKConfig(uint32_t RCC_SYSCLK)
{
  uint32_t tmpreg = 0;
  /* Check the parameters */
  //判断要进行多少的分频
  assert_param(IS_RCC_HCLK(RCC_SYSCLK));
  //选择要进行操纵的寄存器
  tmpreg = RCC->CFGR;
  /* Clear HPRE[3:0] bits */
  /**
	CFGR_HPRE_Reset_Mask:0xFFFFFF0F==》0000 1111
  */
  //表示将CFGR的bit4-bit7位置0
  tmpreg &= CFGR_HPRE_Reset_Mask;
  /* Set HPRE[3:0] bits according to RCC_SYSCLK value */
  //表示将CFGR的bit4-bit7位置1
  tmpreg |= RCC_SYSCLK;
  /* Store the new value */
  RCC->CFGR = tmpreg;
}

9.RCC_PCLK1Config(APB1频率)/RCC_PCLK2Config(APB2频率)

/**
  * @brief  Configures the Low Speed APB clock (PCLK1).
  * @param  RCC_HCLK: defines the APB1 clock divider. This clock is derived from 
  *   the AHB clock (HCLK).
  *   This parameter can be one of the following values:
  *     @arg RCC_HCLK_Div1: APB1 clock = HCLK
  *     @arg RCC_HCLK_Div2: APB1 clock = HCLK/2
  *     @arg RCC_HCLK_Div4: APB1 clock = HCLK/4
  *     @arg RCC_HCLK_Div8: APB1 clock = HCLK/8
  *     @arg RCC_HCLK_Div16: APB1 clock = HCLK/16
  * @retval None
  */
void RCC_PCLK1Config(uint32_t RCC_HCLK)
{
  uint32_t tmpreg = 0;
  /* Check the parameters */
  assert_param(IS_RCC_PCLK(RCC_HCLK));
  tmpreg = RCC->CFGR;
  /* Clear PPRE1[2:0] bits */
  tmpreg &= CFGR_PPRE1_Reset_Mask;
  /* Set PPRE1[2:0] bits according to RCC_HCLK value */
  tmpreg |= RCC_HCLK;
  /* Store the new value */
  RCC->CFGR = tmpreg;
}

/**
  * @brief  Configures the High Speed APB clock (PCLK2).
  * @param  RCC_HCLK: defines the APB2 clock divider. This clock is derived from 
  *   the AHB clock (HCLK).
  *   This parameter can be one of the following values:
  *     @arg RCC_HCLK_Div1: APB2 clock = HCLK
  *     @arg RCC_HCLK_Div2: APB2 clock = HCLK/2
  *     @arg RCC_HCLK_Div4: APB2 clock = HCLK/4
  *     @arg RCC_HCLK_Div8: APB2 clock = HCLK/8
  *     @arg RCC_HCLK_Div16: APB2 clock = HCLK/16
  * @retval None
  */
void RCC_PCLK2Config(uint32_t RCC_HCLK)
{
  uint32_t tmpreg = 0;
  /* Check the parameters */
  assert_param(IS_RCC_PCLK(RCC_HCLK));
  tmpreg = RCC->CFGR;
  /* Clear PPRE2[2:0] bits */
  tmpreg &= CFGR_PPRE2_Reset_Mask;
  /* Set PPRE2[2:0] bits according to RCC_HCLK value */
  tmpreg |= RCC_HCLK << 3;
  /* Store the new value */
  RCC->CFGR = tmpreg;
}

10.RCC_SYSCLKConfig(选择使用哪一个作为系统时钟HSI/HSE/SYS)

/**
  * @brief  Configures the system clock (SYSCLK).
  * @param  RCC_SYSCLKSource: specifies the clock source used as system clock.
  *   This parameter can be one of the following values:
  *     @arg RCC_SYSCLKSource_HSI: HSI selected as system clock
  *     @arg RCC_SYSCLKSource_HSE: HSE selected as system clock
  *     @arg RCC_SYSCLKSource_PLLCLK: PLL selected as system clock
  * @retval None
  */
void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource)
{
  uint32_t tmpreg = 0;
  /* Check the parameters */
  //判断用户输入的参数是否正确
  assert_param(IS_RCC_SYSCLK_SOURCE(RCC_SYSCLKSource));
  tmpreg = RCC->CFGR;
  /* Clear SW[1:0] bits */
  tmpreg &= CFGR_SW_Mask;//置0
  /* Set SW[1:0] bits according to RCC_SYSCLKSource value */
  tmpreg |= RCC_SYSCLKSource;//置1
  /* Store the new value */
  RCC->CFGR = tmpreg;
}

11.RCC_APB2PeriphResetCmd(外设的重新复位)

/**
  * @brief  Forces or releases High Speed APB (APB2) peripheral reset.
  * @param  RCC_APB2Periph: specifies the APB2 peripheral to reset.
  *   This parameter can be any combination of the following values:
  *     @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
  *          RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,
  *          RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,
  *          RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,
  *          RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,
  *          RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,
  *          RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11  
  * @param  NewState: new state of the specified peripheral reset.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  if (NewState != DISABLE)
  {
    RCC->APB2RSTR |= RCC_APB2Periph;
  }
  else
  {
    RCC->APB2RSTR &= ~RCC_APB2Periph;
  }
}

4.注意点:

1.进制问题

我们在寄存器中的偏移量都是以十进制进行设置的,如果想要将其定义在宏定义中,记得将其转换为十六进制

2.位段计算

5.使用库重写时钟设置函数

1.原始函数

#include "clock.h"
#include "gpio.h"

void Set_SysClockTo72M(void){
	
	//检测外部晶振是否准备好
	unsigned int Rcc_CR_HSE_Ready=0;
	//等待开启PLL开启成功
	unsigned int Rcc_CR_PLL_Ready=0;
	//判断切换成PLL是否成功
	unsigned int RCC_CF_SWS_PLL=0;
	unsigned int faultTime=0;//判断等待是否超时
	
	
	//一、复位RCC_CR寄存器
	rRCC_CR = 0x00000083;
	
	//二、开启外部时钟(外部晶振)
	//第一步:先置0【将bit16清零】
	rRCC_CR &= ~(1<<16);//关闭HSEON
	
	//第二步:在置1
	rRCC_CR |= (1<<16);//打开HSEON,让HSE开始工作
	
	//三、检测外部时钟开启是否成功
	do{
		//检测HSEREAY(bit17)是否为1,1表示准备好
		Rcc_CR_HSE_Ready=rRCC_CR&(1<<17);//取出bit17
		faultTime++;
	}while((faultTime<0x0fffffff) && (Rcc_CR_HSE_Ready==0));
	//跳出do-while 1)要么超时2)要么准好了
	
	
	//判断是超时还是准备好
	//注意点:不能直接使用“Rcc_CR_HSE_Ready”因为rRCC_CR是需要读一次寄存器
	//但是读出的结果可能还未改变,所以一定不能直接使用
	if((rRCC_CR&(1<<17))!=0)//rRCC_CR&(1<<17)==1
	{//这里HSE就ready,下面再去配置PLL并且等待他ready
		
		//设置Flash
		rFLASH_ACR |= 0x10;
		rFLASH_ACR &= (~0x03);
		rFLASH_ACR |= (0x02);
		
		
		
		//四、对其进行预分频
		//HPRE【AHB】:对应bit4-bit7:不分频(000)
		//PPRE1【APB1】:对应bit8-bit10:进行二分频(100)
		//PPRE2【APB2】:对应bit11-bit13:不分频(000)
		//AHB和APB2未分频,APB1被2分频
		//所以最终:AHB和APB2都是72MHZ,APB1是36MHZ
		//第一步:先置0
		rRCC_CFGR &=(~((0x0f<<4) | (0x07<<8) | (0x07<<11)));
		//等价于:rRCC_CFGR=(~(0x3ff<<4));
		//第二步:置1
		rRCC_CFGR |=((0x0<<4) | (0x04<<8) | (0x0<<11));
		
		
		//五、设置SHE为输入时钟,同时HSE不分频
		//选择HSE作为PLL输入并且HSE不分频
		//设置为输入时钟:bit16
		//设置为不分频:bit17
		//第一步:先置0
		rRCC_CFGR &=(~((1<<16) | (1<<17)));
		//第二步:置1,bit16
		rRCC_CFGR |= ((1<<16) | (0<<17));
		
		
		//六、设置PLL倍频系数
		//9分频:0111:0x07
		
		rRCC_CFGR &=(~(0x0f<<18));//清零bit18-bit21
		rRCC_CFGR |= (0x07<<18);//设置为9倍频
		
		//七、打开PLL开关
		rRCC_CR |= (1<<24);
		
		
		//八、等待开启PLL开启成功
		//因为前面已经使用到,被累加了,使用这里要重新置0
		faultTime=0;
		do{
			led_init();
			Rcc_CR_PLL_Ready=rRCC_CR & (1<<25);//检测第25位是否为1
			faultTime++;
		}while((faultTime<0x0fffffff) && (Rcc_CR_PLL_Ready==0));
		
		if((rRCC_CR & (1<<25)) == (1<<25)){
					//到这里说明PLL已经稳定,可以用了,下面可以切换成外部时钟了
					
					//九、切换成PLL
					rRCC_CFGR &=(~(0x03)<<0);
					rRCC_CFGR |=(0x02<<0);
					
				//十、判断切换成PLL是否成功
				//因为前面已经使用到,被累加了,使用这里要重新置0
				faultTime=0;
				do{
					RCC_CF_SWS_PLL=rRCC_CFGR & (0x03<<2);//读出bit2-bit3
					faultTime++;
					led_init();
					//0x02<<2:表示此时转换成PLL
				}
			while ((faultTime<0x0FFFFFFF) && (RCC_CF_SWS_PLL!=(0x02<<2)));
					
				//十一、此时PLL转换成功
				if((rRCC_CFGR & (0x03<<2))==(0x02<<2)){
					
					//到这里我们的时钟整个就设置好了,可以结束了
					
				}else{
					//到这里说明PLL输出作为PLL失败
					while(1);
				}
		}
		else{
			//到这里说明PLL启动时出错了,PLL不能稳定工作
			while(1);
		}
	}else{//超时,或者未准备好,此时HSE不可以使用
		while(1);
	}
	
}

2.自己封装一个RCC_WaitForPLLStartUp

此处我们参考【RCC_WaitForHSEStartUp】这个函数来自己写

由源文件中没有定义【PLL_STARTUP_TIMEOUT】所以我们要自定义

#define PLL_STARTUP_TIMEOUT   ((uint16_t)0x0500000)


//本函数作用:等待PLL倍频后输出稳定
//返回值:SUCCESS说明未超时,ERROR超时
ErrorStatus RCC_WaitForPLLStartUp(void)
{
	
  __IO uint32_t StartUpCounter = 0;
  ErrorStatus status = ERROR;
  FlagStatus PLLStatus = RESET;
  
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
  //读取寄存器的值
    PLLStatus = RCC_GetFlagStatus(RCC_FLAG_PLLRDY);
    StartUpCounter++;  //读取是否超时的
  } while((StartUpCounter != PLL_STARTUP_TIMEOUT) && (PLLStatus == RESET));

  //这里判断是防止超时
  if (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) != RESET)
  {
    status = SUCCESS;
  }
  else
  {
  //超时
    status = ERROR;
  }  
  return (status);
}

3.完整使用库函数的clock


//这个函数里面包含了全部外设头文件
#include "stm32f10x.h"
//等价于
//#include"stm32f10x_conf.h"


#define PLL_STARTUP_TIMEOUT   ((uint16_t)0x0500000)


//本函数作用:等待PLL倍频后输出稳定
//返回值:SUCCESS说明未超时,ERROR超时
ErrorStatus RCC_WaitForPLLStartUp(void)
{
	
  __IO uint32_t StartUpCounter = 0;
  ErrorStatus status = ERROR;
  FlagStatus PLLStatus = RESET;
  
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
  //读取寄存器的值
    PLLStatus = RCC_GetFlagStatus(RCC_FLAG_PLLRDY);
    StartUpCounter++;  //读取是否超时的
  } while((StartUpCounter != PLL_STARTUP_TIMEOUT) && (PLLStatus == RESET));

  //这里判断是防止超时
  if (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) != RESET)
  {
    status = SUCCESS;
  }
  else
  {
  //超时
    status = ERROR;
  }  
  return (status);
}



void Set_SysClockTo72M(void){
	
	//接收判断回来的HSE是否已经稳定
	ErrorStatus sta=ERROR;
	
	//faultTime:用来判断是否超时
	unsigned int faultTime=0;
	unsigned int RCC_CF_SWS_PLL=0;
	
	
	//一、先关闭HSEON然后在打开HSEON
	RCC_HSEConfig(RCC_HSE_ON);
	/*
	rRCC_CR = 0x00000083;
	rRCC_CR &= ~(1<<16);//关闭HSEON
	rRCC_CR |= (1<<16);//打开HSEON,让HSE开始工作
	*/
	
	
	//二、等到HSE稳定
		sta=RCC_WaitForHSEStartUp();
	/*
	do{
		//检测HSEREAY(bit17)是否为1,1表示准备好
		Rcc_CR_HSE_Ready=rRCC_CR&(1<<17);//取出bit17
		faultTime++;
	}while((faultTime<0x0fffffff) && (Rcc_CR_HSE_Ready==0));
	//跳出do-while 1)要么超时2)要么准好了
	*/

	

	//三、判断是HSE稳定了还是超时了
	if(sta==SUCCESS)
	//if((rRCC_CR&(1<<17))!=0)//rRCC_CR&(1<<17)==1

	{//这里HSE就ready,下面再去配置PLL并且等待他ready
		
		//四、设置Flash
		FLASH->ACR |= 0x10;
		FLASH->ACR  &= (~0x03);
		FLASH->ACR  |= (0x02);
		/*
		rFLASH_ACR |= 0x10;
		rFLASH_ACR &= (~0x03);
		rFLASH_ACR |= (0x02);
		*/
		
		//AHB和APB2未分频,APB1被2分频
		//所以最终:AHB和APB2都是72MHZ,APB1是36MHZ
		
		//五、配置相关的倍频信息
		//配置HCLK为SYSCLK/1
		RCC_HCLKConfig(RCC_SYSCLK_Div1);
		//配置PCLK1为JCLK的2分频
		RCC_PCLK1Config(RCC_HCLK_Div2);
		//配置PCLK2为JCLK的1分频
		RCC_PCLK2Config(RCC_HCLK_Div1);
		
		/*
		rRCC_CFGR &=(~((0x0f<<4) | (0x07<<8) | (0x07<<11)));
		//等价于:rRCC_CFGR=(~(0x3ff<<4));
		rRCC_CFGR |=((0x0<<4) | (0x04<<8) | (0x0<<11));
		*/
		
		//六、设置HSE/1为PLL时钟源,PLL倍频系数为9
		RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);
		/*
		//设置SHE为输入时钟,同时HSE不分频
		rRCC_CFGR &=(~((1<<16) | (1<<17)));
		rRCC_CFGR |= ((1<<16) | (0<<17));
		//设置PLL倍频系数
		//9分频:0111:0x07
		
		rRCC_CFGR &=(~(0x0f<<18));//清零bit18-bit21
		rRCC_CFGR |= (0x07<<18);//设置为9倍频
		
		*/
		
		
		//七、打开PLL开关
		RCC_PLLCmd(ENABLE);
		//rRCC_CR |= (1<<24);
		
		
		//因为HAL库中没有等到PLL的函数
		//此处我们参考【RCC_WaitForHSEStartUp】这个函数来自己写
		//八、等待开启PLL开启成功
		sta=RCC_WaitForPLLStartUp();
		/*
		//因为前面已经使用到,被累加了,使用这里要重新置0
		faultTime=0;
		do{
			led_init();
			Rcc_CR_PLL_Ready=rRCC_CR & (1<<25);//检测第25位是否为1
			faultTime++;
		}while((faultTime<0x0fffffff) && (Rcc_CR_PLL_Ready==0));
		*/
		
		
		//九、判断PLL稳定还是超时
		//if((rRCC_CR & (1<<25)) == (1<<25)){
		if(sta==SUCCESS){
					//到这里说明PLL已经稳定,可以用了,下面可以切换成外部时钟了
			
				//九、切换成PLL
					RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
				/*
					rRCC_CFGR &=(~(0x03)<<0);
					rRCC_CFGR |=(0x02<<0);
				*/	
			
			
				//十、判断切换成PLL是否成功
			
			
				//因为前面已经使用到,被累加了,使用这里要重新置0
				faultTime=0;
				do{
					RCC_CF_SWS_PLL=RCC->CFGR & (0x03<<2);//读出bit2-bit3
					faultTime++;
					//0x02<<2:表示此时转换成PLL
				}
				while ((faultTime<0x0FFFFFFF) && (RCC_CF_SWS_PLL!=(0x02<<2)));
				
				
				//十一、此时PLL转换成功
				if((RCC->CFGR  & (0x03<<2))==(0x02<<2)){
					
					//到这里我们的时钟整个就设置好了,可以结束了
					
				}else{
					//到这里说明PLL输出作为PLL失败
					while(1);
				}
		}
		else{
			//到这里说明PLL启动时出错了,PLL不能稳定工作
			while(1);
		}
	}else{//超时,或者未准备好,此时HSE不可以使用
		while(1);
	}
	
}

6.SystmInit注意点:

我们在“startup_stm32f10x_md.s”文件中可以看到在执行main函数之前会先执行一个“SystemInit”函数

所以如果我们想要使用自己的设置72MHZ频率的函数,则应该将SystemInit注释调。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1129322.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux环境开发工具yum、makefile的使用 【Linux】

文章目录 Linux软件包管理器 - yumLinux下安装软件的方式yum查找软件包如何实现本地机器和云服务器之间的文件互传卸载软件Linux编译器 - gcc/g 程序的翻译过程1.预编译&#xff08;预处理&#xff09;2.编译&#xff08;生成汇编&#xff09;3.汇编&#xff08;生成机器可识别…

MySQL数据库 #3

文章目录 一、创建表的完整语法二、约束条件1.unsigned &#xff08;无符号&#xff09;2. zerofill &#xff08;0填充&#xff09;3. default &#xff08;默认值&#xff09;4. not null&#xff08;非空&#xff09;5. unique&#xff08;唯一&#xff09;6. primary key &…

1024程序员节背后的秘密:1024程序员节的前世今生

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

中国艺术孙溟㠭篆刻《绕绕》

孙溟展先生现在的这方篆刻作品&#xff0c;没有使用大篆和小篆文字来篆刻&#xff0c;彰显篆刻的金石魅力。一改以往的不同&#xff0c;以圆形组合设计&#xff0c;用篆刻的刀法刻出&#xff0c;即体现篆刻的美&#xff0c;又达到了作者想表达的感情。这方篆刻作品溟展先生起名…

SD NAND

文章目录 前言SD NAND vs SD 卡SD NAND vs SPI NANDCS SD NAND 优势芯片介绍结构框图引脚介绍参考设计 焊接测速单片机读写测试作为 ARM Linux 系统盘使用 前言 提到 SD&#xff0c;最先想到的就是 SD 卡&#xff0c;由于体积关系&#xff0c;TF 卡使用得更为普遍&#xff0c;…

[Ubuntu 18.04] 搭建文件夹共享之Samba服务器

Samba是一个开源项目,允许Windows用户在Linux和Unix系统上进行文件共享。 Samba服务器是一个可以让Linux或Unix系统在网络上充当Windows NT/2000/XP/2003等网络操作系统的共享资源的软件。它允许用户通过SMB/CIFS协议在Linux或Unix系统与Windows共享资源。 Samba服务器的主要…

VRPTW(MATLAB):淘金优化算法GRO求解带时间窗的车辆路径问题VRPTW(提供参考文献及MATLAB代码)

一、VRPTW简介 带时间窗的车辆路径问题(Vehicle Routing Problem with Time Windows, VRPTW)是车辆路径问题(VRP)的一种拓展类型。VRPTW一般指具有容量约束的车辆在客户指定的时间内提供配送或取货服务&#xff0c;在物流领域应用广泛&#xff0c;具有重要的实际意义。VRPTW常…

ARM | 传感器必要总线IIC

IIC总线介绍 1.谈谈你对IIC总线理解&#xff1f; 1&#xff09;IIC总线是串行半双工同步总线,主要用于连接整体电路 2&#xff09;SCL/SDA作用:IIC是两线制,一根是时钟线SCK,用于控制什么时候进行进行数据传输,时钟信号由主机发出; 另一根是数据线SDA,用于进行数据传输,可以从…

垃圾收集器与内存分配策略

概述 垃圾收集需要完成的三件事情&#xff1a; 哪些内存需要回收&#xff1f;什么时候回收&#xff1f;如何回收&#xff1f; 判断对象是都存活的算法&#xff1a; 引用计数法&#xff1a;在对象中添加一个引用计数器&#xff0c;每当有一个地方引用时&#xff0c;计数器值就…

python基础教程:异常处理

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 有时候我们在写程序的时候会出现错误或者异常&#xff0c;导致程序终止&#xff0c;如下这个例子&#xff1a; #!/usr/bin/env python a 2/0 print(a)结果提示如…

Visual Studio Professional 2019 软件安装教程(附安装包下载)

Microsoft Visual Studio 是一个非常强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;适用于 Windows 上的 .NET 和 C 开发人员。它提供了一系列丰富的工具和功能&#xff0c;可以提升和增强软件开发的每个阶段。 Visual Studio IDE 是一个创意启动板&#xff0c;可…

C++ 多线程编程和同步机制:详解和实例演示

C中的多线程编程和同步机制使得程序员可以利用计算机的多核心来提高程序的运行效率和性能。本文将介绍多线程编程和同步机制的基本概念和使用方法。 多线程编程基础 在C中&#xff0c;使用<thread>库来创建和管理线程。线程可以通过函数、成员函数或者Lambda表达式来实现…

基于C#使用winform技术的游戏平台的实现【C#课程设计】

基于C#使用winform技术的游戏平台的实现【C#课程设计】 说明项目结构项目运行截图及实现的功能 部分代码一些说明(个人觉得一些难点的说明)一、ListView &#xff0c;ImageList 的综合使用二、图片上传以及picturebox 图片的动态替换三、图表插件的使用四、SQL工具类封装五、高…

最新哔哩哔哩邮箱绑定接口签名JS逆向分析

本章教程主要逆向分析 哔哩哔哩邮箱绑定接口biliCSRF 和mid 参数。 教程仅供学习参考,请勿滥用,由此带来的法律责任需由自己承担。 目录 一、接口参数分析 二、签名加密代码 三、滑块验证码 一

LabVIEW应用开发——控件的使用(四)

接上文&#xff0c;这篇介绍时间控件。 LabVIEW应用开发——控件的使用&#xff08;三&#xff09; 1、时间控件Time Stamp control 在日常软件开发场景中&#xff0c;时间也是一种常用的控件&#xff0c;用于表达当前时间的显示、对下设置时间、时间同步等等场景。LabVIEW专门…

Redis主从模式(一)----搭建主从结构并演示

目录 一, 主从模式 1.1 单个Redis服务器可能存在的问题 1.2 单点问题 1.3 什么是主从模式 概念 图示 二, 演示Redis的主从复制 2.1 Redis-server进程 2.2 建立复制 1. 首先将redis.conf配置文件复制一份并修改daemonize 为 yes 2.修改配置文件中的端口号 3. 分别在…

力扣每日一题63:不同路径||

题目描述&#xff1a; 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”&#xff09;。 现在考虑网格中有障碍物…

RK3568平台开发系列讲解(应用篇)串口应用编程之串口介绍

🚀返回专栏总目录 文章目录 一、串口介绍1.1、数据传输方式1.2、数据格式1.3、波特率1.4、硬件流控制和软件流控制1.5、错误检测1.6、串口编程二、串口设备节点介绍沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 串口设备是嵌入式开发中最常用的外设之一,通过串口…

2023年信息科学与工程学院学生科协第一次前端培训

目录 一、前端是什么&#xff1f;前端能做什么&#xff1f;前端需要做什么&#xff1f;现阶段如何理解前端 二、前端学习路线html是什么&#xff1f;css是什么&#xff1f;什么是jshtml、css以及js关系掌握三种语言之后的学习路线 三、HTML基础语法标题段落文本换行文本标签图像…

协程和 C++ Boost库的Coroutine2

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 协程和 C Boost库的Coroutine2 摘要为什么不是boost.coroutine&#xff1f; 线程与协程为什么不介绍C20标准的协程C协程与golang的goroutine 二、使用步骤1.引入库2.核心类单…