AM335X-裸機驅RGB屏(HX8369A)


寫在前面

本文使用的屏幕是一塊3.97英寸480*800的TN屏,主控是HX8369A,有點老,主要用在10幾年前的手機上,使用3-Wire SPI+RGB接口,RGB接口的時序配置由SPI初始化階段決定。但是某寶賣家只提供了SPI初始化代碼(提供的初始化代碼還有點問題,坑爹...),並沒有與初始化參數相對應的vsw,vfp,vbp和hsw,hfp,hbp參數。因此想要完美顯示,就需要好好研究下HX8369-A01的手冊了,經過幾個晚上的倒騰,同時參考了網絡上零星的驅動資料(這屏RGB資料比較少,大多數是MIPI的資料)與淘寶賣家的初始化參數,終於是完美顯示了。不過這里還是推薦讀者買通用的40PIN/50PIN屏幕,比較好驅動,下面是我使用的屏幕和通用屏的價格對比。。。(如果同樣選擇TN面板不帶觸摸,價格差32元)

本文所使用的屏幕

通用40PIN屏(驅動IC:ST7262),注意這個是800*480

效果展示

顯示完全正常,只是手機攝像頭有點徑向畸變。。。

屏初始化相關

HX8369A的屏幕接口由幾個引腳配置,LCM在面板出排線時候已經固定好了,用戶沒法自行修改,買屏幕時候需要注意即使屏控制器支持RGB/MCU/MIPI接口,但最終的可用接口還是LCM廠商決定的。

由賣家給的資料可以確定配置電阻接法對應的接口是3WireSPI+RGB

打開HX8369A的手冊,可以看到3線spi的接口時序。從圖中可以看出:3線spi支持9bit模式和16bit模式,對於支持9bit模式SPI的MPU,直接將最高位放上命令/數據選擇位"control bit D/CX",其中"0"表示命令,"1"表示數據。"If DCX is low, the transmission byte is command byte";對於不支持9bit模式SPI的MPU,可以通過傳送16bit的方式通信,其中第一次傳輸的最低位是命令/數據選擇位,后面緊跟的8bit是數據/命令內容(這里也可以發2次8bit數據來模擬一次16bit傳輸,但這期間cs腳不能拉高),需要注意的是每次傳輸的數據都需要帶上控制位。

AM3352的McSPI支持4~32bit的傳輸,設置起來很方便,但是非8位對齊的數據時鍾速率不能太快,我測試下來9bit只能支持到1Mbps,和正常8位對齊的45Mbps還是有點差距的;下面給出的AM335X裸機SPI配置的關鍵部分,在接屏幕前建議使用邏輯分析儀將Reset,cs, mosi, sclk這幾個腳的數據抓取看下,確定spi和屏復位部分信號波形ok了就基本就沒啥問題了。

// GPIO模塊配置
GPIO3ModuleClkConfig();
GPIOModuleEnable(SOC_GPIO_3_REGS);
GPIOModuleReset(SOC_GPIO_3_REGS);
// cs
HWREG(SOC_CONTROL_REGS + SPI1_CS0_GPIO_ADDR) = PAD_FS_RXD_PU_PUPDE(3);
// mosi
HWREG(SOC_CONTROL_REGS + SPI1_D0_GPIO_ADDR) = PAD_FS_RXD_PU_PUPDE(3);
// miso 單驅屏幕這個腳也可以不配置,如果顯示屏的3線spi和其他4線spi設備復用的話就需要配置
HWREG(SOC_CONTROL_REGS + SPI1_D1_GPIO_ADDR) = PAD_FS_RXE_PU_PUPDE(3);
// sclk
HWREG(SOC_CONTROL_REGS + SPI1_SCLK_GPIO_ADDR) = PAD_FS_RXD_PU_PUPDE(3);

// SPI1模塊配置
McSPI1ModuleClkConfig();

McSPIReset(SOC_SPI_1_REGS);
McSPICSEnable(SOC_SPI_1_REGS);

McSPIMasterModeEnable(SOC_SPI_1_REGS);
McSPIMasterModeConfig(SOC_SPI_1_REGS, MCSPI_SINGLE_CH, MCSPI_TX_RX_MODE, MCSPI_DATA_LINE_COMM_MODE_6, MCSPI_CHANNEL_0);

McSPIClkConfig(SOC_SPI_1_REGS, MCSPI_MODEL_CLK, MCSPI1_OUT_FREQ, MCSPI_CHANNEL_0, MCSPI_CLK_MODE_0);
// 3wire spi msb is data/c
McSPIWordLengthSet(SOC_SPI_1_REGS, MCSPI_WORD_LENGTH(9), MCSPI_CHANNEL_0);
McSPICSPolarityConfig(SOC_SPI_1_REGS, MCSPI_CS_POL_LOW, MCSPI_CHANNEL_0);

McSPITxFIFOConfig(SOC_SPI_1_REGS, MCSPI_TX_FIFO_ENABLE, MCSPI_CHANNEL_0);
McSPIRxFIFOConfig(SOC_SPI_1_REGS, MCSPI_RX_FIFO_ENABLE, MCSPI_CHANNEL_0);

McSPIChannelEnable(SOC_SPI_1_REGS, MCSPI_CHANNEL_0);

賣家提供的初始化代碼:https://files.cnblogs.com/files/yanye0xff/15-22511-30832初始化.zip
這份代碼是有問題的,作為下面修改的參考模板,切勿完全照搬了。原始代碼我將以截圖形式展示,正確代碼則以文本方式展示。

屏復位部分與手冊的復位過程有出入,需要修改,原始代碼如下:

手冊配置的復位時序上並沒有對CS腳做要求,且延時時間並不需要那么長

修改后

GPIOPinWrite(SOC_GPIO_0_REGS, HX8269A_RESET_GPIO_PIN, GPIO_PIN_LOW);
// tRESW min = 10 us
SysdelayUs(20);
GPIOPinWrite(SOC_GPIO_0_REGS, HX8269A_RESET_GPIO_PIN, GPIO_PIN_HIGH);
// there is H/W reset complete time (tREST) within 5ms after a rising edge of RESX
SysdelayMs(10);

// It resets the commands and parameters to their S/W Reset default values.
hx8369_write_cmd(0x01);
// it will be necessary to wait 120msec before sending Sleep out command(0x11)
SysdelayMs(120);

顯示接口配置部分需要修改,HX8369A為了支持MCU口(DBI)內部是集成了GRAM的,賣家提供的代碼"0x23"是將RGB接口數據寫入到GRAM的異步模式,這樣的好處是對RGB信號時序沒啥要求,hbp,hfp,vbp,vfp這些參數隨便填就行,但我測試下來,水平掃描始終是錯位的,而且有些像素的顏色也不對...;所以這里我改為使用RGB直通模式,即數據直接送向屏幕的行列驅動器驅動液晶面板,一般的RGB屏都使用這種操作模式。
原來的'0x23'配置為"480RGBX800, DBI Interface (CPU), RGB data bypass GRAM mode",
需要改成"0x29","480RGBX800, DPI Interface (RGB), DPI signal (VSYNC+HSYNC)",
FPBP 需要根據顯示屏頂部和底部是否缺掃描線來調整。
原始代碼如下:

參照:

這里只放了需要修改的部分,其余的部分與原來一致

hx8369_write_data(0x00);
hx8369_write_data(0x29);
// Specify the amount of scan line for back porch(BP).
hx8369_write_data(0x07);
// Specify the amount of scan line for front porch (FP).
hx8369_write_data(0x08);

RGB接口電平配置部分,原始代碼缺失,建議加上

//  Set RGB interface related register
hx8369_write_cmd(0xB3);
// DPL=0,HSPL=0,VSPL=0,EPL=1
// DPL=0, the data is read on the rising edge of PCLK signal.
// HSPL=0, the HS pin is Low active
// VSPL=0, the VS pin is Low active.
// EPL=1: DE = 0 Display:Disable, DE = 1 Display:Enable
hx8369_write_data(0x01);

設置屏幕顯存起始地址,這里可以刪掉,該配置只對MCU接口有效,在RGB模式下MPU內部控制器接管了屏邏輯部分,HX8369A只作為屏驅動IC。

"0x11"退出休眠指令后的延時不需要這么長,這里可以改小(如果sleep out后需要再次進sleep in才需要延時120ms,這里顯然不需要了)

// EXIT SLEEP MODE
hx8369_write_cmd(0x11);
//  It will be necessary to wait 5msec before sending next command
SysdelayMs(10);

這里的設置顯示區域的函數也可以去掉了,在RGB模式下並沒有什么用。

至此初始化代碼都已經修改配置完成了,RGB 時序參數如下
PCLK = Total Width * Total Hight * fps = (528 * 815 * 60)
VSW = 4, VFP = 6, VBP = 5
HSW = 16, HFP = 16, HBP = 16

時鍾有效邊沿配置如下

RasterTiming2Configure(SOC_LCDC_0_REGS, RASTER_FRAME_CLOCK_LOW |
                                        RASTER_LINE_CLOCK_LOW  |
                                        RASTER_PIXEL_CLOCK_LOW|
                                        RASTER_SYNC_EDGE_RISING|
                                        RASTER_SYNC_CTRL_ACTIVE|
                                        RASTER_AC_BIAS_HIGH, 0, 255);

完整的初始化代碼:

inline static void hx8369_write_cmd(uint8_t cmd);
inline static void hx8369_write_data(uint8_t data);

inline static void hx8369_write_cmd(uint8_t cmd) {
    uint32_t command = 0x000;
    command |= cmd;
    McSPI1Write(&command, 1);
}

inline static void hx8369_write_data(uint8_t data) {
    uint32_t outdata = 0x100;
    outdata |= data;
    McSPI1Write(&outdata, 1);
}

void HX8269AInit(void) {
    GPIOPinWrite(SOC_GPIO_0_REGS, HX8269A_RESET_GPIO_PIN, GPIO_PIN_LOW);
    // tRESW min = 10 us
    SysdelayUs(20);
    GPIOPinWrite(SOC_GPIO_0_REGS, HX8269A_RESET_GPIO_PIN, GPIO_PIN_HIGH);
    //  there is H/W reset complete time (tREST) within 5ms after a rising edge of RESX
    SysdelayMs(10);

    //  It resets the commands and parameters to their S/W Reset default values.
    hx8369_write_cmd(0x01);
    //  it will be necessary to wait 120msec before sending Sleep out command(0x11)
    SysdelayMs(120);

    // HX8369A+HSD3.97-RGB MODE'' Initial Code
    // set extended command set access enable
    hx8369_write_cmd(0xB9);
    hx8369_write_data(0xFF);
    hx8369_write_data(0x83);
    hx8369_write_data(0x69);

    // Set power
    hx8369_write_cmd(0xB1);
    hx8369_write_data(0x01);//0X01
    hx8369_write_data(0x00);
    hx8369_write_data(0x34);
    hx8369_write_data(0x07);
    hx8369_write_data(0x00);
    hx8369_write_data(0x0E);
    hx8369_write_data(0x0E);
    hx8369_write_data(0x21);// VSPR
    hx8369_write_data(0x29);// VSNR
    hx8369_write_data(0x3F);
    hx8369_write_data(0x3F);
    hx8369_write_data(0x01);
    hx8369_write_data(0x23);
    hx8369_write_data(0x01);
    hx8369_write_data(0xE6);
    hx8369_write_data(0xE6);
    hx8369_write_data(0xE6);
    hx8369_write_data(0xE6);
    hx8369_write_data(0xE6);

    // Set display related register
    hx8369_write_cmd(0xB2);
    hx8369_write_data(0x00);
    hx8369_write_data(0x29);
    // Specify the amount of scan line for back porch(BP).
    hx8369_write_data(0x07);
    // Specify the amount of scan line for front porch (FP).
    hx8369_write_data(0x08);
    hx8369_write_data(0x70);
    hx8369_write_data(0x00);
    hx8369_write_data(0xFF);
    hx8369_write_data(0x00);
    hx8369_write_data(0x00);
    hx8369_write_data(0x00);
    hx8369_write_data(0x00);
    hx8369_write_data(0x03);
    hx8369_write_data(0x03);
    hx8369_write_data(0x00);
    hx8369_write_data(0x01);

    //  Set RGB interface related register
    hx8369_write_cmd(0xB3);
    // DPL=0,HSPL=0,VSPL=0,EPL=1
    // DPL=0, the data is read on the rising edge of PCLK signal.
    // HSPL=0, the HS pin is Low active
    // VSPL=0, the VS pin is Low active.
    // EPL=1: DE = 0 Display:Disable, DE = 1 Display:Enable
    hx8369_write_data(0x01);

    // Set_address_mode
    hx8369_write_cmd(0x36);
    hx8369_write_data(0x00);

    // SETPANEL
    hx8369_write_cmd(0xCC);
    // SS_PANEL=0 source driver output from S1 to S1440
    // REV_PANEL = 0 normal-white panel
    // BGR_PANEL = 0 color filter of panel is <R><G><B> type
    hx8369_write_data(0x00);

    hx8369_write_cmd(0xB4);
    // 00: column, 05: 1dot, 0A: 2dot
    hx8369_write_data(0x0A);
    hx8369_write_data(0x0C);
    hx8369_write_data(0x84);
    hx8369_write_data(0x0C);
    hx8369_write_data(0x01);

    // SET VCOM -1.504V
    hx8369_write_cmd(0xB6);
    hx8369_write_data(0x1F);
    hx8369_write_data(0x1F);

    hx8369_write_cmd(0xD5);
    hx8369_write_data(0x00);//1,
    hx8369_write_data(0x03);//2,
    hx8369_write_data(0x00);//3,
    hx8369_write_data(0x00);//4,
    hx8369_write_data(0x01);//5,
    hx8369_write_data(0x06);//6,
    hx8369_write_data(0x10);//7,
    hx8369_write_data(0x80);//8,
    hx8369_write_data(0x33);//9,
    hx8369_write_data(0x37);//10,
    hx8369_write_data(0x23);//11,
    hx8369_write_data(0x01);//12,
    hx8369_write_data(0xB9);//13,
    hx8369_write_data(0x75);//14,
    hx8369_write_data(0xA8);//15,
    hx8369_write_data(0x64);//16,
    hx8369_write_data(0x00);//17,
    hx8369_write_data(0x00);//18,
    hx8369_write_data(0x41);//19,
    hx8369_write_data(0x06);//20,
    hx8369_write_data(0x50);//21,
    hx8369_write_data(0x07);//22,
    hx8369_write_data(0x07);//23,
    hx8369_write_data(0x0F);//24,
    hx8369_write_data(0x07);//25,
    hx8369_write_data(0x00);//26,

    hx8369_write_cmd(0xE0);
    hx8369_write_data(0x00);
    hx8369_write_data(0x03);
    hx8369_write_data(0x00);
    hx8369_write_data(0x09);
    hx8369_write_data(0x09);
    hx8369_write_data(0x21);
    hx8369_write_data(0x1B);
    hx8369_write_data(0x2D);
    hx8369_write_data(0x06);
    hx8369_write_data(0x0C);
    hx8369_write_data(0x10);
    hx8369_write_data(0x15);
    hx8369_write_data(0x16);
    hx8369_write_data(0x14);
    hx8369_write_data(0x16);
    hx8369_write_data(0x12);
    hx8369_write_data(0x18);
    hx8369_write_data(0x00);
    hx8369_write_data(0x03);
    hx8369_write_data(0x00);
    hx8369_write_data(0x09);
    hx8369_write_data(0x09);
    hx8369_write_data(0x21);
    hx8369_write_data(0x1B);
    hx8369_write_data(0x2D);
    hx8369_write_data(0x06);
    hx8369_write_data(0x0C);
    hx8369_write_data(0x10);
    hx8369_write_data(0x15);
    hx8369_write_data(0x16);
    hx8369_write_data(0x14);
    hx8369_write_data(0x16);
    hx8369_write_data(0x12);
    hx8369_write_data(0x18);

    // Set_pixel_forma
    hx8369_write_cmd(0x3A);
    // 0x77: 24bit, 0x66: 18bit, 0x55 16bit
    hx8369_write_data(0x77);

    // EXIT SLEEP MODE
    hx8369_write_cmd(0x11);
    //  It will be necessary to wait 5msec before sending next command
    SysdelayMs(10);

    // display on
    hx8369_write_cmd(0x29);
    // write RAM
    hx8369_write_cmd(0x2C);
}

