加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

一個(gè)關(guān)于LCD屏顯示出異常亮點(diǎn)的故事(下)

2020/04/06
139
閱讀需 18 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家分享的是 i.MXRT1060 上 LCD 橫向漸變色顯示出亮點(diǎn)問題的分析解決經(jīng)驗(yàn)。

接上篇《一個(gè)關(guān)于 LCD 屏顯示出異常亮點(diǎn)的故事(上)》咱們繼續(xù)聊,上一篇發(fā)出之后,大家在我的微信公號(hào)文章下面留言很熱烈,大部分朋友都把懷疑點(diǎn)放在了 HyperRAM 時(shí)序配置上,覺得很大概率是 HyperRAM 的數(shù)據(jù)訪問出了問題導(dǎo)致了 LCD 顯示異常,這個(gè)懷疑是非常合情合理的,那么從高效定位問題的角度,我們接下來(lái)應(yīng)該怎么做?

一、問題分析

讓我們回到上一篇的最后,痞子衡列出了所有可能出問題的地方,我們現(xiàn)在需要將這些疑點(diǎn)逐一排除:

客戶 LCD 顯示測(cè)試代碼邏輯是否有問題?

客戶 LCD 屏與 i.MXRT1060 連接(線序)是否有問題?

客戶 LCD 屏的 ST7701S 驅(qū)動(dòng)移植(從 STM32 到 i.MXRT1060)是否有問題?

客戶選用的 HyperRAM 本身質(zhì)量是否有問題?

i.MXRT1060 配置的客戶 HyperRAM 時(shí)序參數(shù)是否有問題?

i.MXRT1060 的 LCD 顯示模塊 eLCDIF 驅(qū)動(dòng)是否有問題?

i.MXRT1060 系統(tǒng)的總線處理(如 Cache、總線競(jìng)爭(zhēng))是否有問題?

這些懷疑點(diǎn)總結(jié)下來(lái)就是兩類,一類是硬件問題(如 2、4),另一類是軟件問題(如 1、3、5、6、7)。痞子衡覺得應(yīng)該從軟件疑點(diǎn)先下手。因?yàn)閺默F(xiàn)象上看,硬件上基本沒啥大問題,LCD 是能夠按代碼設(shè)計(jì)那樣去顯示的,而且硬件問題檢查起來(lái)(可能涉及改板子或者焊接,萬(wàn)一整壞了板子 ...)不如驗(yàn)證軟件問題來(lái)得快,等軟件疑點(diǎn)初步排除了,再找硬件問題也不遲。

確定了從軟件疑點(diǎn)下手,那么從哪一個(gè)開始呢?當(dāng)然是大家都認(rèn)為最可疑的點(diǎn) - HyperRAM 時(shí)序配置問題這點(diǎn)先入手,不過直接去檢查 HyperRAM 時(shí)序配置較為繁瑣,我們有更好的選擇,uint32_t s_frameBuffer[480][480]總大小為 900KB,這小于 i.MXRT1060 內(nèi)部 RAM 總空間(1MB),所以我們完全可以將這個(gè) frameBuffer 鏈接到內(nèi)部 RAM 里來(lái)規(guī)避 HyperRAM 時(shí)序配置問題(疑點(diǎn) 5)以及系統(tǒng)總線處理問題(疑點(diǎn) 7),另外我們還可以直接用 J-Link 修改內(nèi)部 RAM 里的 frameBuffer 數(shù)據(jù)來(lái)規(guī)避客戶測(cè)試代碼邏輯問題(疑點(diǎn) 1)。為了方便地生成 frameBuffer 數(shù)據(jù),我們還需要寫個(gè)簡(jiǎn)單的 Python 腳本,那么我們先嘗試用這一套方法在 LCD 上顯示一個(gè)真實(shí)風(fēng)景照吧。

Note: 這套驗(yàn)證方法的最大好處是高效且省時(shí),不需要在 App 代碼工程里改 frameBuffer 相關(guān)代碼以及一次次地重新編譯下載。

?

二、開始測(cè)試

2.1 將 frameBuffer 鏈接到內(nèi)部 RAM 里

2.1.1 重配 FlexRAM

首先是需要在 App 工程的 startup_MIMXRT1062.s 文件里修改 Reset_Handler 代碼,增加 FlexRAM 重配代碼,因?yàn)槟J(rèn) RAM 配置是 128KB ITCM, 128KB DTCM, 768KB OCRAM,我們要將其調(diào)整為 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]
        ;新增代碼(結(jié)束)

        LDR     R0, =0xE000ED08
        LDR     R1, =__vector_table
        ; ...
?
2.1.2 調(diào)整 MPU 設(shè)置

然后我們要在 App 工程的 board.c 文件里修改 BOARD_ConfigMPU()函數(shù),增加如下代碼,確保全部 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 函數(shù)源文件里將 s_frameBuffer 放在一個(gè)自定義的 .frameBuffer 段里,以便在 App 鏈接文件里將其放到 OCRAM 地址空間里(0x20200000 - 0x202FFFFF)。

main 函數(shù)源文件中的修改:

__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 數(shù)據(jù)

2.2.1 風(fēng)景圖片數(shù)據(jù)

我們可以從網(wǎng)上找一張 .jpg 格式圖片,將其尺寸裁剪到 480x480,然后借助 Pillow 里的 Image 庫(kù)將其轉(zhuǎn)成 XRGB8888 格式的 binary 文件,對(duì)應(yīng) 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 測(cè)試數(shù)據(jù)

此外我們還需要一個(gè)腳本,能夠很容易地修改生成指定的 RGB 測(cè)試數(shù)據(jù),用于定位亮點(diǎn)問題,對(duì)應(yīng) 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 數(shù)據(jù)更新進(jìn) OCRAM

2.3.1 顯示風(fēng)景圖片

我們從網(wǎng)上隨便找一張風(fēng)景圖片 scenery.jpg,使用 convert_jpeg_to_xrgb8888.py 腳本將其轉(zhuǎn)換為 scenery.bin,然后將 J-Link 仿真器掛上芯片,成功連接之后,使用 loadbin scenery.bin 0x20200000 命令將圖片數(shù)據(jù)下載進(jìn)內(nèi)部 RAM。

這時(shí)候你可以看到 LCD 的顯示變成了圖片:

?
2.3.2 顯示 RGB 測(cè)試數(shù)據(jù)

從上一節(jié)測(cè)試的真實(shí)圖片顯示效果上看,似乎看不出明顯的亮點(diǎn)問題,這說(shuō)明亮點(diǎn)在特定 RGB 數(shù)據(jù)內(nèi)容顯示的時(shí)候才會(huì)顯現(xiàn)出來(lái),那么我們現(xiàn)在的任務(wù)就是要找到這個(gè)顯現(xiàn)條件,這時(shí)候需要修改 generate_xrgb8888.py 腳本來(lái)反復(fù)做實(shí)驗(yàn)。

所以痞子衡就不斷地修改腳本、生成數(shù)據(jù)、下載數(shù)據(jù),功夫不負(fù)有心人,痞子衡找到了亮點(diǎn)復(fù)現(xiàn)規(guī)律。

?

三、原因分析

痞子衡發(fā)現(xiàn)的亮點(diǎn)規(guī)律是當(dāng)橫向某兩個(gè)連續(xù)漸變像素點(diǎn) RGB 任一分量出現(xiàn)多 bit 由 1 向 0 跳變時(shí)(比如前一個(gè)像素點(diǎn) B 分量是 8'b01111111,后一個(gè)像素點(diǎn) B 分量是 8'b10000000),則后一個(gè)像素點(diǎn)必是亮點(diǎn),這個(gè)亮點(diǎn)像素點(diǎn)最終顯示的 B 分量極可能變成了 8'b11111111。

所以分析下來(lái)應(yīng)該是 DCLK 信號(hào)的極性設(shè)置在屏的驅(qū)動(dòng) IC 和 i.MXRT1060 的 eLCDIF 模塊里不匹配,RGB 數(shù)據(jù)線采樣時(shí)機(jī)錯(cuò)了,導(dǎo)致實(shí)際顯示的 RGB 數(shù)據(jù)發(fā)生了錯(cuò)誤。

SDK 的 elcdif_rgb example 里關(guān)于 eLCDIF 模塊信號(hào)輸出的極性設(shè)置如下,這里需要注意的是 kELCDIF_DriveDataOnRisingClkEdge 是置 1(即上沿?cái)?shù)據(jù)輸出,下沿?cái)?shù)據(jù)保持的意思)。

#define APP_POL_FLAGS (kELCDIF_DataEnableActiveHigh | kELCDIF_VsyncActiveLow | kELCDIF_HsyncActiveLow | kELCDIF_DriveDataOnRisingClkEdge)

這是 i.MXRT1060 eLCDIF 極性配置相關(guān):

這是 OTA5180A 芯片的默認(rèn)極性配置時(shí)序圖

SDK 里的極性設(shè)置與 i.MXRT1060-EVK 標(biāo)配的 LCD 屏(RK043FN02H-CT)里的驅(qū)動(dòng)芯片 OTA5180A 默認(rèn)配置是相吻合的。

我們現(xiàn)在再來(lái)看看 SDK 里的極性設(shè)置與客戶 LCD 屏的驅(qū)動(dòng)芯片 ST7701S 的極性配置是否匹配,客戶設(shè)置了 ST7701S 的 IM[3:0]狀態(tài)為 4'b1010,即 RGB 模式輸出,且 PCLK 是下沿?cái)?shù)據(jù)輸入,上沿?cái)?shù)據(jù)保持(Latch),因此跟 SDK 里的極性設(shè)置是反相的。

所以最終的解決方法就是要么將 ST7701S 的 IM[3:0]狀態(tài)設(shè)為 4'b0010,要么在 App 代碼里將 APP_POL_FLAGS 定義改用 kELCDIF_DriveDataOnFallingClkEdge。

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

碩士畢業(yè)于蘇州大學(xué)電子信息學(xué)院,目前就職于恩智浦(NXP)半導(dǎo)體MCU系統(tǒng)部門,擔(dān)任嵌入式系統(tǒng)應(yīng)用工程師。痞子衡會(huì)定期分享嵌入式相關(guān)文章