小强Craig

 

新唐M4 (2)时钟配置

一、系统时钟配置

(可使用官方时钟配置工具包协助配置, 工具包链接见新唐M4 (1)初体验)

1.修改晶振频率

  (1)用MDK打开项目后, 左方项目导航栏展开"CMSIS"文件夹, 双击打开文件"system_M451Series.C"

  (2)右击文件空白处,选择"Toggle Header/Code File"; 或者点击"__HXT"后, 按F12跳转

  (3)根据使用的外部晶振频率, 修改宏定义

 

 

 

2.系统初始化配置

  (1)解锁保护寄存器

    向地址0x4000_0100(即寄存器锁定控制寄存器SYS->REGLCTL)依次写入"59h", "16h", "88h", 即可解锁被保护的系统控制寄存器

    库函数: 

__STATIC_INLINE void SYS_UnlockReg(void)
{
  do
  {
    SYS->REGLCTL = 0x59;
    SYS->REGLCTL = 0x16;
    SYS->REGLCTL = 0x88;
  }
  while(SYS->REGLCTL == 0);
}

  (2)配置系统掉电控制寄存器(CLK->PWRCTL), 使能时钟源

    寄存器的低四位依次为:

      [0]外部高速晶振使能位HXTEN

      [1]外部低速晶振使能位LXTEN

      [2]内部高速RC振荡器使能位HIRCEN

      [3]内部低速RC振荡器使能位LIRCEN

    0: 禁用;  1:使能

    库函数:

void CLK_EnableXtalRC(uint32_t u32ClkMask)
{
  CLK->PWRCTL |= u32ClkMask;
}

  u32ClkMask:
    CLK_PWRCTL_HXTEN_Msk
    CLK_PWRCTL_LXTEN_Msk
    CLK_PWRCTL_HIRCEN_Msk
    CLK_PWRCTL_LIRCEN_Msk

  (3)读取时钟状态监测寄存器 (CLK->STATUS), 检查选择的时钟源状态

    寄存器的有效位:

      [0]HXT 时钟源稳定标志(只读)

      [1]LXT 时钟源稳定标志(只读)

      [2]内部 PLL 时钟源稳定标志(只读)

      [3]LIRC 时钟源稳定标志(只读)

      [4]HIRC时钟源稳定标志(只读)

    0:时钟不稳定/禁用;  1:时钟使能并稳定

      [7]时钟切换失败标志(只读)

    0:时钟切换成功;    1:时钟切换失败  注意:该位写1清除

    库函数:

uint32_t CLK_WaitClockReady(uint32_t u32ClkMask)
{
    int32_t i32TimeOutCnt = 2160000;
    while((CLK->STATUS & u32ClkMask) != u32ClkMask)        
    {
        if(i32TimeOutCnt-- <=0)
            return 0;
    }
    return 1;
}
u32ClkMask:
        CLK_STATUS_HXTSTB_Msk
        CLK_STATUS_LXTSTB_Msk
        CLK_STATUS_HIRCSTB_Msk
        CLK_STATUS_LIRCSTB_Msk
        CLK_STATUS_PLLSTB_Msk

