华大HC32f460启动分析和时钟配置
目录
华大HC32f460启动分析和时钟配置
软件环境
1. 启动过程
1.1 第一步:读取地址0x400上的内容
1.2 第二步:复位和系统时钟配置
1.3 第三步:进入用户程序main函数
2. 用户时钟配置分析
3. 调试问题:
?3.1 error: #223-D: function "BSP_CLK_Init" declared implicitly
3.2 error: ?#20: identifier "stc_clk_sysclk_cfg_t" is undefined
3.3 error: ?#254: type name is not allowed
3.5 error: ?#268: declaration may not appear after executable statement in block
3.6 编译通过;
软件环境
系统:win10 sdk版本:hc32f460_ddl_Rev2.0.0 数据手册版本:HC32F460系列用户手册 Rev1.21.pdf ide:keil5 开发板:https://item.taobao.com/item.htm?spm=a2oq0.12575281.0.0.50111debhMzSwz&ft=t&id=660179379335https://item.taobao.com/item.htm?spm=a2oq0.12575281.0.0.50111debhMzSwz&ft=t&id=660179379335 时间:2022
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);
}
首先我们大家要知道,时钟的配置原则应该是从后向前配置,即从外设向晶振方向配置。那下面我们对照时钟系统框图来分析上面的代码。
上面函数对照的时钟系统框图配置方向为下图:
以上部分参考:?
华大MCU(二):HC32f460启动过程和时钟分析_Zhichao_Zhang的博客-CSDN博客_华大单片机时钟
3. 在GPIO例程上修改调试:
?在例程?手把手教程3: F460建立一个GPIO点灯例程? 的基础上, 添加clk设置的部分,
clk开关对应的是哪些功能模块,参考?F460 CLK配置功能模块说明
调试遇到了一些问题,记录如下。
把BSP_CLK_Init添加过来之后,报错无数:
一边编译,一边修改;
?3.1 error: #223-D: function "BSP_CLK_Init" declared implicitly
F460:
在main.c中初始化了BSP_CLK_Init函数(CSDN),在主函数中调用的时候,报了这个错误,提示声明不合法,
参考下面文章,在main.c开头进行了声明调用extern void BSP_CLK_Init(void);
之后报错消失;
参考:KEIL MDK中 warning: #223-D: function "xxx" declared implicitly 解决方法_Grant的工作笔记-CSDN博客
这里其实有点奇怪,BSP_CLK_Init并不是在另一个.c文件中声明,是在main.c文件中的,和main函数在同一个文件中,按说不应该出现这个问题;
先把程序搞通过,这里记录一下,后面再找原因;
3.2 error: ?#20: identifier "stc_clk_sysclk_cfg_t" is undefined
BSP_CLK_Init的错误没有了,还有12个错误:
..\App\src\main.c(92): error: #20: identifier "stc_clk_sysclk_cfg_t" is undefined
stc_clk_sysclk_cfg_t stcSysClkCfg;
..\App\src\main.c(93): error: #20: identifier "stc_clk_xtal_cfg_t" is undefined
stc_clk_xtal_cfg_t stcXtalCfg;
..\App\src\main.c(94): error: #20: identifier "stc_clk_mpll_cfg_t" is undefined
stc_clk_mpll_cfg_t stcMpllCfg;
..\App\src\main.c(103): error: #20: identifier "ClkSysclkDiv1" is undefined
stcSysClkCfg.enHclkDiv = ClkSysclkDiv1;//hclk的时钟来之分频器的1分频
..\App\src\main.c(104): error: #20: identifier "ClkSysclkDiv2" is undefined
stcSysClkCfg.enExclkDiv = ClkSysclkDiv2;//exclk的时钟来之分频器的2分频
..\App\src\main.c(107): error: #20: identifier "ClkSysclkDiv4" is undefined
stcSysClkCfg.enPclk2Div = ClkSysclkDiv4;//Pclk2的时钟来自分频器的4分频
..\App\src\main.c(110): warning: #223-D: function "CLK_SysClkConfig" declared implicitly
CLK_SysClkConfig(&stcSysClkCfg);
..\App\src\main.c(113): error: #20: identifier "ClkXtalModeOsc" is undefined
stcXtalCfg.enMode = ClkXtalModeOsc;
..\App\src\main.c(114): error: #20: identifier "ClkXtalLowDrv" is undefined
stcXtalCfg.enDrv = ClkXtalLowDrv;
..\App\src\main.c(116): warning: #223-D: function "CLK_XtalConfig" declared implicitly
CLK_XtalConfig(&stcXtalCfg);
..\App\src\main.c(117): warning: #223-D: function "CLK_XtalCmd" declared implicitly
CLK_XtalCmd(Enable);
..\App\src\main.c(126): warning: #223-D: function "EFM_Unlock" declared implicitly
EFM_Unlock();
..\App\src\main.c(127): warning: #223-D: function "EFM_SetLatency" declared implicitly
EFM_SetLatency(EFM_LATENCY_5);
..\App\src\main.c(127): error: #20: identifier "EFM_LATENCY_5" is undefined
EFM_SetLatency(EFM_LATENCY_5);
..\App\src\main.c(128): warning: #223-D: function "EFM_Lock" declared implicitly
EFM_Lock();
..\App\src\main.c(136): warning: #223-D: function "CLK_SetPllSource" declared implicitly
CLK_SetPllSource(ClkPllSrcXTAL);//开启外部高速晶振
..\App\src\main.c(136): error: #20: identifier "ClkPllSrcXTAL" is undefined
CLK_SetPllSource(ClkPllSrcXTAL);//开启外部高速晶振
..\App\src\main.c(137): warning: #223-D: function "CLK_MpllConfig" declared implicitly
CLK_MpllConfig(&stcMpllCfg); //配置MPLL
..\App\src\main.c(140): warning: #223-D: function "CLK_MpllCmd" declared implicitly
CLK_MpllCmd(Enable); //开启MPLL
..\App\src\main.c(142): warning: #223-D: function "CLK_GetFlagStatus" declared implicitly
while(Set != CLK_GetFlagStatus(ClkFlagMPLLRdy))
..\App\src\main.c(142): error: #20: identifier "ClkFlagMPLLRdy" is undefined
while(Set != CLK_GetFlagStatus(ClkFlagMPLLRdy))
..\App\src\main.c(147): warning: #223-D: function "PWC_HS2HP" declared implicitly
PWC_HS2HP();
..\App\src\main.c(149): warning: #223-D: function "CLK_SetSysClkSource" declared implicitly
CLK_SetSysClkSource(CLKSysSrcMPLL);
..\App\src\main.c(149): error: #20: identifier "CLKSysSrcMPLL" is undefined
CLK_SetSysClkSource(CLKSysSrcMPLL);
..\App\src\main.c: 12 warnings, 12 errors
分析对比了一下,发现ddl.cfg.h里相关功能模块的clk的开关没有打开,分别都打开:
#define DDL_ICG_ENABLE (DDL_ON) //打开
#define DDL_UTILITY_ENABLE (DDL_ON) //打开
#define DDL_PRINT_ENABLE (DDL_OFF)
#define DDL_ADC_ENABLE (DDL_ON) //打开
#define DDL_AES_ENABLE (DDL_OFF)
#define DDL_CAN_ENABLE (DDL_OFF)
#define DDL_CLK_ENABLE (DDL_ON) //打开
#define DDL_CMP_ENABLE (DDL_OFF)
#define DDL_CRC_ENABLE (DDL_OFF)
#define DDL_DCU_ENABLE (DDL_OFF)
#define DDL_DMAC_ENABLE (DDL_OFF)
#define DDL_EFM_ENABLE (DDL_ON) //打开
#define DDL_EMB_ENABLE (DDL_OFF)
#define DDL_EVENT_PORT_ENABLE (DDL_OFF)
#define DDL_EXINT_NMI_SWI_ENABLE (DDL_ON) //打开
#define DDL_GPIO_ENABLE (DDL_ON) //打开
#define DDL_HASH_ENABLE (DDL_OFF)
#define DDL_I2C_ENABLE (DDL_ON) //打开
#define DDL_I2S_ENABLE (DDL_OFF)
#define DDL_INTERRUPTS_ENABLE (DDL_ON) //打开
#define DDL_INTERRUPTS_SHARE_ENABLE (DDL_ON) //打开
#define DDL_KEYSCAN_ENABLE (DDL_ON) //打开
#define DDL_MPU_ENABLE (DDL_ON) //打开
#define DDL_OTS_ENABLE (DDL_OFF)
#define DDL_PWC_ENABLE (DDL_ON) //打开
#define DDL_QSPI_ENABLE (DDL_OFF)
#define DDL_RMU_ENABLE (DDL_OFF)
#define DDL_RTC_ENABLE (DDL_OFF)
#define DDL_SDIOC_ENABLE (DDL_OFF)
#define DDL_SPI_ENABLE (DDL_OFF)
#define DDL_SRAM_ENABLE (DDL_ON) //打开
#define DDL_SWDT_ENABLE (DDL_OFF)
#define DDL_TIMER0_ENABLE (DDL_OFF)
#define DDL_TIMER4_CNT_ENABLE (DDL_ON) //打开
#define DDL_TIMER4_EMB_ENABLE (DDL_OFF)
#define DDL_TIMER4_OCO_ENABLE (DDL_OFF)
#define DDL_TIMER4_PWM_ENABLE (DDL_OFF)
#define DDL_TIMER4_SEVT_ENABLE (DDL_OFF)
#define DDL_TIMER6_ENABLE (DDL_OFF)
#define DDL_TIMERA_ENABLE (DDL_ON) //打开
#define DDL_TRNG_ENABLE (DDL_OFF)
#define DDL_USART_ENABLE (DDL_ON) //打开
#define DDL_USBFS_ENABLE (DDL_OFF)
#define DDL_WDT_ENABLE (DDL_ON) //打开
将相关的库文件加入到工程里:
?
并打开相应模块的clk之后,BSP_CLK_Init中的报错都消失了:
3.3 error: ?#254: type name is not allowed
在hc32f460_pwc.c(305)中,变量声明在for循环中:
_RAM_FUNC void PWC_EnterPowerDownMd(void)
{
ENABLE_PVD_REG_WRITE();
/* Reset PVD1IRS & PVD2IRS */
M4_SYSREG->PWR_PVDCR1 &= 0xddu;
DISABLE_PVD_REG_WRITE();
ENABLE_PWR_REG_WRITE();
M4_SYSREG->PWR_STPMCR_f.STOP = 1u;
__disable_irq();
M4_SYSREG->PWR_PWRC0_f.PWDN = 1u;
for(uint8_t i = 0u; i < 10u; i++)
//for(i = 0u; i < 10u; i++)
{
__NOP();
}
__enable_irq();
DISABLE_PWR_REG_WRITE();
__WFI();
}
这个文件来自于库函数,应该是可以这样写的,估计哪里选项要修改一下,
为了快速搭建demo,先不找了,直接修改为:
__RAM_FUNC void PWC_EnterPowerDownMd(void)
{
uint8_t i = 0u;
ENABLE_PVD_REG_WRITE();
/* Reset PVD1IRS & PVD2IRS */
M4_SYSREG->PWR_PVDCR1 &= 0xddu;
DISABLE_PVD_REG_WRITE();
ENABLE_PWR_REG_WRITE();
M4_SYSREG->PWR_STPMCR_f.STOP = 1u;
__disable_irq();
M4_SYSREG->PWR_PWRC0_f.PWDN = 1u;
//for(uint8_t i = 0u; i < 10u; i++)
for(i = 0u; i < 10u; i++)
{
__NOP();
}
__enable_irq();
DISABLE_PWR_REG_WRITE();
__WFI();
}
3.5 error: ?#268: declaration may not appear after executable statement in block
在hc32f460_efm.c中,有这个函数
en_result_t EFM_OtpLock(uint32_t u32Addr)
{
DDL_ASSERT(IS_VALID_OTP_LOCK_ADDR(u32Addr));
uint16_t u16Timeout = 0u;
en_result_t enRet = Ok;
/* Enable program. */
EFM_ErasePgmCmd(Enable);
/* Set single program mode. */
M4_EFM->FWMC_f.PEMOD = EFM_MODE_SINGLEPROGRAM;
/* Lock the otp block. */
*(uint32_t*)u32Addr = 0ul;
while(1ul != M4_EFM->FSR_f.RDY)
{
u16Timeout++;
if(u16Timeout > 0x1000u)
{
enRet = ErrorTimeout;
}
}
EFM_ClearFlag(EFM_FLAG_EOP);
/* Set read only mode. */
M4_EFM->FWMC_f.PEMOD = EFM_MODE_READONLY;
EFM_ErasePgmCmd(Disable);
return enRet;
}
变量定义Ai执行语句之后,改为这样:
en_result_t EFM_OtpLock(uint32_t u32Addr)
{
//DDL_ASSERT(IS_VALID_OTP_LOCK_ADDR(u32Addr));
uint16_t u16Timeout = 0u;
en_result_t enRet = Ok;
DDL_ASSERT(IS_VALID_OTP_LOCK_ADDR(u32Addr));
/* Enable program. */
EFM_ErasePgmCmd(Enable);
/* Set single program mode. */
M4_EFM->FWMC_f.PEMOD = EFM_MODE_SINGLEPROGRAM;
/* Lock the otp block. */
*(uint32_t*)u32Addr = 0ul;
while(1ul != M4_EFM->FSR_f.RDY)
{
u16Timeout++;
if(u16Timeout > 0x1000u)
{
enRet = ErrorTimeout;
}
}
EFM_ClearFlag(EFM_FLAG_EOP);
/* Set read only mode. */
M4_EFM->FWMC_f.PEMOD = EFM_MODE_READONLY;
EFM_ErasePgmCmd(Disable);
return enRet;
}
3.6 编译通过;
在F460建立一个GPIO点灯例程的基础上修改完成,编译通过,下载成功,LED在欢快的闪烁!!!
|