Main Site Documentation

G30 SPI problem


#1

Hi Gents,
It’s been a long time ! I have checked your progress with tinyclr and it is impressive ! So I updated my g30th with version 0.11 and I wanted to experiment a little bit. I plugged my tft displayer and I saw that I have the same problem as I had in the past, displayer is initializing, I am starting to display some text and after few seconds displayer is frozen usb debugger is not responding and controller is hanging and it needs to be power off to reboot. Firstly I thought that problem is with my driver but I checked code from g80 from separate thread:

and result is exactly the same. Application is crushing randomly, sometimes after few seconds sometimes immediately, but alkways on spi.Write(…). Have you got any idea what is going on ? I checked it on 0.5 and 0.11 firmware.

Best Regards,
Przemo


#2

Please spin a thread that blinks an LED. When your device hangs, does the LED stop blinking?

Double and triple check your power. In fact use a different power supply.

What do we need to do to reproduce the issue?


#3

How are you powering the G30? Is it a custom board or a TH version?


#4

Hi Gents,
I will check with separate thread but I am quite sure that it will not blink. I will also prepare demo app and send it together with schematic on a weekend to you so you will be able to reproduce it. I am using usb to power it, and I am using LDO to change voltage from 5v to 3v3.

@Dave_McLaughlin It is “TH” version.


#5

What’s the current capable from the LDO and do you have any reservoir caps on it’s output?


#6

Hi Gents,
I supplied it from laboratory power supply and result is the same. Tried with few different LCD … same results.
I have something like that:

https://pl.aliexpress.com/wholesale?catId=0&initiative_id=SB_20180629080107&SearchText=1%2C44+tft

I have plugged:
PC0-> CS
PC1->Reset
PC2-> Shield light
PC3-> A0 (RS)
PB13-> SCK
PB15-> SDA
SPI2 channel MOSI, MISO was not connected but I tried to pull it dwon and up, always with same result.

I have checked also your suggestion Gus with separate thread and led. Led freezes also.

Below you can find my code:

using GHIElectronics.TinyCLR.Pins;
using System;
using System.Diagnostics;
using System.Threading;
using System.Drawing;
namespace TinyCLRSample
{
    class Program
    {
        private static Random random = new Random();

        public static string RandomString(int length)
        {
            var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
            var stringChars = new char[length];
            var random = new Random();
            for (int i = 0; i < stringChars.Length; i++)
            {
                stringChars[i] = chars[random.Next(chars.Length)];
            }
            return new String(stringChars);
        }

        static void Main()
        {
            Thread.Sleep(1000);
            DisplayN18 n18 = new DisplayN18(G30.SpiBus.Spi2, G30.GpioPin.PC1, G30.GpioPin.PC3, G30.GpioPin.PC2, G30.GpioPin.PC0, true);
            Thread.Sleep(100);
           
            while (true)
            {
                Thread.Sleep(50);
                string d = RandomString(5);
                n18.DrawText(20, 20, d,2, Color.Red,Color.Green);
            }
        }
    }
}

N18Driver:

using System;
using GHIElectronics.TinyCLR.Devices.Gpio;
using System.Threading;
using GHIElectronics.TinyCLR.Devices.Spi;
using System.Drawing;
using TinyCLRSample.dev;

namespace TinyCLRSample
{
    /// <summary>A CharacterDisplay module for Microsoft .NET Gadgeteer</summary>
    public class DisplayN18
    {
        #region constants
        private const byte St7735Madctl = 0x36;
        private const byte MadctlMy = 0x80;
        private const byte MadctlMx = 0x40;
        private const byte MadctlMv = 0x20;
        private const byte MadctlBgr = 0x08;
        #endregion

        #region fields
        private readonly SpiDevice _dev;

        private readonly GpioPin _resetPin;
        private readonly GpioPin _backlightPin;
        private readonly GpioPin _rsPin;

        private readonly byte[] _byteArray;
        private readonly ushort[] _shortArray;
        private bool _isBgr;