返回值: 0:时钟不稳定;  1:时钟稳定 

 

  (4)配置PLL控制寄存器(CLK->PLLCTL), 配置PLL时钟频率

    步骤:失能PLL时钟避免配置PLL不稳定-->配置PLL分频-->等待PLL时钟稳定

    1) 向寄存器[16]位写"1", 使PLL进入掉电模式

    2)根据需求以及参考手册,选择时钟源并配置PLL输入/输出分频和反馈分频

 

 

  库函数:

   u32PllClkSrc:
        CLK_PLLCTL_PLLSRC_HXT     //外部高速晶振
        CLK_PLLCTL_PLLSRC_HIRC    //内部高速RC振荡器
    u32PllFreq:
        需要配置的PLL时钟频率

  返回值: 配置成功返回的PLL时钟
  1 {
  2     uint32_t u32PllSrcClk, u32NR, u32NF, u32NO, u32CLK_SRC;
  3     uint32_t u32Tmp, u32Tmp2, u32Tmp3, u32Min, u32MinNF, u32MinNR;
  4 
  5     /* Disable PLL first to avoid unstable when setting PLL */
  6     CLK_DisablePLL();
  7 
  8     /* PLL source clock is from HXT */
  9     if(u32PllClkSrc == CLK_PLLCTL_PLLSRC_HXT)
 10     {
 11         /* Enable HXT clock */
 12         CLK->PWRCTL |= CLK_PWRCTL_HXTEN_Msk;
 13 
 14         /* Wait for HXT clock ready */
 15         CLK_WaitClockReady(CLK_STATUS_HXTSTB_Msk);
 16 
 17         /* Select PLL source clock from HXT */
 18         u32CLK_SRC = CLK_PLLCTL_PLLSRC_HXT;
 19         u32PllSrcClk = __HXT;
 20 
 21         /* u32NR start from 2 */
 22         u32NR = 2;
 23     }
 24 
 25     /* PLL source clock is from HIRC */
 26     else
 27     {
 28         /* Enable HIRC clock */
 29         CLK->PWRCTL |= CLK_PWRCTL_HIRCEN_Msk;
 30 
 31         /* Wait for HIRC clock ready */
 32         CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);
 33 
 34         /* Select PLL source clock from HIRC */
 35         u32CLK_SRC = CLK_PLLCTL_PLLSRC_HIRC;
 36         u32PllSrcClk = __HIRC;
 37 
 38         /* u32NR start from 4 when FIN = 22.1184MHz to avoid calculation overflow */
 39         u32NR = 4;
 40     }
 41 
 42     /* Select "NO" according to request frequency */
 43     if((u32PllFreq <= FREQ_500MHZ) && (u32PllFreq > FREQ_250MHZ))
 44     {
 45         u32NO = 0;
 46     }
 47     else if((u32PllFreq <= FREQ_250MHZ) && (u32PllFreq > FREQ_125MHZ))
 48     {
 49         u32NO = 1;
 50         u32PllFreq = u32PllFreq << 1;
 51     }
 52     else if((u32PllFreq <= FREQ_125MHZ) && (u32PllFreq >= FREQ_50MHZ))
 53     {
 54         u32NO = 3;
 55         u32PllFreq = u32PllFreq << 2;
 56     }
 57     else
 58     {
 59         /* Wrong frequency request. Just return default setting. */
 60         goto lexit;
 61     }
 62 
 63     /* Find best solution */
 64     u32Min = (uint32_t) - 1;
 65     u32MinNR = 0;
 66     u32MinNF = 0;
 67     for(; u32NR <= 33; u32NR++)
 68     {
 69         u32Tmp = u32PllSrcClk / u32NR;
 70         if((u32Tmp > 1600000) && (u32Tmp < 16000000))
 71         {
 72             for(u32NF = 2; u32NF <= 513; u32NF++)
 73             {
 74                 u32Tmp2 = u32Tmp * u32NF;
 75                 if((u32Tmp2 >= 200000000) && (u32Tmp2 <= 500000000))
 76                 {
 77                     u32Tmp3 = (u32Tmp2 > u32PllFreq) ? u32Tmp2 - u32PllFreq : u32PllFreq - u32Tmp2;
 78                     if(u32Tmp3 < u32Min)
 79                     {
 80                         u32Min = u32Tmp3;
 81                         u32MinNR = u32NR;
 82                         u32MinNF = u32NF;
 83 
 84                         /* Break when get good results */
 85                         if(u32Min == 0)
 86                             break;
 87                     }
 88                 }
 89             }
 90         }
 91     }
 92 
 93     /* Enable and apply new PLL setting. */
 94     CLK->PLLCTL = u32CLK_SRC | (u32NO << 14) | ((u32MinNR - 2) << 9) | (u32MinNF - 2);
 95 
 96     /* Wait for PLL clock stable */
 97     CLK_WaitClockReady(CLK_STATUS_PLLSTB_Msk);
 98 
 99     /* Return actual PLL output clock frequency */
