大家好, 我是痞子衡, 是正经搞技术的痞子. 今天痞子衡给大家分享的是 i.MXRT1060 上 LCD 横向渐变色显示出亮点问题的分析解决经验.
接上篇《一个关于 LCD 屏显示出异常亮点的故事(上)》咱们继续聊, 上一篇发出之后, 大家在我的微信公号文章下面留言很热烈, 大部分朋友都把怀疑点放在了 HyperRAM 时序配置上, 觉得很大概率是 HyperRAM 的数据访问出了问题导致了 LCD 显示异常, 这个怀疑是非常合情合理的, 那么从高效定位问题的角度, 我们接下来应该怎么做?
一, 问题分析
让我们回到上一篇的最后, 痞子衡列出了所有可能出问题的地方, 我们现在需要将这些疑点逐一排除:
客户 LCD 显示测试代码逻辑是否有问题?
客户 LCD 屏与 i.MXRT1060 连接 (线序) 是否有问题?
客户 LCD 屏的 ST7701S 驱动移植 (从 STM32 到 i.MXRT1060) 是否有问题?
客户选用的 HyperRAM 本身质量是否有问题?
i.MXRT1060 配置的客户 HyperRAM 时序参数是否有问题?
i.MXRT1060 的 LCD 显示模块 eLCDIF 驱动是否有问题?
i.MXRT1060 系统的总线处理 (如 Cache, 总线竞争) 是否有问题?
这些怀疑点总结下来就是两类, 一类是硬件问题 (如 2,4), 另一类是软件问题(如 1,3,5,6,7). 痞子衡觉得应该从软件疑点先下手. 因为从现象上看, 硬件上基本没啥大问题, LCD 是能够按代码设计那样去显示的, 而且硬件问题检查起来(可能涉及改板子或者焊接, 万一整坏了板子...) 不如验证软件问题来得快, 等软件疑点初步排除了, 再找硬件问题也不迟.
确定了从软件疑点下手, 那么从哪一个开始呢? 当然是大家都认为最可疑的点 - HyperRAM 时序配置问题这点先入手, 不过直接去检查 HyperRAM 时序配置较为繁琐, 我们有更好的选择, uint32_t s_frameBuffer[480][480]总大小为 900KB, 这小于 i.MXRT1060 内部 RAM 总空间 (1MB), 所以我们完全可以将这个 frameBuffer 链接到内部 RAM 里来规避 HyperRAM 时序配置问题(疑点 5) 以及系统总线处理问题(疑点 7), 另外我们还可以直接用 J-Link 修改内部 RAM 里的 frameBuffer 数据来规避客户测试代码逻辑问题(疑点 1). 为了方便地生成 frameBuffer 数据, 我们还需要写个简单的 Python 脚本, 那么我们先尝试用这一套方法在 LCD 上显示一个真实风景照吧.
Note: 这套验证方法的最大好处是高效且省时, 不需要在 App 代码工程里改 frameBuffer 相关代码以及一次次地重新编译下载.
二, 开始测试
2.1 将 frameBuffer 链接到内部 RAM 里
2.1.1 重配 FlexRAM
首先是需要在 App 工程的 startup_MIMXRT1062.s 文件里修改 Reset_Handler 代码, 增加 FlexRAM 重配代码, 因为默认 RAM 配置是 128KB ITCM, 128KB DTCM, 768KB OCRAM, 我们要将其调整为 1MB OCRAM.
- __iomux_gpr16_adr EQU 0x400AC040
- __iomux_gpr17_adr EQU 0x400AC044
- __flexram_bank_cfg EQU 0x55555555
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;; Default interrupt handlers.
- ;;
- THUMB
- PUBWEAK Reset_Handler
- SECTION .text:CODE:REORDER:NOROOT(2)
- Reset_Handler
- CPSID I ; Mask interrupts
; 新增代码(开始)
- LDR R0,=__iomux_gpr17_adr
- MOV32 R1,__flexram_bank_cfg
- STR R1,[R0]
- LDR R0,=__iomux_gpr16_adr
- LDR R1,[R0]
- ORR R1,R1,#4
- STR R1,[R0]
; 新增代码(结束)
- LDR R0, =0xE000ED08
- LDR R1, =__vector_table
- ; ...
2.1.2 调整 MPU 设置
然后我们要在 App 工程的 board.c 文件里修改 BOARD_ConfigMPU()函数, 增加如下代码, 确保全部 1MB OCRAM 地址空间 (0x20200000 开始) 都是 non-cacheable 属性.
- /* Region 6 setting: Memory with Normal type, not shareable, non-cacheable */
- MPU->RBAR = ARM_MPU_RBAR(6, 0x20200000U);
- MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 1, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_1MB);
2.1.3 修改链接文件
最后我们要在 App 工程的 main 函数源文件里将 s_frameBuffer 放在一个自定义的. frameBuffer 段里, 以便在 App 链接文件里将其放到 OCRAM 地址空间里(0x20200000 - 0x202FFFFF).
main 函数源文件中的修改:
__no_init uint32_t s_frameBuffer[APP_IMG_HEIGHT][APP_IMG_WIDTH] @ ".frameBuffer";
App 工程链接文件中的修改:
- define symbol m_data2_start = 0x20200000;
- define symbol m_data2_end = 0x202FFFFF;
- define region DATA2_region = mem:[from m_data2_start to m_data2_end];
- place in DATA2_region {
- section .frameBuffer
- };
2.2 编写 Python 脚本生成 frameBuffer 数据
2.2.1 风景图片数据
我们可以从网上找一张. jpg 格式图片, 将其尺寸裁剪到 480x480, 然后借助 Pillow 里的 Image 库将其转成 XRGB8888 格式的 binary 文件, 对应 Python 脚本 (脚本名为 convert_jpeg_to_xrgb8888.py) 用法和源代码如下:
- import sys, os
- import argparse
- from PIL import Image
- class ConvertJpegToXrgb8888(object):
- def __init__(self):
- pass
- def _read_options(self):
- parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter)
- parser.add_argument("-o", "--output", required=True, metavar="PATH", type=argparse.FileType('wb'), help="Specify the output file.")
- parser.add_argument("input", help="JPEG Image file."),
- return parser.parse_args()
- def run(self):
- args = self._read_options()
- imgObj = Image.open(args.input)
- pixelBuf = imgObj.getdata()
- 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.write(chr(0))
- args.output.close()
- if __name__ == "__main__":
- exit(ConvertJpegToXrgb8888().run())
2.2.2 RGB 测试数据
此外我们还需要一个脚本, 能够很容易地修改生成指定的 RGB 测试数据, 用于定位亮点问题, 对应 Python 脚本 (脚本名为 generate_xrgb8888.py) 用法和源代码如下:
- import sys, os
- import argparse
- class GenerateXrgb8888(object):
- def __init__(self):
- pass
- def _read_options(self):
- parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter)
- parser.add_argument("-o", "--output", required=True, metavar="PATH", type=argparse.FileType('wb'), help="Specify the output file.")
- return parser.parse_args()
- def _make_xrgb8888(self, r, g, b):
- return chr(b) + chr(g) + chr(r) + chr(0)
- def run(self):
- args = self._read_options()
- for i in range(160):
- for j in range(480):
- args.output.write(self._make_xrgb8888(j%256, 0, 0))
- for i in range(160):
- for j in range(480):
- args.output.write(self._make_xrgb8888(0, j%256, 0))
- for i in range(160):
- for j in range(480):
- args.output.write(self._make_xrgb8888(0, 0, j%256))
- args.output.close()
- if __name__ == "__main__":
- exit(GenerateXrgb8888().run())
2.3 使用 J-Link 将 frameBuffer 数据更新进 OCRAM
2.3.1 显示风景图片
我们从网上随便找一张风景图片 scenery.jpg, 使用 convert_jpeg_to_xrgb8888.py 脚本将其转换为 scenery.bin, 然后将 J-Link 仿真器挂上芯片, 成功连接之后, 使用 loadbin scenery.bin 0x20200000 命令将图片数据下载进内部 RAM.
这时候你可以看到 LCD 的显示变成了图片:
2.3.2 显示 RGB 测试数据
从上一节测试的真实图片显示效果上看, 似乎看不出明显的亮点问题, 这说明亮点在特定 RGB 数据内容显示的时候才会显现出来, 那么我们现在的任务就是要找到这个显现条件, 这时候需要修改 generate_xrgb8888.py 脚本来反复做实验.
所以痞子衡就不断地修改脚本, 生成数据, 下载数据, 功夫不负有心人, 痞子衡找到了亮点复现规律.
三, 原因分析
痞子衡发现的亮点规律是当横向某两个连续渐变像素点 RGB 任一分量出现多 bit 由 1 向 0 跳变时(比如前一个像素点 B 分量是 8'b01111111, 后一个像素点 B 分量是 8'b10000000), 则后一个像素点必是亮点, 这个亮点像素点最终显示的 B 分量极可能变成了 8'b11111111.
所以分析下来应该是 DCLK 信号的极性设置在屏的驱动 IC 和 i.MXRT1060 的 eLCDIF 模块里不匹配, RGB 数据线采样时机错了, 导致实际显示的 RGB 数据发生了错误.
在 SDK 的 elcdif_rgb example 里关于 eLCDIF 模块信号输出的极性设置如下, 这里需要注意的是 kELCDIF_DriveDataOnRisingClkEdge 是置 1(即上沿数据输出, 下沿数据保持的意思).
#define APP_POL_FLAGS (kELCDIF_DataEnableActiveHigh | kELCDIF_VsyncActiveLow | kELCDIF_HsyncActiveLow | kELCDIF_DriveDataOnRisingClkEdge)
这是 i.MXRT1060 eLCDIF 极性配置相关:
这是 OTA5180A 芯片的默认极性配置时序图:
SDK 里的极性设置与 i.MXRT1060-EVK 标配的 LCD 屏 (RK043FN02H-CT) 里的驱动芯片 OTA5180A 默认配置是相吻合的.
我们现在再来看看 SDK 里的极性设置与客户 LCD 屏的驱动芯片 ST7701S 的极性配置是否匹配, 客户设置了 ST7701S 的 IM[3:0]状态为 4'b1010, 即 RGB 模式输出, 且 PCLK 是下沿数据输入, 上沿数据保持(Latch), 因此跟 SDK 里的极性设置是反相的.
所以最终的解决方法就是要么将 ST7701S 的 IM[3:0]状态设为 4'b0010, 要么在 App 代码里将 APP_POL_FLAGS 定义改用 kELCDIF_DriveDataOnFallingClkEdge.
欢迎订阅
文章会同时发布到我的 博客园主页, CSDN 主页 https://blog.csdn.net/Henjay724 , 微信公众号 平台上.
微信搜索 "痞子衡嵌入式" 或者扫描下面二维码, 就可以在手机上第一时间看了哦.
来源: https://www.cnblogs.com/henjay724/p/12616586.html