linkedinstm32f4 时钟系统初始化是在system_stm32f4xx.c中的 systeminit()函数中完成的。 对于系统时钟关键寄存器设置主要是在 systeminit 函数中调用 setsysclock()函数来设置的。我们可以先看看 systeminit ()函数体:
void systeminit(void)
{
#if (__fpu_present == 1) && (__fpu_used == 1)
scb-》cpacr |= ((3ul 《《 10*2)|(3ul 《《 11*2));
#endif
rcc-》cr |= (uint32_t)0x00000001;
rcc-》cfgr = 0x00000000;
rcc-》cr &= (uint32_t)0xfef6ffff;
rcc-》pllcfgr = 0x24003010;
rcc-》cr &= (uint32_t)0xfffbffff;
rcc-》cir = 0x00000000;
#if defined (data_in_extsram) || defined (data_in_extsdram)
systeminit_extmemctl();
#endif
setsysclock();
#ifdef vect_tab_sram
scb-》vtor = sram_base | vect_tab_offset;
#else
scb-》vtor = flash_base | vect_tab_offset;
#endif
}
systeminit函数开始先进行浮点运算单元设置,然后是复位pllcfgr,cfgr寄存器,同时通过设置 cr 寄存器的 hsi 时钟使能位来打开 hsi 时钟。默认情况下如果 cfgr 寄存器复位,那么是选择hsi作为系统时钟,这点大家可以查看rcc-》cfgr 寄存器的位描述最低2位可以得知,当低两位配置为 00的时候(复位之后),会选择 hsi振荡器为系统时钟。也就是说,调用 systeminit 函数之后,首先是选择 hsi 作为系统时钟。
在设置完相关寄存器后,接下来systeminit函数内部会调用 setsysclock函数。这个函数比较长,我们就把函数一些关键代码行截取出来给大家讲解一下。这里我们省略一些宏定义标识符值的判断而直接把针对stm32f407 比较重要的内容贴出来:
static void setsysclock(void)
{
__io uint32_t startupcounter = 0, hsestatus = 0;
rcc-》cr |= ((uint32_t)rcc_cr_hseon);
do
{
hsestatus = rcc-》cr & rcc_cr_hserdy;
startupcounter++;
} while((hsestatus == 0) && (startupcounter != hse_startup_timeout));
if ((rcc-》cr & rcc_cr_hserdy) != reset)
{
hsestatus = (uint32_t)0x01;
}
else
{
hsestatus = (uint32_t)0x00;
}
if (hsestatus == (uint32_t)0x01)
{
rcc-》apb1enr |= rcc_apb1enr_pwren;
pwr-》cr |= pwr_cr_vos;
rcc-》cfgr |= rcc_cfgr_hpre_div1;
rcc-》cfgr |= rcc_cfgr_ppre2_div2;
rcc-》cfgr |= rcc_cfgr_ppre1_div4;
rcc-》cfgr |= rcc_cfgr_ppre2_div1;
rcc-》cfgr |= rcc_cfgr_ppre1_div2;
rcc-》pllcfgr = pll_m | (pll_n 《《 6) | (((pll_p 》》 1) -1) 《《 16) |
(rcc_pllcfgr_pllsrc_hse) | (pll_q 《《 24);
rcc-》cr |= rcc_cr_pllon;
while((rcc-》cr & rcc_cr_pllrdy) == 0)
{
}
flash-》acr = flash_acr_prften | flash_acr_icen
|flash_acr_dcen |flash_acr_latency_5ws;
rcc-》cfgr &= (uint32_t)((uint32_t)~(rcc_cfgr_sw));
rcc-》cfgr |= rcc_cfgr_sw_pll;
while ((rcc-》cfgr & (uint32_t)rcc_cfgr_sws ) != rcc_cfgr_sws_pll);
{
}
}
else
{
}
}
这段代码的大致流程是这样的:先使能外部时钟 hse,等待 hse 稳定之后,配置ahb,apb1,apb2 时钟相关的分频因子,也就是相关外设的时钟。等待这些都配置完成之后,打开主pll时钟,然后设置主pll作为系统时钟 sysclk时钟源。如果hse 不能达到就绪状态(比如外部晶振不能稳定或者没有外部晶振),那么依然会是hsi作为系统时钟。
在这里要特别提出来,在设置主pll时钟的时候,会要设置一系列的分频系数和倍频系数参数。大家可以从setsysclock函数的这行代码看出:
rcc-》pllcfgr = pll_m | (pll_n 《《 6) | (((pll_p 》》 1) -1) 《《 16) |
(rcc_pllcfgr_pllsrc_hse) | (pll_q 《《 24);
这些参数是通过宏定义标识符的值来设置的。默认的配置在 system_stm32f4xx.c 文件开头的地方配置。对于我们开发板,我们的设置参数值如下:
#define pll_m 8
#define pll_q 7
#define pll_n 336
#define pll_p 2
所以我们的主pll时钟为:
pll=8mhz * n/ (m*p)=8mhz* 336 /(8*2) = 168mhz
在开发过程中,我们可以通过调整这些值来设置我们的系统时钟。
这里还有个特别需要注意的地方,就是我们还要同步修改 stm32f4xx.h 中宏定义标识符hse_value 的值为我们的外部时钟:
#if !defined (hse_value)
#define hse_value ((uint32_t)8000000)
#endif
这里默认固件库配置的是25000000,我们外部时钟为8mhz,所以我们根据我们硬件情况修改为8000000即可。
讲到这里,大家对 systeminit 函数的流程会有个比较清晰的理解。那么 systeminit 函数是怎么被系统调用的呢?systeminit是整个设置系统时钟的入口函数。这个函数对于我们使用st提供的 stm32f4 固件库的话,会在系统启动之后先执行main函数,然后再接着执行systeminit函数实现系统相关时钟的设置。这个过程设置是在启动文件 startup_stm32f40_41xxx.s中间设置的,我们接下来看看启动文件中这段启动代码:
; reset handler
reset_handler proc
export reset_handler [weak]
import systeminit
import __main
ldr r0, =systeminit
blx r0
ldr r0, =__main
bx r0
endp
这段代码的作用是在系统复位之后引导进入main函数,同时在进入main函数之前,首先
要调用 systeminit系统初始化函数完成系统时钟等相关配置。
最后我们总结一下systeminit()函数中设置的系统时钟大小:
sysclk(系统时钟) =168mhz
ahb总线时钟(hclk=sysclk) =168mhz
apb1总线时钟(pclk1=sysclk/4) =42mhz
apb2总线时钟(pclk2=sysclk/2) =84mhz
pll主时钟 =168mhz
保障房成为主流,公租房管理系统助力保障性住房数字化管理!
周伯文:5G加持下,工业互联网任重道远
为什么处理器一样但是iqoo这么快?
光纤连接器的制作工艺步骤和注意事项
无线充电OLED照明将成为未来照明主流
基于LinkedInSTM32F4时钟系统初始化设置
基于CK6159A4设计的语音报数提示自动跳绳机电路板
配件制造商提前展示新iPhone外形 苹果今年又连发三款手机
AirPods带动无线耳机爆量成长 全年将破1亿组
高低温交变试验箱如何清理冷凝器水垢
深度 | 特朗普为何休战回到谈判桌?富士康告诉他一个残酷真相
IGBT的识别方法及应用电路
中科亿海微入选2023年江苏潜在独角兽企业
普渡科技推出两款全新餐饮机器人,让机器人"再一次普渡"餐饮行业
红外线测量光栅传感器的介绍与应用
CDMA2000分组域核心网移动IP技术
VR技术在医疗领域的应用与挑战
内存经常会出现那些故障?应该如何解决
利用微流控芯片制备海藻酸钠微球
快速学习PLC编程,其实很简单