Problem converting SPI LCD from Panda III to Cobra III

Hi all,

I am having some problems converting a program from Panda III to Cobra III and I reckon I am missing something blindingly obvious that I just can’t see. I am hoping someone can help.

I use a 1.8" SPI LCD on a Panda III program that works well. It uses SPI1. Moving to the Cobra III, all the pins appear to be in the same place and the screen is identical, so I simply shift the pin references. But I can’t seem to get the display to show anything.

A quick demo code for the screen is below (in Cobra III form)

namespace NickLCDTest
{
    using GHI.Pins;
    using GHI.Processor;
    using GHI.Utilities;
    using Microsoft.SPOT;
    using Microsoft.SPOT.Hardware;
    using Microsoft.SPOT.Presentation;
    using Microsoft.SPOT.Presentation.Media;
    using System.Threading;


    public class Program
    {

       static SPI.Configuration MySPIConfig = new SPI.Configuration(FEZCobraIII.Gpio.D10, false, 0, 0, false, true, 40000, FEZCobraIII.SpiBus.Spi1);
        static SPI MySPI = new SPI(MySPIConfig);

        //static long StartMillis, EndMillis;

        static Font smallFont = Resources.GetFont(Resources.FontResources.small);
        static Font NinaFont = Resources.GetFont(Resources.FontResources.NinaB);
        static Bitmap LogoBitmap = new Bitmap(Resources.GetBytes(Resources.BinaryResources.ReGenlogo), Bitmap.BitmapImageType.Gif);
        static Bitmap bmp;
        
        public static void Main()
        {
            


            Display.Type = Display.DisplayType.Spi;
            Display.Width = 128;
            Display.Height = 160;
            Display.BitmapFormat = Bitmaps.Format.Bpp16RgbLe;
            Display.CurrentRotation = Display.Rotation.Clockwise90;
            //Display.CurrentRotation = Display.Rotation.Normal;
            Display.SpiConfiguration = MySPIConfig;
            Display.ControlPin = FEZCobraIII.Gpio.D9;
            Display.ResetPin = FEZCobraIII.Gpio.D8;
            Display.Save();
            

            bmp = new Bitmap(160, 128);

            ShowSplashScreen();

            Thread.Sleep(3000);

            ClearScreen();
            ShowMainScreen();
        }

        public static void ClearScreen()
        {
            bmp.Clear();
            bmp.Flush();
        }

        public static void ShowSplashScreen()
        {
            bmp.DrawRectangle(ColorUtility.ColorFromRGB(0, 0, 255), 5, 0, 0, 160, 128, 0, 0, ColorUtility.ColorFromRGB(255, 0, 0), 0, 0, ColorUtility.ColorFromRGB(0, 255, 0), 128, 160, 100);


            bmp.DrawImage(55, 10, LogoBitmap, 0, 0, 50, 50);

            bmp.Flush();

        }

        public static void ShowMainScreen()
        {
            bmp.DrawRectangle(ColorUtility.ColorFromRGB(0, 0, 255), 5, 0, 0, 160, 128, 0, 0, ColorUtility.ColorFromRGB(255, 0, 0), 0, 0, ColorUtility.ColorFromRGB(0, 255, 0), 128, 160, 100);
            bmp.DrawImage(5, 5, LogoBitmap, 0, 0, 50, 50);
            bmp.DrawText("A: Toggle CAN", smallFont, ColorUtility.ColorFromRGB(255, 255, 255), 60, 5);
            bmp.DrawText("B: Run Var Config", smallFont, ColorUtility.ColorFromRGB(255, 255, 255), 60, 20);
            bmp.DrawText("</>: PowerMode +/-", smallFont, ColorUtility.ColorFromRGB(255, 255, 255), 60, 35);
            bmp.DrawText("Config:001X351SV12v5.iCAN", smallFont, ColorUtility.ColorFromRGB(255, 255, 255), 10, 60);
            bmp.DrawText("CAN = Active", NinaFont, ColorUtility.ColorFromRGB(0, 0, 255), 10, 80);
            //bmp.DrawText("B: Run Variant Config", NinaFont, ColorUtility.ColorFromRGB(0, 0, 255), 10, 90);
            bmp.DrawText("PowerMode = 7", NinaFont, ColorUtility.ColorFromRGB(0, 0, 255), 10, 100);

            

            bmp.Flush();

        }


    }
}

If I simply change the following lines


and

```cs
Display.ControlPin = FEZCobraIII.Gpio.D9;
Display.ResetPin = FEZCobraIII.Gpio.D8;

to


and

```cs
Display.ControlPin = FEZPandaIII.Gpio.D9;
Display.ResetPin = FEZPandaIII.Gpio.D8;

then it works perfectly on the Panda III.

Are there any differences in the SPI1 buses between the Panda III and Cobra III, that I may have missed?
Both are 3.3V logic. I am using 2016 R2 pre-release.

Any suggestions gratefully received!

Nick

The SPI display support is not available on the FEZ Cobra III.

Hi,

Why has this functionality been omitted from the more powerful solution (Cobra III vs Panda III)?

Are there any plans to introduce it?

Thanks,

Jason

@ jasonetherton - There is no specific reason why it has not been added to other boards – we’ve just prioritized other features.

Hmmm. Now I am torn! I moved from Panda III to Cobra III for the In-Field-Update, but have lost display ???

John - are there any other IFU possibilities for the Panda III?

On the display, as well as the 1.8", I am using a 2.2" and I have just found it mentioned in this earlier thread - [url]https://www.ghielectronics.com/community/forum/topic?id=19491&page=1#msg192675[/url] - so I will give that a go to see how it performs on the Cobra III.

Then have to weigh up which way is the path of least resistance.

Nick

@ HalfGeek - The N18 is still usable on the FEZ Cobra III, just not with bitmap.Flush. You’d have to drive it yourself in your application through SPI. IFU from within your application is only available on EMX, G120, and G400 based systems.

@ John - Thanks John. I need IFU, so I guess Cobra III it is, and I will have to do the screen myself a but more than on the Panda III! I am not using the N18 display module - just standard 1.8"/2.2" displays - so I will have to do some reading up to see if I can learn from the N18.

I am sure I will be back to the forum with questions!! :slight_smile:

Nick

1 Like

The ILI9341 drivers will most likely work for you. It’s a simple driver (heck, even I updated it and sent a pull request that got incorporated!) but effective for these cheap screens. I’ve not tried it outside a low end device but can’t think of any reason it won’t work

@ Brett - Thanks, Brett. Yes - I had a quick look and it seemed pretty straight-forward. I’ll let you know how I get on and how snappy it is. Not going to get a chance this week now, typically just as I have a possible solution to investigate!!

Nick

1 Like

Well, I finally found some time and got the 2.2" LCD working on the Cobra III with the ILI9341 drivers, but it is so much slower than the Panda III LCD SPI support. For example this command

var font = new NetduinoHelpersFont(VerdanaBold14.Bitmaps,
                                               VerdanaBold14.Descriptors,
                                               VerdanaBold14.Height,
                                               VerdanaBold14.FontSpace);

takes 6.5 secs on the Cobra III!!! I haven’t tried optimising it yet.

But it works, which is a good start.

Drawing 3 lines of text with this code…

tft.DrawString(050, 045, "Some exciting text!", 0xF800, font);
tft.DrawString(100, 082, "printed", 0xFFFF, font);
tft.DrawString(100, 120, "using tft", 0x009C, font);

took 526ms and the standard clear screen took 133ms. I did add a new clear screen which just wrote a blank buffer array to the screen in one go, and that takes 30ms, so there are definitely some improvements to be made.

Next, I tried to merge with the Microsoft.SPOT.Bitmap class used on the Panda III SPI display. I can draw 6 lines of text, one 50x050 bitmap and a rectangle on to the bitmap (not the screen) with the following code in 39ms, so that is definitely the way to head.

bmp.DrawRectangle(ColorUtility.ColorFromRGB(0, 0, 255), 5, 0, 0, 160, 128, 0, 0, ColorUtility.ColorFromRGB(255, 0, 0), 0, 0, ColorUtility.ColorFromRGB(0, 255, 0), 128, 160, 100);
bmp.DrawImage(5, 5, LogoBitmap, 0, 0, 50, 50);
bmp.DrawText("Some Exciting text", smallFont, ColorUtility.ColorFromRGB(255, 255, 255), 60, 5);
bmp.DrawText("printed", smallFont, ColorUtility.ColorFromRGB(255, 255, 255), 60, 20);
bmp.DrawText("using bmp", smallFont, ColorUtility.ColorFromRGB(255, 255, 255), 60, 35);
bmp.DrawText("also with", smallFont, ColorUtility.ColorFromRGB(255, 255, 255), 10, 60);
bmp.DrawText("a 50x50", NinaFont, ColorUtility.ColorFromRGB(0, 0, 255), 10, 80);
bmp.DrawText("bitmap", NinaFont, ColorUtility.ColorFromRGB(0, 0, 255), 10, 100);

I can also flush a 320x240x16bpp byte array to the screen in <50ms as a byte stream, so the SPI side is quick enough

My problem now is that the Bitmap class appears to be fixed at 32bpp ARGB, and the SPI display accepts 16bpp RGB565 (and some other variations but none at 32bpp ARGB), so I need to convert. It appears the basic System.Drawing.Bitmap class allows me to specify the pixel format, but it does not support all the text and graphics routines of Microsoft.Drawing.Bitmap.

I made a quick and dirty routine just to prove I can get the bitmap on tot the display, but it takes about 24s to make the RGB565 buffer array from the bitmap and then <50ms to dump it to the display!! This is with a full 320x240 bitmap. So, I am not sure of the best way to head next.

public void DrawBitmap(int x, int y, int width, int height, Microsoft.SPOT.Bitmap bitmap)
{
            SetWindow(x, x + width - 1, y, y + height - 1);
            Color tempcolour;
            byte r, g, b;
                var buffer = new ushort[width * height];
                for (int i = 0; i < width; i++)
                {
                    for (int j = 0; j < height; j++)
                    {
                        tempcolour = bitmap.GetPixel(i,j);
                        r = (byte)((ColorUtility.GetRValue(tempcolour) / 255) * 31); //Red
                        g = (byte)((ColorUtility.GetGValue(tempcolour) / 255) * 63); //Green
                        b = (byte)((ColorUtility.GetBValue(tempcolour) / 255) * 31); //Blue
                        buffer[i + j * width] = (ushort)(((r & 0x1f) << 11) | (((g >> 3) & 0x7)<< 8) | ((g & 0x7) << 5) | (b & 0x1f)); //Green (lower 3 bits) + Blue (upper 5 bits)
                    }
                }
                SendData(buffer);
            }

Can anyone suggest the quickest approach to take the Microsoft.SPOT.Bitmap object and get a stream of 2-byte RGB565 data that I can then dump to the SPI display? Is there any native access to the Bitmap pixel data than retrieving a Color object, splitting it and recreating the RGB565? Or perhaps it can be specified to work in RGB565 natively?

Is the code for the SPI support Bitmap.Flush() published anywhere that I can look at to see how it is done on the Panda III?

Thanks for any pointers that anyone can give.

Nick

Lets start a petition - expand the devices with this code in it to include G120 and G400

@ Brett - Consider me signed up :slight_smile:

https://www.ghielectronics.com/community/forum/topic?id=23126

Support my petition!

@ HalfGeek - At least for converting the bitmap format to RGB565, take a look at GHI.Utilities.Bitmaps.Convert. It is done natively so it will be a lot quicker.

@ John - Thanks John. I had a quick look - it looks like that might give me the byte array I need straight away :slight_smile: I should be able to try this later.

Failing that, I found that the Microsoft.SPOT.Bitmap supports a .GetBitmap method which returns a byte array for the pixels. There is very little documentation, so I assume it just comes back in an array composed of multiple 4-bytes in ARGB format. Anecdotally, the .GetPixel appears to be quite slow and the other bitmap classes use a .LockBits method to dump the pixel array much more quickly, but Microsoft.SPOT.Bitmap doesn’t support it - hopefully the Microsoft.SPOT.Bitmap.GetBitmap or the GHI.Utilities.Bitmap.Convert are the equivalents.

I will see how it goes!

Nick

@ John - Inspired suggestion!!

Using this code to replace the bitmap.flush() :

        public void UpdateScreen2(int x, int y, int width, int height, Microsoft.SPOT.Bitmap bitmap)
        {
            SetWindow(x, x + width - 1, y, y + height - 1);
            //var buffer = new ushort[width * height];
            SendData(GHI.Utilities.Bitmaps.Convert(bitmap,GHI.Utilities.Bitmaps.Format.Bpp16BgrBe));
        }

The time to push the bitmap to the screen dropped from 24s to 90ms !!!

That is simply awesome!!

Many thanks again

Nick

1 Like