AM335X LCD控制器結構

AM335X TFT-LCD控制器稱為"raster",在AM335X_StarterWare找相關.c和.h文件即可。

其中FrameBuffer的數據是有格式的(並不是完全對應的屏幕顯示的RGB數據)

對於不使用硬件調色板的場合,直接看下面紅框內即可

AM335X LCD控制器初始化代碼

首先指定一塊內存作為FrameBuffer,為了避免刷新屏幕時出現錯位,對該內存的配置有以下2種方法(都使能了DCACHE):
單緩存模式:

在配置MMU時將該段內存配置為"MMU_NON_CACHEABLE"或者"MMU_CACHE_WT_NOWA"即不使用對該區域內存不使用cache或者使用cache但讀寫時寫透cache和內存,這樣每次FrameBuffer更新后,可用免去調用"CacheDataCleanInvalidateBuff"刷新cache了,缺點是FrameBuffer的內存位置需要固定,且邏輯部分太慢時,仍會出現顯示錯位(Tearing)的情況。

雙緩存和三緩存模式:

推薦使用三緩存模式,配置MMU時整個DDR空間都可配置為"MMU_CACHE_WB_WA"這樣使用了cache會加快整體程序的內存讀寫速度。

我這里由於使用了CGT編譯器,需要手動指定變量到bss段,不然默認就放到data段了,也不知是啥原因

#pragma DATA_ALIGN(FrameBuffer, 4);
uint32_t __attribute__((section("bss"))) FrameBuffer[8 + 480 * 800];

填上占位的頭部數據

// The first entry should be 4000h (bit 14 is 1)
FrameBuffer[0] = 0x4000;
// the remaining entries must be filled with 0
for(uint32_t i = 1; i < 8; i++) {
    FrameBuffer[i] = 0x00;
}

TODO Finish...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM