[Netduino Plus 2]_替Netduino Plus 2 接上ILI9341 320*240 SPI TFT 液晶螢幕

本篇主要筆記如何將SPI TFT螢幕,接上NETDUINO

 

其中TFT Driver是由http://forums.netduino.com/index.php?/topic/11458-ledlcd-tft-screen/

論壇內,作者studlyed所撰寫的,Github位置在https://github.com/veccsolutions/Vecc.Netduino.Drivers.Ili9341

 

我fork出來,做一些修改跟線路說明,還有一些變動的部分:

https://github.com/thkaw/Vecc.Netduino.Drivers.Ili9341

可以先在看下面文章之前,先把我FORK的版本先抓回來,另外Netduino應該也適用,

但記得要把Vecc.Netduino.Drivers.Ili9341.Sandbox\Progame.cs 在using的部分改成using SecretLabs.NETMF.Hardware.Netduino;,

並且記得去專案裡面includ SecretLabs.NETMF.Hardware.Netduino

 


 

I. Wiring

先來講接線好了,這部分我參照alesbedac在官方論壇發的修改的專案,在論壇上可以下載到ILI9341 ND2 Lib.zip

http://forums.netduino.com/index.php?/topic/10785-tft-lcd-ili9341/

 

另外這個版本是studlyed初期引用的版本,後來studlyed替這個lib增強並整理了許多的功能,我也有用alesbedac的版本簡單改了一下

也可以順利運行在netduino plus 2上頭,

放在Bitbucket上:https://bitbucket.org/thkaw/ili9341-nd2-lib-modify

建議也並抓下來,之後再trace code有些參數在alesbedac的版本會比較好懂。

 

但這兩個lib相近之處很像,不過studlyed多了一些字形設定還有封裝的比較完整,所以下文都是用studlyed的版本做

不過接線的部分則是參考alesbedac,所以有改一些接口位置的程式碼及微調細節

 

接線的部分,先來看一下這塊MODULE:

T2UdgjXalaXXXXXXXX_!!39773402.png

 

左邊的四根PIN是 FOR SD卡的,我就不講了,也是走SPI介面

右邊才是面板控制等訊號的腳位,由上至下分別為:

  1. SDO(MISO),不用接,因為沒有資料需要Slave out
  2. LED,背光源,可以直接接到3.3V或者接到Netduino plus 2的任意DIGITAL腳位,可以用專案內的程式控制LED開啟或關閉,我是直接吃3.3V
  3. SCK,SPI CLOCK,接到DIGITAL 13
  4. SDI(MOSI),接到DGITIAL 11
  5. D/C,就是Data Command的意思,接到ANALOG 2
  6. RESET,接到ANALOG 1
  7. CS,CHIP SELECT,接到DIGITAL 10
  8. GND,隨你接GND…
  9. VCC,請接3.3V

 

關於Netduino SPI接腳,可以支援3組SPI裝置,預設我是走第一組,如果要換成別組,請自行更改CS(CHIP SELECT)的接腳

第一組是D10,第二組是D9,第三組是D8。

詳細說明請參考:http://wiki.netduino.com/SPI.ashx

 

 

接腳OK之後,打開專案先直接執行看看螢幕有沒有跑出下面的畫面:

WP_20150602_004

 

如果WORK,可以繼續看下去,如果不WORK,檢查接線…然後還是繼續看下去…

如果遇到螢幕顛倒或者水平垂直相反的問題,也請繼續看下去


 

 

II. Code trace/modify

 

方案打開之後會看到兩個專案Vecc.Netduino.Drivers.Ili9341,Vecc.Netduino.Drivers.Ili9341.Sandbox

主要的Program在Vecc.Netduino.Drivers.Ili9341.Sandbox

 

而Vecc.Netduino.Drivers.Ili9341下面則是Lib, Driver, Font等Source code

 

Vecc.Netduino.Drivers.Ili9341.Sandbox\Program.cs的內容非常簡單:

using SecretLabs.NETMF.Hardware.NetduinoPlus;
using System.Threading;

namespace Vecc.Netduino.Drivers.Ili9341.Sandbox
{
    public class Program
    {
        public static void Main()
        {
            var tft = new Driver(isLandscape: false,
                                 lcdChipSelectPin: Pins.GPIO_PIN_D10,
                                 dataCommandPin: Pins.GPIO_PIN_A2,
                                 resetPin: Pins.GPIO_PIN_A1);
            var font = new StandardFixedWidthFont();
            tft.ClearScreen();
            tft.DrawString(10, 10, "NETDUINO PLUS 2", 0xF800, font);
            tft.DrawString(10, 50, "ILI9341 TFT TESTING PASS!", 0xF800, font);
            tft.DrawString(10, 90, "  WWW.NTEX.TW", 0xF800, font); 
            tft.DrawLine(20, 170, 210, 170, 0x0F00);
            tft.DrawRectangle(20, 180, 200, 50, 0x0f00);
            tft.FillScreen(22, 218, 180, 230, 0xfff0); 
            tft.BacklightOn = true;

            int i = 0;
            while (true) {
                Thread.Sleep(1000);
                tft.DrawString(10, 90, "  WWW.NTEX.TW  ", 0xF800, font);
                Thread.Sleep(1000);
                tft.DrawString(10, 90, ">>WWW.NTEX.TW<<", 0xF800, font);
                tft.DrawString(10, 120, i.ToString(), 0x0FF0, font);
            i++;
            }
        }
    }
}

 

如果你要換接腳接,在new Driver()帶入的建構參數需要跟著一併變動,

lcdChipSelectionPin等於TFT上的CS腳位

dataCommanPin等於D/C腳位

 

另外如果透過new Driver出來的tft物件,下面的function在Visual Studio裡面會顯示參數名稱,不難猜出他的使用方式

我就不一一細提。

 

接著讓我們看回Vecc.Netduino.Drivers.Ili9341\Driver.cs的建構子部分:

        public Driver(bool isLandscape = false,
                      Cpu.Pin lcdChipSelectPin = Cpu.Pin.GPIO_NONE,
                      Cpu.Pin dataCommandPin = Cpu.Pin.GPIO_NONE,
                      Cpu.Pin resetPin = Cpu.Pin.GPIO_NONE,
                      Cpu.Pin backlightPin = Cpu.Pin.GPIO_NONE,
                      uint spiClockFrequency = 1311,
                      SPI.SPI_module spiModule = SPI.SPI_module.SPI1)
        {
            _spi = new SPI(new SPI.Configuration(lcdChipSelectPin, // CS-pin
                                                 false,  // CS-pin active state
                                                 0,      // The setup time for the SS port
                                                 0,      // The hold time for the SS port
                                                 false,  // The idle state of the clock
                                                 true,   // The sampling clock edge
                                                 spiClockFrequency,   // The SPI clock rate in KHz
                                                 spiModule   // The used SPI bus (refers to a MOSI MISO and SCLK pin set)
                                                 ));
            _dataCommandPort = new OutputPort(dataCommandPin, false);
            if (resetPin != Cpu.Pin.GPIO_NONE)  //Poundy: changed to remove netduino dependencies
            {
                _resetPort = new OutputPort(resetPin, false);
            }
            else
            {
                _resetPort = null;
            }
            if (backlightPin != Cpu.Pin.GPIO_NONE) //Poundy: changed to remove netduino dependencies
            {
                _backlightPort = new OutputPort(backlightPin, false);
            }
            else
            {
                _backlightPort = null;
            }
            InitializeScreen();
            SetOrientation(isLandscape);
        }

我改了SPI的速度,從原本的20000HZ調整到1311,這個調整是參照alesbedac以及模組資料得到

 

並且還需要修改一樣是在Driver.cs內,有一個全域變數「lcdPortraitConfig」,否則在垂直顯示的時候,會變成鏡像,由原本的8改為72

const byte lcdPortraitConfig = 72; 

 

如果你的螢幕跟我用同樣晶片但方向不一樣,這個數值可以從alesbedac版本的ILI9341driver.cs找到同名的變數名稱,但在alesbedac的版本,這個數值是透過跟螢幕溝通所取得:

        static byte lcdPortraitConfig = lcdBuildMemoryAccessControlConfig(
                                                    MemoryAccessControlNormalOrder, // rowAddressOrder
                                                    MemoryAccessControlReverseOrder, // columnAddressOrder
                                                    MemoryAccessControlNormalOrder, // rowColumnExchange
                                                    MemoryAccessControlNormalOrder, // verticalRefreshOrder
                                                    MemoryAccessControlColorOrderRGB, // colorOrder
                                                    MemoryAccessControlNormalOrder); // horizontalRefreshOrder

        static byte lcdLandscapeConfig = lcdBuildMemoryAccessControlConfig(
                                                          MemoryAccessControlNormalOrder, // rowAddressOrder
                                                          MemoryAccessControlNormalOrder, // columnAddressOrder
                                                          MemoryAccessControlReverseOrder, // rowColumnExchange
                                                          MemoryAccessControlReverseOrder, // verticalRefreshOrder
                                                          MemoryAccessControlColorOrderRGB, // colorOrder
                                                          MemoryAccessControlReverseOrder); // horizontalRefreshOrder

        static byte lcdBuildMemoryAccessControlConfig(bool rowAddressOrder, bool columnAddressOrder, bool rowColumnExchange,
                        bool verticalRefreshOrder, bool colorOrder, bool horizontalRefreshOrder)
        {
            byte value = 0;
            if (horizontalRefreshOrder) value |= 0x0004;
            if (colorOrder) value |= 0x0008;
            if (verticalRefreshOrder) value |= 0x0010;
            if (rowColumnExchange) value |= 0x0020;
            if (columnAddressOrder) value |= 0x0040;
            if (rowAddressOrder) value |= 0x0080;

            return value;
        }

可以下中斷點在lcdBuildMemoryAccessControlConfig()裡的return value,看一下合適的數字是多少,

而studlyed則是直接寫死,所以遇到了我用他的lib運作會有鏡像,需要改一些code修正的問題。

 

 

若你在studlyed版本遭遇在建構子明明寫LandScape: false,但她卻還是給你橫向顯示時,可以檢查Driver.cs裡面的SetOrientation()

        public void SetOrientation(bool isLandscape)
        {
            lock (this)
            {
                _isLandscape = isLandscape;
                SendCommand(Commands.MemoryAccessControl);

                if (isLandscape)
                {
                    SendData(lcdLandscapeConfig);
                    Width = 320;
                    Height = 240;
                }
                else
                {
                    SendData(lcdPortraitConfig);
                    Width = 240;
                    Height = 320;
                }

                SetWindow(0, Width - 1, 0, Height - 1);
            }
        }

把有問題的Width跟Height數值互換,即可解決。

 

以上,需要更複雜的圖片可能要自己用SETPIXEL去做了,

我也算第一次摸TFT LCD~~發現要顯示圖片沒有想像中的難~~

之後有進階作法再補充了~

 

以上~

 

Leave a comment

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料