瑞萨单片机论坛
直播中

jf_1137202360

8年用户 1378经验值
擅长:嵌入式技术
私信 关注
[经验]

【RA4M2设计挑战赛】+时钟配置与Systick测试

前言
   时钟是MCU的心跳,默认MCU会以内部的RC震荡器作为默认的时钟源,以默认的频率运行,这并不是MCU最高的主频配置,所以我们需要按照MCU的手册配置最高的主频运行以达到最高性能。磨刀不误砍柴工,这篇就先详细进行时钟模块的配置与测试。
时钟模块基本结构
从原理图可以看到外接了12M晶体
我们可以使用该时钟作为时钟源,PLL倍频得到最终的主频。
我们查看手册中的时钟树描述,得到如下画线的配置路径:
我们可以看到各时钟最大值,分频配置时不要超过该最大值。
各时钟源的频率如下
相关寄存器
底层驱动无非就是配置寄存器,所以我们先过一遍寄存器,大概了解下每个寄存器干什么,然后对照手册一个bit一个bit配置。
CGFSAR:安全属性寄存器,默认为1非安全模式。
SCKDIVCR:各模块时钟分频设置
SCKSCR:系统时钟源选择
PLLCCR:PLL配置寄存器
PLLCR:PLL使能控制寄存器
PLL2CCR:PLL2配置寄存器
PLL2CR:PLL2使能控制寄存器
MOSCCR:主时钟振荡器使能控制
SOSCCR:32.768K子时钟振荡器使能控制。
LOCOCR:内部低速振荡器使能控制
HOCOCR:高速内部振荡器使能控制
MOCOCR:中速内部振荡器使能控制
FLLCR1:FLL使能控制
FLLCR2
OSCSF:振荡器稳定标志
OSTDCR:振荡器停止检测控制
OSTDSR:振荡器停止状态
MOSCWTCR:主振荡器稳定等待时间
MOMCR:主时钟模式(有源晶振时钟源还是晶体)
SOMCR:子振荡器模式控制
CKOCR:时钟输出控制
LOCOUTCR,MOCOUTCR,HOCOUTCR
USBCKDIVCR,USBCKCR:USB时钟配置
TRCKCR:TRCKCR时钟配置
配置
注意配置CLK相关寄存器需要先设置PRCR.PRC0=1,设置该位使能注意高8位需要同时写入0xA5.
R_SYSTEM->PRCR |=  0xA501;
然后按照
配置主时钟(外部晶振)->等待外部晶振稳定->配置PLL->等待PLL稳定
->设置各模块分频系数->设置FLASH等待周期->选择PLL作为时钟源->设置时钟输出(我这里未使用) 的步骤进行。
注意选择PLL作为时钟源要放置在最后,否则可能由于FLASH等待周期,分频还未正确设置导致频率超过各模块正常值而导致异常。
另外其他比如32.768KHz的子时钟,内部的高中低速的时钟,USB时钟等按需配置我这里就暂时不配置。
注意一般FLASH的速度没有CPU,所以需要插入等待周期,MCU能在ICLK,50MHzFLASH 0等待周期,性能是不错的,我们ICLK配置为100MHz大于50MHz所以配置等待周期为1(见代码)
代码注释比较详细,对着代码和手册看即可
  1. int clk_init(void)

  2. {

  3. R_SYSTEM->PRCR |=  0xA501;  /* bit0 Enable writing to the registers related to the clock generation circuit */

  4. /*

  5. Main Clock Oscillator Wait Time Setting R/W

  6. 0x0: Wait time = 3 cycles (11.4uS)

  7. 0x1: Wait time = 35 cycles (133.5uS)

  8. 0x2: Wait time = 67 cycles (255.6uS)

  9. 0x3: Wait time = 131 cycles (499.7uS)

  10. 0x4: Wait time = 259 cycles (988.0uS)

  11. 0x5: Wait time = 547 cycles (2086.6uS)

  12. 0x6: Wait time = 1059 cycles (4039.8uS)

  13. 0x7: Wait time = 2147 cycles (8190.2uS)

  14. 0x8: Wait time = 4291 cycles (16368.9uS)

  15. 0x9: Wait time = 8163 cycles (31139.4uS)

  16. Others: Setting prohibited

  17. */

  18. R_SYSTEM->MOSCWTCR_b.MSTS = 5;

  19. R_SYSTEM->MOMCR_b.MOSEL = 0;  /* 0: Resonator 1: External clock input*/

  20. /*

  21. Main Clock Oscillator Drive Capability 0 Switching R/W

  22. 0 0: 20 MHz to 24 MHz

  23. 0 1: 16 MHz to 20 MHz

  24. 1 0: 8 MHz to 16 MHz

  25. 1 1: 8 MHz

  26. */

  27. R_SYSTEM->MOMCR_b.MODRV0 = 2;  /*12MHz */

  28. R_SYSTEM->MOSCCR_b.MOSTP = 0;          /* 0: Operate the main clock oscillator 1: Stop the main clock oscillator */

  29. while(R_SYSTEM->OSCSF_b.MOSCSF == 0);  /* wait Stabilization */


  30. /* PLL Output frequency 100 MHz to 200 MHz  ICLK Up to 100 MHz  

  31.    (12MHz/2) * 16 =96MHz.  100<12*16=192<200

  32. */

  33. R_SYSTEM->PLLCCR_b.PLIDIV = 2;    /*00:/1 01:/2 10:/3 */

  34. R_SYSTEM->PLLCCR_b.PLSRCSEL = 0;  /* 0: Main clock oscillator 1: HOCO */

  35. R_SYSTEM->PLLCCR_b.PLLMUL = 49;   /* 0x13 x10.0 0x14:x10.5 0x3B:x30.0  (x-0x13)*0.5+10*/

  36. R_SYSTEM->PLLCR_b.PLLSTP = 0;     /* 0: PLL is operating 1: PLL is stopped.*/

  37. while(R_SYSTEM->OSCSF_b.PLLSF == 0);  /* wait Stabilization */


  38. R_SYSTEM->LOCOCR_b.LCSTP = 0;



  39. /*

  40. Peripheral Module Clock A (PCLKA) Select R/W

  41. 0 0 0: x 1/1

  42. 0 0 1: x 1/2

  43. 0 1 0: x 1/4

  44. 0 1 1: x 1/8

  45. 1 0 0: x 1/16

  46. 1 0 1: x 1/32

  47. 1 1 0: x 1/64

  48. Others: Setting prohibited.

  49. */

  50. R_SYSTEM->SCKDIVCR_b.PCKA = 0; /* PCLKA Max 100MHz */

  51. R_SYSTEM->SCKDIVCR_b.PCKB = 1; /* PCLKB Max 50MHz */

  52. R_SYSTEM->SCKDIVCR_b.PCKA = 1; /* PCLKC Max 50MHz */

  53. R_SYSTEM->SCKDIVCR_b.PCKB = 0; /* PCLKD Max 100MHz */

  54.   R_SYSTEM->SCKDIVCR_b.BCK = 0;  /*  Set these bits to the same value as PCKB[2:0]. */

  55. R_SYSTEM->SCKDIVCR_b.ICK = 0;  /* ICK Max 100MHz */

  56. R_SYSTEM->SCKDIVCR_b.FCK = 1;  /* FCK Max 50MHz ICLK:FCLK=N:1 (N: integer) */


  57. /*

  58. Flash Wait Cycle R/W

  59. 0 0 0: 0 wait (ICLK = 50 MHz)

  60. 0 0 1: 1 wait (ICLK > 50 MHz)

  61. 0 1 0: 2 wait Not specified

  62. 0 1 1: 3 wait Not specified

  63. Others: Setting prohibited

  64. */

  65. R_FCACHE->FLWT_b.FLWT = 1;

  66. R_FCACHE->FCACHEE_b.FCACHEEN = 0; /* 0: FCACHE is disabled 1: FCACHE is enabled */


  67. /*

  68. Clock Source Select R/W

  69. 0 0 0: HOCO

  70. 0 0 1: MOCO

  71. 0 1 0: LOCO

  72. 0 1 1: Main clock oscillator (MOSC)

  73. 1 0 0: Sub-clock oscillator (SOSC)

  74. 1 0 1: PLL

  75. 1 1 0: Setting prohibited

  76. 1 1 1: Setting prohibited

  77. */

  78. R_SYSTEM->SCKSCR_b.CKSEL = 5;  /* Select PLL */




  79. /*

  80. Clock Out Source Select R/W

  81. 0 0 0: HOCO

  82. 0 0 1: MOCO

  83. 0 1 0: LOCO

  84. 0 1 1: MOSC

  85. 1 0 0: SOSC

  86. 1 0 1: Setting prohibited

  87. Others: Setting prohibited

  88. */

  89. R_SYSTEM->CKOCR_b.CKOSEL = 3;

  90. /*

  91. Clock Output Frequency Division Ratio R/W

  92. 0 0 0: x1/1

  93. 0 0 1: x1/2

  94. 0 1 0: x1/4

  95. 0 1 1: x/8

  96. 1 0 0: x1/16

  97. 1 0 1: x1/32

  98. 1 1 0: x1/64

  99. 1 1 1: x1/128

  100. */

  101. R_SYSTEM->CKOCR_b.CKODIV = 2;  /* 12MHz/4=3MHz */

  102. R_SYSTEM->CKOCR_b.CKOEN = 0;

  103. return 0;

  104. }

