在音频开发中,有可能两个外设输出不同采样率的需求,场景例子:SAI1 输出44.1K SAI2 输出48K
这时候只用 pll audio 是完全不够用的,所以必须在时钟树上再找到一个合适的时钟,能够轻松分频到常用的:11.2896M 12.288M 等等
时钟树如下:
也可以参考fsl_clock.h,每个外设可选择的时钟分支
/* SAI1 */
kCLOCK_SAI1_ClockRoot_MuxOscRc48MDiv2 = 0U, /*!< SAI1 mux from MuxOscRc48MDiv2. */
kCLOCK_SAI1_ClockRoot_MuxOsc24MOut = 1U, /*!< SAI1 mux from MuxOsc24MOut. */
kCLOCK_SAI1_ClockRoot_MuxOscRc400M = 2U, /*!< SAI1 mux from MuxOscRc400M. */
kCLOCK_SAI1_ClockRoot_MuxOscRc16M = 3U, /*!< SAI1 mux from MuxOscRc16M. */
kCLOCK_SAI1_ClockRoot_MuxAudioPllOut = 4U, /*!< SAI1 mux from MuxAudioPllOut. */
kCLOCK_SAI1_ClockRoot_MuxSysPll3Pfd2 = 5U, /*!< SAI1 mux from MuxSysPll3Pfd2. */
kCLOCK_SAI1_ClockRoot_MuxSysPll1Div5 = 6U, /*!< SAI1 mux from MuxSysPll1Div5. */
kCLOCK_SAI1_ClockRoot_MuxSysPll2Pfd3 = 7U, /*!< SAI1 mux from MuxSysPll2Pfd3. */
/* SAI2 */
kCLOCK_SAI2_ClockRoot_MuxOscRc48MDiv2 = 0U, /*!< SAI2 mux from MuxOscRc48MDiv2. */
kCLOCK_SAI2_ClockRoot_MuxOsc24MOut = 1U, /*!< SAI2 mux from MuxOsc24MOut. */
kCLOCK_SAI2_ClockRoot_MuxOscRc400M = 2U, /*!< SAI2 mux from MuxOscRc400M. */
kCLOCK_SAI2_ClockRoot_MuxOscRc16M = 3U, /*!< SAI2 mux from MuxOscRc16M. */
kCLOCK_SAI2_ClockRoot_MuxAudioPllOut = 4U, /*!< SAI2 mux from MuxAudioPllOut. */
kCLOCK_SAI2_ClockRoot_MuxSysPll3Pfd2 = 5U, /*!< SAI2 mux from MuxSysPll3Pfd2. */
kCLOCK_SAI2_ClockRoot_MuxSysPll1Div5 = 6U, /*!< SAI2 mux from MuxSysPll1Div5. */
kCLOCK_SAI2_ClockRoot_MuxSysPll2Pfd3 = 7U, /*!< SAI2 mux from MuxSysPll2Pfd3. */
/* SAI3 */
kCLOCK_SAI3_ClockRoot_MuxOscRc48MDiv2 = 0U, /*!< SAI3 mux from MuxOscRc48MDiv2. */
kCLOCK_SAI3_ClockRoot_MuxOsc24MOut = 1U, /*!< SAI3 mux from MuxOsc24MOut. */
kCLOCK_SAI3_ClockRoot_MuxOscRc400M = 2U, /*!< SAI3 mux from MuxOscRc400M. */
kCLOCK_SAI3_ClockRoot_MuxOscRc16M = 3U, /*!< SAI3 mux from MuxOscRc16M. */
kCLOCK_SAI3_ClockRoot_MuxAudioPllOut = 4U, /*!< SAI3 mux from MuxAudioPllOut. */
kCLOCK_SAI3_ClockRoot_MuxSysPll3Pfd2 = 5U, /*!< SAI3 mux from MuxSysPll3Pfd2. */
kCLOCK_SAI3_ClockRoot_MuxSysPll1Div5 = 6U, /*!< SAI3 mux from MuxSysPll1Div5. */
kCLOCK_SAI3_ClockRoot_MuxSysPll2Pfd3 = 7U, /*!< SAI3 mux from MuxSysPll2Pfd3. */
/* SAI4 */
kCLOCK_SAI4_ClockRoot_MuxOscRc48MDiv2 = 0U, /*!< SAI4 mux from MuxOscRc48MDiv2. */
kCLOCK_SAI4_ClockRoot_MuxOsc24MOut = 1U, /*!< SAI4 mux from MuxOsc24MOut. */
kCLOCK_SAI4_ClockRoot_MuxOscRc400M = 2U, /*!< SAI4 mux from MuxOscRc400M. */
kCLOCK_SAI4_ClockRoot_MuxOscRc16M = 3U, /*!< SAI4 mux from MuxOscRc16M. */
kCLOCK_SAI4_ClockRoot_MuxSysPll3Pfd3 = 4U, /*!< SAI4 mux from MuxSysPll3Pfd3. */
kCLOCK_SAI4_ClockRoot_MuxSysPll3Out = 5U, /*!< SAI4 mux from MuxSysPll3Out. */
kCLOCK_SAI4_ClockRoot_MuxAudioPllOut = 6U, /*!< SAI4 mux from MuxAudioPllOut. */
kCLOCK_SAI4_ClockRoot_MuxSysPll1Div5 = 7U, /*!< SAI4 mux from MuxSysPll1Div5. */
笔者查阅资料发现,能够灵活配置时钟的只有两条合适,一个是:MuxAudioPllOut MuxSysPll1Div5
这里我们重点看看sdk,发现fsl_clock.c中,默认只有pll1 1G的配置:
void CLOCK_InitSysPll1(const clock_sys_pll1_config_t *config)
{
uint8_t div;
uint32_t numerator, denominator;
PMU_StaticEnablePllLdo(ANADIG_PMU);
/* bypass pll */
ANATOP_PllBypass(kAI_Itf_1g, true);
/* sw enable clock */
ANATOP_SysPll1SwEnClk(true);
denominator = 0x0FFFFFFF;
div = 41U;
numerator = 178956970UL;
if (config->ssEnable && (config->ss != NULL))
{
return;
}
/* configure pll */
ANATOP_PllConfigure(kAI_Itf_1g, div, numerator, 0U, denominator,
(config->ssEnable && (config->ss != NULL)) ? config->ss : NULL);
/* toggle hold ring off */
ANATOP_PllToggleHoldRingOff(kAI_Itf_1g, 225);
/* wait pll stable */
ANATOP_SysPll1WaitStable();
/* enabled clock */
ANATOP_PllEnableClk(kAI_Itf_1g, true);
/* ungate clock */
ANATOP_SysPll1Gate(false);
ANATOP_SysPll1Div2En(config->pllDiv2En);
ANATOP_SysPll1Div5En(config->pllDiv5En);
/* bypass pll */
ANATOP_PllBypass(kAI_Itf_1g, false);
}
其实我们只需要修改 denominator,div,numerator变量即可配置出需要的时钟,函数修改如下:
void CLOCK_InitSysPll1OtherFreq(const clock_sys_pll1_config_t *config,const clock_sys_pll1_gpc_config_t *config1)
{
PMU_StaticEnablePllLdo(ANADIG_PMU);
/* bypass pll */
ANATOP_PllBypass(kAI_Itf_1g, true);
/* sw enable clock */
ANATOP_SysPll1SwEnClk(true);
if (config->ssEnable && (config->ss != NULL))
{
return;
}
/* configure pll */
ANATOP_PllConfigure(kAI_Itf_1g, config1->loopDivider, config1->numerator, 0U, config1->denominator,
(config->ssEnable && (config->ss != NULL)) ? config->ss : NULL);
/* toggle hold ring off */
ANATOP_PllToggleHoldRingOff(kAI_Itf_1g, 225);
/* wait pll stable */
ANATOP_SysPll1WaitStable();
/* enabled clock */
ANATOP_PllEnableClk(kAI_Itf_1g, true);
/* ungate clock */
ANATOP_SysPll1Gate(false);
ANATOP_SysPll1Div2En(config->pllDiv2En);
ANATOP_SysPll1Div5En(config->pllDiv5En);
/* bypass pll */
ANATOP_PllBypass(kAI_Itf_1g, false);
}
好了我们来让两个外设输出各自的时钟吧!!!
pll audio
const clock_audio_pll_config_t Audio_11_2896MPllConfig =
{
/*
* Audio pll setting: Frequency = Fref*(DIV_SELECT + NUM/DENOM)
= 24*(30 + 66/625)
* = 722.5344Mhz (11.2896 * 64)
*/
.loopDivider = 30, /* PLL loop divider. Valid range for DIV_SELECT divider value: 27~54. */
.postDivider = 1, /* Divider after the PLL, should only be 1, 2, 4, 8, 16. */
.numerator = 66, /* 30 bit numerator of fractional loop divider. */
.denominator = 625, /* 30 bit denominator of fractional loop divider */
};
const clock_audio_pll_config_t Audio_12_288MPllConfig =
{
/*
* Audio pll setting: Frequency = Fref*(DIV_SELECT + NUM/DENOM)
= 24*(32 + 87/125)
* = 786.432Mhz (12.288 * 64)
*/
.loopDivider = 32, /* PLL loop divider. Valid range for DIV_SELECT divider value: 27~54. */
.postDivider = 1, /* Divider after the PLL, should only be 1, 2, 4, 8, 16. */
.numerator = 96, /* 30 bit numerator of fractional loop divider. */
.denominator = 125, /* 30 bit denominator of fractional loop divider */
// .numerator = 76, /* 30 bit numerator of fractional loop divider. */
// .denominator = 100, /* 30 bit denominator of fractional loop divider */
};
CLOCK_InitAudioPll(&Audio_11_2896MPllConfig);
CLOCK_SetRootClockMux(kCLOCK_Root_Sai1, kCLOCK_SAI1_ClockRoot_MuxAudioPllOut);
CLOCK_SetRootClockDiv(kCLOCK_Root_Sai1, 32);
pll1
const clock_sys_pll1_config_t sysPll1Set =
{
.pllDiv2En = false,
.pllDiv5En = true,
.ssEnable = false,
};
/*
* pll1 setting: Frequency = Fref*(DIV_SELECT + NUM/DENOM)
= 24*(37 + 632/1000)
* = 903.168M -> /5 = 180.63744M
*/
const clock_sys_pll1_gpc_config_t sysPll1Config =
{
.loopDivider = 37,
.numerator = 632,
.denominator = 1000,
};
CLOCK_InitSysPll1OtherFreq(&sysPll1Set,&sysPll1Config);
CLOCK_SetRootClockMux(kCLOCK_Root_Sai2, kCLOCK_SAI2_ClockRoot_MuxSysPll1Div5);
CLOCK_SetRootClockDiv(kCLOCK_Root_Sai2, 16);
补充:像RT1052 1062,时钟树可以考虑用pll5 即videoPll,具体配置可查阅sdk
/*! @brief PLL configuration for AUDIO and VIDEO */
typedef struct _clock_audio_pll_config
{
uint8_t loopDivider; /*!< PLL loop divider. Valid range for DIV_SELECT divider value: 27~54. */
uint8_t postDivider; /*!< Divider after the PLL, should only be 1, 2, 4, 8, 16. */
uint32_t numerator; /*!< 30 bit numerator of fractional loop divider.*/
uint32_t denominator; /*!< 30 bit denominator of fractional loop divider */
uint8_t src; /*!< Pll clock source, reference _clock_pll_clk_src */
} clock_audio_pll_config_t;
/*! @brief PLL configuration for AUDIO and VIDEO */
typedef struct _clock_video_pll_config
{
uint8_t loopDivider; /*!< PLL loop divider. Valid range for DIV_SELECT divider value: 27~54. */
uint8_t postDivider; /*!< Divider after the PLL, should only be 1, 2, 4, 8, 16. */
uint32_t numerator; /*!< 30 bit numerator of fractional loop divider.*/
uint32_t denominator; /*!< 30 bit denominator of fractional loop divider */
uint8_t src; /*!< Pll clock source, reference _clock_pll_clk_src */
} clock_video_pll_config_t;
void CLOCK_InitAudioPll(const clock_audio_pll_config_t *config);
void CLOCK_InitVideoPll(const clock_video_pll_config_t *config);