100     return u32PllSrcClk / ((u32NO + 1) * u32MinNR) * u32MinNF;
101 
102 lexit:
103 
104     /* Apply default PLL setting and return */
105     if(u32PllClkSrc == CLK_PLLCTL_PLLSRC_HXT)
106         CLK->PLLCTL = CLK_PLLCTL_72MHz_HXT; /* 72MHz */
107     else
108         CLK->PLLCTL = CLK_PLLCTL_72MHz_HIRC; /* 71.8848MHz */
109 
110     /* Wait for PLL clock stable */
111     CLK_WaitClockReady(CLK_STATUS_PLLSTB_Msk);
112 
113     return CLK_GetPLLClockFreq();
114 
115 }
uint32_t CLK_EnablePLL(uint32_t u32PllClkSrc, uint32_t u32PllFreq)

  (5)配置时钟源选择控制寄存器 0 (CLK->CLKSEL0), 配置HCLK时钟源/SysTick时钟源/PCLK0(APB0)时钟源/PCLK1(APB1)时钟源;

    配置时钟分频数寄存器 0 (CLK->CLKDIV0), 配置HCLK时钟除频数

    CLK->CLKSEL0寄存器有效位:

      [2:0]HCLK时钟源选择(写保护)  注意: 配置分频器之前需要将时钟源切换位内部高速RC振荡器

        000 = 时钟源为 HXT

        001 = 时钟源为 LXT

        010 = 时钟源为 PLL

        011 = 时钟源为 LIRC

        111 = 时钟源为 HIRC

      [5:3]Cortex® -M4 SysTick 时钟源选择(写保护)

        000 = 时钟源为HXT

        001 = 时钟源为LXT

        010 = 时钟源为HXT/2

        011 = 时钟源为HCLK/2

        111 = 时钟源为HIRC/2

      [6]PCLK0 时钟源选择(写保护)

        0 = APB0 总线时钟源为 HCLK.

        1 = APB0 总线时钟源为 HCLK/2.

      [7]PCLK1 时钟源选择(写保护)

        0 = APB1 总线时钟源为HCLK.

        1 = APB1 总线时钟源为 HCLK/2.

 

    CLK->CLKDIV0寄存器有效位:

      [23:16]EADC时钟除频数

      [11:8]  UART时钟除频数

      [7:4]    USB时钟除频数

      [3:0]    HCLK时钟除频数

  库函数:

 1 //u32Hclk: HCLK频率25 MHz ~ 72 MHz
 2 //返回值:配置成功的HCLK频率
 3 uint32_t CLK_SetCoreClock(uint32_t u32Hclk)//时钟源默认选择PLL; 需要配置时钟源则使用下一个函数,手动配置HCLK
 4 {
 5     uint32_t u32HIRCSTB;
 6 
 7     /* Read HIRC clock source stable flag */
 8     u32HIRCSTB = CLK->STATUS & CLK_STATUS_HIRCSTB_Msk;
 9 
10     /* The range of u32Hclk is 25 MHz ~ 72 MHz */
11     if(u32Hclk > FREQ_72MHZ)
12         u32Hclk = FREQ_72MHZ;
13     if(u32Hclk < FREQ_25MHZ)
14         u32Hclk = FREQ_25MHZ;
15 
16     /* Switch HCLK clock source to HIRC clock for safe */
17     CLK->PWRCTL |= CLK_PWRCTL_HIRCEN_Msk;
18     CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);
19     CLK->CLKSEL0 |= CLK_CLKSEL0_HCLKSEL_Msk;
20     CLK->CLKDIV0 &= (~CLK_CLKDIV0_HCLKDIV_Msk);
21 
22     /* Configure PLL setting if HXT clock is stable */
23     if(CLK->STATUS & CLK_STATUS_HXTSTB_Msk)
24         u32Hclk = CLK_EnablePLL(CLK_PLLCTL_PLLSRC_HXT, (u32Hclk << 1));
25 
26     /* Configure PLL setting if HXT clock is not stable */
27     else
28     {
29         u32Hclk = CLK_EnablePLL(CLK_PLLCTL_PLLSRC_HIRC, (u32Hclk << 1));
30 
31         /* Read HIRC clock source stable flag */
32         u32HIRCSTB = CLK->STATUS & CLK_STATUS_HIRCSTB_Msk;
33     }
34 
35     /* Select HCLK clock source to PLL,
36        Select HCLK clock source divider as 2
37        and update system core clock
38     */
39     CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_PLL, CLK_CLKDIV0_HCLK(2));
40 
41     /* Disable HIRC if HIRC is disabled before setting core clock */
42     if(u32HIRCSTB == 0)
43         CLK->PWRCTL &= ~CLK_PWRCTL_HIRCEN_Msk;
44 
45     /* Return actually HCLK frequency is PLL frequency divide 2 */
46     return u32Hclk >> 1;
47 }
48 //u32ClkSrc : HCLK时钟源
49 //u32ClkDiv : 时钟分频系数
50 void CLK_SetHCLK(uint32_t u32ClkSrc, uint32_t u32ClkDiv)
51 {
52     uint32_t u32HIRCSTB;
53 
54     /* Read HIRC clock source stable flag */
55     u32HIRCSTB = CLK->STATUS & CLK_STATUS_HIRCSTB_Msk;
56 
57     /* Switch to HIRC for Safe. Avoid HCLK too high when applying new divider. */
58     CLK->PWRCTL |= CLK_PWRCTL_HIRCEN_Msk;
59     CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);
60     CLK->CLKSEL0 = (CLK->CLKSEL0 & (~CLK_CLKSEL0_HCLKSEL_Msk)) | CLK_CLKSEL0_HCLKSEL_HIRC;
61 
62     /* Apply new Divider */
63     CLK->CLKDIV0 = (CLK->CLKDIV0 & (~CLK_CLKDIV0_HCLKDIV_Msk)) | u32ClkDiv;
64 
65     /* Switch HCLK to new HCLK source */
66     CLK->CLKSEL0 = (CLK->CLKSEL0 & (~CLK_CLKSEL0_HCLKSEL_Msk)) | u32ClkSrc;
67 
68     /* Update System Core Clock */
69     SystemCoreClockUpdate();
70 
71     /* Disable HIRC if HIRC is disabled before switching HCLK source */
72     if(u32HIRCSTB == 0)
73         CLK->PWRCTL &= ~CLK_PWRCTL_HIRCEN_Msk;
74 }
HCLK配置函数

  注: 配置 APB0 和 APB1 的时钟频率貌似需要手动配置, 库函数中并没有发现相关函数