SYSTICK测试
时钟配置好之后我们就需要测试配置的时钟是否生效
一般可以使用时钟输出引脚测试,但是遗憾的是这个MCU的时钟输出不能选择PLL的输出之后的时钟,只能直接使用各时钟输入,所以不能用它来测试。
我们可以使用SYSTICK来进行测试。
注意SYSTICK可以选择LOCO或者ICLK作为时钟源
如下表和说明只列举了LOCO,应该是手册错误。
如下位置描述是对的
由于我们配置了ICLK100MHz
所以我们设置Systick记载值为100000-1的话,中断周期就是1000uS,1mS
初始化时设置systick_init(100000);
相关接口实现
  1. int systick_init(uint32_t tick)

  2. {

  3. SysTick_Config(tick);

  4. NVIC_EnableIRQ(SysTick_IRQn);

  5. return 0;

  6. }



  7. static uint32_t s_tick_u32 = 0;

  8. void SysTick_Handler(void)

  9. {

  10. //NVIC_ClearPendingIRQ(SysTick_IRQn);

  11. s_tick_u32++;

  12. #if 1

  13. static uint8_t s_led_tog_flag = 0;

  14. if(s_tick_u32 % 1000 == 0)

  15.   {

  16. led_set(0, s_led_tog_flag);

  17. led_set(1, s_led_tog_flag);

  18. led_set(2, s_led_tog_flag);

  19. s_led_tog_flag ^= 1;

  20. }

  21. #endif

  22. }

