• 【STM32】时钟设置函数(寄存器版)


    一、STM32时钟设置函数移植

    1.时钟模块回顾

    一个疑问

    前面代码并没有设置时钟为什么可以直接使用。

    2.时钟树

    3.时钟树分析

    1.内部晶振(HSI)

    内部晶振不稳定,当我们上电后,会自动产生振动,自动产生时钟,但是晶振不稳定。

    不经过PPLMUL,默认使用8MHZ。所以如果我们想要72MHZ,则需要使用外部晶振

      2.外部晶振(HSE)

    当接上外部晶振,当接通电源之后,不用软件操作,会自动产生振动。可以进行分频等操作。

    从外部接上外部晶振的时候,我们需要等待一段时间,让其稳定后,才开始工作。(所以要进行判断)

    3.PLLMUL

    当上电后,经过他时,要等待一段时间,让其稳定后,才可以开始工作。(所以我们有一个寄存器专门用来判断其是否准备好开始工作,当我们去读取到其准备好了才可以进行下一步)

    二、代码移植

    1. #ifndef __CLOCK_H__
    2. #define __CLOCK_H__
    3. #include "gpio.h"
    4. // 寄存器宏定义
    5. // RCC寄存器基地址为0x40021000
    6. #define RCC_BASE 0x40021000 // RCC部分寄存器的基地址
    7. #define RCC_CR (RCC_BASE + 0x00) // RCC_CR的地址
    8. #define RCC_CFGR (RCC_BASE + 0x04)
    9. #define FLASH_ACR 0x40022000
    10. // 用C语言来访问寄存器的宏定义
    11. #define rRCC_CR (*((volatile unsigned int *)RCC_CR))
    12. #define rRCC_CFGR (*((volatile unsigned int *)RCC_CFGR))
    13. #define rFLASH_ACR (*((volatile unsigned int *)FLASH_ACR))
    14. // 函数作用:时钟源切换到HSE并且使能PLL,将主频设置为72MHz
    15. void Set_SysClockTo72M(void);
    16. #endif

    1.复位RCC_CR寄存器

    #define rRCC_APB2ENR     (*((unsigned int *)RCC_APB2ENR))

    RCC->CR就相当于rRCC_APB2ENR

    1. //复位RCC_CR寄存器
    2. rRCC_CR=0x00000083;

    2.开启外部时钟(就是开启外部晶振)

    &:将某一些位置0

    |:将某一些位置1

    1. //开启外部时钟(外部晶振)
    2. //第一步:先置0【将bit16清零】
    3. rRCC_CR &= ~(1<<16);//关闭HSEON
    4. //第二步:在置1
    5. rRCC_CR |= (1<<16);//打开HSEON,让HSE开始工作

    3.检测外部时钟开启是否成功(HSEREDY)

    do while十分适合检测是否超时!!!!!!!

    1. do{
    2. //检测HSEREAY(bit17)是否为1,1表示准备好
    3. Rcc_CR_HSE_Ready=rRCC_CR&(1<<17);//取出bit17
    4. faultTime++;
    5. }while((faultTime<0x0fffffff) && (Rcc_CR_HSE_Ready==0))
    6. //跳出do-while 1)要么超时2)要么准好了

    4.当准备好进入下一步

    5.Flash的设置

    1. rFLASH_ACR |= 0x10;
    2. rFLASH_ACR &= (~0x03);
    3. rFLASH_ACR |= (0x02);

    6.对其进行预分频

    1. //HPRE【AHB】:对应bit4-bit7:不分频(000)
    2. //PPRE1【APB1】:对应bit8-bit10:进行二分频(100)
    3. //PPRE2【APB2】:对应bit11-bit13:不分频(000)
    4. //AHB和APB2未分频,APB1被2分频
    5. //所以最终:AHB和APB2都是72MHZ,APB1是36MHZ
    6. //第一步:先置0
    7. rRCC_CFGR=(~((0x0f<<4) | (0x07<<8) | (0x07<<11)));
    8. //等价于:rRCC_CFGR=(~(0x3ff<<4));
    9. //第二步:置1
    10. rRCC_CFGR=(((0x0<<4) | (0x04<<8) | (0x0<<11)));

    7.设置SHE为输入时钟,同时HSE不分频

    1. //设置为输入时钟:bit16
    2. //设置为不分频:bit17
    3. //第一步:先置0
    4. rRCC_CFGR &=(~((1<<16) | (1<<17)));
    5. //第二步:置1
    6. rRCC_CFGR |= ((1<<18) | (0<<17));

    8.设置PLL倍频系数

    因为我们在开发板上接上的外部晶振就是8MHZ,如果我们想要在内部使用72MHZ,则需要在内部进行分频率(9倍)

    1. //9分频:0111:0x07
    2. rRCC_CFGR &=(~(0x0f<<18));//清零bit18-bit21
    3. rRCC_CFGR |= (0x07<<18);//设置为9倍频

    9.打开使能

    1. //七、打开PLL开关
    2. rRCC_CR |= (1<<24);

    10.等待开启PLL开启成功

    1. //八、等待开启PLL开启成功
    2. do{
    3. Rcc_CR_PLL_Ready=rRcc_CR & (1<<25);//检测第25位是否为1
    4. faultTime++;
    5. }while((faultTime<0x0fffffff) && (Rcc_CR_PLL_Ready==0))

    11.将PLL作为SYSCLK的时钟来源

    1. //到这里说明PLL已经稳定,可以用了,下面可以切换成外部时钟了
    2. rRCC_CFGR &=(~(0x03)<<0);
    3. rRCC_CFGR |=(0x10<<0);

    12. 判断切换成PLL是否成功

    1. do{
    2. RCC_CF_SWS_PLL=rRCC_CFGR & (0x03<<2);//读出bit2-bit3
    3. faultTime++;
    4. //0x02<<2:表示此时转换成PLL
    5. }while((faultTime<0x0fffffff) && (Rcc_CR_PLL_Ready!=(0x02<<2)))

    13.此时PLL转换成功

    14.完整代码

    1. #include "clock.h"
    2. void Set_SysClockTo72M(void){
    3. //检测外部晶振是否准备好
    4. unsigned int Rcc_CR_HSE_Ready=0;
    5. //等待开启PLL开启成功
    6. unsigned int Rcc_CR_PLL_Ready=0;
    7. //判断切换成PLL是否成功
    8. unsigned int RCC_CF_SWS_PLL=0;
    9. unsigned int faultTime=0;//判断等待是否超时
    10. //一、复位RCC_CR寄存器
    11. rRCC_CR = 0x00000083;
    12. //二、开启外部时钟(外部晶振)
    13. //第一步:先置0【将bit16清零】
    14. rRCC_CR &= ~(1<<16);//关闭HSEON
    15. //第二步:在置1
    16. rRCC_CR |= (1<<16);//打开HSEON,让HSE开始工作
    17. //三、检测外部时钟开启是否成功
    18. do{
    19. //检测HSEREAY(bit17)是否为1,1表示准备好
    20. Rcc_CR_HSE_Ready=rRCC_CR&(1<<17);//取出bit17
    21. faultTime++;
    22. }while((faultTime<0x0fffffff) && (Rcc_CR_HSE_Ready==0));
    23. //跳出do-while 1)要么超时2)要么准好了
    24. //判断是超时还是准备好
    25. //注意点:不能直接使用“Rcc_CR_HSE_Ready”因为rRCC_CR是需要读一次寄存器
    26. //但是读出的结果可能还未改变,所以一定不能直接使用
    27. if((rRCC_CR&(1<<17))!=0)//rRCC_CR&(1<<17)==1
    28. {//这里HSE就ready,下面再去配置PLL并且等待他ready
    29. //四、对其进行预分频
    30. //HPRE【AHB】:对应bit4-bit7:不分频(000)
    31. //PPRE1【APB1】:对应bit8-bit10:进行二分频(100)
    32. //PPRE2【APB2】:对应bit11-bit13:不分频(000)
    33. //AHB和APB2未分频,APB1被2分频
    34. //所以最终:AHB和APB2都是72MHZ,APB1是36MHZ
    35. //第一步:先置0
    36. rRCC_CFGR=(~((0x0f<<4) | (0x07<<8) | (0x07<<11)));
    37. //等价于:rRCC_CFGR=(~(0x3ff<<4));
    38. //第二步:置1
    39. rRCC_CFGR=(((0x0<<4) | (0x04<<8) | (0x0<<11)));
    40. //五、设置SHE为输入时钟,同时HSE不分频
    41. //选择HSE作为PLL输入并且HSE不分频
    42. //设置为输入时钟:bit16
    43. //设置为不分频:bit17
    44. //第一步:先置0
    45. rRCC_CFGR &=(~((1<<16) | (1<<17)));
    46. //第二步:置1,bit16
    47. rRCC_CFGR |= ((1<<18) | (0<<17));
    48. //六、设置PLL倍频系数
    49. //9分频:0111:0x07
    50. rRCC_CFGR &=(~(0x0f<<18));//清零bit18-bit21
    51. rRCC_CFGR |= (0x07<<18);//设置为9倍频
    52. //七、打开PLL开关
    53. rRCC_CR |= (1<<24);
    54. //八、等待开启PLL开启成功
    55. do{
    56. Rcc_CR_PLL_Ready=rRCC_CR & (1<<25);//检测第25位是否为1
    57. faultTime++;
    58. }while((faultTime<0x0fffffff) && (Rcc_CR_PLL_Ready==0));
    59. if((rRCC_CR & (1<<25)) == (1<<25)){
    60. //到这里说明PLL已经稳定,可以用了,下面可以切换成外部时钟了
    61. //九、切换成PLL
    62. rRCC_CFGR &=(~(0x03)<<0);
    63. rRCC_CFGR |=(0x10<<0);
    64. //十、判断切换成PLL是否成功
    65. do{
    66. RCC_CF_SWS_PLL=rRCC_CFGR & (0x03<<2);//读出bit2-bit3
    67. faultTime++;
    68. //0x02<<2:表示此时转换成PLL
    69. }while((faultTime<0x0fffffff) && (Rcc_CR_PLL_Ready!=(0x02<<2)));
    70. //十一、此时PLL转换成功
    71. if((rRCC_CFGR & (0x03<<2))==(0x02<<2)){
    72. //到这里我们的时钟整个就设置好了,可以结束了
    73. }else{
    74. //到这里说明PLL输出作为PLL失败
    75. while(1);
    76. }
    77. }
    78. else{
    79. //到这里说明PLL启动时出错了,PLL不能稳定工作
    80. while(1);
    81. }
    82. }else{//超时,或者未准备好,此时HSE不可以使用
    83. while(1);
    84. }
    85. }

    三、问题解决

    1.我们想要让led快速闪3下,然后换成72MHZ的频率接着闪

    1. void delay(){
    2. unsigned int i=0,j=0;
    3. for(i=0;i<1000;i++){
    4. for(j=0;j<2000;j++){
    5. }
    6. }
    7. }
    8. void led_init(){
    9. rRCC_APB2ENR = 0x00000008;
    10. rGPIOB_CRH = 0x33333333;
    11. rGPIOB_ODR = 0x0000ff00;//全灭
    12. }
    13. void led_flash(void){
    14. unsigned int i=0;
    15. for(i=0;i<3;i++){
    16. rGPIOB_ODR = 0x00000000;//全亮
    17. delay();
    18. rGPIOB_ODR = 0x0000ff00;//全灭
    19. delay();
    20. }
    21. }
    22. void main(void){
    23. led_init();
    24. led_flash();
    25. Set_SysClockTo72M();
    26. led_flash();
    27. }

    但是实际上并无法实现,只能在闪烁完3次后就熄灭。

    2.问题解决

    led初始化时,默认是全亮的

    1.degger方法

    把点亮led灯的函数加到clock中去,看看代码运行到哪里不会亮

    2.判断超时变量的初始化

    因为我们多次使用到超时变量,则每一个进入do-while循环之前要重新置0

    3.出错点

  • 相关阅读:
    Linux grep命令详细教程
    简单剖析Hashmap
    【Golang星辰图】Go语言的机器学习之旅:从基础知识到实际应用的综合指南
    SaaSBase:K2是什么?
    ES6 入门教程 21 async 函数 21.1 含义 & 21.2 基本用法
    用于对话场景的文本转语音-chattts
    nfs挂载错误wrong fs type, bad option, bad superblock
    7-77 求给定精度的简单交错序列部分和
    人大与加拿大女王大学金融硕士——与其羡慕他人,不如充实自己
    30余种加密编码类型的密文特征
  • 原文地址:https://blog.csdn.net/m0_63077733/article/details/133972177