新唐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 }
(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 }
注: 配置 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; }
浙公网安备 33010602011771号