本文主要是通过迁移的思维,记录本人初次使用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的工程样例来学习该模块。

rtx2070支持的主板(基于RT1170支持MIPIDSI显示)(1)

每一篇文章我均会截图该模块相关的时钟载图,其实是为了快速理解该模块的时钟初始化的相关代码。

rtx2070支持的主板(基于RT1170支持MIPIDSI显示)(2)

rtx2070支持的主板(基于RT1170支持MIPIDSI显示)(3)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qhcguD0z-1615432805647)(http://139.224.41.215:4999/server/../Public/Uploads/2021-03-08/6045ea3d3a0f5.png)]

1. 首先阅读原理图

  MIPI的硬件设计,如下所示:MIPI_DSI_CLK_PMIPI_DSI_CLK_NMIPI_DIS_D0_PMIPI_DSI_D0_NMIPI_DSI_D1_PMIPI_DSI_D1_NMIPI_DSI_PWM----GPIO_AD_11MIPI_DSI_RST----GPIO_AD_12MIPI数据线和时钟线是从芯片引出来的。MIPI_DSI_PWM用于控制屏的背光,MIPI_DSI_RST用于屏的复位控制。

rtx2070支持的主板(基于RT1170支持MIPIDSI显示)(4)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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接口内部的模块架构。

rtx2070支持的主板(基于RT1170支持MIPIDSI显示)(5)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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相关的学习记录。

rtx2070支持的主板(基于RT1170支持MIPIDSI显示)(6)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t19ePW8t-1615432805661)(http://139.224.41.215:4999/server/../Public/Uploads/2021-03-10/6048245420f6e.png)]

,