Main Site Documentation

Multithreading and SerialLCD driver


#1

Does anyone have experience with multithreading and using the SerialLCD driver?
I am using the FEZ Panda II with a Serial LCD.

I have 4 threads running, three of them to monitor sensors and control motors, and one of them is to use the SerialLCD for displaying messages. I have found that if I’m in the middle of a method in SerialLCD and another thread is given control, the serial LCD timing is screwed up and I get funny characters printed out on the display. A SerialLCD.ClearScreen() doesn’t do any good as it seems like the LCD goes out to lunch, and I need to reboot the LCD hardware to get rid of the problem.

I do not have this problem if I freeze all other threads from running.

I assume that I will need to block all other threads from processing simply to run the Print method on the SerialLCD driver, or any other timing critical methods for that matter. The display certainly is a low priority task in my system.

Any thoughts on the best way to handle this?

Thanks much,
Greg


#2

Try raising the threads priority


#3

Does the display use output compare to simulate UART or it uses actual UART?


#4

Here is the SerialLCD driver code:



/*
Copyright 2010 GHI Electronics LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 
*/

using System;
using System.IO.Ports;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.FEZ;

namespace ChickenCoopAutomation
{
    public class SerialLCD : IDisposable
    {
        const byte DISP_ON = 0xC;    //Turn visible LCD on
        const byte CLR_DISP = 0x01;      //Clear display
        const byte CUR_HOME = 2;      //Move cursor home and clear screen memory
        const byte SET_CURSOR = 0x80;   //SET_CURSOR + X : Sets cursor position to X
        const byte Move_CURSOR_LEFT = 0x10;

        OutputCompare oc;
        const int MAX_TIMINGS_BUFFER_SIZE = 10;
        uint[] buffer = new uint[MAX_TIMINGS_BUFFER_SIZE];
        const int BAUD_RATE = 2400;
        const int BIT_TIME_US = 1 * 1000 * 1000 / BAUD_RATE;
        readonly int BYTE_TIME_MS;
        
        public SerialLCD(FEZ_Pin.Digital pin)
        {
            BYTE_TIME_MS = (int)Math.Ceiling((double)BIT_TIME_US * MAX_TIMINGS_BUFFER_SIZE / 1000);
            oc = new OutputCompare((Cpu.Pin)pin, true, MAX_TIMINGS_BUFFER_SIZE);
            
            // Initilaize LCD

            SendCommand(DISP_ON);
            SendCommand(CLR_DISP);
        }

        public void Dispose()
        {
            oc.Dispose();
            buffer = null;
        }

        private void SendByte(byte b)
        {
            bool currentPinState;
            int currentBufferIndex = 0;
            uint currentStateTiming;

            // start bit
            currentPinState = false;
            currentStateTiming = BIT_TIME_US;

            // data bits
            for (int i = 0; i < 8; i++)
            {
                bool neededState = (b & (1 << i)) != 0;

                if (neededState != currentPinState)
                {
                    buffer[currentBufferIndex] = currentStateTiming;
                    currentStateTiming = BIT_TIME_US;
                    currentPinState = neededState;
                    currentBufferIndex++;
                }
                else
                {
                    currentStateTiming += BIT_TIME_US;
                }
            }

            // stop bit
            if (currentPinState != true)
            {
                buffer[currentBufferIndex] = currentStateTiming;
                currentBufferIndex++;
            }
            
            oc.Set(false, buffer, 0, currentBufferIndex, false);

            // wait till data is sent
            Thread.Sleep(BYTE_TIME_MS);
        }

        public void PutC(char c)
        {
            SendByte((byte)c);
        }

        private void SendCommand(byte cmd)
        {
            SendByte(0xFE);
            SendByte(cmd);
        }

        public void Print(string str)
        {
            for (int i = 0; i < str.Length; i++)
                PutC(str[i]);
        }

        public void ClearScreen()
        {
            SendCommand(CLR_DISP);
        }

        public void CursorHome()
        {
            SendCommand(CUR_HOME);
        }

        public void SetCursor(byte row, byte col)
        {
            SendCommand((byte)(SET_CURSOR | row << 6 | col));
        }

        public void MoveLeft()
        {
            SendCommand(Move_CURSOR_LEFT);
        }
    }
}




#5

Ouch.


#6

This driver uses output compare to simulate UART TX on any IO. While this works great in most uses, if you have heavy loads in interrupts or a load on blocking methods, OC timing may not be perfect.
Does one of your threads use onewire?

I suggest you use a real serial port and you will be fine.


#7

Actually 2 of my threads use OneWire for talking to DS18B20s. Is there a serial version of the SerialLCD driver somewhere?


#8

I figured it out and wrote a class. You can find it here. It did the trick with the multithreading problem. Thanks Gus!

http://code.tinyclr.com/project/388/seriallcd-uses-uart/