I.MX RT1176笔记(7)-- 多个SAI同时输出不同采样率

本文介绍了在音频开发中如何处理两个外设输出不同采样率的需求,例如SAI1输出44.1K,SAI2输出48K。通过分析时钟树和修改特定时钟配置,如使用Audio PLL和Sys PLL1,实现了灵活的时钟分频。详细展示了如何修改CLOCK_InitSysPll1函数以配置所需时钟频率,并给出了针对不同采样率的配置示例。此外,还提及了RT10521062平台时钟配置的可能性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在音频开发中,有可能两个外设输出不同采样率的需求,场景例子: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);

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值