UNO R3從SD卡讀取圖片並顯示到2.2寸液晶屏上(220x176)
折騰了一個週末,終於基本搞定。之前也玩過一陣,但沒能解決圖片大到一定程度後記憶體不足或者陣列超限的問題,
所以決定再試一下用SD卡。220x176的資料比較少,查了一圈壇內壇外中文外文的網站之後總算找到了一些思路。
大思路就是從SD卡中讀指定圖片檔案的每個畫素的RGB,然後在對應位置顯示。
我用的液晶模組解析度220x176,帶SD卡槽的,支援直插UNO,但SD卡相關的針腳要自己焊。另外還有一種320x240的也是2.2寸,看到的資料說是要電平轉換之後才能用。這個暫時先放一放。
接線很簡單,顯示相關的直接插進UNO R3,從5V開始一直到A5對準就行。原裝正品有個插座是悶掉的,所以液晶模組對應的引腳要掰掉。相容UNO在這個位置多數是個缺口所以可以不用管它。
SD卡部分接線也很簡單,按照PIN11 - PIN13的硬體SPI定義對應起來就行(官方示例的註釋中也有詳細說明)。另外有個SD_CS引腳,我是接在了10上。這個不是原則問題,因為一會兒程式碼中SD.begin的引數就是SD_CS。
反正SD部分不是很複雜,看下示例中的SD的DumpFile基本上就差不多了。多說一句,網上有說SD最好用2G以下,但是我用的SD卡是4G的。另外,示例中把pinMode是註釋掉的,我這裡不行,必須顯式指定它是OUTPUT,否則就是認不出SD卡。
圖片檔案我也走了點彎路,目前為止不算徹底解決,只是用了種比較麻煩的變通方式。理想狀況是用Bitmap檔案(JPEG、PNG等等都是編過碼的,考慮到微控制器可憐的16M時鐘頻率還要去解碼。。。所以資料還得直接喂。就這樣載入完都得近1分鐘)。seek(0x12)得到寬度,seek(0x16)得到高度,seek(0x36)起得到資料。這樣做是可行的,但我這裡測試下來遇到的最大問題是缺色。這麼說吧,人臉都是阿凡達。。。。。。Bitmap檔案結構還沒仔細研究過,估計跟製作Bitmap時的儲存選項或者到手的BMP檔案的調色盤有關係,稍後再折騰吧。
現在的做法是,自己用C#寫了個程式直接轉成一個畫素RGB檔案,沒有任何檔案頭。比方說有個寬3畫素高2畫素的圖片,第一行都是紅色,第二行都是綠色,那麼我輸出的檔案二進位制就是:
0xFF 0x00 0x00 0xFF 0x00 0x00 0xFF 0x00 0x00
0x00 0xFF 0x00 0x00 0xFF 0x00 0x00 0xFF 0x00
(為了照顧人類的閱讀習慣加了空格和換行,實際上沒有。缺點顯而易見,得自己知道寬高。)
共3*2*3=18位元組。
我相信是有現成的這種格式的,但不知道查詢的關鍵字。猜過RAW和MemoryBmp格式,但在我手裡都沒成功。
Arduino裡依次讀取每個位元組,每讀三次就能拿到一個畫素的RGB資訊,就可以在當前座標上顯示了。實際效果如圖:
主要程式碼如下,略去了很多異常判斷。完整演算法請參考官方示例。
#include <UTFT.h>
#include <SPI.h>
#include <SD.h>
const int chipSelect = 10;
UTFT lcd(QD220A, A2, A1, A5, A4, A3);
void setup()
{
int r, g, b;
//Serial.begin(9600);
pinMode(chipSelect, OUTPUT);
lcd.InitLCD();
lcd.clrScr();
int width;
int height;
if (SD.begin(chipSelect)) {
File bmpImage = SD.open("TEST.RAW", FILE_READ);
/*
bmpImage.seek(0x12); // 寬度
width = bmpImage.read();
bmpImage.seek(0x16); // 高度
height = bmpImage.read();
//Serial.println(width);
//Serial.println(height);
bmpImage.seek(0x36); //跳過Bitmap檔案頭
*/
width = 220;
height = 176;
for (int y = 0; y < height; y ++) {
for (int x = 0; x < width; x++) {
r = bmpImage.read();
g = bmpImage.read();
b = bmpImage.read();
lcd.setColor(r, g, b);
lcd.drawPixel(x, y);
}
}
bmpImage.close();
}
}
void loop()
{
// 啥都不用做
}