宝马 NXP LPC1768试用体验:修正*羅斯俄**方塊範例破圖

<前言>

本篇主要是修正DVD內的TFT範例-*羅斯俄**方塊,在經過幾輪測試後,判定是ILI9325內部的Register和GRAM寫入速度不同所致,初步版本會嚴重影響刷新速率,經過優化後,可與原先速度一樣,因此本文只敘述優化後的版本和修正方法,特別要注意的是這是描繪基礎圖形的重要範例,所以可參考此範例來做延伸

<準備>

1. 寶馬LPC1768開發板

2. JLink或STLink,也可由Serial直接燒寫,板上有預載STLink

3. MicroUSB傳輸線

<問題>

1. 寶馬的開發板提供豐富的外設,當然有TFT的話就從TFT來玩好了,挑了一個*羅斯俄**方塊的範例工程,燒寫後發現描繪的邊緣都有殘點,變成字體模糊或者方塊殘留痕跡,因此就打開原始碼來看看是哪裡有Bug

2. 經過幾輪測試,在ili_lcd_general.c中,經過測試的函式列表如下

  1. void write_data(unsigned short data ) <=> 對指定的TFT Register寫入資料,會先轉存到D-Latch,用來轉成16Bit資料

  2. lcd_inline void write_cmd (unsigned char c) <=> 用來指定TFT Register,會先轉存到D-Latch,用來轉成16Bit資料

  3. lcd_inline void lcd_send (unsigned short byte) <=> 對D-Latch寫入資料

  4. void write_reg(unsigned char reg_addr,unsigned short reg_val) <=> 對指定TFT Register寫入資料,為write_data和write_cmd複合版

  5. void lcd_SetCursor(unsigned int x,unsigned int y) <=> 對TFT指定描點座標,使用到write_reg

  6. void lcd_clear(unsigned short Color) <=> 對TFT清屏成特定背景色

  7. void lcd_Initializtion() <=> 初始函式

以上函式歸納幾點,因為lcd_SetCursor函式在寫入座標時後正常但顏色不正確,而lcd_Initializtion函式初始化沒問題,這證明了write_data和write_cmd,以及lcd_send都是沒問題的,因此修改延時也無效果,可懷疑是上層繪圖函式的問題

3. 往上層找繪圖的函式,在ili_lcd_general.c中,列表如下

  1. void DrawPoint(int16_t x,int16_t y,unsigned int color) <=> 畫點

  2. void DrawXLine(int16_t x1,int16_t x2,int16_t y,unsigned int color) <=> 畫橫線

  3. void DrawYLine(int16_t x,int16_t y1,int16_t y2,unsigned int color) <=> 畫縱線

  4. void DrawRect( int16_t x1,int16_t y1,int16_t x2,int16_t y2,int color) <=> 畫空心矩形

  5. void FillRect( int16_t x1,int16_t y1,int16_t x2,int16_t y2,unsigned int color) <=> 畫實心矩形

  6. void DrawBox(int16_t x,int16_t y,int color) <=> 畫*羅斯俄**方塊,為DrawRect和FillRect複合

  7. void CoverBox(int16_t x,int16_t y) <=> 塗掉*羅斯俄**方塊

  8. void WriteEnglishWord(int16_t x,int16_t y,uint8_t str,unsigned int color,unsigned int xcolor) <=> 寫英文字

  9. void WriteEnglishString(int16_t x,int16_t y,char *s,unsigned int color,unsigned int xcolor) <=> 寫英文字串

  10. void WriteChineseWord(int16_t x,int16_t y,uint8_t str,unsigned int color,unsigned int xcolor) <=> 寫中文字

  11. 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色彩

宝马NXPLPC1768试用体验:修正*羅斯俄**方塊範例破圖

ILI9325的對GRAM寫入的Register說明

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

宝马NXPLPC1768试用体验:修正*羅斯俄**方塊範例破圖

ILI9325的對GRAM更新方向性說明,已經準備好開始修正了

<實作>

