其实这部分功能我大致是会的,之前工作中在类似芯片上也实现过。今天想整理一下主要是因为我觉得我想整理一下学习的思路,可以作为其他人的一个参考。
我觉得学习与开发在实际的操作上应该是有较大区别的。开发的目的很纯粹,就是让功能可用。但是,学习的目的与之不同,应该是以点窥面,能够从系统观、大局观上掌握相应对象的全部功能并且探索使用场景。
如果是进行系统时钟,或者说是整个FMPLL的功能配置,或许开发中只是能够实现其运行即可,但是学习我得找到一种方法论。
以下是MCU手册中的一个机理图:

我们需要的是最后的输出,而向前找,其实是VCO的输出。而这个是有一个范围要求的,因此我们需要在这个约束条件下进行我们相应功能的探索。
而这个设计又有一个关键的点,其实在于寄存器的取值约束。
综合上面的两个条件约束,结合手册中给出的公式,其实这个配置就非常容易了。

既然,增强模式是手册中推荐的,自然应该采用增强模式。而这个模式下,其实还有一个调制的功能可以用。针对系统时钟而言,最简单的实现还是上述两个约束条件加上三组公式中的后两组。
核心设计代码如下:
uint8_t fmpll_init(uint8_t ref_clock, uint8_t system_clk)
{
uint8_t emfd = 0U; /* 32~96 */
uint8_t eprediv = 0U; /* 0-14 */
uint8_t erfd = 0U; /* 0-3 */
float f_sys = 0.0F;
float f_vco = 0.0F;
uint8_t ret = FMPLL_SET_FAILED;
/* check for f_vco, this should be 256-512 */
for (emfd = 32; emfd <= 96; emfd++)
{
for (eprediv = 0; eprediv <= 14; eprediv++)
{
for (erfd = 0; erfd <= 3; erfd++)
{
/* set for system clock */
f_sys = ref_clock * emfd / ((eprediv + 1) * pow(2, erfd + 1));
if(fabs(f_sys - system_clk) < 0.00001F)
{
f_vco = f_sys * pow(2, erfd + 1);
if((f_vco >= 256) && (f_vco <= 512))
{
goto SET_SYSTEM_CLK;
}
}
}
}
}
ret = FMPLL_SET_FAILED;
SET_SYSTEM_CLK:
FMPLL.ESYNCR1.B.CLKCFG = 0b001;
FMPLL.ESYNCR1.B.EMODE = 0b1;
FMPLL.ESYNCR1.B.EPREDIV = eprediv;
FMPLL.ESYNCR1.B.EMFD = emfd;
FMPLL.ESYNCR2.B.ERFD = erfd;
SIU.SYSDIV.B.BYPASS = 1;
FMPLL.ESYNCR1.B.CLKCFG = 7;
while(FMPLL.SYNSR.B.LOCK == 0)
{
/* wait here*/
}
/* clear loss of lock flag */
FMPLL.SYNSR.B.LOLF = 1;
/* Normal mode with crystal reference */
FMPLL.ESYNCR1.B.CLKCFG = 0b111;
return ret;
}
可以通过PIT的中断看一下准确度。为了测试,我加了一个LED闪烁,简单秒表统计了一下。
40M:PASS
50M:PASS
60M:PASS
70M:PASS
80M:PASS
有意思的是,手册中描述了这个芯片的最高时钟是80M,但是实际的使用中我使用了120M居然依然可以正常工作,而且计时准确。
















