基於STM32的uCGUI移植和優化
首先在開始這個說明之前,要簡要說明下具體的環境:
編譯工具:MDK4.20
開發板:安富萊v2版開發板
調試器:JLink v8盜版
移植篇
相信大家有移植經驗的都知道,移植確實是一件非常墨跡的事情,怎么說呢,代碼都是別人的,風格也是別人的,文件結構,定義之類都是別人的,看別人的東西是種進步,但是,也是一個痛苦的過程,因為有時候資料確實很少,而且有時候還是E文的,專業名詞一大堆,我們根本沒有辦法想象工作量是多么的巨大.
不過事情都是這樣,你不懂他的時候他就像是巨山,但是一旦你理解他的時候,你才會感覺到原來他是那么的簡單(從我的經驗上來看,至少應該是這樣的).
好吧,閑話少說,我們就來開始我們的移植之旅把.
首先,我們需要准備的東西有uCGUI3.90,這個版本是大家現在用的比較多的,效率也比較高,別人都是這么評論的,至於其他版本的,我沒有接觸很多,所以不能過多評論.
uCGUI有三個文件夾,一個是tool,這個文件夾是用來使用一些uCgui的上位機程序,基本都是字體和模板查看之類的.在sample文件夾下面是已經別人幫你寫好了很多有用的東西,像跟操作系統有關的GUI_X或者一些模板(后面我們會用到的自己定義的Demo),或者是gui配置.后面再一一詳細敘說這個文件夾的功能.在Start文件夾里面,這是我們最主要的文件夾.里面就包含了uCGUI的源代碼,uCGUI的作者把源代碼放進vc里面進行編譯了(當然,這是用標准C語言寫的程序,所以我們可以放在任何C語言平台下編譯而不會擔心兼容性問題,這個uCGUI在這方面做的算是完美了),所以,我們可以在vc平台下寫界面,然后再把代碼拷進我們的下位機編譯器進行編譯,這樣子效率就會非常高了.(像51那時候寫界面就是瘋狂的一次一次的燒,真是糾結..).
然后這里放的就是uCGUI的源代碼了,在GUI文件夾下面.
這則是每個文件夾的功能(參考uCGUI中文手冊,ucgui.com翻譯).
大概看一下就可以了,這個跟我們移植的關系不大,關鍵點是帶*的可以不包含進去(待會配置會講到.).然后其他的都要包含進去.
接着我們要把我們的文件包含進我們已經搭建好的工程,這里說明下我們的工程要求.
一般來說,我們要畫一個圖形,最基本的就是從點開始,從點到線,從點到面...,所以在已經建好的工程里面你要能點亮你的屏幕,能點出最基本的點,能填充出最基本矩陣(這是uCGUI最包含的函數),反正我移植的時候涉及到的包括三個函數,LCD_Init();LCD_Draw_Point(x,y,color),LCD_Fillcircuit(x1,x2,y1,y2).這三個函數是必須的,后面也會說明如何把這三個函數進行填充.
當我們把文件復制進去的時候,再加上我們一開始已經創建好的工程的時候,文件結構差不多就是這個樣子了,截圖如下
user包括,main函數就是我們初始化和函數調用,繪圖用的文件,另外那幾個文件相信大家都明白了把,tft_lcd.c就是你在,沒有移植uCGUI的情況下,純液晶屏驅動,這里建議把液晶屏的API和最底層驅動(API就是畫圓啊,畫橢圓啊,清除屏幕之類的,底層驅動就是驅動液晶屏的管腳運作,fsmc初始化,時鍾配置之類的),不過我這里也是集成在一起了,比較懶,大家別學.
其他文件夾我都包含進去了,在沒包含進去的時候,編譯是可以通過的,但是,那么多文件包含進去,有些配置還是沒有設定好的.所以會有錯誤,蠻編譯一下.沒事的.
這里我們需要修改的文件只有這幾個:,
,
,這是ucgui開放給我們的用戶層的文件,在ucgui中,lcdDrive文件夾要自己加進去,GUI_X.c也是,另外三個文件都是包含了,在GUIConf.h中
1 #ifndef GUICONF_H
2 #define GUICONF_H
3
4 #define GUI_OS (0) // 這里指的是對操作系統的支持,因為我們這里只有單純的移植uCGUI,
5 // 所以,要把這個關閉,不然后面會有很多東西編譯進去
6 // 不然到時候編譯的時候會發生很多你無法修改的錯誤
7 #define GUI_SUPPORT_TOUCH (0) // 這里則是對觸摸屏的支持,觸摸屏我是能做,但是沒有用,
8 //所以省去麻煩,把觸摸屏關掉,相信移植好之后,要支持觸摸屏大家都會有門路了
9 #define GUI_SUPPORT_UNICODE (1) // UNICODE編碼支持,如果大家只是單純的想用英文顯示,而不移植中文字庫進去,
10 //這個是可以關掉的,因為UNICODE是向下支持的,所以開不開無所謂
11 // 反正都是能夠正常顯示的
12 #define GUI_DEFAULT_FONT &GUI_Font6*8 // 這里是設定默認字體的,我們可以在要寫什么字的時候把該字號的字體.
13 //c包含進我們的主函數里面,所以這里不用改
14 #define GUI_ALLOC_SIZE 5000 // 這里講的是動態內存機制
15 // 這里rgb接口模式的可能會有用到,uCGUI就是在我們的ram開辟一塊空間,
//然后uCGUI把運算好的每個點都放進我們主控ram里面的空間
16 // 所以,這里就相當於把寫進液晶gram里面的操作變成了寫進主控ram里面,
//那么大家可能就會問了,干嘛這么多次一舉,直接寫進去不就可以了
17
18 /*原理: 一般來說,在大的屏幕上面(4.0以上吧,印象中),都是沒有控制器,(像我的液晶屏就是spfd5420,當然,
19 不同的屏幕的液晶主控都是不一樣的,但是寄存器操作都是差不多的,
20 所以有些初始化配置還是能互用的.)所以呢,這時候我們要用到的就只有RGB接口了,
21 RGB要求我們要不斷的刷新屏幕,刷新率越高,效果就越好,因為一般這種用來做動態的,uCGUI就是屬於靜態類型的
22 像如果我們要用stm32主控做視頻應用的時候,就是動態的,我們需要不斷的刷新屏幕,但是當我們主控一邊運算,
23 一邊往液晶接口送數據的時候,會有明顯刷屏的感覺(運算->畫點->運算->畫點....,這個運算
24 ->運算.......畫點->畫點->畫點...是不一樣的,因為對屏幕一直畫點,填充,而中間不用插入運算,
25 刷一個屏幕時間時間倍速差別是非常巨大的,后面大家也會見識到這種差別.),所以,用GUI申請的空間里面
26 邊運算,邊填充,填充完再一次性運出去(這里可以通過DMA控制FSMC總線,不斷的從外置SRAM往GRAM自動搬運數據,
27 這是不用主控去插手的,所以,主控大部分時間是負責運算,其他時間可以空閑出來,
28 讓DMA自己去忙活),同理,因為dma跟cpu的分工,所以,這里同樣的把畫點,畫點,運算,運算不完全的分開了,
29 屏幕刷新速度非常之可觀(DMA的速度相比大家還是非常了解的,它就是為速度而生的.),*/
30
31 //#define GUI_ALLOC_SIZE 1024*1024
/* Size of dynamic memory ... For WM and memory devices*/
32
33 /*********************************************************************
34 *
35 * Configuration of available packages
36 */
37
38 #define GUI_WINSUPPORT 1 // 這個是窗口支持,一般開始開着的
39 #define GUI_SUPPORT_MEMDEV 1 // 內存控制,開
40 #define GUI_SUPPORT_AA 0 // 抗鋸齒,為了性能着想,還是關了比較好
41
42 #endif /* Avoid multiple inclusion */
GUITouchConf.h是有關於觸摸屏配置的,這里我們就略過了.
1 #ifndef LCDCONF_H
2 #define LCDCONF_H
3
4 /*********************************************************************
5 *
6 * General configuration of LCD
7 *
8 **********************************************************************
9 */
10
11 /*
12 * 這個定義的是你x軸的長度,像我這里的屏幕長為400個像素
13 */
14 #define LCD_XSIZE (400)
15
16 /*
17 * 這里這是屏幕的寬
18 */
19 #define LCD_YSIZE (240) /* Y軸長度 */
20
21 /*
22 * 這里是屏幕的顏色有多少個位
23 */
24 #define LCD_BITSPERPIXEL (16) /* 定義數據長度為16bit*/
25
26 /*
27 * 控制器類型,如果你里面有包含這些判斷變量,這個最好改成你認識的
28 */
29 #define LCD_CONTROLLER 9325 /* 定義控制器類型 */
30
31
32 #endif /* LCDCONF_H */
配置層的東西我們都已經搞定了,接下來我們要修改的是uCGUI開放給我們的用戶層的東西,GUI_X.c可以直接拷進去,這個是用戶層和系統層的關聯文件,一些demo也會用到這個文件的時間函數或者延遲函數,所以這個文件拷進去放着就可以了.
1 #include "GUI.h"
2 #include "GUI_X.h"
3
4 /*********************************************************************
5 *
6 * Global data
7 */
8 volatile int OS_TimeMS;
9
10 /*********************************************************************
11 *
12 * Timing:
13 * GUI_X_GetTime()
14 * GUI_X_Delay(int)
15
16 Some timing dependent routines require a GetTime
17 and delay function. Default time unit (tick), normally is
18 1 ms.
19 譯:一些需要時間的相關函數需要用到gettime和延遲.
20 默認時間單位為1ms.
21 */
22
23 int GUI_X_GetTime(void) {
24 return 0;
25 }
26
27 void GUI_X_Delay(int ms)
28 {
29 }
30
31 /*********************************************************************
32 *
33 * GUI_X_Init()
34 *
35 * Note:
36 * GUI_X_Init() is called from GUI_Init is a possibility to init
37 * some hardware which needs to be up and running before the GUI.
38 * If not required, leave this routine blank.
39 *
40 * 譯:GUI_X_Init()是在gui_init()調用前,gui啟動或者運行前准備.
41 * 如果不是必須的,可以把這個函數留空白.
42 */
43
44 void GUI_X_Init(void)
45 {
46
47 }
48
49
50
51 /*********************************************************************
52 *
53 * GUI_X_ExecIdle
54 *
55 * Note:
56 * Called if WM is in idle state
57 * 譯:視窗管理器空閑時候調用
58 */
59
60 void GUI_X_ExecIdle(void) {}
61
62 /*********************************************************************
63 *
64 * Logging: OS dependent
65
66 Note:
67 Logging is used in higher debug levels only. The typical target
68 build does not use logging and does therefor not require any of
69 the logging routines below. For a release build without logging
70 the routines below may be eliminated to save some space.
71 (If the linker is not function aware and eliminates unreferenced
72 functions automatically)
73 譯:系統日志層應用程序
74
75 */
76
77 void GUI_X_Log (const char *s) {}
78 void GUI_X_Warn (const char *s) {}
79 void GUI_X_ErrorOut(const char *s) {}
在uCGUI和底層驅動的接口文件時LCDDriver.c,大家打開文件夾可以看到這幾個文件:lcdwin.c,lcdnull.c,lcdDummy.c,這三個文件你隨便修改哪個都行,一開始我是直接修改lcdnull.c的,不過一開始沒經驗,一直不能正常調用我的uCGUI函數,所以,我的隊友告訴我,要修改另外一個(lcdwin.c),這次才反應過來,理論上,修改lcdnull.c也是可以的,就是一些細節性的東西可以要多注意點.下面我講解下怎么修改lcdwin.c這個文件.
源文件我先貼出來:
這個文件可能有點大,500多行,都是密密麻麻的代碼,不過,我們要想使我們的uCgui能夠使用,要修改的函數其實也就每幾句.
我先給大家講解下這里面函數的關系:
#if LCD_BITSPERPIXEL <= 8
#define PIXELINDEX U8
#else
#define PIXELINDEX WORD
#endif
這里我們定義的是16位的像素點(在前面那三個配置文件里面),定義為word.
#define SETPIXEL(x, y, c) LCDSIM_SetPixelIndex(x, y, c, LCD_DISPLAY_INDEX)
#endif
#define XORPIXEL(x, y) _XorPixel(x,y)
這里定義的是顯示點的函數,我們需要把所有LCDSIM_SetPixelIndex(x, y, c, LCD_DISPLAY_INDEX),都換成你的畫點函數,比如我這里使用的是
LCD_L0_SetPixelIndex(x軸坐標,y軸坐標,該點顏色);然后呢,就是修改LCD_L0_FillRect(int x0, int y0, int x1, int y1);這個是填充矩形函數,
你可以直接把函數里面的東西清空,然后寫上自己在底層硬件驅動的api,我的函數優化得體無完膚了,基本沒有辦法再拷貝過來,所以待會講這函數的時候,直接跳過去.
最后最重要的要修改的就是LCD_L0_Init(void);這函數里面可以直接填充你的初始化函數,好了,剩下基本就不需要修改了,其他的修改就留給優化了.
下面我給大家詳細講解下每個函數的用途
1 /*********************************************************************
2 *
3 * LCD_L0_SetPixelIndex
4 */
5 void LCD_L0_SetPixelIndex(int x, int y, int PixelIndex) {
6 /* 設定指定點顏色值 */
7 }
8
9 /*********************************************************************
10 *
11 * LCD_L0_GetPixelIndex
12 */
13 unsigned int LCD_L0_GetPixelIndex(int x, int y) {
14 /* 取得某點像素顏色值,並返回 */
15 }
16
17 /*********************************************************************
18 *
19 * LCD_L0_XorPixel
20 */
21 void LCD_L0_XorPixel(int x, int y) {
22 /* 反轉像素函數 */
23 }
24
25 /*********************************************************************
26 *
27 * LCD_L0_DrawHLine
28 */
29 void LCD_L0_DrawHLine(int x0, int y, int x1) {
30 /* 繪制水平線函數 */
31 }
32
33 /*********************************************************************
34 *
35 * LCD_L0_DrawVLine
36 */
37 void LCD_L0_DrawVLine(int x, int y0, int y1) {
38 /* 繪制垂直線函數 */
39 }
40
41 /*********************************************************************
42 *
43 * LCD_L0_FillRect
44 */
45 void LCD_L0_FillRect(int x0, int y0, int x1, int y1) {
46 /* 填充矩陣函數 */
47 }
48
49 /*********************************************************************
50 *
51 * LCD_L0_DrawBitmap
52 */
53 void LCD_L0_DrawBitmap(int x0, int y0,
54 int xsize, int ysize,
55 int BitsPerPixel,
56 int BytesPerLine,
57 const U8 GUI_UNI_PTR * pData, int Diff,
58 const LCD_PIXELINDEX* pTrans)
59 {
60 /* 畫位圖函數 */
61 }
62
63 /*********************************************************************
64 *
65 * LCD_L0_SetOrg
66 */
67 void LCD_L0_SetOrg(int x, int y) {
68 /* 該函數保留 */
69 }
70
71 /*********************************************************************
72 *
73 * LCD_On / LCD_Off
74 */
75 void LCD_On (void) {
76 /* 開啟LCD */
77 }
78 void LCD_Off(void) {
79 /* 關閉LCD */
80 }
81
82 /*********************************************************************
83 *
84 * LCD_L0_Init
85 */
86 int LCD_L0_Init(void) {
87 /* LCD初始化 */
88 }
89
90 /*********************************************************************
91 *
92 * LCD_L0_SetLUTEntry
93 */
94 void LCD_L0_SetLUTEntry(U8 Pos, LCD_COLOR Color) {
95 /* 修改 LCD 控制器的 LUT 的中的單個條目 */
96 }
97
98 #else
99
100 void LCDNull_c(void);
101 void LCDNull_c(void) {} /* 請保持這個函數為空 */
102
103 #endif
移植講到這邊差不多就結束了,因為是憑着自己的感覺寫的,所以可能很多東西講的不是很清楚,有需要我精講的可以聯系我,我會陸續修改,接下來我們開始我們的優化之旅
補充:
優化篇
這里的優化會介紹兩種模式,GPIO口模擬程序,FSMC模塊模式.
FSMC模式只有在STM32大容量模式的主控才有的,優化起來效果確實很是最好的,因為當你根據液晶屏ic配置好fsmc的時候,液晶屏的寄存器和GRAM就會被映射到系統的4G內存空間的某一塊區域,我們往指定地址寫數據或讀數據,FSMC就會自動幫你把數據送到液晶屏控制器上面了,所以這里面我們省去了對GPIO管腳和接口的操作(...這個確實很強大,因為你可以花更多的時間來進行運算,每個點都是通過指令來產生的,產生指令也是需要時間的,同樣,模擬IO口時序也是需要時間的,把這部分時間剩下來,是非常可觀的),至於一些人不理解FSMC其實也不是很打緊啦,你就把GRAM和液晶屏寄存器想象成一個完整的外置SRAM就可以了,因為他們的時序都是同一個原理的,而FSMC就是會自動幫你模擬各種IO口操作,我把它簡單的理解成(你寫的io口操作程序,他用硬件來實現了).
相信大家都有看到別人移植好的demo程序,里面最開始以來就是一個speedDEMO,這個是測試每秒鍾可以通過計算可以畫多少個點,里面包含了隨即填充矩陣函數.
分數越高,代表着你刷頻也就是越快,當然,跟着我的腳步走,可以快到把刷這個字去掉(不嚴格意義上);
在做優化的時候,我們得提前做好准備,因為優化更多代表着是結構性的破壞,可讀性的瘋狂下降,因為從最底層的函數開始一層一層封裝上去,使得函數可以很容易的去理解調用,我們優化就是拆開這些函數調用,並把一些有關硬件層的驅動進行修改,對算法進行更新.
舉個函數調用的例子,填充矩陣:
這個比較大家可看可不看,涉及到一些匯編的知識.略過對后面的理解沒有問題.
1 void fill_Rect(int v, int h, int x, int y)
2 {
3 for (i<v)
4 {
5 for (j<h)
6 {
7 Darw_Point(a,b);
8 }
9 }
10 }
11
12 void Darw_Point(int x,int y)
13 {
14 Set_LCD_reg();
15 Set_LCD_gram();
16 }
17
18 void Set_LCD_reg()
19 {
20 /* 寫入寄存器 */
21 }
22
23 void Set_LCD_gram()
24 {
25 /* 寫入顯存 */
26 }
這是我隨便寫的一個函數,我們差不多都是這樣子調用一個寫點的函數把,通過計算可得,假設我們要繪制一個100*100的矩陣,
那么就要調用10000次
Darw_Point函數,10000次Set_LCD_reg函數,10000Set_LCD_gram()函數,而每次調用一個函數的時候,要包括如下過程:
1.把要傳遞的參數壓入堆棧中
2.把上一層程序的返回地址壓入堆棧中
3.對程序的運行指針進行偏移操作,指向調用程序的入口地址
4.執行程序時候如果有需要要進行堆棧保護
5.執行程序完畢,堆棧要進行釋放,還原給調用它的函數使用
6.彈出返回地址
7.返回,並繼續執行上層程序.
而真正的,我們執行程序的時候,嵌套了那么多層的調用,執行的只有一個寄存器賦值,所以系統更多時候是在瘋狂的進行壓棧和出棧活動,
如果我們把這些時間都去掉,
效率就是呈現幾何倍數增長,這種是毋庸置疑的.
出了通過對函數進行拆解,算法和硬件的結合也是非常重要的,像你畫個矩形,一個通過點來填充,通過直線來填充,甚至,
為什么你沒有想到通過矩陣來填充呢?有時候想法
就是這樣,這在很多IC上是可以很輕松的實現的,前提是你得通讀datasheet.
具體的我通過函數來為大家進行一一講解:
這是優化后的填充點函數:
/*********************************************************************已經最優化
*
* LCD_L0_SetPixelIndex
*
* Purpose:
* Writes 1 pixel into the display.
*/
void LCD_L0_SetPixelIndex(int x, int y, int ColorIndex)
{
/* 填充x軸坐標和y軸坐標 */
*(__IO uint16_t *)(Bank4_LCD_C) = 0x0200;
*(__IO uint16_t *)(Bank4_LCD_D) = y;
*(__IO uint16_t *)(Bank4_LCD_C) = 0x0201;
*(__IO uint16_t *)(Bank4_LCD_D) = 399 - x;
/* 寫顯存前准備 */
*(__IO uint16_t *)(Bank4_LCD_C) = 0x0202;
/* 寫入數據 */
*(__IO uint16_t *)(Bank4_LCD_D) = ColorIndex;
}
與優化前的進行對比:這里的的數據全部改成對地址進行操作(BANk4_LCD_D和C都是被映射了的內存地址).模擬IO口可能會有差別
大家在進行寫函數的時候,對管腳進行配置都是直接調用庫函數的setbit或者resetbit來進行的,我們可以直接查詢庫函數,對寄存器進行操作:
void GPIO_SetBits ( GPIO_TypeDef * GPIOx,
uint16_t GPIO_Pin
)
Sets the selected data port bits.
參數:
GPIOx,: where x can be (A..G) to select the GPIO peripheral.
GPIO_Pin,: specifies the port bits to be written. This parameter can be any combination of GPIO_Pin_x where x can be (0..15).
返回值:
None
在文件stm32f10x_gpio.c第358行定義。
參考 GPIO_TypeDef::BSRR、IS_GPIO_ALL_PERIPH及IS_GPIO_PIN.
{
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Pin));
GPIOx->BSRR = GPIO_Pin;
}
庫函數大家可以字節查詢源代碼,最后修改后demo代碼如下:
#ifdef HARDWARE_PLATFORM_ALI
//0~15 作為數據線
#define LCD_DATA_BUS GPIO_Pin_All
//片選信號
// #define LCD_CS_LOW GPIO_ResetBits(GPIOC, GPIO_Pin_9)
// #define LCD_CS_HIGH GPIO_SetBits(GPIOC, GPIO_Pin_9)
// Ver.HXH
#define LCD_CS_LOW GPIOC->BRR = GPIO_Pin_9;
#define LCD_CS_HIGH GPIOC->BSRR = GPIO_Pin_9;
//數據/命令
// #define LCD_RS_LOW GPIO_ResetBits(GPIOC, GPIO_Pin_8)
// #define LCD_RS_HIGH GPIO_SetBits(GPIOC, GPIO_Pin_8)
//Ver.HXH
#define LCD_RS_LOW GPIOC->BRR = GPIO_Pin_8;
#define LCD_RS_HIGH GPIOC->BSRR = GPIO_Pin_8;
//End.HXH
//寫數據
// #define LCD_WR_LOW GPIO_ResetBits(GPIOC, GPIO_Pin_7)
// #define LCD_WR_HIGH GPIO_SetBits(GPIOC, GPIO_Pin_7)
//Ver.HXH
#define LCD_WR_LOW GPIOC->BRR = GPIO_Pin_7;
#define LCD_WR_HIGH GPIOC->BSRR = GPIO_Pin_7;
//End.HXH
//讀數據
// #define LCD_RD_LOW GPIO_ResetBits(GPIOC, GPIO_Pin_6)
// #define LCD_RD_HIGH GPIO_SetBits(GPIOC, GPIO_Pin_6)
//Ver.HXH
#define LCD_RD_LOW GPIOC->BRR = GPIO_Pin_6;
#define LCD_RD_HIGH GPIOC->BSRR = GPIO_Pin_6;
//End.HXH
//PB0~15,作為數據線
#define DATAOUT(x) GPIOB->ODR=x; //數據輸出
#define DATAIN GPIOB->IDR; //數據輸入
#endif
把對管腳置高置底進行的操作完全跟改成寄存器操作,當然,你也可以改成對寄存器指針進行操作,不過效率是一樣的,因為define的效果是復制,所以,通過觀察源代碼:
00127 #define GPIO_Pin_0 ((uint16_t)0x0001)
00128 #define GPIO_Pin_1 ((uint16_t)0x0002)
00129 #define GPIO_Pin_2 ((uint16_t)0x0004)
00130 #define GPIO_Pin_3 ((uint16_t)0x0008)
00131 #define GPIO_Pin_4 ((uint16_t)0x0010)
所以,當程序在編譯的時候,也是把地址進行簡單的拷貝,所以這部分功夫是可以完全剩下來的.
接下來是關於填充矩陣的函數操作:
/*********************************************************************
*
* LCD_L0_FillRect
*/
void LCD_L0_FillRect(int x0, int y0, int x1, int y1)
{
/* 最后修改.2011.7.23 */
for (; y0 <= y1; y0++)
{
int x;
/* 填充x軸坐標和y軸坐標 */
*(__IO uint16_t *)(Bank4_LCD_C) = 0x0200;
*(__IO uint16_t *)(Bank4_LCD_D) = y0;
*(__IO uint16_t *)(Bank4_LCD_C) = 0x0201;
*(__IO uint16_t *)(Bank4_LCD_D) = 399 - x0;
/* 寫顯存前 准備 */
*(__IO uint16_t *)(Bank4_LCD_C) = 0x0202;
/* 開始寫入顯存 */
x=x0;
for (;x0 <= x1; x0++)
{
*(__IO uint16_t *)(Bank4_LCD_D) = LCD_COLORINDEX;
}
x0=x;
}
// /* 最后修改.2011.7.26 */
// u32 n;
// /* 設定窗口 */
// *(__IO uint16_t *)(Bank4_LCD_C) = 0x0210;
// *(__IO uint16_t *)(Bank4_LCD_D) = y0;
// *(__IO uint16_t *)(Bank4_LCD_C) = 0x0211;
// *(__IO uint16_t *)(Bank4_LCD_D) = y1;
// *(__IO uint16_t *)(Bank4_LCD_C) = 0x0212;
// *(__IO uint16_t *)(Bank4_LCD_D) = 399-x1;
// *(__IO uint16_t *)(Bank4_LCD_C) = 0x0213;
// *(__IO uint16_t *)(Bank4_LCD_D) = 399-x0;
// /* 設定開始位置 */
// *(__IO uint16_t *)(Bank4_LCD_C) = 0x0200;
// *(__IO uint16_t *)(Bank4_LCD_D) = y0;
// *(__IO uint16_t *)(Bank4_LCD_C) = 0x0201;
// *(__IO uint16_t *)(Bank4_LCD_D) = 399-x0;
// /* 進行填充 */
// n = (u32)(y1-y0+1)*(x1-x0+1);
// *(__IO uint16_t *)(Bank4_LCD_C) = 0x0202;
// while (n--)
// {
// *(__IO uint16_t *)(Bank4_LCD_D) = LCD_COLORINDEX;
// }
// /* 恢復窗口 */
// *(__IO uint16_t *)(Bank4_LCD_C) = 0x0210;
// *(__IO uint16_t *)(Bank4_LCD_D) = 0x0000;
// *(__IO uint16_t *)(Bank4_LCD_C) = 0x0211;
// *(__IO uint16_t *)(Bank4_LCD_D) = 0x00EF;
// *(__IO uint16_t *)(Bank4_LCD_C) = 0x0212;
// *(__IO uint16_t *)(Bank4_LCD_D) = 0x0000;
// *(__IO uint16_t *)(Bank4_LCD_C) = 0x0213;
// *(__IO uint16_t *)(Bank4_LCD_D) = 0x018f;
}
這里有關於兩種填充方式,都是避開函數操作,被注釋掉的是對窗口進行操作的,而沒被注釋掉的是對線條進行填充.
對線條進行操作的相信大家應該非常了解了,這里詳細解釋下對窗口進行操作的一些細節:
窗口:也就是可以進行填充的區域,液晶驅動里面,每個物理像素對應的坐標已經是固定的,但是窗口可以不固定,窗口就是可以進行填充的區域,你如果要在窗口外面
進行填充,是無法進行的,同樣的,當你填充到窗口邊緣的時候,會自動跳轉到下一行進行填充,只要你設定的點正確,那么整個你設定的窗口區域都會被填充完畢,這段期間
你要做的知識單純的填充數據,不需要進行設定點的操作,也不需要換行,這樣子屏幕填充矩陣操作看起來效果就不會有刷屏的感覺了.
填充行:對行進行填充,只需要在換行的時候進行坐標切換,我用整個函數,慢了30萬個點每秒把.
在優化的時候,我只是拋磚引玉的給大家介紹下怎么用什么樣的思路進行優化,細節性的東西還是要大家好好去琢磨的.
初稿到這邊就差不多結束了,后面會陸續補充,只要大家想知道都可以直接進行聯系.