5. 其實範例本身也有跡可循,在清除屏幕的函式中

  1. void lcd_clear(unsigned short Color)

  2. {

  3. unsigned int index=0;

  4. lcd_SetCursor(0,0);

  5. rw_data_prepare();

  6. for (index=0; index<(LCD_WIDTH*LCD_HEIGHT); index++)

  7. {

  8. write_data(Color);

  9. write_data(Color);

  10. }

  11. }

可發現硬是使用兩次write_data,可是每當寫入成功一次,就會使address counter(AC)自動加1,即目前所在的描點位置加1,所以這段Code會清除整個屏幕兩次,若comment掉其中一行,會使得背景沒清除完整,可得出如下

  1. Register寫入速度和GRAM寫入速度不同,嚴格來說是GRAM寫入較慢

6. 首先我們修正不使用WriteChineseWord會造成方塊描繪方向異常,只要把初始化函式lcd_Initializtion修改

  1. write_reg(0x0003,(1<<12)|(3<<4)|(1<<0) );

對應的Register(R03h)如下

宝马NXPLPC1768试用体验:修正*羅斯俄**方塊範例破圖

因此只要設定AM Bit即可,讓GRAM更新的XY坐標都是遞增的

7. 修改各個繪圖函式,我們從步驟5和前面說明知道,要讓同一點多描繪同個顏色幾次,至於次數就是慢慢增加,我測試結果是4次就完成沒有破圖現象了

  1. void DrawXLine(int16_t x1,int16_t x2,int16_t y,unsigned int color)

  2. {

  3. DrawPoint(x1,y,color);

  4. DrawPoint(x1,y,color);

  5. DrawPoint(x1,y,color);

  6. for(;x1<=x2;x1++)

  7. DrawPoint(x1,y,color);

  8. }

改寫DrawXLine函式,對於基礎圖形來說,因為描繪是同一個顏色,所以我們只需要在第一點描繪4次即可

  1. void DrawYLine(int16_t x,int16_t y1,int16_t y2,unsigned int color)

  2. {

  3. DrawPoint(x,y1,color);

  4. DrawPoint(x,y1,color);

  5. DrawPoint(x,y1,color);

  6. for(;y1<=y2;y1++)

  7. DrawPoint(x,y1,color);

  8. }

改寫DrawYLine函式,理由同上,改寫完成後,使用DrawRect畫空心矩形就會正常

  1. void FillRect( int16_t x1,int16_t y1,int16_t x2,int16_t y2,unsigned int color)

  2. {

  3. int16_t i;

  4. DrawPoint(x1,y1,color);

  5. DrawPoint(x1,y1,color);

  6. DrawPoint(x1,y1,color);

  7. i=x1;

  8. for(;y1<=y2;y1++)

  9. {

  10. for(x1=i;x1<=x2;x1++)

  11. DrawPoint(x1,y1,color);

  12. }

  13. }

改寫FillRect函式,因為實心部分也是同個顏色,一樣只要對第一點描繪4次即可

8. 以上是基礎圖形的改寫方法,就是第一點去寫入4次即可,再來就是寫英文字和中文字的處理方式了,因為整個文字顏色是兩色相間,即文字本身和背景色,因此改寫如下

  1. void WriteEnglishWord(int16_t x,int16_t y,uint8_t str,unsigned int color,unsigned int xcolor)

  2. {

  3. int16_t xpos = x;

  4. int16_t ypos = y;

  5. unsigned char avl,i;

  6. for(i=0; i<16; i++)

  7. {

  8. avl= (english[str-32][i]);

  9. for(xpos=x; xpos<x+8; xpos++)

  10. {

  11. if(avl&0x80)

  12. {

  13. DrawPoint(xpos,ypos,color);

  14. DrawPoint(xpos,ypos,color);

  15. DrawPoint(xpos,ypos,color);

  16. DrawPoint(xpos,ypos,color);

  17. }

  18. else

  19. {

  20. DrawPoint(xpos,ypos,xcolor);

  21. DrawPoint(xpos,ypos,xcolor);

  22. DrawPoint(xpos,ypos,xcolor);

  23. DrawPoint(xpos,ypos,xcolor);

  24. }

  25. avl<<=1;

  26. }

  27. ypos++;

  28. }

  29. }

