devxlogo

Discover a New Frontier for .NET Development: Program an LCD Display

Discover a New Frontier for .NET Development: Program an LCD Display

One of the most fascinating aspects of programming is making things work. And that is not limited to just what you can do with your computer; a much more exciting world exists outside of the computer. In this article I will show you how to add a cool and interesting secondary display to your computer. In particular, you will learn how to connect to a LCD display and use .NET to display information on it.

For this article, I will use the 4×20 Serial LCD (model LK204-25; this LCD is manufactured by Matrix Orbital and sold by Parallax) with Keypad Interface from Parallax ($99.95; see Figure 1). LCD displays have been used in a wide variety of electronic devices. The next time you use your credit card at the shopping mall, take a good look at the terminal (see left of Figure 2); you’ll almost certainly find that it is LCD. Besides being embedded in electronic devices, LCD screens are increasingly getting the attention of modders, who like to embed them in drive bays and use them to display system information (see right of Figure 2).

Most LCD screens in the market mainly come in two types of interfaces: parallel and serial. Technically, all LCD screens use parallel interfaces. However, due to the complexity of wiring and programming, some manufacturers add circuits to convert the parallel interface into a serial one. The end result is fewer wires to connect and a much simpler way of programming.

 

For this article, I used the serial version of the LCD with the new SerialPort class available in .NET 2.0. This makes programming an application much simpler and allows me to concentrate on exploring the features of the LCD.

