<前言>
本篇主要是修正DVD內的TFT範例-*羅斯俄**方塊,在經過幾輪測試後,判定是ILI9325內部的Register和GRAM寫入速度不同所致,初步版本會嚴重影響刷新速率,經過優化後,可與原先速度一樣,因此本文只敘述優化後的版本和修正方法,特別要注意的是這是描繪基礎圖形的重要範例,所以可參考此範例來做延伸
<準備>
1. 寶馬LPC1768開發板
2. JLink或STLink,也可由Serial直接燒寫,板上有預載STLink
3. MicroUSB傳輸線
<問題>
1. 寶馬的開發板提供豐富的外設,當然有TFT的話就從TFT來玩好了,挑了一個*羅斯俄**方塊的範例工程,燒寫後發現描繪的邊緣都有殘點,變成字體模糊或者方塊殘留痕跡,因此就打開原始碼來看看是哪裡有Bug
2. 經過幾輪測試,在ili_lcd_general.c中,經過測試的函式列表如下
-
void write_data(unsigned short data ) <=> 對指定的TFT Register寫入資料,會先轉存到D-Latch,用來轉成16Bit資料
-
lcd_inline void write_cmd (unsigned char c) <=> 用來指定TFT Register,會先轉存到D-Latch,用來轉成16Bit資料
-
lcd_inline void lcd_send (unsigned short byte) <=> 對D-Latch寫入資料
-
void write_reg(unsigned char reg_addr,unsigned short reg_val) <=> 對指定TFT Register寫入資料,為write_data和write_cmd複合版
-
void lcd_SetCursor(unsigned int x,unsigned int y) <=> 對TFT指定描點座標,使用到write_reg
-
void lcd_clear(unsigned short Color) <=> 對TFT清屏成特定背景色
-
void lcd_Initializtion() <=> 初始函式
以上函式歸納幾點,因為lcd_SetCursor函式在寫入座標時後正常但顏色不正確,而lcd_Initializtion函式初始化沒問題,這證明了write_data和write_cmd,以及lcd_send都是沒問題的,因此修改延時也無效果,可懷疑是上層繪圖函式的問題
3. 往上層找繪圖的函式,在ili_lcd_general.c中,列表如下
-
void DrawPoint(int16_t x,int16_t y,unsigned int color) <=> 畫點
-
void DrawXLine(int16_t x1,int16_t x2,int16_t y,unsigned int color) <=> 畫橫線
-
void DrawYLine(int16_t x,int16_t y1,int16_t y2,unsigned int color) <=> 畫縱線
-
void DrawRect( int16_t x1,int16_t y1,int16_t x2,int16_t y2,int color) <=> 畫空心矩形
-
void FillRect( int16_t x1,int16_t y1,int16_t x2,int16_t y2,unsigned int color) <=> 畫實心矩形
-
void DrawBox(int16_t x,int16_t y,int color) <=> 畫*羅斯俄**方塊,為DrawRect和FillRect複合
-
void CoverBox(int16_t x,int16_t y) <=> 塗掉*羅斯俄**方塊
-
void WriteEnglishWord(int16_t x,int16_t y,uint8_t str,unsigned int color,unsigned int xcolor) <=> 寫英文字
-
void WriteEnglishString(int16_t x,int16_t y,char *s,unsigned int color,unsigned int xcolor) <=> 寫英文字串
-
void WriteChineseWord(int16_t x,int16_t y,uint8_t str,unsigned int color,unsigned int xcolor) <=> 寫中文字
-
void WriteChineseString(int16_t x,int16_t y,int16_t start,int16_t length,unsigned int color,unsigned int xcolor) <=> 寫中文字
以上函式觀察到都是在指定Register(R22h)開始後,整個GRAM的寫入時序都不同,變成對該點畫A色彩卻是前一個B色彩,應該是落後的時序才對,因此整個GRAM的顏色往後偏移,所以顯示時,開始的幾點還是前一個B色彩

ILI9325的對GRAM寫入的Register說明
4. 另外觀察到當不使用WriteChineseWord時,會產生方塊異常縱線顯示,發現到是GRAM更新方向性的問題

ILI9325的對GRAM更新方向性說明,已經準備好開始修正了
<實作>
5. 其實範例本身也有跡可循,在清除屏幕的函式中
-
void lcd_clear(unsigned short Color)
-
{
-
unsigned int index=0;
-
lcd_SetCursor(0,0);
-
rw_data_prepare();
-
for (index=0; index<(LCD_WIDTH*LCD_HEIGHT); index++)
-
{
-
write_data(Color);
-
write_data(Color);
-
}
-
}
可發現硬是使用兩次write_data,可是每當寫入成功一次,就會使address counter(AC)自動加1,即目前所在的描點位置加1,所以這段Code會清除整個屏幕兩次,若comment掉其中一行,會使得背景沒清除完整,可得出如下
-
Register寫入速度和GRAM寫入速度不同,嚴格來說是GRAM寫入較慢
6. 首先我們修正不使用WriteChineseWord會造成方塊描繪方向異常,只要把初始化函式lcd_Initializtion修改
-
write_reg(0x0003,(1<<12)|(3<<4)|(1<<0) );
對應的Register(R03h)如下