其中SysTick_Config的实现位于srcARMCM33Includecore_cm33.h
  1. /**

  2.   ingroup  CMSIS_Core_FunctionInterface

  3.   defgroup CMSIS_Core_SysTickFunctions SysTick Functions

  4.   brief    Functions that configure the System.

  5.   @{

  6. */



  7. #if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U)



  8. /**

  9.   brief   System Tick Configuration

  10.   details Initializes the System Timer and its interrupt, and starts the System Tick Timer.

  11.            Counter is in free running mode to generate periodic interrupts.

  12.   param [in]  ticks  Number of ticks between two interrupts.

  13.   return          0  Function succeeded.

  14.   return          1  Function failed.

  15.   note    When the variable __Vendor_SysTickConfig is set to 1, then the

  16.            function SysTick_Config is not included. In this case, the file device.h

  17.            must contain a vendor-specific implementation of this function.

  18. */

  19. __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)

  20. {

  21.   if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)

  22.   {

  23.     return (1UL);                                                   /* Reload value impossible */

  24.   }



  25.   SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */

  26.   NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */

  27.   SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */

  28.   SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |

  29.                    SysTick_CTRL_TICKINT_Msk   |

  30.                    SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */

  31.   return (0UL);                                                     /* Function successful */

  32. }

