SC20100 Graphics throws System.InvalidOperationException

I’m playing with the SC20100 Board testing the Onboard display.
I used the example from here
http://new-docs.ghielectronics.com/software/tinyclr/tutorials/graphics.html

The example code worked as expected.
However when I wanted to update the Display repeatedly I get System.InvalidOperationException in the Graphics_OnFlushEvent.

I made this simple modification of the code which should show the issue.

Only replace the Main() routine and the ‘Graphics_OnFlushEvent’ handler with the following code.

static void Main()
    {
        var spi = SpiController.FromName(SC20100.SpiBus.Spi3);
        var gpio = GpioController.GetDefault();

        st7735 = new ST7735Controller(
            spi.GetDevice(ST7735Controller.GetConnectionSettings
            (SpiChipSelectType.Gpio, SC20100.GpioPin.PD10)), //CS pin.
            gpio.OpenPin(SC20100.GpioPin.PC4), //RS pin.
            gpio.OpenPin(SC20100.GpioPin.PE15) //RESET pin.
        );

        var backlight = gpio.OpenPin(SC20100.GpioPin.PE5);
        backlight.SetDriveMode(GpioPinDriveMode.Output);
        backlight.Write(GpioPinValue.High);

        var displayController = DisplayController.FromProvider(st7735);
        st7735.SetDataAccessControl(true, true, false, false); //Rotate the screen.

        displayController.SetConfiguration(new SpiDisplayControllerSettings
        {
            Width = SCREEN_WIDTH,
            Height = SCREEN_HEIGHT,
            DataFormat = DisplayDataFormat.Rgb565
        });

        displayController.Enable();

        // Create flush event
        Graphics.OnFlushEvent += Graphics_OnFlushEvent;

        // Create bitmap buffer
        var screen = Graphics.FromImage(new Bitmap
            (displayController.ActiveConfiguration.Width,
            displayController.ActiveConfiguration.Height));

        var image = Resources.GetBitmap(Resources.BitmapResources.Image4);
        var font = Resources.GetFont(Resources.FontResources.Arial12);

        screen.Clear(Color.Black);
        
        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.SetPixel(80, 92, 0xFF0000);

        while (true)
        { 

        screen.DrawString("Hello world!", font, new SolidBrush(Color.Blue), 50, 110);

        screen.Flush();

        Thread.Sleep(2000);

        screen.DrawString("Hello world!", font, new SolidBrush(Color.Black), 50, 110);

        screen.Flush();

        Thread.Sleep(2000);

        screen.DrawString("Hello City!", font, new SolidBrush(Color.Blue), 50, 110);

        screen.Flush();

        Thread.Sleep(2000);

        screen.DrawString("Hello City!", font, new SolidBrush(Color.Black), 50, 110);

        screen.Flush();

        Thread.Sleep(2000);

        }
        Thread.Sleep(-1);

    }

    private static void Graphics_OnFlushEvent(IntPtr hdc, byte[] data)
    {
        try
        {
            st7735.DrawBuffer(data);
        }
        catch (Exception ex)
        {
            string mess1 = ex.Message;
        }
    }

The shown text changes several times as expected but after some time an exception is thrown and the display isn’t updated any more

It’s throwing an out of memory exception.

I have the same issue and after talking with @Dat_Tran the fix looks like adding GC.WaitForPendingFinalizers();

My test is using a 4.3 on 260 but basically the same.

for (; ; )
            {
                screen.FillRectangle(blackPen.Brush, 10, 100, 200, 60);
                screen.DrawString(GHIElectronics.TinyCLR.Native.Memory.ManagedMemory.FreeBytes.ToString(), font, whitePen.Brush, 10, 100);
                screen.Flush();
                GC.WaitForPendingFinalizers();
                Thread.Sleep(1);
            }

Without the call to GC.WaitForPendingFinalizers(); it will raise a memory exception but with it it runs as expected and i cant see any (visual) performance drop.

Hi Justin,
thanks for your answer.
Unfortunately adding ‘GC.WaitForPendingFinalizers();’ after each screen.flush() didn’t fix it.
Furthermore it is not always the same count of changing the text. Using an additional power source was of no apparent influence as well. Sometime the shown text of the last successful action is ‘corrupted’ (only unreadable pixels in the wrong lines). Could this point on bad electrical signals?

The full fix for me for now is this:

 long mem = GHIElectronics.TinyCLR.Native.Memory.ManagedMemory.FreeBytes;
            long loops = 0;
            for (; ; )
            {
                mem = GHIElectronics.TinyCLR.Native.Memory.ManagedMemory.FreeBytes;
                screen.FillRectangle(blackPen.Brush, 10, 100, 260, 60);
                screen.DrawString(loops + " " + mem, font, whitePen.Brush, 10, 100);
                screen.Flush();
                if (mem < 2000)
                {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                }

                loops++;
            }

I will do the same test on the 100

No, still the same.

This works as expected on the 100