因此只要設定AM Bit即可,讓GRAM更新的XY坐標都是遞增的
7. 修改各個繪圖函式,我們從步驟5和前面說明知道,要讓同一點多描繪同個顏色幾次,至於次數就是慢慢增加,我測試結果是4次就完成沒有破圖現象了
-
void DrawXLine(int16_t x1,int16_t x2,int16_t y,unsigned int color)
-
{
-
DrawPoint(x1,y,color);
-
DrawPoint(x1,y,color);
-
DrawPoint(x1,y,color);
-
for(;x1<=x2;x1++)
-
DrawPoint(x1,y,color);
-
}
改寫DrawXLine函式,對於基礎圖形來說,因為描繪是同一個顏色,所以我們只需要在第一點描繪4次即可
-
void DrawYLine(int16_t x,int16_t y1,int16_t y2,unsigned int color)
-
{
-
DrawPoint(x,y1,color);
-
DrawPoint(x,y1,color);
-
DrawPoint(x,y1,color);
-
for(;y1<=y2;y1++)
-
DrawPoint(x,y1,color);
-
}
改寫DrawYLine函式,理由同上,改寫完成後,使用DrawRect畫空心矩形就會正常
-
void FillRect( int16_t x1,int16_t y1,int16_t x2,int16_t y2,unsigned int color)
-
{
-
int16_t i;
-
DrawPoint(x1,y1,color);
-
DrawPoint(x1,y1,color);
-
DrawPoint(x1,y1,color);
-
i=x1;
-
for(;y1<=y2;y1++)
-
{
-
for(x1=i;x1<=x2;x1++)
-
DrawPoint(x1,y1,color);
-
}
-
}
改寫FillRect函式,因為實心部分也是同個顏色,一樣只要對第一點描繪4次即可
8. 以上是基礎圖形的改寫方法,就是第一點去寫入4次即可,再來就是寫英文字和中文字的處理方式了,因為整個文字顏色是兩色相間,即文字本身和背景色,因此改寫如下
-
void WriteEnglishWord(int16_t x,int16_t y,uint8_t str,unsigned int color,unsigned int xcolor)
-
{
-
int16_t xpos = x;
-
int16_t ypos = y;
-
unsigned char avl,i;
-
for(i=0; i<16; i++)
-
{
-
avl= (english[str-32][i]);
-
for(xpos=x; xpos<x+8; xpos++)
-
{
-
if(avl&0x80)
-
{
-
DrawPoint(xpos,ypos,color);
-
DrawPoint(xpos,ypos,color);
-
DrawPoint(xpos,ypos,color);
-
DrawPoint(xpos,ypos,color);
-
}
-
else
-
{
-
DrawPoint(xpos,ypos,xcolor);
-
DrawPoint(xpos,ypos,xcolor);
-
DrawPoint(xpos,ypos,xcolor);
-
DrawPoint(xpos,ypos,xcolor);
-
}
-
avl<<=1;
-
}
-
ypos++;
-
}
-
}
基於以上說明,改寫函式WriteEnglishWord,因為寫字串的內容顏色不再是相同,所以只能同個點去描繪4次,但也因為遊戲中顯示字和刷新字的次數少很多,因此效能上幾乎無影響
-
void WriteChineseWord(int16_t x,int16_t y,uint8_t str,unsigned int color,unsigned int xcolor)//写字符
-
{
-
int16_t xpos = x;
-
int16_t ypos = y;
-
unsigned char low,high,i;
-
for(i=0; i<16; i++)
-
{
-
low = chinese[str*32+2*i];
-
high= chinese[str*32+2*i+1];
-
for(xpos =x; xpos<x+8; xpos++)
-
{
-
if(low&0x80)
-
{
-
DrawPoint(xpos,ypos,color);
-
DrawPoint(xpos,ypos,color);
-
DrawPoint(xpos,ypos,color);
-
DrawPoint(xpos,ypos,color);
-
}
-
else
-
{
-
DrawPoint(xpos,ypos,xcolor);
-
DrawPoint(xpos,ypos,xcolor);
-
DrawPoint(xpos,ypos,xcolor);
-
DrawPoint(xpos,ypos,xcolor);
-
}
-
low<<=1;
-
}
-
for(xpos=x+8; xpos<x+16; xpos++)
-
{
-
if(high&0x80)
-
{
-
DrawPoint(xpos,ypos,color);
-
DrawPoint(xpos,ypos,color);
-
DrawPoint(xpos,ypos,color);
-
DrawPoint(xpos,ypos,color);
-
}
-
else
-
{
-
DrawPoint(xpos,ypos,xcolor);
-
DrawPoint(xpos,ypos,xcolor);
-
DrawPoint(xpos,ypos,xcolor);
-
DrawPoint(xpos,ypos,xcolor);
-
}
-
high<<=1;
-
}
-
ypos++;
-
}
-
}
同理,改寫函式WriteChineseWord,因為中文字是全型,所以有高低8位資料,至此就修改完畢
<編譯和燒寫>
9. 改寫完畢後,就可以開始進行編譯了

確保無錯誤和警告
10. 使用MicroUSB傳輸線連接電腦和開發板,開始進行燒寫,我是用額外的JLink插上JTAG接口

附帶一提的是,開發板支援SW和JTAG Port

燒錄成功
<實測>
11. 以下是修正前的圖片

可看出方塊左上方起始點殘點外,中英文字也破圖

您看得出來是GAME OVER嗎?
12. 以下是修正後的圖片

殘點消失了,中英文字體也正常了

清晰的GAME OVER,再重新挑戰吧!

<小結>
本文是一個意外的插曲,測試*羅斯俄**方塊範例的途中發現Bug並加以修正,之後移植其他遊戲也更為方便,省去很多Debug的時間,也分享給需要使用基礎繪圖功能的小伙伴們,要特別注意的是因為產生額外的寫入步驟,也可稱為緩衝,會一定程度的影響刷新率,所以本文也加以優化,使得速度和原先速度相同,讓遊戲更加美觀
宝马 NXP LPC1768开发套件免费试用活动正在云汉电子社区火热进行中,只要你关注云汉电子社区微信公众号ickeybbs,回复"1768",就可以免费获得体验资格.