Setting Up the LCD Display
The first step is to connect the LCD display to your PC. In order to do so, you need to perform a TTL-to-RS-232 level shifting so that the data can be read via a serial port. One way is to connect the LCD display to Parallax’s RS-232 DCE AppMod (http://www.parallax.com/detail.asp?product_id=29120; $29; see Figure 3).

For this project, however, I chose to use the Javelin Demo Board ($119; see Figure 4) to connect to the LCD display.

 

Figure 5 shows the wiring on the back of the LCD display. Be careful not to reverse the connection of the +5V and Ground connectors. The Rx connector allows data/instructions to be sent to the LCD display while the Tx connector allows data to be read from the LCD display. In most cases, you can simply connect only the Rx connector since all you want is to send data to the LCD.

 

Testing the Connections
If you did your connections correctly, you can now power up the LCD display. When connected properly, the screen should look as shown in Figure 7.

Programming the LCD
The 4×20 Serial LCD (LK204-25) display is a text-only LCD. However, its built-in character font (ASCII) includes some extended characters (see Figure 8) as well.

 

In addition, you can define up to eight custom characters. This is useful for defining your own logo, or smileys. It also supports drawing of vertical and horizontal bar graphs.

Programming the LK204-25 is straightforward. The following sections summarize the things you need to know to program the LK204-25 using the SerialPort class in .NET 2.0.

Issuing Commands
To issue commands to the LCD, such as clearing the screen, send a byte array containing the following: 254, [command]. For example, to clear the screen, the command is 254, 88.

In .NET, you can send the command to the LCD by setting a byte array to:

_data = New Byte() {254, 88}serialPort.Write(_data, 0, _data.Length)

The number 254 and 88 are expressed in decimal. Alternatively, you can also express them in hexadecimal:

_data = New Byte() {&HFE, &H58}serialPort.Write(_data, 0, _data.Length)

You can also express the command using its corresponding ASCII character, in this case ‘X’:

_data = New Byte() {254, Asc("X")}serialPort.Write(_data, 0, _data.Length)

Notice how the character X corresponds to column 5 and row 8 in Figure 8. (Note: The list of commands for the 4×20 Serial LCD can be found at http://www.parallax.com/dl/docs/prod/audiovis/LK204-25-2.x.pdf .)

Displaying Text
To send a string to the display, simply write the string directly to the SerialPort object:

serialPort.Write("Hello!")

Displaying Special Characters
To print out a particular character in the character set (this is especially useful for special characters), use the following format:

_data = New Byte() {&HEF}serialPort.Write(_data, 0, _data.Length)

where EF is the character in column E and row F of the character table.

One noteworthy point is that the characters displayed by my LCD do not match those listed in Figure 8, possibly because some characters were changed in the ROM. Hence, it would be best if you print out the character set of your LCD. You can do so by using the following code snippet:

        Dim num As Integer = &H0 '---start from 0        For i As Integer = 0 To 255            _data = New Byte() {num}            serialPort.Write(num & " - ")            serialPort.Write(_data, 0, _data.Length)            serialPort.WriteLine("")            num += 1            System.Threading.Thread.Sleep(500)        Next

DDisplay the CPU usage

  • Display the current time
  • Display RSS feeds

Packaging the LCD Functionalities into a Class Library
Before writing the sample application, I want to package all the core functionalities of the LCD display into a class library. Thus, if you have the LK204-25 serial LCD, you can simply program it by calling the relevant methods exposed by the class library.

Using Visual Studio 2005, create a new Windows application and name it C:LCD. Add a new class to the project and name it as LCD.vb

First, define the following enumerated types:

'---used for defining custom characters---Public Enum CustomChar    ch0 = 0    ch1 = 1    ch2 = 2    ch3 = 3    ch4 = 4    ch5 = 5    ch6 = 6    ch7 = 7End Enum'---used for setting the direction of horizontal bar graph---Public Enum Direction    left = 1    right = 0End Enum

Declare the following constant, member variables and events within the LCD class:

Public Class LCD    '---define the width of the bar graph---    Const BARGRAPH_WIDTH As Integer = 20    '---store the values of the bar graph---    Private _values(BARGRAPH_WIDTH) As Integer    '---keep track of the current index of the bar graph---    Private _index As Integer = 0    '---data received from the LCD---    Public Event DataFromLCD(ByVal str As String)    '----create the serial port---    Private WithEvents serialPort As New IO.Ports.SerialPort    '---data to send to the LCD---    Private _data() As Byte    '---parameters used by the other functions---    Private _col, _row As Integer    Private _c As Integer ' 0 to 7    Private _cBytes() As Byte    Private _lengthofVBar As Short    Private _lengthofHBar As Short    Private _d As Short = 0        '---0 is right 1 is left    Private _digit As Short        '---0 to 9    Private _minutes As Integer    '---0 or a number    Private _contrast As Integer   '---0 to 255

In the constructor of the LCD class, initialize the array used for storing the values in a bar graph:

    '---initialize the bar graph---    Public Sub New()        For i As Integer = 0 To BARGRAPH_WIDTH - 1            _values(i) = 0        Next    End Sub

Define the Connect() subroutine to open the serial port that is connected to the LCD display. You must set the BaudRate property to 19200, otherwise you may not be able to communicate with the LK204-25 LCD display.

    '---connect to the LCD using serial port---    Public Sub Connect(ByVal PortNo As String)        If serialPort.IsOpen Then            serialPort.Close()        End If        Try            With serialPort                .PortName = PortNo                .BaudRate = 19200                .Parity = IO.Ports.Parity.None                .DataBits = 8                .StopBits = IO.Ports.StopBits.One                .Handshake = IO.Ports.Handshake.None            End With            serialPort.Open()        Catch ex As Exception            MsgBox(ex.ToString)        End Try    End Sub

The DataReceived event will be fired when incoming data is received from the serial port. It will in turn fire off the DataFromLCD event to notify the calling routine that there is incoming data from the LCD display:

    '---data received from the LCD---    Private Sub DataReceived( _       ByVal sender As Object, _       ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _       Handles serialPort.DataReceived        Dim str As String = serialPort.ReadExisting        '---cause the event on the caller to fire---        RaiseEvent DataFromLCD(str)    End Sub

The Write() subroutine will send a string to the LCD display via the serial port. It supports delayed sending of data, and hence will send individual characters to the LCD at a delay of 100 milliseconds per character.

    '---display text with optional delay---    Public Sub Write(ByVal text As String, ByVal delay As Boolean)        If delay Then            For i As Integer = 0 To text.Length - 1                serialPort.Write(text(i))                System.Threading.Thread.Sleep(100)            Next        Else            serialPort.Write(text)        End If    End Sub

To simplify communicating with the LCD, the Command() subroutine will take in a command identifier and send the appropriate commands to the LCD (see Listing 1).

For example, to clear a screen, you would call Me.Command(“X”) and the subroutine will do the work of sending the correct command to the LCD display. Notice that this subroutine is only visible within the class, as you do not want the user to directly call the commands.

To make it easier for users to send the appropriate commands to the LCD, define the subroutines in Listing 2 (their use is self explanatory).

Finally, define the DrawBarGraph() subroutine to draw a moving bar graph:

    '---draw a bar graph---    Public Sub DrawBarGraph(ByVal value As Integer)        '---assign the value into the array---        If _index > BARGRAPH_WIDTH Then            '---shift all the values 1 position up            For i As Integer = 1 To BARGRAPH_WIDTH - 1                _values(i) = _values(i + 1)            Next            _values(BARGRAPH_WIDTH) = value        Else            _values(_index) = value        End If        _index += 1        '---draw the bars in the graph        For i As Integer = 1 To BARGRAPH_WIDTH            Me.DrawVertBarGraph(i, (_values(i) / 100) * 32)        Next        '---display some text---        Me.SetCursorPosition(1, 7)        Me.Write("CPU Usage: " & value.ToString & "%  ", False)    End Sub

Testing the Library
Now it’s time to test the library you just created. Populate the default Form1 with the following controls (see Figure 11):

  • Button controls
  • CheckBox control
  • Label control
  • TrackBar control

In the code-behind of Form1, import the following namespaces:

Imports System.ManagementImports System.NetImports System.IOImports System.XmlImports System.Threading

Declare the following member variables:

Public Class Form1    '---create an instance of the LCD class---    Private WithEvents LCD_Display As New LCD    '---true when form is finished loading---    Private FormLoaded As Boolean = False    '---threads for asynchronous execution---    Private t1, t2, t3 As Thread

When the form is first loaded, open a connection to the serial port connecting to the LCD display:

    Private Sub Form1_Load( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles MyBase.Load        FormLoaded = True        LCD_Display.Connect("COM3")    End Sub

Here, I am assuming that the COM3 is used to connect your PC to the LCD display.

Clear Display
To clear the LCD display, call the ClearDisplay() method of the LCD class:

    Private Sub btnClearDisplay_Click( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles btnClearDisplay.Click        LCD_Display.ClearDisplay()    End Sub

Custom Characters
To define a custom character, first initialize the byte array for the character and then call the DefineCustomChar() method of the LCD class.

    Private Sub btnCustomChar_Click( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles btnCustomChar.Click        Dim bytes(7) As Byte        bytes(0) = &H0        bytes(1) = &HA        bytes(2) = &HA        bytes(3) = &H0        bytes(4) = &H11        bytes(5) = &HE        bytes(6) = &H6        bytes(7) = &H0        '---defines the custom character---        LCD_Display.DefineCustomChar(CustomChar.ch0, bytes)        '---display the custom char at a specific location---        LCD_Display.SetCursorPosition(4, 15)        LCD_Display.WriteCustomChar(CustomChar.ch0)    End Sub

To test that the custom character is defined correctly, you will write the custom character at a specified location. These two tasks are accomplished by the WriteCustomChar() and SetCursorPosition() methods, respectively.

Display CPU Usage Graph
You can obtain the CPU utilization using WMI (Windows Management Instrumentation). Using WMI, you can obtain detailed information about your hardware and devices. The Display_CPU_Usage() subroutine repeatedly queries the CPU utilization rate and then updates the graph:

    '---display CPU usage using a graph---    Public Sub Display_CPU_Usage()        LCD_Display.ClearDisplay()        LCD_Display.InitThickVerticalBarGraph()        Dim oQ As ObjectQuery = _           New ObjectQuery("select * from Win32_Processor")        Dim searcher As ManagementObjectSearcher = _           New ManagementObjectSearcher(oQ)        While True            For Each CPU As ManagementObject In searcher.Get()                LCD_Display.DrawBarGraph(CPU("LoadPercentage"))                Exit For '---skip the next processor---            Next        End While    End Sub

Notice that in the For Each loop, I have inserted an Exit For statement. This is because I am testing this application on a dual core CPU, and in this case I am only interested in the CPU utilization of the first processor.

Author’s Note: You need to add the System.Management DLL to the project for WMI to work.

As the Display_CPU_Usage() subroutine runs in an infinite loop, you should not call the function directly from the event hander of the button as this will freeze the UI. Instead, use a separate thread to call it:

    Private Sub btnCPUUsage_Click( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles btnCPUUsage.Click        t1 = New Thread(AddressOf Display_CPU_Usage)        t1.Start()    End Sub

Display RSS Feeds
One good use of the LCD display is to display headlines from an RSS feed. The Display_RSS() subroutine retrieves a RSS document and then displays the title of each post on the LCD:

    '---display RSS feeds---    Private Sub Display_RSS()        Dim req As HttpWebRequest        Dim xmlDoc As XmlDocument = Nothing        Try            LCD_Display.ClearDisplay()            '---download the RSS document---            req = _        SendRequest("http://services.devx.com/outgoing/devxfeed.xml", _        "GET")            Dim xmlData As String = GetResponse(req)            xmlDoc = New XmlDocument()            xmlDoc.LoadXml(xmlData)            '---Select the title of the document---            Dim titlesNode As XmlNodeList = _               xmlDoc.DocumentElement.SelectNodes("channel/item/title")            For i As Integer = 0 To titlesNode.Count - 1                LCD_Display.Write("* " & _                   titlesNode(i).InnerText.ToString(), True)                LCD_Display.Write(vbCrLf & vbCrLf, True)            Next        Catch ex As Exception            MsgBox(ex.ToString)        End Try    End Sub

The two supporting functions used by the Display_RSS() subroutine are defined below:

    Public Function SendRequest( _        ByVal URI As String, _        ByVal requestType As String) As HttpWebRequest        Dim req As HttpWebRequest = Nothing        Try            '---Creates a HTTP request---            req = HttpWebRequest.Create(URI)            req.Method = requestType '---GET or POST---        Catch ex As Exception            Throw New Exception("Error")        End Try        Return req    End Function    Public Function GetResponse( _       ByVal req As HttpWebRequest) As String        Dim body As String = String.Empty        Try            '---Get a response from server---            Dim resp As HttpWebResponse = req.GetResponse()            Dim stream As Stream = resp.GetResponseStream()            '---Use a StreamReader to read the response---            Dim reader As StreamReader = _               New StreamReader(stream, System.Text.Encoding.UTF8)            body = reader.ReadToEnd()            stream.Close()        Catch ex As Exception            Throw New Exception("Error")        End Try        Return body    End Function

To ensure that displaying the RSS feed does not freeze up the UI, use a thread to invoke it:

    Private Sub btnDisplayRSS_Click( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles btnDisplayRSS.Click        t2 = New Thread(AddressOf Display_RSS)        t2.Start()    End Sub

Display Time
The LK204-25 supports large digits and this is useful for displaying information such as time. The Display_Time() subroutine continuously displays the current time and updates it every one second (1000 milliseconds):

    '---display the current time---    Public Sub Display_Time()        LCD_Display.BlockCursorONOFF(False)        LCD_Display.ClearDisplay()        LCD_Display.InitLargeDigits()        While True            Dim hour, minute, second As String            hour = Now.Hour.ToString("0#")            minute = Now.Minute.ToString("0#")            second = Now.Second.ToString("0#")            '---display the hour---            LCD_Display.PlaceLargeDigits(1, CInt(hour(0).ToString))            LCD_Display.PlaceLargeDigits(4, CInt(hour(1).ToString))            '---display the minute---            LCD_Display.PlaceLargeDigits(8, CInt(minute(0).ToString))            LCD_Display.PlaceLargeDigits(11, CInt(minute(1).ToString))            '---display the second---            LCD_Display.PlaceLargeDigits(15, CInt(second(0).ToString))            LCD_Display.PlaceLargeDigits(18, CInt(second(1).ToString))            Thread.Sleep(1000)        End While    End Sub

As usual, to avoid freezing the UI, you should call the subroutine using a separate thread:

    Private Sub btnDisplayTime_Click( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles btnDisplayTime.Click        t3 = New Thread(AddressOf Display_Time)        t3.Start()    End Sub

Read Version Number
You can read the firmware version number of the LK204-25 by sending the appropriate command to it. This is a good chance to demonstrate how to read data sent from the LCD display. The is accomplished by the ReadVerNumber() method of the LCD class:

    Private Sub btnReadVersionNum_Click( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles btnReadVersionNum.Click        LCD_Display.ReadVerNumber()    End Sub

When data is received, the DataFromLCDDisplay event handler will fire:

    '---when data is received from the LCD---    Public Sub DataFromLCDDisplay( _       ByVal str As String) Handles LCD_Display.DataFromLCD        MsgBox(str)    End Sub
Author’s Note: For some unknown reason, my LCD display always returns a “T” when queried about its firmware information.

Set the Contrast and Turn On/Off the Backlight
You can set the contrast of the LCD by moving the TrackBar control:

    Private Sub TrackBar1_Scroll( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles TrackBar1.Scroll        LCD_Display.SetContrast(TrackBar1.Value)    End Sub

To turn on/off the backlight of the LK204-25, check (or uncheck) the chkBacklight control:

    Private Sub chkBacklight_CheckedChanged( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles chkBacklight.CheckedChanged        If Not FormLoaded Then Exit Sub        If chkBacklight.Checked Then            LCD_Display.BackLightON(0)        Else            LCD_Display.BackLightOFF()        End If    End Sub

Stopping the Threads
To stop the threads running the various functions, code the three Stop buttons as follows:

    Private Sub btnStopCPUUsage_Click( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles btnStopCPUUsage.Click        t1.Abort()    End Sub    Private Sub btnStopRSSFeeds_Click( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles btnStopRSSFeeds.Click        t2.Abort()    End Sub    Private Sub btnStopDisplayTime_Click( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles btnStopDisplayTime.Click        t3.Abort()    End Sub

Testing the Application
Finally, you are now ready to test the LCD. Press F5 to debug the application. Ensure that your serial cable is connected to both the PC and the LCD. Click on the various buttons and observe the output on the screen. You can also view the following videos to see it in action.

The videos show, from left to right, the Timer function, the CPU Usage function, and the Display RSS feeds function. Click each Play button twice to start the video.

In this article, you have seen how you can easily connect an external device such as a LCD display to your computer. More importantly, you have learnt how to use .NET to communicate with these devices, and in this particular case, how the SerialPort class is put into action. I hope you have fun with this project; send me your ideas for using the LCD display! In future articles on DevX I will help you expand the skills learned here to interface .NET applications with external devices in other ways.

 

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist