上一篇發出之後,大家在我的微信公號文章下麵留言很熱烈,大部分朋友都把懷疑點放在了HyperRAM時序配置上,覺得很大概率是HyperRAM的數據訪問出了問題導致了LCD顯示異常,這個懷疑是非常合情合理的,那麼從高效定位問題的角度,我們接下來應該怎麼做? ...
大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是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主頁、微信公眾號 平臺上。
微信搜索"痞子衡嵌入式"或者掃描下麵二維碼,就可以在手機上第一時間看了哦。