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

什么是定位LCD花屏显示问题的第一大法?

csdh11 2024-12-23 09:26 21 浏览

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是 i.MXRT1170 上 LCD 花屏显示问题的分析解决经验。

痞子衡最近这段时间在参与一个基于 i.MXRT1170 的大项目(先保个密),需要做一个开机动画功能,板子连接的 LCD 屏分辨率是 1280x480,因为开机动画要求达到 30fps,并且要画质清晰,如果是从 SD 卡里读 mp4 或者 jpeg 去解码,这么高分辨率的图像(暂不考虑低分辨率的图片再用 PXP 模块去拉伸的方案)解码耗时比较长,恐怕难以达成 30fps,所以痞子衡打算直接把图片的裸 rgb 数据事先存在 Flash 里,然后 LCD 模块直接去刷 Flash 里的数据去显示。

板子上的 SPI NOR Flash有两种,默认是八线 DDR 高性能 Flash,还有一个可选的四线 SDR 普通 Flash,痞子衡做好的代码在默认高性能 Flash 上跑得没问题,换到另一块 rework 为普通四线 Flash 上就出问题了,显示完全是花屏,没有一点图片的影子,到底是怎么回事?跟着痞子衡一起去发现答案吧。

一、项目板卡简图

先来看一下这个项目板卡简图,简图里只示意了痞子衡今天要分享的 LCD 问题相关的器件,显示屏是 TM103XDKP13 控制器驱动的 LVDS 接口屏,跟 i.MXRT 连接的话需要有一个 RGB2LVDS 转接。Flash 都是选的旺宏的,一个是 MX25UW51345(200MHz,8bit,DDR),还有一个是 MX25U25645(133MHz,4bit,SDR)。此外还有两个 16bit 的 W9825G6KH 组成的 32bit SDRAM 做显存,总容量是 64MB。

二、在 Flash 中准备好图片裸数据

首先我们需要在 Flash 中存入图片数据,1280x480-24bpp (rgb888)图片一张的裸数据大小是 1800KB,32MB 的 Flash 最大可以存 18 张图片,为了给程序存储留点空间,我们就存 17 张,从 Flash 偏移 0x100000 处开始存图片。

2.1 截取一段 mp4 视频

痞子衡本地有一个 NXP 十周年宣传视频(MP4 格式),原始分辨率是 1920x1080,可以先用 ffmpeg 或者格式工厂将其转换成 1280x480,然后可以直接用 Windows 自带的图片软件里的 Trim 功能截取其中一段,30fps 帧率的视频截取 1 秒就够了。

2.2 使用 ScreenToGif 软件分离出图片

这时候可以用非常好用的 GIF 制作软件 ScreenToGif 打开这个 1 秒的 MP4,可以看到一共有 31 张图片,可以删掉其中一些留下 17 张,然后将其保存为图片(当前版本仅能保存为 png 格式),可以再用格式工厂软件将图片格式转为 jpg,存在 D:/nxp_logo 文件夹下。

2.3 Python 脚本转成 rgb888 裸数据

有了 17 张 jpg 图片,这时候写一个 Python 脚本(jpg2rgb.py),借助 Image 库将 17 张 jpg 图片中的 rgb 数据全部抽取出来保存在一个 bin 文件中,下面脚本使用命令为 python jpg2rgb.py D:/nxp_logo/ -o startup_video_white_rgb888_17f.bin 。

import sys, os
import argparse
import Image

parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("-o", "--output", required=True, metavar="PATH", type=argparse.FileType('wb'))
parser.add_argument("input", help="JPEG Image folder.")
args = parser.parse_args

imgFiles = 

# 获取指定文件夹中所有 jpg 图片路径
imgFolder = os.path.abspath(args.input)
inputFiles = os.listdir(imgFolder)
for idx in range(len(inputFiles)):
    imgFiles.append(os.path.join(imgFolder, inputFiles[idx]))

for idx in range(len(imgFiles)):
    # 使用 Image 库打开 jpg 图片
    imgObj = Image.open(imgFiles[idx])
    pixelBuf = imgObj.getdata
    # 抽取 rgb 裸数据写入 bin 文件
    for i in range(len(pixelBuf)):
        for j in range(len(pixelBuf[i])):
 args.output.write(chr(pixelBuf[i][len(pixelBuf[i]) - j - 1]))
args.output.close

2.4 将图片裸数据 bin 文件下载进 Flash

现在可以借助 MCUBootUtility 的通用编程器功能将 startup_video_white_rgb888_17f.bin 文件烧录进 Flash 里 0x100000 处偏移的地方。至此,准备工作已经就绪。

