百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

基于RT1170 支持MIPI DSI显示(六)

haoteby 2025-01-23 17:18 2 浏览

本文主要是通过迁移的思维,记录本人初次使用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)]

相关推荐

简单Labview实操案例

有几位条友私信我说Labview是怎么学的,怎么才能学好Labview,今天给大家简单介绍一下,如果想学上位机,Labview是相对来说比较容易上手的,而且开发速度也比较快,但是运行时候比较吃内存,...

关于LabVIEW用于仪器测控的自动测试程序的程序框架的选择问题!

有很长一段时间没有在公众号平台上输出、总结关于LabVIEW的知识文字内容了!主要是这段时间自己本职工作任务甚为繁重,加上各种家庭事宜的牵绊,耗费了过多的时间和精力,也就无力及时更新了。今天是端午节假...

LabVIEW编程基础:分割条控件的使用

1、分割条控件简介同其它高级编程语言类似,在LabVIEW中分割条控件也是界面设计中常用的一种控件元素,利用分割条控件可以将前面板划分为多个独立的区域,每个区域都是一个单独的窗格,这些窗格具有前面板的...

csgo一直显示正在连接到csgo网络怎么办?三招帮你解决

  CSGO是一款射击类的游戏,它的全名叫反恐精英:全球攻势,是一款由VALVE与HiddenPathEntertainment合作开发、ValveSoftware发行的第一人称射击游戏,相信很...

cs1.6没有bot怎么办

Hi~大家好啊,这里是聚合游戏,每天为你分享游戏相关的内容,喜欢的快来关注哟~...

《反恐精英:全球攻势2》 漏洞暴露玩家的IP地址

#文章首发挑战赛#据报道,在全球知名的电子游戏——CS2(《反恐精英:全球攻势2》)中存在一个HTML注入漏洞,这个漏洞被广泛利用来在游戏中注入图片并获取其他玩家的IP地址。...

《电子宠物》《007黄金眼》《雷神之锤》入选世界电子游戏名人堂

世界电子游戏名人堂5月8日公布了新的四位入选者《防卫者》《电子宠物》《007黄金眼》和《雷神之锤》,以向改变游戏行业规则的经典游戏致敬。世界电子游戏名人堂每年都会表彰那些具有持久热度并对视频游戏行业或...

V社修复《反恐精英2》游戏漏洞:可抓取玩家IP地址、发起XSS攻击

IT之家12月12日消息,Valve旗下《反恐精英2》游戏被曝光新的安全漏洞,攻击者通过注入恶意代码来抓取玩家的IP地址,并能对同一游戏大厅中的所有玩家发起跨站脚本攻击(XSS)。攻击...

粉丝自制《CS》1.6重制版将于2025年登陆Steam

基于Valve官方起源引擎SDK,由多位“CSPromod”粉丝项目前开发人员从头构建的《反恐精英》1.6版本重制版《CS:Legacy》日前宣布将于2025年在Steam发布。开发团...

知名网游源代码泄漏 ,外挂潮将来?

SteamDatabase近日发布消息称Valve旗下游戏《反恐精英:全球攻势》(CS:GO)与《军团要塞2》(TF2)的源代码疑遭泄露。据了解,游戏源代码如果泄露的话,黑客可以更为轻松地开发出外挂,...

Pandas每日函数学习之apply函数

...

求斐波那契数列(Fibonacci Numbers)算法居然有9种,你知道几种?

ByLongLuo斐波那契数列...

三维基因组:Loop结构 差异分析(2)

通过聚合峰分析进行可视化既然已经找出了“WT”和“FS”条件之间的差异loop结构,就可以利用聚合峰分析(APA)来直观地展示loop结构调用的质量。APA是一种以Hi-C数据中的中心loop像...

用Excel制作动态图表(动态名称法)

动态图表也称交互式图表,指图表的内容可以随用户的选择而变化,是图表分析中比较高级的形式。使用动态图表能够突出重点数据,避免被其他不需要的数据干扰,从而提高数据分析效率。一个好的动态图表,可以让人从大量...

Prometheus PromQL语法简介

...