FEZ Touch Driver Ver 2.1

Andreas,

It looks like you are passing text to GetTextWidth that contains a character that is not in the font. The fonts I generated only contain ASCII characters 0x20 to 0x7E. Could set a break point and figure out what character is causing the exception?

Andreas,

Your changes to the interrupt driven touch driver look very interesting. Where do you attach the event handler [quote]touch_OnInterrupt[/quote] the first time?

Just copied and pasted the 2.2 driver into my program.

Changed the orientation to, LandscapeInverse.
Everything looks right.
Thanks for saving a bunch of my gray cells!
Dont mean the ones in my hair?

Jasdev
sorry, I forgot to paste that snipped. Here it is:

            private Thread touchThread;
            private void touch_OnInterrupt(uint iPort, uint iState, DateTime time)
            {
                terminateTouchThread = (iState == 1);   //   0: down 1:up
                if (!bIRQrunning)
                {
                    touchThread = new Thread(TouchThread);
                    touchThread.Priority = ThreadPriority.AboveNormal;
                    touchThread.Start();
                }
            }

Jasdev
I looked in your DrawString routine. Gratulation: sophisticated!

If I unterstand enaugh, you could improve the performance if you display not char after char, but collect them into a line buffer and send then the whole buffer. So you can save some calculations and send a bulk to the display. This should be faster.
(forgive me for my English made in Switzerland)

Jasdev