其中SysTick_CTRL_CLKSOURCE_Msk置位就是选择的ICLK,我们也可以不置位使用LOCO(频率是32.768KHz)测试。
注意中断向量的配置
srcARMCM33SourceARMstartup_ARMCM33.S中注释掉
Set_Default_Handler  SysTick_Handler (.s中弱定义了一个实现,我们使用外部的实现,所以不用.S里定义的即可)
.S中设置为了.long    SysTick_Handler                    /*  -1 SysTick Handler */
SysTick_Handler即我们自己实现的服务函数地址。
srcARMCM33Sourcesystem_ARMCM33.c
SystemInit中设置SCB->VTOR = (uint32_t) &(__VECTOR_TABLE[0]);
extern const VECTOR_TABLE_Type __VECTOR_TABLE[496];
srcARMCM33SourceARMstartup_ARMCM33.S
对应如下中断向量,我们需要调用自己的中断服务函数只需要替换对应的函数地址即可,后后面预留了.space   (470 * 4)各空间,我们可以按照手册的向量号添加即可。
  1. __Vectors:

  2.                 .long    __INITIAL_SP                       /*     Initial Stack Pointer */

  3.                 .long    Reset_Handler                      /*     Reset Handler */

  4.                 .long    NMI_Handler                        /* -14 NMI Handler */

  5.                 .long    HardFault_Handler                  /* -13 Hard Fault Handler */

  6.                 .long    MemManage_Handler                  /* -12 MPU Fault Handler */

  7.                 .long    BusFault_Handler                   /* -11 Bus Fault Handler */

  8.                 .long    UsageFault_Handler                 /* -10 Usage Fault Handler */

  9.                 .long    SecureFault_Handler                /*  -9 Secure Fault Handler */

  10.                 .long    0                                  /*     Reserved */

  11.                 .long    0                                  /*     Reserved */

  12.                 .long    0                                  /*     Reserved */

  13.                 .long    SVC_Handler                        /*  -5 SVCall Handler */

  14.                 .long    DebugMon_Handler                   /*  -4 Debug Monitor Handler */

  15.                 .long    0                                  /*     Reserved */

  16.                 .long    PendSV_Handler                     /*  -2 PendSV Handler */

  17.                 .long    SysTick_Handler                    /*  -1 SysTick Handler */



  18.                 /* Interrupts */

  19.                 .long    Interrupt0_Handler                 /*   0 Interrupt 0 */

  20.                 .long    Interrupt1_Handler                 /*   1 Interrupt 1 */

  21.                 .long    Interrupt2_Handler                 /*   2 Interrupt 2 */

  22.                 .long    Interrupt3_Handler                 /*   3 Interrupt 3 */

  23.                 .long    Interrupt4_Handler                 /*   4 Interrupt 4 */

  24.                 .long    Interrupt5_Handler                 /*   5 Interrupt 5 */

  25.                 .long    Interrupt6_Handler                 /*   6 Interrupt 6 */

  26.                 .long    Interrupt7_Handler                 /*   7 Interrupt 7 */

  27.                 .long    Interrupt8_Handler                 /*   8 Interrupt 8 */

  28.                 .long    Interrupt9_Handler                 /*   9 Interrupt 9 */



  29.                 .space   (470 * 4)                          /* Interrupts 10 .. 480 are left out */

  30. __Vectors_End:

  31.                 .equ     __Vectors_Size, __Vectors_End - __Vectors

  32.                 .size    __Vectors, . - __Vectors

我们在100次中断即1S进行一次翻转LED,肉眼可以看到翻转频率对不对,然后使用逻辑分析仪测试周期。看到周期误差是0.068mS/1000mS是非常准确的,因为这里还有程序处理时间,使用的逻辑分析仪采样只有12MHz等有误差等。
顺便提一下仿真可以查看外设寄存器进行调试
可以确认CLK SYSTICK等相关寄存器是否正确配置
参考
参考手册《8.Clock Generation Circuit》,其他章节也按需参考。
总结
注意CLK寄存器配置需要先解除写保护,寄存器的操作要注意顺序,一般是先配置再等待稳定最后再切换时钟源。
注意一般要配置FLASH等待周期,MCUICLK不超过50MHzFLASH可以0等待周期,我们大于50MHz需要设置为1
注意SYSTICKCLK可以选择不同的时钟源,手册中有一些描述写的只能是LOCO是不对的。
我们以上完全通过阅读手册,操作寄存器从零开始配置时钟,这样就会对时钟有一个比较深入的理解,代码也简洁清晰,如果用fsp生成的代码则过于复杂干扰因素太多,知其然不知其所以然,做嵌入式一定任何事都要了然于胸尤其是初学者,所以本文尽可能写的详细仅供参考。

附件: 您需要登录才可以下载或查看附件。没有帐号?注册

更多回帖

发帖
×
20
完善资料,
赚取积分