Hello Gus,
Here is a link for a SPI version.
My displays have no touch chip but it does not really matter. As you write in your issue, it’s all about write-only. So, no read involved.
By the way, the initial issue was more about the 64K limit, not SPI speed. But if both can be improved, I take !
And about my lazyness, well… you’re not that far from the truth 
Anyway, thank you for taking care of this request !
Edit : here the code. It’s still work in progress but the main functions are fine.
using System;
using System.Diagnostics;
using System.Threading;
using GHIElectronics.TinyCLR.Devices.Gpio;
using GHIElectronics.TinyCLR.Devices.Spi;
namespace MBN.Modules.ILI9341
{
public enum ILI9341CommandId : Byte
{
SWRESET = 0x01, // Software Reset
SLPOUT = 0x11, // Sleep Out
INVOFF = 0x20, // Display Inversion Off
INVON = 0x21, // display Inversion On
GAMMASET = 0x26, // Digital Gamma Control
DISPOFF = 0x28, // Display Off
DISPON = 0x29, // Display On
CASET = 0x2A, // Column Address Set
PASET = 0x2B, // Page Address Set
RAMWR = 0x2C, // Memory Write
MADCTL = 0x36, // Memory Access control
PIXFMT = 0x3A, // Pixel Color Format
FRMCTR1 = 0xB1, // Frame Control 1 for normal dispay
DFUNCTR = 0xB6, // Display Function Control
EMSET = 0xB7, // Entry Mode Set
MADCTL_MY = 0x80,
MADCTL_MX = 0x40,
MADCTL_MV = 0x20,
MADCTL_BGR = 0x08,
MADCTL_RGB = 0x00
}
public class ILI9341Controller
{
private readonly byte[] buffer1 = new byte[1];
private readonly byte[] buffer4 = new byte[4];
private byte[] buffer;
private readonly SpiDevice spi;
private readonly GpioPin control;
private readonly GpioPin reset;
private int bpp = 16;
private bool rowColumnSwapped;
public int Width
{
get; private set;
}
public int Height
{
get; private set;
}
public int Orientation
{
get; private set;
}
public int MaxWidth => rowColumnSwapped ? 320 : 240;
public int MaxHeight => rowColumnSwapped ? 240 : 320;
public ILI9341Controller(Hardware.Socket socket)
{
spi = SpiController.FromName(socket.SpiBus).GetDevice(new SpiConnectionSettings()
{
ChipSelectType = SpiChipSelectType.Gpio,
ChipSelectLine = GpioController.GetDefault().OpenPin(socket.Cs),
Mode = SpiMode.Mode0,
ClockFrequency = SpiController.FromName(socket.SpiBus).MaxClockFrequency
});
Debug.WriteLine($"SPI freq = {spi.ConnectionSettings.ClockFrequency}");
var backlight = GpioController.GetDefault().OpenPin(socket.PwmPin);
backlight.SetDriveMode(GpioPinDriveMode.Output);
backlight.Write(GpioPinValue.High);
control = GpioController.GetDefault().OpenPin(socket.AnPin);
control.SetDriveMode(GpioPinDriveMode.Output);
reset = GpioController.GetDefault().OpenPin(socket.Rst);
reset.SetDriveMode(GpioPinDriveMode.Output);
Width = 240;
Height = 320;
Reset();
Initialize();
SetActiveWindow(0, 0, MaxWidth, MaxHeight);
Enable();
}
private void Reset()
{
reset.Write(GpioPinValue.Low);
Thread.Sleep(50);
reset.Write(GpioPinValue.High);
Thread.Sleep(200);
}
private void Initialize()
{
SendCommand(ILI9341CommandId.SWRESET); // Software Reset
Thread.Sleep(10);
SendCommand(ILI9341CommandId.DISPOFF); // Display Off
SendCommand(ILI9341CommandId.MADCTL); // Memory Access Control
SendData(0x08 | 0x40); // Set initially to Portrait Orientation.
SendCommand(ILI9341CommandId.PIXFMT);
SendData(0x55); //16-bits per pixel
SendCommand(ILI9341CommandId.FRMCTR1); // Frame Control 1
SendData(0x00);
SendData(0x1B);
SendCommand(ILI9341CommandId.GAMMASET); // Set Digital Gamma Control
SendData(0x01); // Use Gamma Set 1
SendCommand(ILI9341CommandId.CASET); // Width of the screen 239 (0 - 239) or 240 pixels wide.
SendData(0x00);
SendData(0x00);
SendData(0x00);
SendData(0xEF);
SendCommand(ILI9341CommandId.PASET); // Width of the screen 319 (0 - 319) or 320 pixels high.
SendData(0x00);
SendData(0x00);
SendData(0x01);
SendData(0x3F);
SendCommand(ILI9341CommandId.EMSET); // Entry Mode Set
SendData(0x07); // Low voltage detect - disable, Normal On
SendCommand(ILI9341CommandId.DFUNCTR); // Display function Control
SendData(0x0A);
SendData(0x82);
SendData(0x27);
SendData(0x00);
SendCommand(ILI9341CommandId.SLPOUT); // Sleep Out
Thread.Sleep(120);
SendCommand(ILI9341CommandId.DISPON); // Display On
Thread.Sleep(100);
}
public void Dispose()
{
spi.Dispose();
control.Dispose();
reset?.Dispose();
}
public void Enable() => SendCommand(ILI9341CommandId.DISPON);
public void Disable() => SendCommand(ILI9341CommandId.DISPOFF);
private void SendCommand(ILI9341CommandId command)
{
buffer1[0] = (byte)command;
control.Write(GpioPinValue.Low);
spi.Write(buffer1);
}
private void SendData(byte data)
{
buffer1[0] = data;
control.Write(GpioPinValue.High);
spi.Write(buffer1);
}
private void SendData(byte[] data)
{
control.Write(GpioPinValue.High);
spi.Write(data);
}
public void SetActiveWindow(int x, int y, int width, int height)
{
var x_end = x + width - 1;
var y_end = y + height - 1;
SendCommand(ILI9341CommandId.CASET);
SendData((Byte)(x >> 8));
SendData((Byte)x);
SendData((Byte)(x_end >> 8));
SendData((Byte)x_end);
SendCommand(ILI9341CommandId.PASET);
SendData((Byte)(y >> 8));
SendData((Byte)y);
SendData((Byte)(y_end >> 8));
SendData((Byte)y_end);
SendCommand(ILI9341CommandId.RAMWR);
Width = width;
Height = height;
}
public void DrawBuffer(byte[] buffer)
{
SendCommand(ILI9341CommandId.RAMWR);
control.Write(GpioPinValue.High);
try
{
BitConverter.SwapEndianness(buffer, 2);
spi.Write(buffer, 0, 1024 * 64 - 1);
spi.Write(buffer, 1024 * 64-1, 1024 * 64 - 1);
spi.Write(buffer, 1024 * 64 * 2 -2, 22530);
}
catch
{
Debug.WriteLine("Exception SPI");
}
}
public void SetOrientation(int orientationDegrees)
{
switch (orientationDegrees)
{
case 0:
Width = 240;
Height = 320;
Orientation = 0;
SetActiveWindow(0, 0, 240, 320);
SendCommand(ILI9341CommandId.MADCTL);
SendData(0x48);
break;
case 90:
Width = 320;
Height = 240;
Orientation = 90;
SetActiveWindow(0, 0, 320, 240);
SendCommand(ILI9341CommandId.MADCTL);
SendData(0xE8);
break;
case 180:
Width = 240;
Height = 320;
Orientation = 180;
SetActiveWindow(0, 0, 240, 320);
SendCommand(ILI9341CommandId.MADCTL);
SendData(0x88);
break;
case 270:
Width = 320;
Height = 240;
Orientation = 270;
SetActiveWindow(0, 0, 320, 240);
SendCommand(ILI9341CommandId.MADCTL);
SendData(0x28);
break;
default:
break;
}
}
}
}
And here is a test code :
using GHIElectronics.TinyCLR.Devices.Gpio;
using GHIElectronics.TinyCLR.Devices.Spi;
using MBN;
using MBN.Modules.ILI9341;
using System;
using System.Drawing;
using System.Collections;
using System.Text;
using System.Threading;
using GHIElectronics.TinyCLR.Native;
using System.Diagnostics;
namespace ILI9341
{
class Program
{
private static ILI9341Controller ILI9341;
private const int SCREEN_WIDTH = 240;
private const int SCREEN_HEIGHT = 320;
private static int framerate;
private static Object LockFramerate = new object();
static void Main()
{
Info();
new Thread(DisplayFramerate).Start();
TestILI9341(Hardware.SocketTwo);
Thread.Sleep(Timeout.Infinite);
}
private static void Info()
{
Debug.WriteLine($"Device name : {DeviceInformation.DeviceName}");
Debug.WriteLine($"+-----------+------------+------------+");
Debug.WriteLine($"| Memory | Used | Free |");
Debug.WriteLine($"+-----------+------------+------------+");
Debug.WriteLine($"| Managed | {Memory.ManagedMemory.UsedBytes,10:N0} | {Memory.ManagedMemory.FreeBytes,10:N0} |");
Debug.WriteLine($"| Unmanaged | {Memory.UnmanagedMemory.UsedBytes,10:N0} | {Memory.UnmanagedMemory.FreeBytes,10:N0} |");
Debug.WriteLine($"+-----------+------------+------------+\r\n");
}
private static void TestILI9341(Hardware.Socket socket)
{
ILI9341 = new ILI9341Controller(socket);
ILI9341.SetOrientation(180);
// Create flush event
Graphics.OnFlushEvent += Graphics_OnFlushEvent;
// Create bitmap buffer
var screen = Graphics.FromImage(new Bitmap(SCREEN_WIDTH, SCREEN_HEIGHT));
//var image = Properties.Resources.GetBitmap(Properties.Resources.BitmapResources.smallJpegBackground);
var font = Properties.Resources.GetFont(Properties.Resources.FontResources.droid_reg24);
screen.Clear();
screen.FillEllipse(new SolidBrush(Color.FromArgb(255, 255, 0, 0)), 0, 0, 80, 64);
screen.FillEllipse(new SolidBrush(Color.FromArgb(255, 0, 0, 255)), 80, 0, 80, 64);
screen.FillEllipse(new SolidBrush(Color.FromArgb(128, 0, 255, 0)), 40, 0, 80, 64);
//screen.DrawImage(image, 56, 50);
screen.DrawRectangle(new Pen(Color.Yellow), 10, 80, 40, 25);
screen.DrawEllipse(new Pen(Color.Purple), 60, 80, 40, 25);
screen.FillRectangle(new SolidBrush(Color.Teal), 110, 80, 40, 25);
screen.DrawLine(new Pen(Color.White), 10, 127, 150, 127);
screen.DrawLine(new Pen(Color.White), 120, 1, 120, 319);
screen.SetPixel(80, 92, 0xFF0000);
screen.DrawString("Hello world!", font, new SolidBrush(Color.Yellow), 50, 110);
screen.DrawString("Hello world!", font, new SolidBrush(Color.Yellow), 10, 180);
//while (true)
{
screen.Flush();
}
}
private static void Graphics_OnFlushEvent(IntPtr hdc, byte[] data)
{
ILI9341.DrawBuffer(data);
framerate++;
}
private static void DisplayFramerate()
{
while (true)
{
//Debug.WriteLine($"Framerate : {framerate}");
framerate = 0;
Thread.Sleep(1000);
}
}
}
}
You will have to remove the comments on the while (true)
statement (before the screen.Flush()
) and the one to display FPS in the DisplayFramerate()
method.