[quote]It looks like you are passing text to GetTextWidth that contains a character that is not in the font. [/quote]yes and no: your font generator generates only the font for the text in the left window. But your CharInfo assumes IMHO, that [italic]all [/italic]ascii-chars exist. If this is not the case, the descriptorIndex points out of bounds of charDescriptors
[line]Ok now I see: The idea of the left window of your generator is not to reduce letters (I used only the aktuell needed Text, but to select parts of the whole Alphabet. This is a little missleading. I would prefer, that the left window only displays the letter selection, but is not editible.
If I use your selection, it works fine! (Thank you) But I would be happy if the °-Sign would be accessible.

Andreas,

Thanks :slight_smile:

Your suggestion for speeding up the DrawText routine is very good. Actually if you look at the original driver code from GHI, they did a similar thing to what you are suggesting. I didn’t implement it that way, because it was more complicated.

I’ll look at the Font Generator again, and see what improvements I can make. You should be able to edit the selection on the left, and CharInfo is supposed to work correctly.

Is TouchThread started first, and then within the TouchThread code the event handler is attached to the interrupt port?

Jasdev

[quote]Is TouchThread started first, and then within the TouchThread code the event handler is attached to the interrupt port?[/quote]No, the interrupt starts the TouchThread, if the interrupt comes with iStatus “touch down”. Then the TouchThread loops and fires MoveEvents with coordinates until the interrupt comes with a iStatus “touch up”. The TouchThread fires an final UpEvent and dies.
Because the spi-access fires also IRQ’s the IRQ must switched off while spi is handled.

here is jasdevs FEZ TouchDriver 2.2 with IRQ-driven TouchEvent as described in this thread above. I do not know how to emplement it in the code site…


/*
Copyright 2011 GHI Electronics LLC
*/

using System;
using System.Threading;

using Microsoft.SPOT.Hardware;

using GHIElectronics.NETMF.Hardware;


namespace GHIElectronics.NETMF.FEZ
{
    public static partial class FEZ_Components
    {
        public class FEZTouch : IDisposable
        {
            #region Enumerated Values

            public enum Orientation
            {
                Portrait = 0,
                Landscape = 1,
                LandscapeInverse = 2
            }

            public enum Color : ushort
            {
                White = 0xFFFF,
                Black = 0x0000,
                Red = (0xFF >> 3) | ((0 & 0xFC) << 3) | ((0 & 0xF8) << 8),
                Blue = (0 >> 3) | ((0 & 0xFC) << 3) | ((0xFF & 0xF8) << 8),
                Green = (0 >> 3) | ((0xFF & 0xFC) << 3) | ((0 & 0xF8) << 8),
                Cyan = (0 >> 3) | ((0xFF & 0xFC) << 3) | ((0xFF & 0xF8) << 8),
                Gray = (0x80 >> 3) | ((0x80 & 0xFC) << 3) | ((0x80 & 0xF8) << 8),
                Magneta = (0xFF >> 3) | ((0 & 0xFC) << 3) | ((0xFF & 0xF8) << 8),
                Yellow = (0xFF >> 3) | ((0xFF & 0xFC) << 3) | ((0 & 0xF8) << 8),
            }

            public enum PowerMode
            {
                Normal = 0,
                Sleep = 1,
                StandBy = 2,
                DeepStandBy = 3
            }

            public enum DisplayMode
            {
                Normal = 0,
                Dim = 1,
                Off = 2
            }

            #endregion


            #region Events

            public delegate void TouchEventHandler(int x, int y);

            public event TouchEventHandler TouchDownEvent = delegate { };
            public event TouchEventHandler TouchMoveEvent = delegate { };
            public event TouchEventHandler TouchUpEvent = delegate { };

            #endregion


            #region Constructors

            protected FEZTouch()
            {
                // not allowed to call this constructor
            }

            public FEZTouch(LCDConfiguration lcdConfig)
            {
                this.InitLCD(lcdConfig);
            }

            public FEZTouch(LCDConfiguration lcdConfig, TouchConfiguration touchConfig)
            {
                this.InitLCD(lcdConfig);
                this.InitTouch(touchConfig);
            }

            #endregion


            #region Destructors

            ~FEZTouch()
            {
                this.Dispose();
            }

            public void Dispose()
            {
                if (this.disposed == false)
                {
                    this.disposed = true;

                    if (spi != null)
                    {
                        terminateTouchThread = true;
                        touchThread.Join();

                        spi.Dispose();
                        touchIRQ.Dispose();
                    }

                    this.TouchDownEvent = null;
                    this.TouchMoveEvent = null;
                    this.TouchUpEvent = null;

                    this.parallelPort.Dispose();
                    this.lcdReset.Dispose();
                    this.lcdChipSelect.Dispose();
                    this.lcdRegSelect.Dispose();
                    this.lcdBackLight.Dispose();
                }
            }

            #endregion


            #region Properties

            public int ScreenWidth
            {
                get { return (this.lcdOrientation == Orientation.Portrait ? 240 : 320); }
            }

            public int ScreenHeight
            {
                get { return (this.lcdOrientation == Orientation.Portrait ? 320 : 240); }
            }

            #endregion


            #region Control Methods

            private void InitLCD(LCDConfiguration lcdConfig)
            {
                // save config values
                this.parallelPort = new ParallelPort(lcdConfig.DataPins, lcdConfig.WritePin, lcdConfig.ReadPin);
                this.lcdReset = new OutputPort(lcdConfig.Reset, true);
                this.lcdChipSelect = new OutputPort(lcdConfig.ChipSelect, true);
                this.lcdRegSelect = new OutputPort(lcdConfig.RS, true);
                this.lcdBackLight = new OutputCompare(lcdConfig.BackLight, true, 2);
                this.lcdOrientation = lcdConfig.LCDOrientation;

                // set initial power and display modes
                this.displayMode = DisplayMode.Normal;
                this.backlightLevel = 100;

                // toggle reset pin
                this.lcdReset.Write(true);
                Thread.Sleep(5);
                this.lcdReset.Write(false);
                Thread.Sleep(5);
                this.lcdReset.Write(true);
                Thread.Sleep(5);

                this.lcdChipSelect.Write(false);

                //************* Start Initial Sequence **********//
                this.WriteRegister(0x01, 0x0100); // set SS and SM bit
                this.WriteRegister(0x02, 0x0200); // set 1 line inversion
                switch (this.lcdOrientation)
                {
                    case Orientation.Portrait:
                        // AM		=	0
                        // ID1-ID0	=	10
                        // ORG		=	1
                        // HWM		=	1
                        this.WriteRegister(0x03, 0x0230);
                        break;
                    case Orientation.Landscape:
                        // AM		=	1
                        // ID1-ID0	=	11
                        // ORG		=	0
                        // HWM		=	1
                        this.WriteRegister(0x03, 0x02A8);
                        break;
                    case Orientation.LandscapeInverse:
                        // AM		=	1
                        // ID1-ID0	=	01
                        // ORG		=	0
                        // HWM		=	1
                        this.WriteRegister(0x03, 0x0298);
                        break;
                }
                this.WriteRegister(0x04, 0x0000); // Resize register
                this.WriteRegister(0x08, 0x0207); // set the back porch and front porch
                this.WriteRegister(0x09, 0x0000); // set non-display area refresh cycle ISC[3:0]
                this.WriteRegister(0x0A, 0x0000); // FMARK function
                this.WriteRegister(0x0C, 0x0000); // RGB interface setting
                this.WriteRegister(0x0D, 0x0000); // Frame marker Position
                this.WriteRegister(0x0F, 0x0000); // RGB interface polarity

                //*************Power On sequence ****************//
                this.WriteRegister(0x10, 0x0000); // SAP, BT[3:0], AP, DSTB, SLP, STB
                this.WriteRegister(0x11, 0x0007); // DC1[2:0], DC0[2:0], VC[2:0]
                this.WriteRegister(0x12, 0x0000); // VREG1OUT voltage
                this.WriteRegister(0x13, 0x0000); // VDV[4:0] for VCOM amplitude
                this.WriteRegister(0x07, 0x0001);
                Thread.Sleep(200); // Dis-charge capacitor power voltage
                this.WriteRegister(0x10, 0x1690); // SAP, BT[3:0], AP, DSTB, SLP, STB
                this.WriteRegister(0x11, 0x0227); // Set DC1[2:0], DC0[2:0], VC[2:0]
                Thread.Sleep(50); // Delay 50ms
                this.WriteRegister(0x12, 0x000D); // 0012
                Thread.Sleep(50); // Delay 50ms
                this.WriteRegister(0x13, 0x1200); // VDV[4:0] for VCOM amplitude
                this.WriteRegister(0x29, 0x000A); // 04 VCM[5:0] for VCOMH
                this.WriteRegister(0x2B, 0x000D); // Set Frame Rate
                Thread.Sleep(50); // delay 50ms
                this.WriteRegister(0x20, 0x0000); // GRAM horizontal Address
                this.WriteRegister(0x21, 0x0000); // GRAM Vertical Address

                // ----------- Adjust the Gamma Curve ----------//
                this.WriteRegister(0x30, 0x0000);
                this.WriteRegister(0x31, 0x0404);
                this.WriteRegister(0x32, 0x0003);
                this.WriteRegister(0x35, 0x0405);
                this.WriteRegister(0x36, 0x0808);
                this.WriteRegister(0x37, 0x0407);
                this.WriteRegister(0x38, 0x0303);
                this.WriteRegister(0x39, 0x0707);
                this.WriteRegister(0x3C, 0x0504);
                this.WriteRegister(0x3D, 0x0808);

                //------------------ Set GRAM area ---------------//
                this.WriteRegister(0x50, 0x0000); // Horizontal GRAM Start Address
                this.WriteRegister(0x51, 0x00EF); // Horizontal GRAM End Address
                this.WriteRegister(0x52, 0x0000); // Vertical GRAM Start Address
                this.WriteRegister(0x53, 0x013F); // Vertical GRAM Start Address
                this.WriteRegister(0x60, 0xA700); // Gate Scan Line
                this.WriteRegister(0x61, 0x0001); // NDL, VLE, REV
                this.WriteRegister(0x6A, 0x0000); // set scrolling line

                //-------------- Partial Display Control ---------//
                this.WriteRegister(0x80, 0x0000);
                this.WriteRegister(0x81, 0x0000);
                this.WriteRegister(0x82, 0x0000);
                this.WriteRegister(0x83, 0x0000);
                this.WriteRegister(0x84, 0x0000);
                this.WriteRegister(0x85, 0x0000);

                //-------------- Panel Control -------------------//
                this.WriteRegister(0x90, 0x0010);
                this.WriteRegister(0x92, 0x0000);
                this.WriteRegister(0x07, 0x0133); // 262K color and display ON

                this.lcdChipSelect.Write(true);
            }
            private void touch_OnInterrupt(uint iPort, uint iState, DateTime time)
            {
                terminateTouchThread = (iState == 1);   //   0: down 1:up
                if (!bIRQrunning)
                {
                    touchThread = new Thread(TouchThread);
                    touchThread.Priority = ThreadPriority.AboveNormal;
                    touchThread.Start();
                }
            }

            private void InitTouch(TouchConfiguration touchConfig)
            {
                spi = new SPI(new SPI.Configuration(touchConfig.ChipSelect, false, 1, 1, false, true, 2000, touchConfig.Channel));
                touchIRQ = new InterruptPort(touchConfig.TouchIRQ, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);
                touchIRQ.OnInterrupt += new NativeEventHandler(touch_OnInterrupt);
                terminateTouchThread = false;
            }

            /*
            public void SetPowerMode(PowerMode powerMode)
            {
                if (this.powerMode != powerMode)
                {
                    // save power mode
                    this.powerMode = powerMode;
 
                    // set new power mode
                    this.lcdChipSelect.Write(false);
 
                    switch (this.powerMode)
                    {
                        case PowerMode.Normal:
                            this.WriteRegister(0x10, 0x1690);
                            break;
                        case PowerMode.Sleep:
                            this.WriteRegister(0x10, 0x1691);
                            break;
                        case PowerMode.StandBy:
                            this.WriteRegister(0x10, 0x1692);
                            break;
                        case PowerMode.DeepStandBy:
                            this.WriteRegister(0x10, 0x1694);
                            break;
                    }
 
                    this.lcdChipSelect.Write(true);
                }
            }
            */

            public void SetDisplayMode(DisplayMode displayMode)
            {
                if (this.displayMode != displayMode)
                {
                    // save display mode
                    this.displayMode = displayMode;

                    // set new display mode
                    this.lcdChipSelect.Write(false);

                    switch (this.displayMode)
                    {
                        case DisplayMode.Normal:
                            this.SetBackLightLevel(100);
                            this.WriteRegister(0x07, 0x0133);
                            break;
                        case DisplayMode.Dim:
                            this.SetBackLightLevel(5);
                            this.WriteRegister(0x07, 0x0133);
                            break;
                        case DisplayMode.Off:
                            this.SetBackLightLevel(0);
                            this.WriteRegister(0x07, 0x0131);
                            break;
                    }

                    this.lcdChipSelect.Write(true);
                }
            }

            public void SetBackLightLevel(int backlightLevel)
            {
                // check for valid parameters
                if (backlightLevel < 0 || backlightLevel > 100)
                {
                    throw new ArgumentException();
                }

                if (this.backlightLevel != backlightLevel)
                {
                    // save back light level
                    this.backlightLevel = backlightLevel;

                    // set new back light level
                    if (this.backlightLevel == 0)
                    {
                        this.lcdBackLight.Set(false);
                    }
                    else if (this.backlightLevel == 100)
                    {
                        this.lcdBackLight.Set(true);
                    }
                    else
                    {
                        const int PERIOD = 10000;
                        int highTime = PERIOD * this.backlightLevel / 100;
                        this.lcdBackLight.Set(true, new uint[2] { (uint)highTime, (uint)(PERIOD - highTime) }, 0, 2, true);
                    }
                }
            }

            #endregion


            #region Drawing Methods

            public void ClearScreen()
            {
                this.FillRectangle(0, 0, this.ScreenWidth, this.ScreenHeight, FEZ_Components.FEZTouch.Color.Black);  // fill screen with black
            }

            public Color ColorFromRGB(byte red, byte green, byte blue)
            {
                return (Color)((red >> 3) | ((green & 0xFC) << 3) | ((blue & 0xF8) << 8));
            }

            private void SetPixelAddress(int x, int y)
            {
                this.WriteRegister(0x20, (ushort)x);
                this.WriteRegister(0x21, (ushort)y);
            }

            private void SetDrawingWindow(int x, int y, int width, int height)
            {
                // pixel address
                this.SetPixelAddress(x, y);

                // window
                this.WriteRegister(0x50, (ushort)x);
                this.WriteRegister(0x52, (ushort)y);
                this.WriteRegister(0x51, (ushort)(x + width - 1));
                this.WriteRegister(0x53, (ushort)(y + height - 1));
            }

            public void SetPixel(int x, int y, Color color)
            {
                if (x < 0 || y < 0 || x >= this.ScreenWidth || y >= this.ScreenHeight)
                {
                    throw new ArgumentException("SetPixel: Invalid parameter values.");
                }

                this.lcdChipSelect.Write(false);

                this.buffer[0] = (byte)((int)color >> 8);
                this.buffer[1] = (byte)(color);

                this.SetDrawingWindow(x, y, 1, 1);

                this.SetRegister(REGISTER_WRITE_GRAM);
                this.parallelPort.Write(this.buffer, 0, 2);

                this.lcdChipSelect.Write(true);
            }

            public void DrawLine(int xStart, int yStart, int xEnd, int yEnd, Color color)
            {
                if (xStart < 0 || yStart < 0 || xStart >= this.ScreenWidth || yStart >= this.ScreenHeight)
                {
                    throw new ArgumentException("DrawLine: Invalid start position values.");
                }

                if (xEnd < 0 || yEnd < 0 || xEnd >= this.ScreenWidth || yEnd >= this.ScreenHeight)
                {
                    throw new ArgumentException("DrawLine: Invalid end position values.");
                }

                // translate coordinates based on display orientation
                int x0 = 0;
                int y0 = 0;
                int x1 = 0;
                int y1 = 0;
                switch (this.lcdOrientation)
                {
                    case Orientation.Portrait:
                        x0 = xStart;
                        y0 = yStart;
                        x1 = xEnd;
                        y1 = yEnd;
                        break;
                    case Orientation.Landscape:
                        x0 = SCREEN_WIDTH - yStart - 1;
                        y0 = xStart;
                        x1 = SCREEN_WIDTH - yEnd - 1;
                        y1 = xEnd;
                        break;
                    case Orientation.LandscapeInverse:
                        x0 = yStart;
                        y0 = SCREEN_HEIGHT - xStart - 1;
                        x1 = yEnd;
                        y1 = SCREEN_HEIGHT - xEnd - 1;
                        break;
                }

                this.lcdChipSelect.Write(false);

                this.buffer[0] = (byte)((int)color >> 8);
                this.buffer[1] = (byte)(color);

                int dy = y1 - y0;
                int dx = x1 - x0;

                float m = 0;
                int b = 0;

                if (dx != 0)
                {
                    m = ((float)(dy)) / (dx);
                    b = y0 - (int)(m * x0);
                }

                if (global::System.Math.Abs(dx) >= global::System.Math.Abs(dy))
                {
                    if (x0 > x1)
                    {
                        this.Swap(ref x0, ref x1);
                        this.Swap(ref y0, ref y1);
                    }

                    while (x0 <= x1)
                    {
                        this.SetDrawingWindow(x0, y0, 1, 1);

                        this.SetRegister(REGISTER_WRITE_GRAM);
                        this.parallelPort.Write(this.buffer, 0, 2);

                        x0++;

                        if (x0 <= x1)
                        {
                            y0 = (int)(m * x0) + b;
                        }
                    }
                }
                else
                {
                    if (y0 > y1)
                    {
                        this.Swap(ref x0, ref x1);
                        this.Swap(ref y0, ref y1);
                    }

                    while (y0 <= y1)
                    {
                        this.SetDrawingWindow(x0, y0, 1, 1);

                        this.SetRegister(REGISTER_WRITE_GRAM);
                        this.parallelPort.Write(this.buffer, 0, 2);

                        y0++;

                        if (y0 <= y1)
                        {
                            if (dx != 0)
                            {
                                x0 = (int)((float)(y0 - b) / m);
                            }
                        }
                    }
                }

                this.lcdChipSelect.Write(true);
            }

            public void FillRectangle(int xPos, int yPos, int rectWidth, int rectHeight, Color color)
            {
                // validate parameter values
                if (xPos < 0 || yPos < 0 || (xPos + rectWidth) > this.ScreenWidth || (yPos + rectHeight) > this.ScreenHeight)
                {
                    throw new ArgumentException("FillRectangle: Invalid parameter values.");
                }

                // translate coordinates based on display orientation
                int x = 0;
                int y = 0;
                int width = 0;
                int height = 0;
                switch (this.lcdOrientation)
                {
                    case Orientation.Portrait:
                        x = xPos;
                        y = yPos;
                        width = rectWidth;
                        height = rectHeight;
                        break;
                    case Orientation.Landscape:
                        x = SCREEN_WIDTH - yPos - rectHeight;
                        y = xPos;
                        width = rectHeight;
                        height = rectWidth;
                        break;
                    case Orientation.LandscapeInverse:
                        x = yPos;
                        y = SCREEN_HEIGHT - xPos - rectWidth;
                        width = rectHeight;
                        height = rectWidth;
                        break;
                }

                this.lcdChipSelect.Write(false);

                int pixelCount = width * height;
                int bufferPixels = this.buffer.Length / 2; // every pixel is 2 bytes
                byte h = (byte)((int)color >> 8);
                byte l = (byte)(color);

                // fill buffer
                for (int i = 0; i < this.buffer.Length; i = i + 2)
                {
                    this.buffer[i] = h;
                    this.buffer[i + 1] = l;
                }

                this.SetDrawingWindow(x, y, width, height);

                this.SetRegister(REGISTER_WRITE_GRAM);

                int loops = pixelCount / bufferPixels;

                for (int i = 0; i < loops; i++)
                {
                    this.parallelPort.Write(this.buffer, 0, this.buffer.Length);
                }

                int pixelsLeft = pixelCount % bufferPixels;
                if (pixelsLeft > 0)
                {
                    // every pixel is 2 bytes
                    this.parallelPort.Write(this.buffer, 0, pixelsLeft * 2);
                }

                this.lcdChipSelect.Write(true);
            }

            public void DrawImage(int xPos, int yPos, Image image)
            {
                // validate parameter values
                if (xPos < 0 || yPos < 0 || (xPos + image.Width) > this.ScreenWidth || (yPos + image.Height) > this.ScreenHeight)
                {
                    throw new ArgumentException("DrawImage: Invalid parameter values.");
                }

                // translate coordinates based on display orientation
                int x = 0;
                int y = 0;
                int width = 0;
                int height = 0;
                switch (this.lcdOrientation)
                {
                    case Orientation.Portrait:
                        x = xPos;
                        y = yPos;
                        width = image.Width;
                        height = image.Height;
                        break;
                    case Orientation.Landscape:
                        x = SCREEN_WIDTH - yPos - image.Height;
                        y = xPos;
                        width = image.Height;
                        height = image.Width;
                        break;
                    case Orientation.LandscapeInverse:
                        x = yPos;
                        y = SCREEN_HEIGHT - xPos - image.Width;
                        width = image.Height;
                        height = image.Width;
                        break;
                }

                this.lcdChipSelect.Write(false);
                this.SetDrawingWindow(x, y, width, height);
                this.SetRegister(REGISTER_WRITE_GRAM);
                this.parallelPort.Write(image.ImageBytes, Image.IMG_PIXELS_INDEX, image.ImageBytes.Length - Image.IMG_PIXELS_INDEX);
                this.lcdChipSelect.Write(true);
            }

            public void DrawString(int xPos, int yPos, string text, Color fgColor, Color bgColor, Font font)
            {
                // validate parameter values
                if (xPos < 0 || yPos < 0)
                {
                    throw new ArgumentException("DrawString: Screen position (x,y) is invalid.");
                }

                if ((xPos + font.GetTextWidth(text)) > this.ScreenWidth || (yPos + font.Height) > this.ScreenHeight)
                {
                    throw new ArgumentException("DrawString: Text string is too wide or too high to fit on the screen.");
                }

                // translate coordinates based on display orientation
                int x = 0;
                int y = 0;
                int width = 0;
                int height = 0;
                switch (this.lcdOrientation)
                {
                    case Orientation.Portrait:
                        x = xPos;
                        y = yPos;
                        break;
                    case Orientation.Landscape:
                        x = SCREEN_WIDTH - yPos - font.Height;
                        y = xPos;
                        break;
                    case Orientation.LandscapeInverse:
                        x = yPos;
                        y = SCREEN_HEIGHT - xPos - font.AverageWidth;
                        break;
                }

                // split colors into bytes
                byte fgColorHigh = (byte)((int)fgColor >> 8);
                byte fgColorLow = (byte)(fgColor);
                byte bgColorHigh = (byte)((int)bgColor >> 8);
                byte bgColorLow = (byte)(bgColor);

                // draw each character
                char currentChar = ' ';
                CharInfo charInfo = null;
                int bytesInBuffer = 0;
                for (int charIndex = 0; charIndex < text.Length; charIndex++)
                {
                    // get character
                    currentChar = text[charIndex];

                    // get character info
                    charInfo = font[currentChar];

                    // translate width and height based on display orientation
                    switch (this.lcdOrientation)
                    {
                        case Orientation.Portrait:
                            width = charInfo.Width;
                            height = charInfo.Height;
                            break;
                        case Orientation.Landscape:
                            width = charInfo.Height;
                            height = charInfo.Width;
                            break;
                        case Orientation.LandscapeInverse:
                            width = charInfo.Height;
                            height = charInfo.Width;
                            break;
                    }

                    // get character pixels
                    bytesInBuffer = font.FillBitmapBuffer(charInfo, this.buffer, fgColorLow, fgColorHigh, bgColorLow, bgColorHigh);

                    // output character pixels
                    this.lcdChipSelect.Write(false);
                    this.SetDrawingWindow(x, y, width, height);
                    this.SetRegister(REGISTER_WRITE_GRAM);
                    this.parallelPort.Write(this.buffer, 0, bytesInBuffer);
                    this.lcdChipSelect.Write(true);

                    // update x position
                    switch (this.lcdOrientation)
                    {
                        case Orientation.Portrait:
                            x += width;
                            break;
                        case Orientation.Landscape:
                            y += height;
                            break;
                        case Orientation.LandscapeInverse:
                            y -= height;
                            break;
                    }
                }
            }

            #endregion


            #region Interface Methods

            private void Swap(ref int a1, ref int a2)
            {
                int temp = a1;
                a1 = a2;
                a2 = temp;
            }

            private void SetRegister(byte register)
            {
                this.lcdRegSelect.Write(false);

                this.regBuffer[0] = 0;
                this.regBuffer[1] = register;
                this.parallelPort.Write(this.regBuffer, 0, 2);

                this.lcdRegSelect.Write(true);
            }

            private void WriteRegister(byte register, ushort value)
            {
                this.SetRegister(register);

                this.regBuffer[0] = (byte)(value >> 8);
                this.regBuffer[1] = (byte)(value);
                this.parallelPort.Write(regBuffer, 0, 2);
            }

            #endregion


            #region Touch Thread & Methods

            private void TouchThread()
            {
                if (bIRQrunning) return;
                bIRQrunning = true;
                int x = 0;
                int y = 0;
                getTouchCoord(ref x, ref y);
                FireTouchDownEvent(x, y);

                while (terminateTouchThread == false)
                {
                    Thread.Sleep(TOUCH_SAMPLING_TIME);
                    getTouchCoord(ref x, ref y);
                    this.FireTouchMoveEvent(x, y);
                }
                getTouchCoord(ref x, ref y);
                this.FireTouchUpEvent(x, y);
                bIRQrunning = false;
            }

            static byte[] writeBuffer = new byte[] { 0, 0, 0, 0 };
            static byte[] readBuffer = new byte[2];
            private const int TOUCHXMAX = 3385;
            private const int TOUCHXMIN = 620;
            private const int TOUCHYMIN = 800;
            private const int TOUCHYMAX = 3500;
            //private int xMin=5000;
            //private int xMax = 0;
            //private int yMin = 5000;
            //private int yMax = 0;
            private void getTouchCoord(ref int x, ref int y)
            {
                touchIRQ.OnInterrupt -= new NativeEventHandler(touch_OnInterrupt);
                writeBuffer[0] = 0x90;
                spi.WriteRead(writeBuffer, readBuffer, 1);
                y = readBuffer[0];
                y <<= 8;
                y |= readBuffer[1];
                y >>= 3;

                writeBuffer[0] = 0xD0;
                spi.WriteRead(writeBuffer, readBuffer, 1);
                touchIRQ.OnInterrupt += new NativeEventHandler(touch_OnInterrupt);
                x = readBuffer[0];
                x <<= 8;
                x |= readBuffer[1];
                x >>= 3;
                //if (x != 0 && x != 4095 && y != 0 && y != 4095) //get extrema
                //{
                //    if (x < xMin) { xMin = x; Debug.Print("xmin=" + xMin.ToString()); }
                //    if (x > xMax) { xMax = x; Debug.Print("xmax=" + xMax.ToString()); }
                //    if (y < yMin) { yMin = y; Debug.Print("ymin=" + yMin.ToString()); }
                //    if (y > yMax) { yMax = y; Debug.Print("ymax=" + yMax.ToString()); }
                //}
                // calibrate 

                x = (TOUCHXMAX - x) * (SCREEN_WIDTH - 1) / (TOUCHXMAX - TOUCHXMIN);
                y = (TOUCHYMAX - y) * (SCREEN_HEIGHT - 1) / (TOUCHYMAX - TOUCHYMIN);
                y = y > SCREEN_HEIGHT ? SCREEN_HEIGHT : y; y = y < 0 ? 0 : y;
                x = x > SCREEN_WIDTH ? SCREEN_WIDTH : x; x = x < 0 ? 0 : x;
            }

            private void FireTouchDownEvent(int xPos, int yPos)
            {
                if (this.TouchDownEvent != null)
                {
                    // translate coordinates based on display orientation
                    int x = 0;
                    int y = 0;
                    switch (this.lcdOrientation)
                    {
                        case Orientation.Portrait:
                            x = xPos;
                            y = yPos;
                            break;
                        case Orientation.Landscape:
                            x = yPos;
                            y = SCREEN_WIDTH - xPos - 1;
                            break;
                        case Orientation.LandscapeInverse:
                            x = SCREEN_HEIGHT - yPos - 1;
                            y = xPos;
                            break;
                    }

                    // fire the event
                    this.TouchDownEvent(x, y);
                }
            }

            private void FireTouchMoveEvent(int xPos, int yPos)
            {
                if (this.TouchMoveEvent != null)
                {
                    // translate coordinates based on display orientation
                    int x = 0;
                    int y = 0;
                    switch (this.lcdOrientation)
                    {
                        case Orientation.Portrait:
                            x = xPos;
                            y = yPos;
                            break;
                        case Orientation.Landscape:
                            x = yPos;
                            y = SCREEN_WIDTH - xPos - 1;
                            break;
                        case Orientation.LandscapeInverse:
                            x = SCREEN_HEIGHT - yPos - 1;
                            y = xPos;
                            break;
                    }

                    // fire the event
                    this.TouchMoveEvent(x, y);
                }
            }

            private void FireTouchUpEvent(int xPos, int yPos)
            {
                if (this.TouchUpEvent != null)
                {
                    // translate coordinates based on display orientation
                    int x = 0;
                    int y = 0;
                    switch (this.lcdOrientation)
                    {
                        case Orientation.Portrait:
                            x = xPos;
                            y = yPos;
                            break;
                        case Orientation.Landscape:
                            x = yPos;
                            y = SCREEN_WIDTH - xPos - 1;
                            break;
                        case Orientation.LandscapeInverse:
                            x = SCREEN_HEIGHT - yPos - 1;
                            y = xPos;
                            break;
                    }

                    // fire the event
                    this.TouchUpEvent(x, y);
                }
            }

            #endregion


            #region Font Classes

            public class Font
            {
                // CONSTRUCTORS
                protected Font()
                {
                    // this constructor is not for external access
                    this.avgWidth = 0;
                    this.maxWidth = 0;
                    this.height = 0;
                    this.startChar = '\x00';
                    this.endChar = '\x00';
                    this.charDescriptors = null;
                    this.charBitmaps = null;
                }


                // PROPERTIES
                public ushort AverageWidth
                {
                    get { return this.avgWidth; }
                }

                public ushort MaxWidth
                {
                    get { return this.maxWidth; }
                }

                public ushort Height
                {
                    get { return this.height; }
                }

                public CharInfo this[char newChar]
                {
                    get
                    {
                        // validate parameter
                        if (newChar < this.startChar || newChar > this.endChar)
                        {
                            throw new ArgumentException("Font class: character not found.");
                        }

                        ushort bitMask = 0x7FFF;
                        int descriptorIndex = (newChar - this.startChar) * constSizeOfCharDescriptor;
                        ushort charWidth = (ushort)(this.charDescriptors[descriptorIndex] & bitMask);
                        ushort charHeight = (ushort)(this.charDescriptors[descriptorIndex + 1] & bitMask);
                        ushort bitmapStartIndex = (ushort)(this.charDescriptors[descriptorIndex + 2] & bitMask);
                        ushort bitmapLength = (ushort)(this.charDescriptors[descriptorIndex + 3] & bitMask);

                        return new CharInfo(newChar, charWidth, charHeight, bitmapStartIndex, bitmapLength);
                    }
                }


                // METHODS
                public int GetTextWidth(string text)
                {
                    int totalPixels = 0;

                    foreach (char character in text)
                    {
                        totalPixels += this[character].Width;
                    }

                    return totalPixels;

                }

                public int FillBitmapBuffer(CharInfo charInfo, byte[] buffer, byte fgColorLow, byte fgColorHigh, byte bgColorLow, byte bgColorHigh)
                {
                    // check the buffer size
                    int charBytesReq = charInfo.Width * charInfo.Height * 2;
                    int totalBytesReq = charInfo.Width * this.height * 2;
                    if (buffer.Length < totalBytesReq)
                    {
                        throw new ArgumentException("Buffer length is not large enough for this character.");
                    }

                    // fill the buffer
                    ushort bitmapValue = 0;
                    int bufferIndex = 0;
                    ushort bitmapIndex = charInfo.BitmapStartIndex;
                    for (int i = 0; i < charInfo.BitmapLength; i++, bitmapIndex++)
                    {
                        bitmapValue = (ushort)this.charBitmaps[bitmapIndex];
                        for (ushort bitMask = (ushort)0x0001; bitMask < (ushort)0x8000; )
                        {
                            // add pixel color
                            if ((bitmapValue & bitMask) == bitMask)
                            {
                                // add foreground color
                                buffer[bufferIndex] = fgColorHigh;
                                buffer[bufferIndex + 1] = fgColorLow;
                            }
                            else
                            {
                                // add background color
                                buffer[bufferIndex] = bgColorHigh;
                                buffer[bufferIndex + 1] = bgColorLow;
                            }

                            // adjust bitMask, increment pixelsRead, and increment bufferIndex
                            bitMask = (ushort)(bitMask << 1);
                            bufferIndex += 2;

                            // check if we are done
                            if (bufferIndex >= charBytesReq)
                            {
                                break;
                            }
                        }
                    }

                    // add blank rows
                    while (bufferIndex < totalBytesReq)
                    {
                        // add background color
                        buffer[bufferIndex] = bgColorHigh;
                        buffer[bufferIndex + 1] = bgColorLow;
                        bufferIndex += 2;
                    }

                    // return num bytes in buffer
                    return bufferIndex;
                }


                // MEMBER FIELDS
                protected ushort avgWidth;
                protected ushort maxWidth;
                protected ushort height;
                protected char startChar;
                protected char endChar;
                protected string charDescriptors;
                protected string charBitmaps;
                protected const int constSizeOfCharDescriptor = 4;	// number of chars in each descriptor
            }

            public class CharInfo
            {
                // CONSTRUCTORS
                public CharInfo(char character, ushort charWidth, ushort charHeight, ushort bitmapStartIndex, ushort bitmapLength)
                {
                    this.character = character;
                    this.charWidth = charWidth;
                    this.charHeight = charHeight;
                    this.bitmapStartIndex = bitmapStartIndex;
                    this.bitmapLength = bitmapLength;
                }

                // PROPERTIES
                public char Character
                {
                    get { return this.character; }
                }

                public ushort Width
                {
                    get { return this.charWidth; }
                }

                public ushort Height
                {
                    get { return this.charHeight; }
                }

                public ushort BitmapStartIndex
                {
                    get { return this.bitmapStartIndex; }
                }

                public ushort BitmapLength
                {
                    get { return this.bitmapLength; }
                }


                // MEMBER FIELDS
                private char character;
                private ushort charWidth;
                private ushort charHeight;
                private ushort bitmapStartIndex;
                private ushort bitmapLength;
            }

            #endregion


            #region Image Class

            public class Image
            {
                // CONSTRUCTORS
                public Image(byte[] imgBytes)
                {
                    if (Utility.ExtractValueFromArray(imgBytes, 0, 4) != SIGNATURE)
                    {
                        throw new ArgumentException("Image Class: Signature bytes not found.");
                    }

                    int width = (int)Utility.ExtractValueFromArray(imgBytes, 4, 2);
                    int height = (int)Utility.ExtractValueFromArray(imgBytes, 6, 2);

                    if (width * height * 2 + 8 != imgBytes.Length)
                    {
                        throw new ArgumentException("Image class: Width and height do not match size of byte array.");
                    }

                    this.ImageBytes = imgBytes;
                    this.Width = width;
                    this.Height = height;
                }

                // PROPERTIES
                public readonly int Width;
                public readonly int Height;
                public const uint SIGNATURE = 0x354A82B8;
                public const int IMG_PIXELS_INDEX = 8;
                public byte[] ImageBytes;
            }

            #endregion


            #region LCD Configuration Class

            public class LCDConfiguration
            {
                // CONSTRUCTORS
                public LCDConfiguration(FEZ_Pin.Digital reset,
                    FEZ_Pin.Digital chipSelect,
                    FEZ_Pin.Digital RS,
                    FEZ_Pin.Digital lcdBackLight,
                    FEZ_Pin.Digital[] dataPins,
                    FEZ_Pin.Digital writePin,
                    FEZ_Pin.Digital readPin,
                    Orientation lcdOrientation)
                {
                    this.DataPins = new Cpu.Pin[8];

                    for (int i = 0; i < 8; i++)
                    {
                        this.DataPins[i] = (Cpu.Pin)dataPins[i];
                    }

                    this.WritePin = (Cpu.Pin)writePin;
                    this.ReadPin = (Cpu.Pin)readPin;
                    this.ChipSelect = (Cpu.Pin)chipSelect;
                    this.Reset = (Cpu.Pin)reset;
                    this.RS = (Cpu.Pin)RS;
                    this.BackLight = (Cpu.Pin)lcdBackLight;
                    this.LCDOrientation = lcdOrientation;
                }

                // PROPERTIES
                public Cpu.Pin[] DataPins;
                public Cpu.Pin WritePin;
                public Cpu.Pin ReadPin;
                public Cpu.Pin ChipSelect;
                public Cpu.Pin Reset;
                public Cpu.Pin RS;
                public Cpu.Pin BackLight;
                public Orientation LCDOrientation;
            }

            #endregion


            #region Touch Configuration Class

            public class TouchConfiguration
            {
                public SPI.SPI_module Channel;
                public Cpu.Pin ChipSelect;
                public Cpu.Pin TouchIRQ;

                public TouchConfiguration(SPI.SPI_module channel, FEZ_Pin.Digital chipSelect, FEZ_Pin.Digital touchIRQ)
                {
                    this.Channel = channel;
                    this.ChipSelect = (Cpu.Pin)chipSelect;
                    this.TouchIRQ = (Cpu.Pin)touchIRQ;
                }
            }

            #endregion


            #region Member Fields

            // constants
            private const int SCREEN_WIDTH = 240;
            private const int SCREEN_HEIGHT = 320;
            private const int BUFFER_SIZE = 2024;
            private const byte REGISTER_WRITE_GRAM = 0x22;
            private const int TOUCH_SAMPLING_TIME = 10;

            private bool disposed = false;
            private byte[] buffer = new byte[BUFFER_SIZE];
            private byte[] regBuffer = new byte[2];

            // lcd configuration
            private ParallelPort parallelPort;
            private OutputPort lcdReset;
            private OutputPort lcdChipSelect;
            private OutputPort lcdRegSelect;
            private OutputCompare lcdBackLight;
            private Orientation lcdOrientation;

            // touch configuration
            private static SPI spi;
            public static InterruptPort touchIRQ;
            private static bool terminateTouchThread;
            private static bool bIRQrunning = false;
            private Thread touchThread;

            // display mode
            private DisplayMode displayMode;
            private int backlightLevel;

            #endregion
        }
    }
}

Thanks Andreas. I’m not at home right now, and won’t be back until Wednesday night, so I can’t add your changes to the code until then.

@ Mike
I looked a little in your code of the FEZTouch driver (drawString). The main problem is, that large fonts make the app slow. First I tried to combine all letters of one line. But this approach costs too much memory.

The bodleneck is probably the translation from font bitmask to color. I tried to change the output buffer from byte[] to ushort[], because you can then reduce the corresponding for and while loop, if you use ushort color instead of byte colorHigh/ColorLow.

But I was not able to interpret a byte[] array as a ushort[] array (without copying). So I stoped my investigations. Perhaps you have an idea, how to do that. I would change the buffer globally to ushort[] and translate ushort[] to byte[] in the low level routines (ParalellPort.Write). This should accelerate the fond coding.

In a real application my irq solution has to be modified. (the SPI-Touch coord are coming very slow) So here is the modified TouchThread routine

            private static byte[] writeBuffer0 = new byte[] { 0x90, 0, 0, 0 };
            private static byte[] readBuffer0 = new byte[2];
            private static byte[] writeBuffer1 = new byte[] { 0xD0, 0, 0, 0 };
            private static byte[] readBuffer1 = new byte[2];
            private static int iCount;
            private static int xIrq = 0, x=0;
            private static int yIrq = 0, y=0;
            private static bool bFireDown;

            private void TouchThread()
            {
                if (bIRQrunning) return;
                bIRQrunning = true;
                xIrq = yIrq = 0;
                bFireDown=false;
                while (terminateTouchThread == false)
                {
                    getTouchCoord(ref xIrq, ref yIrq);
                    if (!bFireDown && xIrq > 0)
                    {
                        FireTouchDownEvent(xIrq, yIrq);
                        bFireDown = true;
                    }
                    else if (xIrq > 0)
                    {
                        this.FireTouchMoveEvent(x=xIrq, y=yIrq);
                    }
                    Thread.Sleep(TOUCH_SAMPLING_TIME);
                }
                this.FireTouchUpEvent(x, y);
                bIRQrunning = false;
            }

            private const int TOUCHXMAX = 3385;
            private const int TOUCHXMIN = 620;
            private const int TOUCHYMIN = 800;
            private const int TOUCHYMAX = 3500;
            //private int xMin=5000;
            //private int xMax = 0;
            //private int yMin = 5000;
            //private int yMax = 0;
            private void getTouchCoord(ref int x, ref int y)
            {
                touchIRQ.OnInterrupt -= new NativeEventHandler(touch_OnInterrupt);
                spi.WriteRead(writeBuffer0, readBuffer0, 1);
                spi.WriteRead(writeBuffer1, readBuffer1, 1);
                y = readBuffer0[0];
                y <<= 8;
                y |= readBuffer0[1];
                y >>= 3;

                x = readBuffer1[0];
                x <<= 8;
                x |= readBuffer1[1];
                x >>= 3;
                touchIRQ.OnInterrupt += new NativeEventHandler(touch_OnInterrupt);
                //if (x != 0 && x != 4095 && y != 0 && y != 4095) //get extrema
                //{
                //    if (x < xMin) { xMin = x; Debug.Print("xmin=" + xMin.ToString()); }
                //    if (x > xMax) { xMax = x; Debug.Print("xmax=" + xMax.ToString()); }
                //    if (y < yMin) { yMin = y; Debug.Print("ymin=" + yMin.ToString()); }
                //    if (y > yMax) { yMax = y; Debug.Print("ymax=" + yMax.ToString()); }
                //}
                // calibrate 

                x = (TOUCHXMAX - x) * (SCREEN_WIDTH - 1) / (TOUCHXMAX - TOUCHXMIN);
                y = (TOUCHYMAX - y) * (SCREEN_HEIGHT - 1) / (TOUCHYMAX - TOUCHYMIN);
                y = y > SCREEN_HEIGHT ? SCREEN_HEIGHT : y; y = y < 0 ? 0 : y;
                x = x > SCREEN_WIDTH ? SCREEN_WIDTH : x; x = x < 0 ? 0 : x;
            }