基於RT1170 支持MIPI DSI顯示(六)


本文主要是通過遷移的思維,記錄本人初次使用NXP MCUXpresso SDK API進行BSP開發

MIPI 掃盲
  MIPI DSI顯示是本人在項目中初次接觸的接口。由於相關知識缺失,直接去看工程代碼,相關的選項沒有看懂。所以通過網上先找了鏈接進行學習,然后再粗略看RT1170 Chapter 44--53 章節的內容。學習是需要發時間的,不要浮躁。
MCUXpresso SDK MIPI DSI API 接口鏈接
  在MCUXpresso SDK 框架下提供了對MIPI DSI設備進行操作的接口,可以發送數據和命令。針對MIPI DSI當作輸出使用,一般只需要設置顯示的參數如屏參,LANE個數及相關的時鍾,不同的屏根據需要可能需要發送特定的Display Command Set (DCS)。本文本想使用MCUXpresso Config Tools v9來生成相關的代碼,但是由於MIPI DSI的輸出,內部需要經過LCDIFv2,但是MCUXpresso Config Tools v9工具沒有該選項。所以只能通過SDK的工程樣例來學習該模塊。

每一篇文章我均會截圖該模塊相關的時鍾載圖,其實是為了快速理解該模塊的時鍾初始化的相關代碼。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-qhcguD0z-1615432805647)(http://139.224.41.215:4999/server/../Public/Uploads/2021-03-08/6045ea3d3a0f5.png)]

1. 首先閱讀原理圖

  MIPI的硬件設計,如下所示:
MIPI_DSI_CLK_P
MIPI_DSI_CLK_N
MIPI_DIS_D0_P
MIPI_DSI_D0_N
MIPI_DSI_D1_P
MIPI_DSI_D1_N
MIPI_DSI_PWM----GPIO_AD_11
MIPI_DSI_RST----GPIO_AD_12
MIPI數據線和時鍾線是從芯片引出來的。MIPI_DSI_PWM用於控制屏的背光,MIPI_DSI_RST用於屏的復位控制。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-2VRbghtw-1615432805652)(http://139.224.41.215:4999/server/../Public/Uploads/2021-03-09/6047079a132be.png)]

2. SDK api 應用

工程在driver_examples\lcdifv2\store\cm7,該工程顯示彩條。對於配置比較復雜的模塊,先跑一個正常的樣例,通過官方手冊及分析代碼流程來學習。先"知其然",再"知其所以然"。

2.1 主流程

int main(void)
{
    BOARD_ConfigMPU();
    BOARD_BootClockRUN();
    BOARD_ResetDisplayMix();
    BOARD_InitLpuartPins();
    BOARD_InitMipiPanelPins();
    BOARD_InitDebugConsole();
    BOARD_InitLcdifClock();

    PRINTF("LCDIF v2 store example start...\r\n");

    DEMO_LCDIFV2_InitDisplay();

    DEMO_LCDIFV2_ConfigInputLayer();

    DEMO_LCDIFV2_Store();

    DEMO_LCDIFV2_VerifyStore();

    PRINTF("LCDIF v2 store example success...\r\n");

    while (1)
    {
    }
}

分析一下BOARD_InitLcdifClock()函數的實現,屏參數據會在屏的數據手冊上體現。

/*
根據屏參算出pixel clock所需要的時鍾
再去設置lcdifv2模塊的時鍾
*/
void BOARD_InitLcdifClock(void)
{
    /*
     * The pixel clock is (height + VSW + VFP + VBP) * (width + HSW + HFP + HBP) * frame rate.
     *
     * Use PLL_528 as clock source.
     *
     * For 60Hz frame rate, the RK055IQH091 pixel clock should be 36MHz.
     * the RK055AHD091 pixel clock should be 62MHz.
     */
    const clock_root_config_t lcdifv2ClockConfig = {
        .clockOff = false,
        .mfn      = 0,
        .mfd      = 0,
        .mux      = 4, /*!< PLL_528. */
#if (USE_MIPI_PANEL == MIPI_PANEL_RK055AHD091)
        .div = 9,
#elif (USE_MIPI_PANEL == MIPI_PANEL_RK055IQH091)
        .div = 15,
#elif (USE_MIPI_PANEL == MIPI_PANEL_JD9366)
        .div = 12,
#endif
    };

    CLOCK_SetRootClock(kCLOCK_Root_Lcdifv2, &lcdifv2ClockConfig);

    mipiDsiDpiClkFreq_Hz = CLOCK_GetRootClockFreq(kCLOCK_Root_Lcdifv2);
}

分析DEMO_LCDIFV2_InitDisplay()函數的實現

void DEMO_LCDIFV2_InitDisplay(void)
{
    /*根據屏參構建顯示配置結構體*/
    const lcdifv2_display_config_t lcdifv2Config = {
        .panelWidth    = DEMO_PANEL_WIDTH,
        .panelHeight   = DEMO_PANEL_HEIGHT,
        .hsw           = DEMO_HSW,
        .hfp           = DEMO_HFP,
        .hbp           = DEMO_HBP,
        .vsw           = DEMO_VSW,
        .vfp           = DEMO_VFP,
        .vbp           = DEMO_VBP,
        .polarityFlags = DEMO_POL_FLAGS,
        .lineOrder     = kLCDIFV2_LineOrderRGB,
    };

    if (kStatus_Success != BOARD_InitDisplayInterface())
    {
        PRINTF("Display interface initialize failed\r\n");

        while (1)
        {
        }
    }
     /*主要是打開LCDIFv2的時鍾,並初始化相關的外轉寄存大器,並使能該模塊*/
    LCDIFV2_Init(DEMO_LCDIFV2);
    /*設置LCDIFv2的顯示配置*/
    LCDIFV2_SetDisplayConfig(DEMO_LCDIFV2, &lcdifv2Config);
}

在上面的分析當中,BOARD_InitDisplayInterface函數沒有分析,在這里單獨拿出來分析。在芯片手冊里面沒有找到相關的模塊初始化操作流程,這也是我們分析代碼的原因,以便於之后更換屏幕時,知道如何修改,在哪里修改。

status_t BOARD_InitDisplayInterface(void)
{
  
   /* LCDIF v2 output to MIPI DSI. */
	 //LCDIFv2的數據可以流向普通的RGB接口,也可以流向MIPI DSI接口,在這里設置LCDIFv2流向MIPI DSI。
    CLOCK_EnableClock(kCLOCK_Video_Mux);
    VIDEO_MUX->VID_MUX_CTRL.SET = VIDEO_MUX_VID_MUX_CTRL_MIPI_DSI_SEL_MASK;
	

    /* 1. Power on and isolation off. */
    PGMC_BPC4->BPC_POWER_CTRL |= (PGMC_BPC_BPC_POWER_CTRL_PSW_ON_SOFT_MASK | PGMC_BPC_BPC_POWER_CTRL_ISO_OFF_SOFT_MASK);

    /* 2. Assert MIPI reset. */
    IOMUXC_GPR->GPR62 &=
        ~(IOMUXC_GPR_GPR62_MIPI_DSI_PCLK_SOFT_RESET_N_MASK | IOMUXC_GPR_GPR62_MIPI_DSI_ESC_SOFT_RESET_N_MASK |
          IOMUXC_GPR_GPR62_MIPI_DSI_BYTE_SOFT_RESET_N_MASK | IOMUXC_GPR_GPR62_MIPI_DSI_DPI_SOFT_RESET_N_MASK);

    /* 3. Setup clock. */
	/*主要是設置MIPI ESC 的 TX/RX時鍾,及MIPI D-PHY的參考時鍾*/
    BOARD_InitMipiDsiClock();

    /* 4. Deassert PCLK and ESC reset. */
    IOMUXC_GPR->GPR62 |=
        (IOMUXC_GPR_GPR62_MIPI_DSI_PCLK_SOFT_RESET_N_MASK | IOMUXC_GPR_GPR62_MIPI_DSI_ESC_SOFT_RESET_N_MASK);

    /* 5. Configures peripheral. */
    BOARD_SetMipiDsiConfig();

    /* 6. Deassert BYTE and DBI reset. */
    IOMUXC_GPR->GPR62 |=
        (IOMUXC_GPR_GPR62_MIPI_DSI_BYTE_SOFT_RESET_N_MASK | IOMUXC_GPR_GPR62_MIPI_DSI_DPI_SOFT_RESET_N_MASK);

    /* 7. Configure the panel. */
	/*打開MIPI屏的電源,背光,設置RESET引腳電平,並通過MIPI接口給屏發送相關的命令*/
    return BOARD_InitLcdPanel();
}

分析DEMO_LCDIFV2_InitDisplay()函數的實現

void DEMO_LCDIFV2_InitDisplay(void)
{
    const lcdifv2_display_config_t lcdifv2Config = {
        .panelWidth    = DEMO_PANEL_WIDTH,
        .panelHeight   = DEMO_PANEL_HEIGHT,
        .hsw           = DEMO_HSW,
        .hfp           = DEMO_HFP,
        .hbp           = DEMO_HBP,
        .vsw           = DEMO_VSW,
        .vfp           = DEMO_VFP,
        .vbp           = DEMO_VBP,
        .polarityFlags = DEMO_POL_FLAGS,
        .lineOrder     = kLCDIFV2_LineOrderRGB,
    };

    if (kStatus_Success != BOARD_InitDisplayInterface())
    {
        PRINTF("Display interface initialize failed\r\n");

        while (1)
        {
        }
    }

    LCDIFV2_Init(DEMO_LCDIFV2);
    /*設置lcdifv2的數據格式,極性,分辮率及HSW,HFP,HBP,VSW,VHF,VBP等關鍵參數*/
    LCDIFV2_SetDisplayConfig(DEMO_LCDIFV2, &lcdifv2Config);
}

分析DEMO_LCDIFV2_ConfigInputLayer()函數的實現,要理解下面的函數,首先得了解LCDIFv2接口內部的模塊架構。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FsBqvf0y-1615432805656)(http://139.224.41.215:4999/server/../Public/Uploads/2021-03-09/6047561db2f72.png)]

正常來說,我們看到的圖像,有可能是由多個圖層疊加混合之后的結果。圖層可分為背景圖層,視頻圖層,用戶圖層。叫法可能有所不同,還有專門用來顯示字幕的OSD圖層,比如顯示器上的配置界面。這個和芯片本身的顯示硬件設計相關。普通單片機的RGB接口其實就是一個靜態的背景圖層。

void DEMO_LCDIFV2_ConfigInputLayer(void)
{
    const lcdifv2_buffer_config_t fbConfig = {
        .strideBytes = DEMO_INPUT_IMG_WIDTH * DEMO_INPUT_BYTE_PER_PIXEL,
        .pixelFormat = kLCDIFV2_PixelFormatIndex8BPP,
    };

    memset(s_inputBuffer, 0, sizeof(s_inputBuffer));
    /*設置第一個圖層的lut數據*/
    LCDIFV2_SetLut(DEMO_LCDIFV2, 0, lut0, ARRAY_SIZE(lut0), false);
    LCDIFV2_SetLut(DEMO_LCDIFV2, 1, lut1, ARRAY_SIZE(lut1), false);
    LCDIFV2_SetLut(DEMO_LCDIFV2, 2, lut2, ARRAY_SIZE(lut2), false);
    LCDIFV2_SetLut(DEMO_LCDIFV2, 3, lut3, ARRAY_SIZE(lut3), false);
    /*設置每一個圖層的偏移*/
    LCDIFV2_SetLayerOffset(DEMO_LCDIFV2, 0, 0, 0);
    LCDIFV2_SetLayerOffset(DEMO_LCDIFV2, 1, 0, DEMO_INPUT_IMG_HEIGHT * 1);
    LCDIFV2_SetLayerOffset(DEMO_LCDIFV2, 2, 0, DEMO_INPUT_IMG_HEIGHT * 2);
    LCDIFV2_SetLayerOffset(DEMO_LCDIFV2, 3, 0, DEMO_INPUT_IMG_HEIGHT * 3);
    
    for (uint8_t layer = 0; layer < DEMO_INPUT_LAYER_COUNT; layer++)
    {
	    /*設置圖層的數據格式*/
        LCDIFV2_SetLayerBufferConfig(DEMO_LCDIFV2, layer, &fbConfig);
        /*設置圖層的大小*/
        LCDIFV2_SetLayerSize(DEMO_LCDIFV2, layer, DEMO_INPUT_IMG_WIDTH, DEMO_INPUT_IMG_HEIGHT);
        /*設置圖層的地址*/
        LCDIFV2_SetLayerBufferAddr(DEMO_LCDIFV2, layer, (uint32_t)s_inputBuffer);
        /*使能圖層*/
        LCDIFV2_EnableLayer(DEMO_LCDIFV2, layer, true);
        /*調用LCDIFV2_TriggerLayerShadowLoad,圖層顯示的數據為lut數據*/
        LCDIFV2_TriggerLayerShadowLoad(DEMO_LCDIFV2, layer);
    }
}

分析DEMO_LCDIFV2_Store函數的實現,將輸出幀的內容輸出至s_outputBuffer當中。

void DEMO_LCDIFV2_Store(void)
{
    const lcdifv2_store_buffer_config_t storeConfig = {
        .bufferAddr  = (uint32_t)s_outputBuffer,
        .strideBytes = DEMO_OUTPUT_IMG_WIDTH * DEMO_OUTPUT_BYTE_PER_PIXEL,
        .pixelFormat = kLCDIFV2_StorePixelFormatARGB8888,
    };

    LCDIFV2_EnableDisplay(DEMO_LCDIFV2, true);

    LCDIFV2_SetStoreBufferConfig(DEMO_LCDIFV2, &storeConfig);

    LCDIFV2_StartStore(DEMO_LCDIFV2, false);

    while ((kLCDIFV2_StoreFrameDoneInterrupt & LCDIFV2_GetInterruptStatus(DEMO_LCDIFV2, DEMO_CORE_ID)) == 0)
    {
    }
}

分析DEMO_LCDIFV2_VerifyStore函數的實現,主要驗證s_outputBuffer當中數據是否與之前各圖層的設置的lut數據一致。

void DEMO_LCDIFV2_VerifyStore(void)
{
    uint32_t row, col;

    for (row = 0; row < DEMO_INPUT_IMG_HEIGHT; row++)
    {
        for (col = 0; col < DEMO_INPUT_IMG_WIDTH; col++)
        {
            if (s_outputBuffer[row][col] != DEMO_LAYER0_COLOR_ARGB)
            {
                PRINTF("Error at row %d col %d\r\n", row, col);
                while (1)
                    ;
            }
        }
    }

    for (; row < DEMO_INPUT_IMG_HEIGHT * 2; row++)
    {
        for (col = 0; col < DEMO_INPUT_IMG_WIDTH; col++)
        {
            if (s_outputBuffer[row][col] != DEMO_LAYER1_COLOR_ARGB)
            {
                PRINTF("Error at row %d col %d\r\n", row, col);
                while (1)
                    ;
            }
        }
    }

    for (; row < DEMO_INPUT_IMG_HEIGHT * 3; row++)
    {
        for (col = 0; col < DEMO_INPUT_IMG_WIDTH; col++)
        {
            if (s_outputBuffer[row][col] != DEMO_LAYER2_COLOR_ARGB)
            {
                PRINTF("Error at row %d col %d\r\n", row, col);
                while (1)
                    ;
            }
        }
    }

    for (; row < DEMO_INPUT_IMG_HEIGHT * 4; row++)
    {
        for (col = 0; col < DEMO_INPUT_IMG_WIDTH; col++)
        {
            if (s_outputBuffer[row][col] != DEMO_LAYER3_COLOR_ARGB)
            {
                PRINTF("Error at row %d col %d\r\n", row, col);
                while (1)
                    ;
            }
        }
    }
}

4. 總結

工程的實際運行效里如下所示,這是本人接觸MIPI DSI相關的學習記錄。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-t19ePW8t-1615432805661)(http://139.224.41.215:4999/server/../Public/Uploads/2021-03-10/6048245420f6e.png)]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM