你們好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給你們分享的是i.MXRT1060上LCD橫向漸變色顯示出亮點問題的分析解決經驗。html
接上篇《一個關於LCD屏顯示出異常亮點的故事(上)》我們繼續聊,上一篇發出以後,你們在個人微信公號文章下面留言很熱烈,大部分朋友都把懷疑點放在了HyperRAM時序配置上,以爲很大機率是HyperRAM的數據訪問出了問題致使了LCD顯示異常,這個懷疑是很是合情合理的,那麼從高效定位問題的角度,咱們接下來應該怎麼作?python
讓咱們回到上一篇的最後,痞子衡列出了全部可能出問題的地方,咱們如今須要將這些疑點逐一排除:微信
- 客戶LCD顯示測試代碼邏輯是否有問題?
- 客戶LCD屏與i.MXRT1060鏈接(線序)是否有問題?
- 客戶LCD屏的ST7701S驅動移植(從STM32到i.MXRT1060)是否有問題?
- 客戶選用的HyperRAM自己質量是否有問題?
- i.MXRT1060配置的客戶HyperRAM時序參數是否有問題?
- i.MXRT1060的LCD顯示模塊eLCDIF驅動是否有問題?
- i.MXRT1060系統的總線處理(如Cache、總線競爭)是否有問題?
這些懷疑點總結下來就是兩類,一類是硬件問題(如二、4),另外一類是軟件問題(如一、三、五、六、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相關代碼以及一次次地從新編譯下載。測試
首先是須要在App工程的startup_MIMXRT1062.s文件裏修改Reset_Handler代碼,增長FlexRAM重配代碼,由於默認RAM配置是128KB ITCM, 128KB DTCM, 768KB OCRAM,咱們要將其調整爲1MB OCRAM。flex
__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 ; ...
而後咱們要在App工程的board.c文件裏修改BOARD_ConfigMPU()函數,增長以下代碼,確保所有1MB OCRAM地址空間(0x20200000開始)都是non-cacheable屬性。ui
/* 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);
最後咱們要在App工程的main函數源文件裏將s_frameBuffer放在一個自定義的.frameBuffer段裏,以便在App連接文件裏將其放到OCRAM地址空間裏(0x20200000 - 0x202FFFFF)。
main函數源文件中的修改:.net
__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 };
咱們能夠從網上找一張.jpg格式圖片,將其尺寸裁剪到480x480,而後藉助Pillow裏的Image庫將其轉成XRGB8888格式的binary文件,對應Python腳本(腳本名爲convert_jpeg_to_xrgb8888.py)用法和源代碼以下:3d
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())
此外咱們還須要一個腳本,可以很容易地修改生成指定的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())
咱們從網上隨便找一張風景圖片scenery.jpg,使用convert_jpeg_to_xrgb8888.py腳本將其轉換爲scenery.bin,而後將J-Link仿真器掛上芯片,成功鏈接以後,使用loadbin scenery.bin 0x20200000命令將圖片數據下載進內部RAM。
這時候你能夠看到LCD的顯示變成了圖片:
從上一節測試的真實圖片顯示效果上看,彷佛看不出明顯的亮點問題,這說明亮點在特定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主頁、微信公衆號 平臺上。
微信搜索"痞子衡嵌入式"或者掃描下面二維碼,就能夠在手機上第一時間看了哦。