using System;
using System.Collections;
using System.Drawing;
using System.Text;
using System.Threading;
using GHIElectronics.TinyCLR.Devices.Display;
using GHIElectronics.TinyCLR.Devices.Gpio;
using GHIElectronics.TinyCLR.Devices.Spi;
using GHIElectronics.TinyCLR.Drivers.Sitronix.ST7735;
using GHIElectronics.TinyCLR.Pins;

namespace Display100
{
    class Program
    {
        const int SCREEN_WIDTH = 128;
        const int SCREEN_HEIGHT = 160;
        static ST7735Controller st7735 = null;
        static void Main()
        {
            var spi = SpiController.FromName("GHIElectronics.TinyCLR.NativeApis.STM32H7.SpiController\\2");
            var gpio = GpioController.GetDefault();

            st7735 = new ST7735Controller(
                spi.GetDevice(ST7735Controller.GetConnectionSettings(SpiChipSelectType.Gpio, 3 * 16 + 10)), // ChipSelect 
                gpio.OpenPin(2 * 16 + 4), // Pin RS
                gpio.OpenPin(4 * 16 + 15) // Pin RESET

            );

            var bl = gpio.OpenPin(4 * 16 + 5); // back light

            bl.Write(GpioPinValue.High);
            bl.SetDriveMode(GpioPinDriveMode.Output);

            // Hook N18 to TinyCLR DisplayController
            var displayController = DisplayController.FromProvider(st7735);

            st7735.SetDataAccessControl(!true, !true, false, false); // rotate the screen

            displayController.SetConfiguration(new SpiDisplayControllerSettings { Width = SCREEN_WIDTH, Height = SCREEN_HEIGHT, DataFormat = DisplayDataFormat.Rgb565 });

            displayController.Enable();


            Graphics.OnFlushEvent += Graphics_OnFlushEvent;


            var screen = Graphics.FromImage(new Bitmap(displayController.ActiveConfiguration.Width, displayController.ActiveConfiguration.Height));


            var font = Resource1.GetFont(Resource1.FontResources.Arial24);



            var whitePen = new Pen(Color.White);
            var blackPen = new Pen(Color.Black);
            int loop = 0;

            long mem = GHIElectronics.TinyCLR.Native.Memory.ManagedMemory.FreeBytes;
            long loops = 0;
            for (; ; )
            {
                mem = GHIElectronics.TinyCLR.Native.Memory.ManagedMemory.FreeBytes;
                screen.FillRectangle(blackPen.Brush, 0, 100, 128, 60);
                screen.DrawString(loops + " " + mem, font, whitePen.Brush, 0, 100);
                screen.Flush();
                if (mem < 2000)
                {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                }

                loops++;
            }


        }
        private static void Graphics_OnFlushEvent(IntPtr hdc, byte[] data)
        {
            st7735.DrawBuffer(data);
        }
    }
    }
1 Like

I rotated the screen by changing the above

const int SCREEN_WIDTH = 160;
const int SCREEN_HEIGHT = 128;
st7735.SetDataAccessControl(true, true, false, false); // rotate the screen

Here is the result, you see the ram drop then get collected at < 2000 bytes - rinse and repeat

2 Likes

46,656 loops and counting…

1 Like

Looks good. I‘ll try your Code later

I have the same problem with your code on my board. Program crashes after some loops and ends Visual Studio as well. Perhaps is it really an electrical / hardware problem.
Is it correct that R8, R9, R10 and R12 are not at their places?

Try a powered hub or add a power pack to try out please.

Is working fine for me on the 100, just a USB for power…

I already tried a powered hub and the other power connector. No change.

I’m able to replicate your issue here on my SC20100. So the team here can check it out.

Thanks, let’s hope that it is only a minor issue. Btw. I’m not at home for the next three days so that I can’t assist until thursday.

1 Like

These are expected needed improvements. You guys are the very first on Earth to user this software. I am feeling great about all this so far

2 Likes

how to add font for display (it do not show to me font) …

sorry for dummy questions

found info
http://new-docs.ghielectronics.com/software/tinyclr/tutorials/resources.html

2 Likes

Change the ST7735 SpiConfiguration :

var spiCconfiguration = new SpiConnectionSettings
            {
                Mode = SpiMode.Mode0,
                ClockFrequency = 4_000_000,
                DataBitLength = 8,
                ChipSelectType = SpiChipSelectType.Gpio,
                ChipSelectLine = SC20100.GpioPin.PD10,
                ChipSelectHoldTime = TimeSpan.FromTicks(1), 
                ChipSelectSetupTime = TimeSpan.FromTicks(1)


            };

            st7735 = new ST7735Controller(
                spi.GetDevice(spiCconfiguration), // new config
                gpio.OpenPin(SC20100.GpioPin.PC4), // Pin RS
                gpio.OpenPin(SC20100.GpioPin.PE15) // Pin RESET

            );

We will change the N18 driver later, but that way you can change the configuration directly.

2 Likes

Thanks, I‘ll try it when I am back Home.