LCD驅動其實對TinyCLR並沒有必要,特別是在EM-STM3210E開發板上,由於該開發板上的內存過小了,片內64K,片外擴展了128K,加起來也不過172K,而咱們知道針對320*240的顯示大小,16bit的位圖所佔的大小就是150K,很顯然.Net Micro Framework所提供的圖形庫如不加修改是很難正常運行的,不過對咱們來講在LCD屏幕上顯示文字信息也是值得期待的,若是修改一下圖形庫,在LCD上畫個線、畫個圓和顯示個位圖也毫不成問題。c++
和咱們之前開發的驅動相比,LCD的驅動開發仍是比較繁瑣一些的,由於LCD的驅動代碼分散在三個目錄中(題外話,我以爲針對.Net Micro Framework來講,最難的驅動是網卡驅動(特別是wifi驅動)、其次是USB驅動,和它們相比,LCD驅動就是小菜了)。ide
和其它驅動相似,在具體寫LCD驅動以前,咱們先在CortexM3.h頭文件裏,寫一個和LCD寄存器相關的結構體,以便以於操做LCD寄存器,這種作法其實也是.Net Micro Framework驅動代碼的一種風格。函數
- struct CortexM3_LCD
- {
- //LCD /CS is CE4 - Bank 4 of NOR/SRAM Bank 1~4
- static const UINT32 c_Base = 0x6C000000;
- /****/ volatile UINT16 REG;
- /****/ volatile UINT16 RAM;
- void WriteReg(UINT8 Reg,UINT16 Value)
- {
- REG = Reg;
- RAM = Value;
- }
- UINT16 ReadReg(UINT8 Reg)
- {
- REG = Reg;
- return RAM;
- }
- void SetCursor(UINT16 x,UINT16 y)
- {
- WriteReg(32,x);
- WriteReg(33,y);
- }
- void SetPixel(UINT16 x,UINT16 y,UINT16 c)
- {
- WriteReg(32,x);
- WriteReg(33,y);
- WriteReg(34,c);
- }
- void WriteRAM_Prepare()
- {
- REG = 34;
- }
- };
首先咱們在\DeviceCode\Targets\Native\CortexM3\DeviceCode目錄建立LCD子目錄,該驅動文件其實實現的功能很簡單,歸結起來就以下四個函數。測試
- LCD_Controller_Initialize LCD初始化
- LCD_Controller_Uninitialize
- LCD_Controller_Enable LCD使能
- LCD_GetFrameBuffer 得到LCD顯示緩衝區首地址
可是對咱們的ILI9320 LCD來講,LCD_Controller_Enable和LCD_GetFrameBuffer都不是必要的,從咱們開發板附送的LCD示例上看,LCD顯示緩衝區首地址的概念彷佛是不存在的,咱們直接寫RAM便可,至於寫入座標是經過代碼 WriteReg(32,x)和WriteReg(33,y)來控制(其實LCD驅動並不像我想象的那麼簡單,有不少功能須要仔細研究技術手冊才能發現,不過爲了簡單起見,咱們先按示例提供的方式來顯示)。優化
在LCD_Controller_Initialize初始化代碼中須要幾個延時(我在寫SysTick驅動的文章中提到了這一點),以下代碼所示:this
- CortexM3_LCD &LCD = CortexM3::LCD();
- HAL_Time_Sleep_MicroSeconds_InterruptEnabled(50000);
- LCD.WriteReg(227, 0x3008); // Set internal timing
- LCD.WriteReg(231, 0x0012); // Set internal timing
- LCD.WriteReg(239, 0x1231); // Set internal timing
- LCD.WriteReg(1 , 0x0100); // set SS=1:0x0100 and SM=0:0x0400 bit
- LCD.WriteReg(2 , 0x0700); // set 1 line inversion
- LCD.WriteReg(3 , 0x1030); // set GRAM write direction and BGR=1.
- //放縮 0x0000 無 0x0001 1/2 0x0003 1/4
- LCD.WriteReg(4 , 0x0000); // Resize register
- LCD.WriteReg(8 , 0x0207); // set the back porch and front porch
- LCD.WriteReg(9 , 0x0000); // set non-display area refresh cycle ISC[3:0]
- LCD.WriteReg(10 , 0x0000); // FMARK function
- //0x0000 18bit RGB接口 0x0001 16bit RGB接口 0x0002 6bit RGB接口
- LCD.WriteReg(12 , 0x0000); // RGB interface setting
- LCD.WriteReg(13 , 0x0000); // Frame marker Position
- LCD.WriteReg(15 , 0x0000); // RGB interface polarity
- /**************Power On sequence ****************/
- LCD.WriteReg(16 , 0x0000); // SAP, BT[3:0], AP, DSTB, SLP, STB
- LCD.WriteReg(17 , 0x0007); // DC1[2:0], DC0[2:0], VC[2:0]
- LCD.WriteReg(18 , 0x0000); // VREG1OUT voltage
- LCD.WriteReg(19 , 0x0000); // VDV[4:0] for VCOM amplitude
- HAL_Time_Sleep_MicroSeconds_InterruptEnabled(200000); // Delay 200 MS , Dis-charge capacitor power voltage
- LCD.WriteReg(16 , 0x1690); // SAP, BT[3:0], AP, DSTB, SLP, STB
- LCD.WriteReg(17 , 0x0227); // R11H=0x0221 at VCI=3.3V, DC1[2:0], DC0[2:0], VC[2:0]
- HAL_Time_Sleep_MicroSeconds_InterruptEnabled(50000); // Delay 50ms
- LCD.WriteReg(18 , 0x001D); // External reference voltage= Vci;
- HAL_Time_Sleep_MicroSeconds_InterruptEnabled(50000); // Delay 50ms
- LCD.WriteReg(19 , 0x0800); // R13H=1D00 when R12H=009D;VDV[4:0] for VCOM amplitude
- LCD.WriteReg(41 , 0x0014); // R29H=0013 when R12H=009D;VCM[5:0] for VCOMH
- LCD.WriteReg(43 , 0x000B); // Frame Rate = 96Hz
- HAL_Time_Sleep_MicroSeconds_InterruptEnabled(50000); // Delay 50ms
- LCD.WriteReg(32 , 0x0000); // GRAM horizontal Address
- LCD.WriteReg(33 , 0x0000); // GRAM Vertical Address
對NativeSample項目來講,在debug版本直接使用HAL_Time_Sleep_MicroSeconds_InterruptEnabled是沒有問題的,可是在Release版本上面的函數前面要加一句GLOBAL_LOCK(irq)代碼,也就是循環期間要關閉中斷,才能正常運行。不過不管加不加這句代碼,在TinyCLR項目中,Release版本也是沒法正常運行的,甚至是直接用for循環也不行,不知道MDK的編譯優化到底改變了些什麼,有時間須要深刻研究一下(固然上班以後,我也能夠在RVDS上調試看看,有時候就是這樣,一樣的代碼用MDK調試通不過,可是用RVDS就沒有問題)。spa
接下來咱們要在\DeviceCode\Drivers\Display目錄下新建ILI9320子目錄,在該目錄的代碼中咱們實現文字顯示(固然指西文字符,要顯示漢字,在如此小的內存中咱們得須要特別的技巧)。插件
詳細的代碼這裏我就不貼了,不過要特別說明的是,EM-STM3210E的開發板有些問題,LCD實際上是倒着裝的,按開發板的測試示例來移植咱們的代碼,最終的顯示是倒的,以下圖所示:debug
因此咱們一是要重設LCD相應的寄存器,把顯存翻轉過來,代碼以下:3d
- /* 數據顏色序(BGR): 0x1000 BGR 0x0000 RGB */
- /* 掃描方向(AM): 0x0008 從右到左 0x0000 從上到下 */
- /* 數據填充方向(I/D):0x0000 0x0010 0x0020 0x0030 */
- LCD.WriteReg(3 , 0x1000 | 0x0000 ); //0x0030 正常模式
其次是修改WriteChar函數裏的代碼,讓它適應這個變化,修改後的代碼以下,注意要用LCD的寬度和高度值減去x,y的座標。
- const UINT8* font = Font_GetGlyph( c );
- CortexM3_LCD &LCD = CortexM3::LCD();
- int cx=0,ry=0;
- for(int y = 0; y < Font_Height(); y++)
- {
- for(int x = 0; x < Font_Width(); x+=2)
- {
- cx=g_ILI9320_Config.ControllerConfig.Width-(col+x)-1;
- ry=g_ILI9320_Config.ControllerConfig.Height-(row+y)-1;
- // the font data is mirrored
- if(ILI9320_GETBIT(Font_Width() - x ,y,font,1)) LCD.SetPixel(cx, ry,0x07e0);
- else LCD.SetPixel(cx, ry,0);
- if(ILI9320_GETBIT(Font_Width() - (x+1),y,font,1)) LCD.SetPixel(cx-1, ry,0x07e0);
- else LCD.SetPixel(cx-1, ry,0);
- }
- }
修改代碼後,咱們的顯示就已經翻轉了,以下圖所示:
最後咱們還要在\Solutions\STM3210E\DeviceCode目錄下新建子目錄Display,在該目錄下新建ILI9320_config.cpp文件,該文件主要完成LCD的一些參數配置,主要配置以下:
- #define ILI9320_SCREEN_WIDTH 240
- #define ILI9320_SCREEN_HEIGHT 320
- #define ILI9320_ENABLE_TFT TRUE
- #define ILI9320_ENABLE_COLOR TRUE
- #define ILI9320_PIXEL_POLARITY FALSE
- #define ILI9320_FIRST_LINE_POLARITY FALSE
- #define ILI9320_LINE_PULSE_POLARITY FALSE
- #define ILI9320_SHIFT_CLK_POLARITY FALSE
- #define ILI9320_OUTPUT_ENABLE_POLARITY FALSE
- #define ILI9320_CLK_IDLE_ENABLE TRUE
- #define ILI9320_CLK_SELECT_ENABLE TRUE
- #define ILI9320_PIXELCLOCKDIVIDER 9
- #define ILI9320_BUS_WIDTH 16
- #define ILI9320_BITS_PER_PIXEL 16
- #define ILI9320_ORIENTATION 0
這時候也許有人會問,何須這麼麻煩,把一個LCD驅動分解到三個目錄中,都放在一個目錄行不行? 固然這是能夠的,這樣作的目的就是,針對不一樣開發板,有些代碼能夠方便地複用。
開發完LCD驅動,若是僅僅顯示文字,不顯示一個位圖,總以爲缺乏點什麼。說幹就幹,我顯示一個320*240的位圖。
但提及來容易作起來難,一個16bit的320*240的位圖要150k,先不說如何去顯示它,光說它放在什麼地方,就是一個頭疼的地方,若是把它編寫到代碼中,咱們的片內Flash才512k,很顯然放不下,放到NandFlash中,咱們沒有實現文件系統,在說也沒有實現UsbMassStorage功能,咱們也沒法把圖片拷貝到NandFlash中。
幸虧在開發wifi驅動過程當中我實現了一個MFDeploy的插件,經過該插件能夠把位圖下載到NandFlash中(由於Ti提供的wifi開發板,wifi初始化時須要加載三個文件,原先是放在文件系統中的,單這樣作代價高了點,不只要實現文件系統,還要實現UsbMassStorage功能,不然文件也是無法放到NandFlash上的,因此纔開發了這個插件)。至於這個插件是如何實現的,我在之後準備要寫的【玩轉.Net MF】系列文章中我會詳細介紹的。
在下載位圖以前,咱們先作一張320*240的位圖,保存時要作以下選擇,以下圖:
要選擇R5G6B5,此外要勾選翻轉行序(也能夠不勾選,不過代碼要作些調整,不然圖形是倒的)。
咱們的實現思路是,從NandFlash中先讀一部分數據,而後再顯示一部分圖形,最終完成一幅圖的顯示,這樣就避開了內容小的問題。
LCD驅動中位圖顯示函數LCD_BitBltEx中的代碼以下:
- UINT16 * StartOfLine_src = (UINT16 *)&data[0];
- CortexM3_LCD &LCD = CortexM3::LCD();
- if(x==0 && y==0) LCD.SetCursor(g_ILI9320_Config.ControllerConfig.Width-1,g_ILI9320_Config.ControllerConfig.Height-1);
- LCD.WriteRAM_Prepare();
- for(int i=0;i<width*height;i++)
- {
- LCD.RAM = *StartOfLine_src++;
- }
NativeSample.cpp中的測試代碼以下:
- BYTE bytReadData[15360];
- UINT32 index=0x00520046;
- BlockStorageDevice *device= BlockStorageList::GetFirstDevice();
- for(int y=0;y<320;y+=32)
- {
- device->Read(index,15360,bytReadData);
- LCD_BitBltEx(0,y,240,32,(UINT32 *)bytReadData);
- index+=15360;
- }
最終的效果圖以下: