EMX LCD Interface

You have access to the processor resisters so you can configure the LCD controller anyway you like. I am trying to save you time and tell you 60Hz can’t be done on 800x480 on EMX, according to tests we have done before.

If you need further assistance, please phone GHI directly and we will be more than happy to answer your questions.

Gus, what has GHI found can be achieved for a refresh rate? In my tests it takes the EMX almost 500ms to transfer new data to the internal LCD frame buffer. I can see this by alternating between two colors at a set interval and watching the data lines. What I see is the display controller itself updating at a reasonable rate (~30-40Hz) so it has to be the memory buffer that is the bottleneck.

Is this consistent with GHI’s findings? I know the LPC2478 is capable of moving the data, as I got confirmation from NXP’s technical support on that.

500ms is not normal! Can you show us your test code?

Here is my test code:

(Program.cs)


using System;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Touch;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Media;

using GHIElectronics.NETMF.Hardware;

namespace BSTDigitalBoard
{
    public class DigitalBoard
    {
		//Boolean TouchCalibrationFlag = true;

        public static void Main()
        {
            // if the target is the emulator, skip the GHI LCD initialization
            if (SystemInfo.SystemID.Model != 2)
            {
				Configuration.LCD.Configurations lcdConfig = new Configuration.LCD.Configurations();
				lcdConfig.Width = 800;
				lcdConfig.Height = 480;

				// Only use if needed, see documentation.
				lcdConfig.PriorityEnable = true;

				lcdConfig.OutputEnableIsFixed = false;
				lcdConfig.OutputEnablePolarity = true;
				lcdConfig.PixelPolarity = false;
				lcdConfig.HorizontalSyncPolarity = false;
				lcdConfig.VerticalSyncPolarity = false;
				lcdConfig.HorizontalSyncPulseWidth = 150;
				lcdConfig.HorizontalBackPorch = 150;
				lcdConfig.HorizontalFrontPorch = 150;
				lcdConfig.VerticalSyncPulseWidth = 2;
				lcdConfig.VerticalBackPorch = 2;
				lcdConfig.VerticalFrontPorch = 2;
				lcdConfig.PixelClockDivider = 3;

				// Set config
                if (Configuration.LCD.Set(lcdConfig) && Configuration.Heap.SetCustomHeapSize(4 * 1024 * 1024))
					PowerState.RebootDevice(false);

                Configuration.LCD.SetRotation(Configuration.LCD.Rotation.Rotate_CW_90);
            }

			AppControl BSTApp = new AppControl();

			Thread.Sleep(-1);
        }
    }
}

(AppControl.cs)


using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Input;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Touch;

namespace BSTDigitalBoard
{
	class AppControl
	{
        private BaseBitmap ScreenBuffer;
        private Timer ScreenUpdateTimer;
        TimeSpan timerspan = new TimeSpan(0, 0, 0, 0, 50);
        TimerCallback ScreenUpdateTimerCallback;
        Boolean ScreenFlag = false, ScreenBufferUpdate = false;

        private Timer BackgroundUpdateTimer;
        TimeSpan bkspan = new TimeSpan(0, 0, 0, 0, 2000);
        TimerCallback BKUpdateTimerCallback;

        public AppControl()
        {
            ScreenBuffer = new BaseBitmap();

            ScreenUpdateTimerCallback = new TimerCallback(ScreenTimerCallback);
            ScreenUpdateTimer = new Timer(ScreenUpdateTimerCallback, null, timerspan, timerspan);

            BKUpdateTimerCallback = new TimerCallback(BKTimerCallback);
            BackgroundUpdateTimer = new Timer(BKUpdateTimerCallback, null, bkspan, bkspan);
        }

        public void BKTimerCallback(Object sender)
        {
            if (ScreenFlag)
            {
                ScreenBuffer.SetBackground(Colors.White);
                ScreenFlag = false;
            }
            else
            {
                ScreenBuffer.SetBackground(Colors.Blue);
                ScreenFlag = true;
            }

            ScreenBufferUpdate = true;
        }

        public void ScreenTimerCallback(Object sender)
        {
            if (ScreenBufferUpdate)
            {
                ScreenBuffer.Buffer.Flush();
                ScreenBuffer.Buffer.Dispose();
                ScreenBufferUpdate = false;
            }
        }
    }
}

(BaseBitmap.cs)


using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Input;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Media;

namespace BSTDigitalBoard
{
    public class BaseBitmap
    {
        private const Color BlueBackground = (Color)0x8F0000;
		public Bitmap Buffer;

		public BaseBitmap()
        {            

		}

        public void SetBackground(Color c)
        {
            Buffer = new Bitmap(SystemMetrics.ScreenWidth, SystemMetrics.ScreenHeight);

            Buffer.DrawRectangle(c,
                                 0,
                                 0, 0,
                                 SystemMetrics.ScreenWidth, SystemMetrics.ScreenHeight,
                                 0, 0,
                                 c,
                                 0, 0,
                                 c,
                                 0, 0,
                                 Bitmap.OpacityOpaque);
        }
    }
}

I added the call to Dispose(), but that had no noticeable effect nor did adding the call to change the CustomHeap size.

Your test is not just flushing to screen plus you are rotating the image which has a high performance penalty. Your test has much more than the original idea.

The test should not rotate image and the loop would be simple Bitmap class. WPF is too slow even on smaller displays. Take the GHI demo or pyxis OS for example.

something like…

Bitmap LCD = new …
while(true)
{
LCD.DrawSomething()
ReadTimer()
}

From library documentation

The rotation is necessary since that is the orientation that the device will be in. This should not be that big of a performance hit if the driver just performs a transform on the memory pointer location. The math to do this should take microseconds, and the memory map would be untouched. At most this should be a one-time cost at start-up and after that the memory map is set.

What is my test doing other than alternating the display color? I have timers to flag the display color change and to flush to the display frame buffer. What else do you see being done here?

Like I said, rotation actually need a lot of processing not moving pointers. You can try and compare the difference. You can also try what I suggested if you like

You are right, the rotation is eating up a lot of process time. Is this a feature GHI added in managed code or did you modify the native driver?

It is an added feature but done in native code. The LCD controller does not support rotating. Graphics are actually rotated in software (native code) before flushing to the LCD.