硬汉嵌入式论坛

 找回密码
 立即注册
查看: 405|回复: 1
收起左侧

[STM32H7] [分享]一个简单粗暴的LTDC时钟自动配置程序

[复制链接]

5

主题

8

回帖

23

积分

新手上路

积分
23
发表于 2023-10-26 21:50:32 | 显示全部楼层 |阅读模式

心血来潮弄了个配置LTDC时钟的程序,基本方法就是简单粗暴的循环+判断对PLL3进行配置。
当前只对PLL3R负责并尽可能让另外两个的时钟速度高一些

[C] 纯文本查看 复制代码
/**
 * @brief
 *
 * @param freq LTDC clock, MHz
 */
static void lcd_set_ltdc_clock(uint8_t freq)
{
    /**
     * @brief Set Clock PLL3R
     * PLL3_VCO Input = HSE_VALUE / PLL3M
     * PLL3_VCO Output = PLL3_VCO Input * PLL3N
     * PLLLCDCLK = PLL3_VCO Output / PLL3R
     *
     * /DIVM3 = 1~63
     * xDIVN3 = 4~512
     * /DIVP3 = 2
     * /DIVR3 = 1~128
     */
    uint32_t pll3m, pll3n, pll3p, pll3q, pll3r;
    uint16_t freq_osc, freq_sys, freq_pll3m, freq_pll3n; // MHz
    uint16_t freq_pll3p, freq_pll3q, freq_pll3r;         // MHz

    log_i("try to set LTDC clock %uMhz", freq);

    // 确定时钟源
    if (RCC_GET_PLL_OSCSOURCE() == RCC_PLLSOURCE_HSE)
    {
        // HSE = 4~48M
        freq_osc = HSE_VALUE / 1000000UL;
        pll3m = HSE_VALUE / 1000000UL; // 1MHz
        log_d("RCC_GET_PLL_OSCSOURCE is HSE");
    }
    else if (RCC_GET_PLL_OSCSOURCE() == RCC_PLLSOURCE_HSI)
    {
        // HSI = 64M.
        // TODO:未测试
        freq_osc = HSI_VALUE / 1000000UL;
        pll3m = HSE_VALUE / 2000000UL; // 2MHz
        log_d("RCC_GET_PLL_OSCSOURCE is HSI");
    }
    else
    {
        log_e("Failed to check PLL source");
        return;
    }

    /**
     * @brief 遍历PLL3参数
     * PLL3频率尽可能高
     * DIVP3=2 && PLL3P<SysClock
     * DIVQ3=1~128 && PLL3Q<SysClock
     * PLL3R尽可能接近目标值
     */
    uint16_t last_freq_pll3r_err = 0xFFFF;
    uint16_t last_freq_pll3q = 0;
    freq_sys = HAL_RCC_GetSysClockFreq() / 1000000UL;
    freq_pll3m = freq_osc / pll3m;
    for (uint16_t divn3 = 4; divn3 <= 512; divn3++) // DIVN3
    {
        uint16_t divq3, divr3;
        freq_pll3n = freq_pll3m * divn3;

        // PLL3P <= SysClock
        pll3p = 2; // 固定值
        freq_pll3p = freq_pll3n / pll3p;
        if (freq_pll3p > freq_sys)
            continue;

        // PLL3Q <= SysClock
        uint8_t err = 1;
        for (divq3 = 1; divq3 <= 128; divq3++)
        {
            freq_pll3q = freq_pll3n / divq3;
            if (freq_pll3q <= freq_sys)
            {
                err = 0;
                break;
            }
        }
        if (err)
            continue;

        // PLL3R = freq
        uint8_t save = 0;
        for (divr3 = 3; divr3 <= 128; divr3++) // DIVR3
        {
            // 计算误差
            int32_t freq_err;
            freq_pll3r = freq_pll3n / divr3;
            freq_err = freq_pll3r - freq;
            if (freq_err < 0)
                freq_err = -freq_err;

            // 误差尽可能小,频率尽可能大
            if (last_freq_pll3r_err >= freq_err)
            {
                last_freq_pll3r_err = freq_err;
                /**
                 * @brief 二次判断
                 * 配置25M时存在极值DIVN3=480,PLL3P=240,PLL3Q=480,PLL3R=25
                 */
                if (freq_pll3r != freq ||
                    last_freq_pll3q < freq_pll3q)
                {
                    last_freq_pll3q = freq_pll3q;
                    save = 1;
                    break;
                }
            }
        }

        if (save)
        {
            pll3n = divn3;
            pll3q = divq3;
            pll3r = divr3;
            // elog_raw("DIVN3=%u,PLL3P=%u,PLL3Q=%u,PLL3R=%u\r\n",
            //          divn3, freq_pll3p, freq_pll3q, freq_pll3r);
        }
    }

    log_d("DIVM3=%u, DIVN3=%u", pll3m, pll3n);
    log_d("DIVP3=%u, DIVQ3=%u, DIV3R=%u", pll3p, pll3q, pll3r);

    // Config
    RCC_PeriphCLKInitTypeDef PeriphClkIniture;
    PeriphClkIniture.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
    PeriphClkIniture.PLL3.PLL3M = pll3m;
    PeriphClkIniture.PLL3.PLL3N = pll3n;
    PeriphClkIniture.PLL3.PLL3P = pll3p;
    PeriphClkIniture.PLL3.PLL3Q = pll3q;
    PeriphClkIniture.PLL3.PLL3R = pll3r;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkIniture) != HAL_OK)
    {
        log_e("failed to set LTDC clk");
        return;
    }

    /*Print result*/
    freq_pll3n = freq_osc / PeriphClkIniture.PLL3.PLL3M * PeriphClkIniture.PLL3.PLL3N;
    freq_pll3p = freq_pll3n / PeriphClkIniture.PLL3.PLL3P;
    freq_pll3q = freq_pll3n / PeriphClkIniture.PLL3.PLL3Q;
    freq_pll3r = freq_pll3n / PeriphClkIniture.PLL3.PLL3R;

    log_i("Set PLL3 Freq: P=%uMHz, Q=%uMHz, R=%uMHz",
          freq_pll3p, freq_pll3q, freq_pll3r);
}



刚写完,测试了25MHz、33MHz、50MHz和70MHz的配置
HSE配置25MHz.png HSE配置33MHz.png HSE配置50MHz.png HSE配置70MHz.png

回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106907
QQ
发表于 2023-10-27 07:37:11 | 显示全部楼层
谢谢楼主分享。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-9 14:38 , Processed in 0.175770 second(s), 29 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表