文章目录
- 华大HC32f460启动分析和时钟配置
- 声明:个人因学习而写的心得,因为能力有限,可能会有错误
- 软件环境
- 1. 启动过程
- 1.1 第一步:读取地址0x400上的内容
- 1.2 第二步:复位和系统时钟配置
- 1.3 第三步:进入用户程序main函数
- 2. 用户时钟配置分析
软件环境
- 系统:win10
- sdk版本:hc32f460_ddl_Rev2.0.0
- 数据手册版本:HC32F460系列用户手册 Rev1.21.pdf
- ide:keil5
- 开发板:官方评估板(EVB-HC32F460)
- 时间:2021.7.1
1. 启动过程
1.1 第一步:读取地址0x400上的内容
数据手册中写道如下:
我们要注意两个地方:
- 复位解除后执行,这就说明,这个读flash的0x400地址是要优于程序的,也就是要在程序启动前执行。
- 由硬件电路读取,这就说明,这个读取的位置无法人为控制,就是0x400,我们只能在程序下载的时候将信息写道0x400开始的位置。
这个0x400配置了很有用的东西,其中就有看门狗,其实stm32的看门狗一直有一个缺陷,那就是若在芯片启动到main函数执行这个时间发生了跑飞,那么程序就没办法重启,就死机了,而hc32避免了这个问题,可以在复位解除的时候直接配上看门狗,提高了系统的稳定性。至于其他功能,请自行查阅用户手册。
1.2 第二步:复位和系统时钟配置
这段代码在汇编中执行,也就是.s文件,这个与传统cortex-m的芯片一样,都是执行reset_handler,配置系统时钟systeninit,在执行__main,最后跳到用户函数main,因为和传统stm32的一样,这个不展开细讨论。
- 要注意这个配置系统时钟systeninit往往使用的是芯片内部的高速时钟,这个配置系统时钟函数由sdk提供,不建议直接在这一步修改为外部时钟(不要随意修改sdk的东西),所以等到执行用户程序的时候需要重新配置到外部高速时钟。
- 同时要注意在执行配置系统时钟systeninit函数,以及之前这段时间,由于没有时钟配置,所以使用的是内部高速时钟直连的方式驱动的,所以那段程序执行是很慢的。
- 在这个.s文件,当看到那些中断号的时候可能会很怪异,当从stm32过来的人都会有错觉,把中断号写成序号IRQ001_Handler、002的是为了啥,这是因为华大的中断是很灵活的,需要在程序配置中断的时候为某个中断事件绑定一个中断号还有一个回调函数,这是为了解决入中断在判断的问题:如在stm32中,当我们同时开启串口接收中断和发送中断,这时候两个中断发生会进入一个中断函数,然后在这个中断函数里面加if来判断串口究竟是的那个中断发生了,而华大可以将每个事件都绑一个中断号,这样串口接收中断,发送中断就会进入不一样的中断函数。
1.3 第三步:进入用户程序main函数
进入main函数,即进入了用户自己的程序,这个时候首先要重新配置时钟,因为内部高速时钟往往不准,易受干扰,配置函数需要对照mcu的时钟系统框图来书写,感谢的是官网提供的官方评估板(EVB-HC32F460)软件包中写了一个时钟配置函数void BSP_CLK_Init(void),对照现成的改,就容易的多了。下面我们就对照着这个函数和时钟系统框图来分析一下。
2. 用户时钟配置分析
在官网提供的官方评估板(EVB-HC32F460)软件包中,ev_hc32f460_lqfp100_v2.c 文件里提供了一个利用外部高速晶振配置到200MHZ的方法函数,下面我们就对照分析一下配置的过程,配置时钟程序如下:
//华大初始化时钟为200MHZ
void BSP_CLK_Init(void)
{
stc_clk_sysclk_cfg_t stcSysClkCfg;
stc_clk_xtal_cfg_t stcXtalCfg;
stc_clk_mpll_cfg_t stcMpllCfg;
stc_sram_config_t stcSramConfig;
MEM_ZERO_STRUCT(stcSysClkCfg);
MEM_ZERO_STRUCT(stcXtalCfg);
MEM_ZERO_STRUCT(stcMpllCfg);
MEM_ZERO_STRUCT(stcSramConfig);
/* Set bus clk div. */
stcSysClkCfg.enHclkDiv = ClkSysclkDiv1; //hclk的时钟来之分频器的1分频
stcSysClkCfg.enExclkDiv = ClkSysclkDiv2; //exclk的时钟来之分频器的2分频
stcSysClkCfg.enPclk0Div = ClkSysclkDiv1; //Pclk0的时钟来自分频器的1分频
stcSysClkCfg.enPclk1Div = ClkSysclkDiv2; //Pclk1的时钟来自分频器的2分频
stcSysClkCfg.enPclk2Div = ClkSysclkDiv4; //Pclk2的时钟来自分频器的4分频
stcSysClkCfg.enPclk3Div = ClkSysclkDiv4; //Pclk3的时钟来自分频器的4分频
stcSysClkCfg.enPclk4Div = ClkSysclkDiv2; //Pclk4的时钟来自分频器的2分频
CLK_SysClkConfig(&stcSysClkCfg);
/* Config Xtal and Enable Xtal */
stcXtalCfg.enMode = ClkXtalModeOsc;
stcXtalCfg.enDrv = ClkXtalLowDrv;
stcXtalCfg.enFastStartup = Enable;
CLK_XtalConfig(&stcXtalCfg);
CLK_XtalCmd(Enable);
/* sram init include read/write wait cycle setting */
stcSramConfig.u8SramIdx = Sram12Idx | Sram3Idx | SramHsIdx | SramRetIdx;
stcSramConfig.enSramRC = SramCycle2;
stcSramConfig.enSramWC = SramCycle2;
SRAM_Init(&stcSramConfig);
/* flash read wait cycle setting */
EFM_Unlock();
EFM_SetLatency(EFM_LATENCY_5);
EFM_Lock();
/* MPLL config (XTAL / pllmDiv * plln / PllpDiv = 200M). */
stcMpllCfg.pllmDiv = 1ul;
stcMpllCfg.plln = 50ul;
stcMpllCfg.PllpDiv = 2ul;
stcMpllCfg.PllqDiv = 2ul;
stcMpllCfg.PllrDiv = 2ul;
CLK_SetPllSource(ClkPllSrcXTAL);//开启外部高速晶振
CLK_MpllConfig(&stcMpllCfg); //配置MPLL
/* Enable MPLL. */
CLK_MpllCmd(Enable);//开启MPLL
/* Wait MPLL ready. */
while(Set != CLK_GetFlagStatus(ClkFlagMPLLRdy))
{
;
}
/* Switch driver ability */
PWC_HS2HP();
/* Switch system clock source to MPLL. */
CLK_SetSysClkSource(CLKSysSrcMPLL);
}
首先我们大家要知道,时钟的配置原则应该是从后向前配置,即从外设向晶振方向配置。那下面我们对照时钟系统框图来分析上面的代码。
上面函数对照的时钟系统框图配置方向为下图: