基于RT1170 支持MIPI DSI显示(六)
haoteby 2025-01-23 17:18 9 浏览
本文主要是通过迁移的思维,记录本人初次使用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)]
相关推荐
- 二次面试终拿到offer,百度Android面试真题解析我整理出来了
-
找工作的大潮来临了,这边给大家分享一下面试会遇到的问题。Android开发了5年,之前一直都是在小公司码着代码,对大厂一直有着憧憬,我是在去年年初的时候通过朋友的内推面试了百度,结果被怼的没话说,那叫...
- Android 开发中文引导-应用小部件
-
应用小部件是可以嵌入其它应用(例如主屏幕)并收到定期更新的微型应用视图。这些视图在用户界面中被叫做小部件,并可以用应用小部件提供者发布。可以容纳其他应用部件的应用组件叫做应用部件的宿主(1)。下面的截...
- Win10桌面/手机版最深层次开发功能挖掘
-
IT之家讯Win10开发者预览版为我们提供了一个Win10大框架的早期概览,使开发者与热心用户都可以提前感受Win10带来的新特性,尝试新工具,而作为开发者,最关心的莫过于Windows多平台通用应...
- Android TabLayout + ViewPager2使用
-
1、xml文件<!--明细列表--><com.google.android.material.tabs.TabLayoutandroid:id="@+id/ty_...
- android培训学习的大纲_android软件开发培训
-
第一阶段android基础:1.基础javaJava概述,进制,数据类型,常量变量,运算符,表达式关系运算符,逻辑运算符,if语句,switch语句while循环,do...while循环,for循环...
- 背了几十份面经还是连挂6个面试,最终拿下腾讯后总结了这些坑点
-
刚开始面试的时候我真的是处处碰壁,面一家挂一家,面完之后怀疑自我,是不是自己真的太菜了找不到工作。工作本身就是双向选择,一家不行再换一家,总有合适的,千万不要因为别人的一句话就全盘否定自己,一定要自信...
- webview 渲染机制:硬件加速方式渲染的Android Web
-
webview渲染是什么?webview渲染是用于展现web页面的控件;webview可以内嵌在移动端,实现前端的混合式开发,大多数混合式开发框架都是基于webview模式进行二次开发的...
- ExpandListView 的一种巧妙写法_仿写丁香结的写法写一种花梅花
-
ExpandListView大家估计也用的不少了,一般有需要展开的需求的时候,大家不约而同的都想到了它然后以前自己留过记录的一般都会找找以前自己的代码,没有记录习惯的就会百度、谷歌,这里吐槽一下,好几...
- Android监听滚动视图_滚动监听代码
-
AndroidUILibs之Android-ObservableScrollView1.说明Android-ObservableScrollView,顾名思义,Android上观察滚动的视图,可...
- Flutter 之 ListView_flutter开发
-
在Flutter中,ListView可以沿一个方向(垂直或水平方向)来排列其所有子Widget,常被用于需要展示一组连续视图元素的场景ListView构造方法ListView:仅适用于列表中...
- Android之自定义ListView(一)_android 自定义listview
-
PS:自定义View是Android中高手进阶的路线.因此我也打算一步一步的学习.看了鸿洋和郭霖这两位大牛的博客,决定一步一步的学习,循序渐进.学习内容:1.自定义View实现ListView的Ite...
- ListView 使用详解_listview在哪里
-
阅读五分钟,每日十点,和您一起终身学习,这里是程序员Android本篇文章主要介绍Android开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:ListView主要使用方法使用androi...
- 穿裙子的李宇春,需要向谁解释吗?
-
...
- 明星们“不想占用”的公共资源,到底是个啥?
-
近日,社交媒体上可谓“一瓜未平一瓜又起”,明星们隔三差五地在热搜上道歉、澄清,好不热闹。道歉原因千万种,道歉话术却雷同——通常都以“无意占用公共资源”开头,不管是澄清、官宣,还是回应、声明,都会带上这...
- 选择女人的模板,模板不同,生活方式不同
-
文/高阳人生在世,选择伴侣成了头等大事。尤其是对于男人来说,“选择女人的模板”直接决定了你未来几十年的生活方式,有时简直像组装拼图,每一块都有代价。莫言曾说过:“你选择能干的女人,就得接受她的强势;你...