1. 程式人生 > >Arduino驅動ILI9341彩屏(一)——顏色問題

Arduino驅動ILI9341彩屏(一)——顏色問題

 

最近在淘寶的店鋪上淘到了一塊ILI9341的彩色液晶屏,打算研究一下如何使用。

淘寶店鋪購買螢幕之後有附原始碼可供下載,程式碼質量慘不忍睹,各種縮排不規範就不說了,先拿來試一下吧。

這是淘寶店鋪程式碼的核心部分:

void setup()
{
  Lcd_Init();
 //LCD_Clear(0xf800);
}

void loop()
{  
   LCD_Clear(0xf800);
   LCD_Clear(0x07E0);
   LCD_Clear(0x001F);
  /*   
  for(int i=0;i<1000;i++)
  {
    Rect(random(300),random(300),random(300),random(300),random(65535)); // rectangle at x, y, with, hight, color
  }*/
  
//  LCD_Clear(0xf800);
}

程式碼裡面的setup()和loop()是arduino特有的主函式,和普通C程式的main()函式一樣。

setup()函式在開機時只執行一次,執行完之後就開始迴圈執行loop()函式。

程式先在setup()函式裡做了一下初始化操作Lcd_Init(),接著開始連續用不同顏色清屏。

這裡的LCD_Clear()就是清屏函數了,原型如下:

void LCD_Clear(unsigned int j)                   
{    
  unsigned int i,m;
 Address_set(0,0,240,320);
  //Lcd_Write_Com(0x02c); //write_memory_start
  //digitalWrite(LCD_RS,HIGH);
  digitalWrite(LCD_CS,LOW);


  for(i=0;i<240;i++)
    for(m=0;m<320;m++)
    {
      Lcd_Write_Data(j>>8);
      Lcd_Write_Data(j);

    }
  digitalWrite(LCD_CS,HIGH);   
}

縮排不規範就不吐槽了(;へ:),連變數名都起得亂七八糟,簡直慘不忍睹。稍微重寫了一下函式,長這樣:

void LCD_Clear(unsigned int color){
  Address_set(0,0,240,320);
  digitalWrite(LCD_CS,LOW);
  for(int i=0;i<240;i++){
    for(int m=0;m<320;m++){
      Lcd_Write_Data(color>>8);
      Lcd_Write_Data(color);
    }
  }
  digitalWrite(LCD_CS,HIGH);
}

這個函式先使用Address_set()設定了重新整理區域,然後把LCD_CS針腳電壓拉低,之後迴圈寫入color。

color分兩次寫入,一次寫入高八位(16位整形前面8個bit),一次寫入低八位。

看上去好像沒什麼問題,但loop()函式中LCD_Clear()卻是直接用十六進位制寫入的。

寫一個RGB()函式把RGB顏色轉換成十六進位制,不是更人性化嗎?

讀了一遍原始碼,結果真的找到了店家的RGB函式:

int RGB(int r,int g,int b)
{return r << 16 | g << 8 | b;
}

還是不規範的縮排(╯︵╰)。但有總比沒有好,輸出紅色試一下:

void setup()
{
  Lcd_Init();
  LCD_Clear(RGB(255,0,0));
}

void loop()
{  
   //nothing
}

出故障了。

 

 

 

Arduino重啟後,螢幕輸出了黑色!再試著排除一下故障,把RGB(255,0,0)改成RGB(0,255,0),輸出綠色試試:

 

 

 

 

結果輸出了橙色!

之後我又反覆嘗試了,沒有一次輸出正確的顏色。莫非是這個RGB()函式有問題,淘寶店鋪才用十六進位制數字?

再仔細推導了一下:return r << 16 | g << 8 | b;把紅色左移16位,綠色左移8位,藍色不動,所以合成的二進位制應該是這樣的:

RRRRRRRRGGGGGGGGBBBBBBBB

R代表紅色位,G代表綠色位,B代表藍色位,每種顏色8位,總共24位。計算了一下可能性:

 

 

 總共1677萬種可能,也就是1677萬種顏色,這就是普通電腦的真彩顏色。但LCD_Clear()函式是這麼寫的:

Lcd_Write_Data(color>>8);
Lcd_Write_Data(color);

總的只能寫入十六個bit,也就是16位,這和24位對不上號啊?

再回頭看了一下,店鋪程式碼的setup()函式中有這樣一行白色清屏指令:

//LCD_Clear(0xf800);

0xf800換算成十進位制,是63488,有沒有感覺很接近一個數?

沒錯,就是65535,單個16位無符號整數的最大儲存範圍。

16位整型變數,顧名思義就是用16個0和1組成的變數。可以儲存的整數範圍是-32768 ~ 32767,32768 + 32767剛好等於65535,換算到二進位制,就是1111111111111111,16個1。

 

這時,真相出現了——這臺機器所採用的,是16位顏色,也被成為RGB565顏色模式。

 

早期的16位計算機由於架構的設計,一次只能處理一個16位二進位制數。而圖形顯示對速度要求特別高,所以一個畫素必須要用一個16進位制數來表示,也就是16位顏色。

如果用採用24位顏色,就需要兩個16進位制數,也就是2Bytes,速度就慢了一半。

而每個畫素都是使用紅黃藍三基色來顯示的,所以一個16進位制數必須分3份,來分別表示紅、黃、藍的資料。

這就出現了一個問題:

16 / 3 = 5.33333

紅黃藍三種顏色平均佔用5.33333個bit。

可bit是計算機儲存的基本單位,要麼是1,要麼是0,不能再分割。必須要有一種顏色多用一個位,才能充分單個利用16進位制整數。

人體的綠色視錐細胞比較敏感,正好,那綠色就用6位,紅色藍色就用5位吧。

這就是著名的RGB565模式,總共能儲存65535種顏色。

早期的遊戲都採用這種模式,所以顏色不夠豐富,很有特色:

 

 

 

 這塊ILI9341顯示屏模組(注意不是ILI9341晶片本身)也剛好只有16根資料引腳,所以就採用了這種RGB565的顏色模式。

找到了問題,那就改一下程式吧:

int RGB(int r,int g,int b)
{
  return r << 11 | g << 5 | b;
}

光改RGB()函式還不夠,現在使用了RGB565模式,所以綠色範圍是從0-63,紅色、藍色的範圍是0-31。

所以還得改setup裡面的清屏函式:

void setup()
{
  Lcd_Init();
  LCD_Clear(RGB(0,63,0));
}

重新下載了程式,螢幕成功顯示,輸出了正確的綠色!⁄(⁄⁄•⁄ω⁄•⁄⁄)⁄

 

 

 那麼問題來了,開頭店家給的LCD_Clear(0xf800)這條清屏指令,是怎麼來的?畢竟他連RGB565都不知道呢!

這是我提供的一種可能性:

“0xf600試一下?”

“不行,太灰了!”

“那0xf700呢?”

“還是不行!老闆,我們都試了一下午了,肯定是螢幕壞了!”

“加油,還差一點點了,肯定可以的!”

“0xf800好像還行,但是還是有點灰!”

“沒關係,反正買家能點亮螢幕就行,其他的我們不管!”

“……”

所以這家淘寶店鋪根本不知道自己在買什麼。ヽ( ̄▽ ̄)ノ