        public enum DisplayOrientation
        {
            Square = 0,
            Portrait = 0xC8,
            Landscape = 0x68
        }


        private readonly DisplayOrientation _orientation;
        #endregion

        #region property
        public int Height { get; }

        public int Width { get; }

        /// <summary>Whether or not the backlight is enabled.</summary>
        public bool BacklightEnabled
        {
            get => _backlightPin.Read() == GpioPinValue.High;
            set => _backlightPin.Write(value ? GpioPinValue.High : GpioPinValue.Low);
        }
        #endregion

        #region constructor
        /// <summary>
        /// Constructs a new instance.
        /// </summary>
        /// <param name="spiControllerName"></param>
        /// <param name="digitalResetPin3"> Pin3 of S socket which is the reset pin</param>
        /// <param name="digitalBackLightPin4">Pin4 of S socket which is the backlight pin</param>
        /// <param name="digitalRsPin5">Pin5 of S socket which is the RS pin</param>
        /// <param name="digitalCsPin6"></param>
        /// <param name="isBrg">Set to true if color must be in BRG order, false for RGB order mode</param>
        public DisplayN18(string spiControllerName, int digitalResetPin3, int digitalBackLightPin4, int digitalRsPin5, int digitalCsPin6, bool isBrg = false)
        {
            _orientation = DisplayOrientation.Square;

            _byteArray = new byte[1];
            _shortArray = new ushort[2];
            _isBgr = isBrg;

            var controller = GpioController.GetDefault();

            _resetPin = controller.OpenPin(digitalResetPin3);
            _resetPin.SetDriveMode(GpioPinDriveMode.Output);

            _backlightPin = controller.OpenPin(digitalBackLightPin4);
            _backlightPin.SetDriveMode(GpioPinDriveMode.Output);
            BacklightEnabled = false;

            _rsPin = controller.OpenPin(digitalRsPin5);
            _rsPin.SetDriveMode(GpioPinDriveMode.Output);

            var settings = new SpiConnectionSettings(digitalCsPin6)
            {
                ClockFrequency = 40000000, // 12 000 kHz
                Mode = SpiMode.Mode0,
                SharingMode = SpiSharingMode.Exclusive,
                DataBitLength = 8
            };

            _dev = SpiDevice.FromId(spiControllerName, settings);

            Reset();

            ConfigureDisplay();

            Height = 128;
            Width = 128;
            Clear();
            BacklightEnabled = true;
        }
        #endregion

        #region methods
        public void Clear()
        {
            var data = new byte[256];
            for (int i = 0; i < 128; i++)
            {
                DrawRaw(data, 0, i, 128, 1);
            }
        }

        private void ConfigureDisplay()
        {
            WriteCommand(0x11);//Sleep exit
            Thread.Sleep(120);

            //ST7735R Frame Rate
            WriteCommand(0xB1);
            WriteData(0x01); WriteData(0x2C); WriteData(0x2D);
            WriteCommand(0xB2);
            WriteData(0x01); WriteData(0x2C); WriteData(0x2D);
            WriteCommand(0xB3);
            WriteData(0x01); WriteData(0x2C); WriteData(0x2D);
            WriteData(0x01); WriteData(0x2C); WriteData(0x2D);

            WriteCommand(0xB4); //Column inversion
            WriteData(0x07);

            //ST7735R Power Sequence
            WriteCommand(0xC0);
            WriteData(0xA2); WriteData(0x02); WriteData(0x84);
            WriteCommand(0xC1); WriteData(0xC5);
            WriteCommand(0xC2);
            WriteData(0x0A); WriteData(0x00);
            WriteCommand(0xC3);
            WriteData(0x8A); WriteData(0x2A);
            WriteCommand(0xC4);
            WriteData(0x8A); WriteData(0xEE);

            WriteCommand(0xC5); //VCOM
            WriteData(0x0E);

            SetOrientationOverride(DisplayOrientation.Square);

            //ST7735R Gamma Sequence
            WriteCommand(0xe0);
            WriteData(0x0f); WriteData(0x1a);
            WriteData(0x0f); WriteData(0x18);
            WriteData(0x2f); WriteData(0x28);
            WriteData(0x20); WriteData(0x22);
            WriteData(0x1f); WriteData(0x1b);
            WriteData(0x23); WriteData(0x37); WriteData(0x00);

            WriteData(0x07);
            WriteData(0x02); WriteData(0x10);
            WriteCommand(0xe1);
            WriteData(0x0f); WriteData(0x1b);
            WriteData(0x0f); WriteData(0x17);
            WriteData(0x33); WriteData(0x2c);
            WriteData(0x29); WriteData(0x2e);
            WriteData(0x30); WriteData(0x30);
            WriteData(0x39); WriteData(0x3f);
            WriteData(0x00); WriteData(0x07);
            WriteData(0x03); WriteData(0x10);

            WriteCommand(0x2a);
            WriteData(0x00); WriteData(0x00);
            WriteData(0x00); WriteData(0x7f);
            WriteCommand(0x2b);
            WriteData(0x00); WriteData(0x00);
            WriteData(0x00); WriteData(0x9f);

            WriteCommand(0xF0); //Enable test command
            WriteData(0x01);
            WriteCommand(0xF6); //Disable ram power save mode
            WriteData(0x00);

            WriteCommand(0x3A); //65k mode
            WriteData(0x05);

            WriteCommand(0x29); //Display on
        }

        /// <summary>Draws an image to the screen.</summary>
        /// <param name="bitmap">The bitmap to be drawn to the screen</param>
        /// <param name="x">Starting X position of the image.</param>
        /// <param name="y">Starting Y position of the image.</param>
        public void DrawBitmap(Bitmap bitmap, int x, int y)
        {
            var vram = new byte[bitmap.Width * bitmap.Height * 2];
            NativeBitmapConverter(bitmap, vram);
            DrawRaw(vram, x, y, bitmap.Width, bitmap.Height);
        }

        /// <summary>
        /// Draws a string to the screen
        /// </summary>
        /// <param name="str">String to display</param>
        /// <param name="font">Font used to display text</param>
        /// <param name="x">Starting X position of the text</param>
        /// <param name="y">Starting Y position of the text</param>
        /// <param name="color">Color used to display text</param>
        public void DrawString(string str, Font font, int x, int y, Color color)
        {
            var image = new Bitmap(12 * str.Length, 15);
            var grap = Graphics.FromImage(image);
            grap.DrawString(str, font, new SolidBrush(color), 0, 0);
            DrawBitmap(image, x, y);
        }

        /// <summary>
        /// Draw a fill rectangle on display
        /// </summary>
        /// <param name="x">x coordinate of pixel</param>
        /// <param name="y">y coordinate of pixel</param>
        /// <param name="width">width of rectangle</param>
        /// <param name="height">height of rectangle</param>
        /// <param name="color">color of rectangle</param>
        public void DrawFillRect(int x, int y, int width, int height, Color color)
        {
            // Prepare data
            byte[] data = new byte[width * height * 2];
            byte[] a = color.To565ByteArray();
            for (int i = 0; i < width * height; i++)
            {
                data[i * 2] = a[0];
                data[i * 2 + 1] = a[1];
            }
            DrawRaw(data, x, y, width, height);
        }

        public void DrawChar(int x, int y, char c, int size, Color colorFont, Color colorBackground)
        {

            if ((x >= Width) || // Clip right
               (y >= Height) || // Clip bottom
               ((x + 6 * size - 1) < 0) || // Clip left
               ((y + 8 * size - 1) < 0))   // Clip top
                return;

            for (byte i = 0; i < 6; i++)
            {
                byte line;
                if (i == 5)
                    line = 0x0;
                else
                    //line = pgm_read_byte(font+(c*5)+i);
                    line = TinyCLRSample.dev.Glcfont.MEM[(c * 5) + i];

                for (byte j = 0; j < 8; j++)
                {
                    if ((line & 0x1) != 0)
                    {
                        if (size == 1) // default size
                            DrawPixel(x + i, y + j, colorFont);
                        else
                        {  // big size
                            DrawFillRect(x + (i * size), y + (j * size), size, size, colorFont);
                        }
                    }
                    else
                    {
                        if (size == 1) // default size
                            DrawPixel(x + i, y + j, colorBackground);
                        else if (colorBackground != Color.Transparent)
                        {  // big size
                            DrawFillRect(x + i * size, y + j * size, size, size, colorBackground);
                        }
                    }
                    line >>= 1;
                }
            }
        }

        public void DrawLine(int startX, int startY, int endX, int endY, Color color)
        {
            int steep = (System.Math.Abs(endY - startY) > System.Math.Abs(endX - startX)) ? 1 : 0;

            if (steep != 0)
            {
                int tmp;
                tmp = startX;
                startX = startY;
                startY = tmp;
                tmp = endX;
                endX = endY;
                endY = tmp;
            }

            if (startX > endX)
            {
                int tmp;

                tmp = startX;
                startX = endX;
                endX = tmp;
                tmp = startY;
                startY = endY;
                endY = tmp;
            }

            int dx, dy;
            dx = endX - startX;
            dy = System.Math.Abs(endY - startY);

            int err = dx / 2;
            int ystep = 0;

            if (startY < endY)
            {
                ystep = 1;
            }
            else
            {
                ystep = -1;
            }

            for (; startX < endX; startX++)
            {
                if (steep != 0)
                {
                    DrawPixel(startY, startX, color);
                }
                else
                {
                    DrawPixel(startX, startY, color);
                }
                err -= dy;
                if (err < 0)
                {
                    startY += ystep;
                    err += dx;
                }
            }
        }

        public void DrawText(int x, int y, string txt, int size, Color colortext, Color colorBackground, bool wrap = true)
        {
            int cursorX = x;
            int cursorY = y;
            char[] text = txt.ToCharArray();

            foreach (char c in text)
            {
                if (c == '\n')
                {
                    cursorY += size * 8;
                    cursorX = 0;
                }
                else if (c == '\r')
                {
                    // skip em
                }
                else
                {
                    DrawChar(cursorX, cursorY, c, size, colortext, colorBackground);
                    cursorX += size * 6;
                    if (wrap && (cursorX > (Width - size * 6)))
                    {
                        cursorY += size * 8;
                        cursorX = 0;
                    }
                }
            }

        }

        public void DrawPixel(int x, int y, Color color)
        {
            byte[] a = color.To565ByteArray();
            DrawRaw(a, x, y, 1, 1);
        }

        private void DrawRaw(byte[] rawData, int x, int y, int width, int height)
        {
            var orientedWidth = Width;
            var orientedHeight = Height;

            if (x > orientedWidth || y > orientedHeight)
                return;

            if (x + width > orientedWidth)
                width = orientedWidth - x;

            if (y + height > orientedHeight)
                height = orientedHeight - y;

            SetClippingArea(x, y, width - 1, height - 1);
            WriteCommand(0x2C);
            WriteData(rawData);
        }

        private void NativeBitmapConverter(Bitmap bitmap, byte[] vram)
        {
            // TODO: method must be optimized: for display on full screen in FEZ Spider II it takes around 25 sec.
            int idx = 0;
            for (int j = 0; j < bitmap.Height; j++)
                for (int i = 0; i < bitmap.Width; i++)
                {
                    var ab = bitmap.GetPixel(i, j).To565ByteArray();
                    vram[idx] = ab[0];
                    vram[idx + 1] = ab[1];
                    idx += 2;
                }
        }

        private void Reset()
        {
            _resetPin.Write(GpioPinValue.Low);
            Thread.Sleep(150);
            _resetPin.Write(GpioPinValue.High);
        }
        private void SetClippingArea(int x, int y, int width, int height)
        {
            _shortArray[0] = (ushort)x;
            _shortArray[1] = (ushort)(x + width);
            WriteCommand(0x2A);
            WriteData(_shortArray);

            _shortArray[0] = (ushort)y;
            _shortArray[1] = (ushort)(y + height);
            WriteCommand(0x2B);
            WriteData(_shortArray);

        }

        private void SetFormat(DisplayOrientation orientation, bool isBgr)
        {
            WriteCommand(St7735Madctl);

            var bgr = (byte)(isBgr ? MadctlBgr : 0);
            switch (orientation)
            {
                case DisplayOrientation.Square: WriteData((byte)(0x00 | bgr)); break;
                case DisplayOrientation.Portrait: WriteData((byte)(MadctlMx | MadctlMy | bgr)); break;
                case DisplayOrientation.Landscape: WriteData((byte)(MadctlMv | MadctlMy | bgr)); break;
                default: throw new ArgumentException("orientation");
            }
        }
        private void SetOrientationOverride(DisplayOrientation orientation)
        {
            SetFormat(orientation, _isBgr);
            Thread.Sleep(1);
        }

        /// <summary>Swaps the red and blue channels if your display has them reversed.</summary>
        public void SwapRedBlueChannels()
        {
            _isBgr = !_isBgr;
            SetFormat(_orientation, _isBgr);
        }

        private void WriteCommand(byte command)
        {
            byte[] byteArray = new byte[1];
            byteArray[0] = command;
            _rsPin.Write(GpioPinValue.Low);
            _dev.Write(byteArray);
        }

        private void WriteData(byte data)
        {
            byte[] byteArray = new byte[1];
            byteArray[0] = data;
            WriteData(byteArray);
        }

        private void WriteData(byte[] byteArray)
        {
            _rsPin.Write(GpioPinValue.High);
            _dev.Write(byteArray);
        }

        private void WriteData(ushort[] data)
        {
            _rsPin.Write(GpioPinValue.High);
            foreach (ushort us in data)
            {
                // Data must be sent in reverse order
                byte[] bData = BitConverter.GetBytes(us);
                byte[] dataToSend = new byte[bData.Length];
                for (int i = 0; i < bData.Length; i++)
                {
                    dataToSend[dataToSend.Length - 1 - i] = bData[i];
                }
                _dev.Write(dataToSend);
            }
        }
        #endregion
    }

    public static class DisplayN18Extension
    {
        public static byte[] To565ByteArray(this Color color)
        {
            byte[] ba = new byte[2];
            var c = color.To565Color();
            ba[0] = (byte)(((c.R & 0x1f) << 3) | ((c.G >> 3) & 0x07));
            ba[1] = (byte)(((c.G & 0x07) << 5) | (c.B & 0x1f));
            return ba;
        }

        private static Color To565Color(this Color color)
        {
            var c = Color.FromArgb((byte)(color.R / 255.0 * 31), (byte)(color.G / 255.0 * 63),
                (byte)(color.B / 255.0 * 31));
            return c;
        }


    }
}

Best Regards


#7

It’s not the input supply, it’s the regulator you need to have a look at. What part number did you use?

Its output must be stable. If you use a 500mA capable regulator and draw close to 500mA you will need a good reservoir cap to handle the dips in the supply rail.

Can you show how this is all connected?


#8

Hi Dave,
I plugged laboratory supply directly on 3v3 pin. Anyway the whole it taking ~ 0.05 A.Please check photos I hope that they are good enough.


#9

Put a capacitor across the LDO output. If you check the datasheet you will see that they need this to work properly. Also check on the input. Long power lines require a cap on the input too.


#10

Hi Dave, I placed capacitor as you suggested but issue is still visible,
When I provided power from laboratory supplier I had very stable current and same issue so it is probably not this path ;/
I am not sure if it will help, but when I placed debug info, it shows always crashes on function _dev.Write(byteArray);