三、引出 LCD 花屏显示问题

现在让我们开始设计开机动画程序,可以基于 \SDK_2.x.x_MIMXRT1170-EVK\boards\evkmimxrt1170\jpeg_examples\sd_jpeg 例程,将其中的 LCD 配置,Pinmux 配置稍微改一下,适配这个项目的板子,然后主函数可以精简如下(sd 卡读,libjpeg 解码函数全部去掉):

#define APP_FB_HEIGHT 480
#define APP_FB_WIDTH  1280
/* LCD frame buffer byte per pixel, RGB888 format, 24-bit. */
#define APP_FB_BPP 3

const uint32_t s_imagePics = 17;
const uint32_t s_imageStartAddr = 0x30100000;

int main(void)
{
    uint8_t *imageAddr = (uint8_t *)s_imageStartAddr;
    uint32_t imageBytes = APP_FB_HEIGHT * APP_FB_WIDTH * APP_FB_BPP;

    BOARD_ConfigMPU;
    BOARD_InitBootPins;
    BOARD_BootClockRUN;
    BOARD_ResetDisplayMix;
    APP_InitDisplay;

    while (1)
    {
        /* Wait for the previous set frame buffer active. */
        while (s_newFrameShown == false);

        /* Now new frame is ready, pass it to LCDIF. */
        s_newFrameShown = false;
        g_dc.ops->setFrameBuffer(&g_dc, 0, imageAddr);

        imageAddr += imageBytes;
        if ((uint32_t)imageAddr >= (s_imageStartAddr + imageBytes * s_imagePics))
        {
 break;
        }
    }
}

static void APP_BufferSwitchOffCallback(void *param, void *switchOffBuffer)
{
    s_newFrameShown = true;
}

这时候把代码下载进高性能 DDR Flash 的那块板子,我们的代码可以链接到 TCM 里执行,这样不占用运行时 Flash 访问带宽,不与 LCD 抢带宽。断电重启可以看到在 60Hz 的 LCD 刷新率下,开机动画效果显示杠杠的。

现在把代码下载进普通 SDR Flash 的板子试试,可以看到 LCD 显示花屏了,完全没有图像的影子,这时候该怎么定位问题?

四、尝试降低 LCD 刷新率

在尝试降低 LCD 刷新率之前,痞子衡额外做了一些 debug 工作来确认是不是 Flash 焊接的问题,首先是调试器挂上去查看 PC 指针停在哪里,经调试发现,PC 指针是在 TCM 里,根据工程 map 文件可以查到其地址对应的是程序的结尾,说明代码正常跑完了,这至少证明芯片能够正常从 Flash 启动。

然后痞子衡又对程序作了一些改动,将 Flash 中的图片数据拷贝到 SDRAM 中,让 LCD 模块去刷 SDRAM,这时候图像显示是正常的,这几乎就可以定位问题了,是普通 SDR Flash 带宽不够,Flash 访问速度撑不起 60Hz 刷新率。

#define DEMO_HSW        (1U)
#define DEMO_HBP        (48U)
#define DEMO_HFP        (16U)
#define DEMO_VSW        (1U)
#define DEMO_VBP        (3U)
#define DEMO_VFP        (5U)

static void BOARD_InitLcdifClock(void)
{
    /*
     * The pixel clock is (height + VSW + VFP + VBP) * (width + HSW + HFP + HBP) * frame rate.
     *
     * For 60Hz frame rate, the TM103XDKP13 pixel clock should be 40MHz.
     *
     */
    const clock_root_config_t lcdifv2ClockConfig = {
        .clockOff = false,
        .mfn      = 0,
        .mfd      = 0,
        .mux      = 4, /*!< PLL_528. */
        .div      = 12,
    };

    CLOCK_SetRootClock(kCLOCK_Root_Lcdifv2, &lcdifv2ClockConfig);
}

让我们尝试降低 LCD 刷新率来验证是不是 Flash 带宽的问题,在 BOARD_InitLcdifClock函数中修改 lcdifv2ClockConfig.div 的值,慢慢增大该值,经痞子衡测试,当 div 设为 22 时(即对应 LCD 刷新率为 33.9Hz),终于能够正常显示开机动画了。

五、关于带宽的分析

现在给出痞子衡的观点,对于一个新项目,如果首次测试 LCD 显示,建议先从低刷新率开始,只有低刷新率调试通过,再逐渐增大刷新率,否则会因为带宽问题浪费不少时间。