基於以上說明,改寫函式WriteEnglishWord,因為寫字串的內容顏色不再是相同,所以只能同個點去描繪4次,但也因為遊戲中顯示字和刷新字的次數少很多,因此效能上幾乎無影響

  1. void WriteChineseWord(int16_t x,int16_t y,uint8_t str,unsigned int color,unsigned int xcolor)//写字符

  2. {

  3. int16_t xpos = x;

  4. int16_t ypos = y;

  5. unsigned char low,high,i;

  6. for(i=0; i<16; i++)

  7. {

  8. low = chinese[str*32+2*i];

  9. high= chinese[str*32+2*i+1];

  10. for(xpos =x; xpos<x+8; xpos++)

  11. {

  12. if(low&0x80)

  13. {

  14. DrawPoint(xpos,ypos,color);

  15. DrawPoint(xpos,ypos,color);

  16. DrawPoint(xpos,ypos,color);

  17. DrawPoint(xpos,ypos,color);

  18. }

  19. else

  20. {

  21. DrawPoint(xpos,ypos,xcolor);

  22. DrawPoint(xpos,ypos,xcolor);

  23. DrawPoint(xpos,ypos,xcolor);

  24. DrawPoint(xpos,ypos,xcolor);

  25. }

  26. low<<=1;

  27. }

  28. for(xpos=x+8; xpos<x+16; xpos++)

  29. {

  30. if(high&0x80)

  31. {

  32. DrawPoint(xpos,ypos,color);

  33. DrawPoint(xpos,ypos,color);

  34. DrawPoint(xpos,ypos,color);

  35. DrawPoint(xpos,ypos,color);

  36. }

  37. else

  38. {

  39. DrawPoint(xpos,ypos,xcolor);

  40. DrawPoint(xpos,ypos,xcolor);

  41. DrawPoint(xpos,ypos,xcolor);

  42. DrawPoint(xpos,ypos,xcolor);

  43. }

  44. high<<=1;

  45. }

  46. ypos++;

  47. }

  48. }

同理,改寫函式WriteChineseWord,因為中文字是全型,所以有高低8位資料,至此就修改完畢

<編譯和燒寫>

9. 改寫完畢後,就可以開始進行編譯了

宝马NXPLPC1768试用体验:修正*羅斯俄**方塊範例破圖

確保無錯誤和警告

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

宝马NXPLPC1768试用体验:修正*羅斯俄**方塊範例破圖

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

宝马NXPLPC1768试用体验:修正*羅斯俄**方塊範例破圖

燒錄成功

<實測>

11. 以下是修正前的圖片

宝马NXPLPC1768试用体验:修正*羅斯俄**方塊範例破圖

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

宝马NXPLPC1768试用体验:修正*羅斯俄**方塊範例破圖

您看得出來是GAME OVER嗎?

12. 以下是修正後的圖片

宝马NXPLPC1768试用体验:修正*羅斯俄**方塊範例破圖

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

宝马NXPLPC1768试用体验:修正*羅斯俄**方塊範例破圖

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

宝马NXPLPC1768试用体验:修正*羅斯俄**方塊範例破圖

<小結>

本文是一個意外的插曲,測試*羅斯俄**方塊範例的途中發現Bug並加以修正,之後移植其他遊戲也更為方便,省去很多Debug的時間,也分享給需要使用基礎繪圖功能的小伙伴們,要特別注意的是因為產生額外的寫入步驟,也可稱為緩衝,會一定程度的影響刷新率,所以本文也加以優化,使得速度和原先速度相同,讓遊戲更加美觀

宝马 NXP LPC1768开发套件免费试用活动正在云汉电子社区火热进行中,只要你关注云汉电子社区微信公众号ickeybbs,回复"1768",就可以免费获得体验资格.