CLK->CLKSEL0 = CLK->CLKSEL0 & ~CLK_CLKSEL0_PCLK0SEL_Msk;    //置0, APB0总线时钟源为HCLK
CLK->CLKSEL0 = CLK->CLKSEL0 | CLK_CLKSEL0_PCLK1SEL_Msk;     //置1, APB1总线时钟源为HCLK/2

  (6)更新/计算系统内核时钟频率

  库函数:

void SystemCoreClockUpdate(void)             /* Get Core Clock Frequency      */
{
#if 1
    uint32_t u32Freq, u32ClkSrc;
    uint32_t u32HclkDiv;

    /* Update PLL Clock */
    PllClock = CLK_GetPLLClockFreq();

    u32ClkSrc = CLK->CLKSEL0 & CLK_CLKSEL0_HCLKSEL_Msk;

    if(u32ClkSrc == CLK_CLKSEL0_HCLKSEL_PLL)
    {
        /* Use PLL clock */
        u32Freq = PllClock;
    }
    else
    {
        /* Use the clock sources directly */
        u32Freq = gau32ClkSrcTbl[u32ClkSrc];
    }

    u32HclkDiv = (CLK->CLKDIV0 & CLK_CLKDIV0_HCLKDIV_Msk) + 1;

    /* Update System Core Clock */
    SystemCoreClock = u32Freq / u32HclkDiv;


    //if(SystemCoreClock == 0)
    //    __BKPT(0);

    CyclesPerUs = (SystemCoreClock + 500000) / 1000000;
#endif
}

  (7)重新上锁保护寄存器

    向地址0x4000_0100(SYS->REGLCTL)写入"00h"

   库函数:

__STATIC_INLINE void SYS_LockReg(void)
{
    SYS->REGLCTL = 0;
}

 

 

 

 

 

 

 

 

               

 

posted on 2021-07-01 15:27  小强Craig  阅读(1171)  评论(0)    收藏  举报

导航