最后再让我们通过理论公式来推算这款普通 SDR Flash 能支持最大的刷新率,计算公式其实很简单:

LCD 最大刷新率 = (Flash 时钟频率 * Flash 数据位) / 图片大小 = 133MHz * 4bit / (1280 * 480 * 24bit) = 36.08Hz

理论计算值 36.08Hz 跟我们实测值 33.9Hz 很接近,那点差值应该是 FLEXSPI 和 eLCDIF 模块的开销。

至此,i.MXRT1170 上 LCD 花屏显示问题的分析解决经验痞子衡便介绍完毕了,掌声在哪里~~~

相关推荐

Github霸榜的SpringBoot全套学习教程,从入门到实战,内容超详细

前言...

SpringBoot+LayUI后台管理系统开发脚手架

源码获取方式:关注,转发之后私信回复【源码】即可免费获取到!项目简介本项目本着避免重复造轮子的原则,建立一套快速开发JavaWEB项目(springboot-mini),能满足大部分后台管理系统基础开...

Spring Boot+Vue全栈开发实战,中文版高清PDF资源

SpringBoot+Vue全栈开发实战,中文高清PDF资源,需要的可以私我:)SpringBoot致力于简化开发配置并为企业级开发提供一系列非业务性功能,而Vue则采用数据驱动视图的方式将程序...

2021年超详细的java学习路线总结—纯干货分享

本文整理了java开发的学习路线和相关的学习资源,非常适合零基础入门java的同学,希望大家在学习的时候,能够节省时间。纯干货,良心推荐!第一阶段:Java基础...

探秘Spring Cache:让Java应用飞起来的秘密武器

探秘SpringCache:让Java应用飞起来的秘密武器在当今快节奏的软件开发环境中,性能优化显得尤为重要。SpringCache作为Spring框架的一部分,为我们提供了强大的缓存管理能力,让...

3,从零开始搭建SSHM开发框架(集成Spring MVC)

目录本专题博客已共享在(这个可能会更新的稍微一些)https://code.csdn.net/yangwei19680827/maven_sshm_blog...

Spring Boot中如何使用缓存?超简单

SpringBoot中的缓存可以减少从数据库重复获取数据或执行昂贵计算的需要,从而显著提高应用程序的性能。SpringBoot提供了与各种缓存提供程序的集成,您可以在应用程序中轻松配置和使用缓...

我敢保证,全网没有再比这更详细的Java知识点总结了,送你啊

接下来你看到的将是全网最详细的Java知识点总结,全文分为三大部分:Java基础、Java框架、Java+云数据小编将为大家仔细讲解每大部分里面的详细知识点,别眨眼,从小白到大佬、零基础到精通,你绝...

1,从零开始搭建SSHM开发框架(环境准备)

目录本专题博客已共享在https://code.csdn.net/yangwei19680827/maven_sshm_blog1,从零开始搭建SSHM开发框架(环境准备)...

做一个适合二次开发的低代码平台,把程序员从curd中解脱出来-1

干程序员也有好长时间了,大多数时间都是在做curd。现在想做一个通用的curd平台直接将我们解放出来;把核心放在业务处理中。用过代码生成器,在数据表设计好之后使用它就可以生成需要的controller...

设计一个高性能Java Web框架(java做网站的框架)

设计一个高性能JavaWeb框架在当今互联网高速发展的时代,构建高性能的JavaWeb框架对于提升用户体验至关重要。本文将从多个角度探讨如何设计这样一个框架,让我们一起进入这段充满挑战和乐趣的旅程...

【推荐】强&amp;牛!一款开源免费的功能强大的代码生成器系统!

今天,给大家推荐一个代码生成器系统项目,这个项目目前收获了5.3KStar,个人觉得不错,值得拿出来和大家分享下。这是我目前见过最好的代码生成器系统项目。功能完整,代码结构清晰。...

Java面试题及答案总结(2025版持续更新)

大家好,我是Java面试分享最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试场景题及答案。...

Java开发网站架构演变过程-从单体应用到微服务架构详解

Java开发网站架构演变过程,到目前为止,大致分为5个阶段,分别为单体架构、集群架构、分布式架构、SOA架构和微服务架构。下面玄武老师来给大家详细介绍下这5种架构模式的发展背景、各自优缺点以及涉及到的...

本地缓存GuavaCache(一)(guava本地缓存原理)

在并发量、吞吐量越来越大的情况下往往是离不开缓存的,使用缓存能减轻数据库的压力,临时存储数据。根据不同的场景选择不同的缓存,分布式缓存有Redis,Memcached、Tair、EVCache、Aer...