Chapter 7: Examples with Displaying Data


Introduction

Microcontrollers deal very well with 0’s and 1’s, but humans do not. We need indicator lights, numbers, letters, charts, beepers… In order to comprehend the information presented quicker and better, we need that information to be displayed to us in many different ways. In practice, human - machine communication can require substantial (machine) resources, so it is sometimes better to dedicate an entire microcontroller to that task. This device is then called the Human - Machine Interface or simply HMI. The second microcontroller is then required to get the human wishes from HMI, “do the job” and put the results back to HMI, so that operator can see it.

Clearly, the most important form of communication between the microcontroller system and a man is the visual communication. In this chapter we will discuss various ways of displaying data, from the simplest to more elaborate ones. You’ll see how to use LED diodes, Seven-Segment Displays, character- and graphic LCDs. We will also consider using BASIC for sound signalization necessary in certain applications.

Just remember: the more profound communication you wish to be, the more MCU resources it’ll take.

7.1 LED Diode

One of the most frequently used components in electronics is surely the LED diode (LED stands for Light Emitting Diode). Some of common LED diode features include: size, shape, color, working voltage (Diode voltage) Ud and electric current Id. LED diode can be round, rectangular or triangular in shape, although manufacturers of these components can produce any shape needed for specific purposes. Size i.e. diameter of round LED diodes ranges from 3 to 12 mm, with 3 - 5 mm sizes most commonly used. Common colors include red, yellow, green, orange, blue, etc. Working voltage is 1.7V for red, 2.1V for green and 2.3 for orange color. This voltage can be higher depending on the manufacturer. Normal current Id through diode is 10 mA, while maximal current reaches 25 mA. High current consumption can present problem to devices with battery power supply, so in that case low current LED diode (Id ~ 1-2 mA) should be used. For LED diode to emit light with maximum capacity, it is necessary to connect it properly or it might get damaged.

The positive pole is connected to anode, while ground is connected to cathode. For matter of differentiating the two, cathode is marked by mark on casing and shorter pin. Diode will emit light only if current flows from anode to cathode; in the other case there will be no current. Resistor is added serial to LED diode, limiting the maximal current through diode and protecting it from damage. Resistor value can be calculated from the equation on the picture above, where Ur represents voltage on resistor. For +5V power supply and 10 mA current resistor used should have value of 330Ώ.

LED diode can be connected to microcontroller in two ways. One way is to have microcontroller "turning on" LED diode with logical one and the other way is with logical zero. The first way is not so frequent (which doesn't mean it doesn't have applications) because it requires the microcontroller to be diode current source. The second way works with higher current LED diodes.

The following example toggles LEDs of PORTB every second.

program LED_Blinking

main:
  TRISB = 0           ' PORTB is output
  PORTB = %11111111   ' Turn ON diodes on PORTB
  Delay_ms(1000)      ' Wait for 1 second
  PORTB = %00000000   ' Turn OFF diodes on PORTB
  Delay_ms(1000)      ' Wait for 1 second
  goto main           ' Endless loop
end.

7.2 Seven-Segment Displays

Seven-segment digits represent more advanced form of visual communication. The name comes from the seven diodes (there is an eighth diode for a dot) arranged to form decimal digits from 0 to 9. Appearance of a seven-segment digit is given on a picture below.

As seven-segment digits have better temperature tolerance and visibility than LCD displays, they are very common in industrial applications. Their use satisfies all criteria including the financial one. They are commonly used for displaying value read from sensors, etc.

One of the ways to connect seven-segment display to the microcontroller is given in the figure below. System is connected to use seven-segment digits with common cathode. This means that segments emit light when logical one is brought to them, and that output of all segments must be a transistor connected to common cathode, as shown on the picture. If transistor is in conducting mode any segment with logical one will emit light, and if not no segment will emit light, regardless of its pin state.

Bases of transistors T1 and T2 are connected to pin0 and pin1 of PORTA. Setting those pins turns on the transistor, allowing every segment from "a" to "h", with logical one on it, to emit light. If zero is on transistor base, none of the segments will emit light, regardless of the pin state.

Using the previous scheme, we could display a sequence of nine digits like this:

program seven_seg_onedigit

dim i as byte

' Function mask returns mask of parameter 'num'
' for common cathode 7-seg. display

sub function mask(dim num as byte) as byte

  select case num
    case 0  result = $3F
    case 1  result = $06
    case 2  result = $5B
    case 3  result = $4F
    case 4  result = $66
    case 5  result = $6D
    case 6  result = $7D
    case 7  result = $07
    case 8  result = $7F
    case 9  result = $6F
  end select
end sub

main:

INTCON = 0        ' Disable PEIE, INTE, RBIE, T0IE
TRISA  = 0
TRISB  = 0
PORTB  = 0
PORTA  = 2

do
  for i = 0 to 9
    PORTB = mask(i)
    Delay_ms(1000)
  next  i
loop until false  ' Endless loop

end.

Purpose of the program is to display numbers 0 to 9 on the ones digit, with 1 second delay. In order to display a number, its mask must be sent to PORTB. For example, if we need to display "1", segments b and c must be set to 1 and the rest must be zero. If (according to the scheme above) segments b and c are connected to the first and the second pin of PORTB, values 0000 and 0110 should be set to PORTB. Thus, mask for number "1" is value 0000 0110 or 06 hexadecimal. The following table contains corresponding mask values for numbers 0-9:

Digit Seg. h Seg. g Seg. f Seg. e Seg. d Seg. c Seg. b Seg. a HEX
0 0 0 1 1 1 1 1 1 $3F
1 0 0 0 0 0 1 1 0 $06
2 0 1 0 1 1 0 1 1 $5B
3 0 1 0 0 1 1 1 1 $4F
4 0 1 1 0 0 1 1 0 $66
5 0 1 1 0 1 1 0 1 $6D
6 0 1 1 1 1 1 0 1 $7D
7 0 0 0 0 0 1 1 1 $07
8 0 1 1 1 1 1 1 1 $7F
9 0 1 1 0 1 1 1 1 $6F

You are not, however, limited to displaying digits. You can use 7seg Display Decoder, a built-in tool of mikroBasic, to get hex code of any other viable combination of segments you would like to display.

But what do we do when we need to display more than one digit on two or more displays? We have to put a mask on one digit quickly enough and activate its transistor, then put the second mask and activate the second transistor (of course, if one of the transistors is in conducting mode, the other should not work because both digits will display the same value). The process is known as “multiplexing”: digits are displayed in a way that human eye gets impression of simultaneous display of both digits – actually only one display emits at any given moment.

Now, let’s say we need to display number 38. First, the number should be separated into tens and ones (in this case, digits 3 and 8) and their masks sent to PORTB. The rest of the program is very similar to the last example, except for having one transition caused by displaying one digit after another:

program seven_seg_twodigits

dim    v    as byte
dim    por1 as byte
dim    por2 as byte

sub procedure interrupt
begin
  if v = 0 then
     PORTB = por2    ' Send mask of tens to PORTB
     PORTA = 1       ' Turn on 1st 7seg, turn off 2nd
     v = 1
  else
     PORTB = por1    ' Send mask of ones to PORTB
     PORTA = 2       ' Turn on 2nd 7seg, turn off 1st
     v = 0
  end if
  TMR0 = 0           ' Clear TMRO
  INTCON = $20       ' Clear TMR0IF and set TMR0IE
end sub

main:

OPTION_REG = $80     ' Pull-up resistors
TRISA      =   0     ' PORTA is output
TRISB      =   0     ' PORTB is output
PORTB      =   0     ' Clear PORTB (make sure LEDs are off)
PORTA      =   0     ' Clear PORTA (make sure both displays are off)
TMR0       =   0     ' Clear TMRO
por1       = $7F     ' Mask for '8' (check the table above)
por2       = $4F     ' Mask for '3' (check the table above)
INTCON     = $A0     ' Enable T0IE

while true           ' Endless loop, wait for interrupt
  nop
wend

end.

The multiplexing problem is solved for now, but your program probably doesn’t have a sole purpose of printing constant values on 7seg display. It is usually just a subroutine for displaying certain information. However, this approach to printing data on display has proven sto be very convenient for more complicated programs. You can also move part of the program for refreshing the digits (handling the masks) to the interrupt routine.

The following example increases variable i from 0 to 99 and prints it on displays. After reaching 99, counter begins anew.

program seven_seg_counting

dim    i    as byte
dim    j    as byte
dim    v    as byte
dim    por1 as byte
dim    por2 as byte


' This function returns masks
' for common cathode 7-seg display

sub function mask(dim num as byte) as byte

  select case num
    case 0  result = $3F
    case 1  result = $06
    case 2  result = $5B
    case 3  result = $4F
    case 4  result = $66
    case 5  result = $6D
    case 6  result = $7D
    case 7  result = $07
    case 8  result = $7F
    case 9  result = $6F
    end select

end sub

sub procedure interrupt

  if v = 0 then
      PORTB = por2      ' Prepare mask for digit
      PORTA = 1         ' Turn on 1st, turn off 2nd 7seg
      v = 1
  else
      PORTB = por1      ' Prepare mask for digit
      PORTA = 2         ' Turn on 2nd, turn off 1st 7seg
      v = 0
  end if
  TMR0 = 0
  INTCON = $20
end sub

main:

OPTION_REG = $80
por2       = $3F
j          =   0
TMR0       =   0
INTCON     = $A0        ' Disable PEIE, INTE, RBIE, T0IE
TRISA      =   0
TRISB      =   0
PORTB      =   0
PORTA      =   0

do
  for i = 0 to 99       ' Count from 0 to 99

      ' Prepare ones digit
      j = i mod 10
      por1 = mask(j)

      ' Prepare tens digit
      j = (i div 10) mod 10
      por2 = mask(j)

      Delay_ms(1000)

  next i
loop until false

end.

In the course of the main program, programmer doesn’t need to worry of refreshing the display. Just call the subroutine mask every time display needs to change.

7.3 LCD Display, 4-bit and 8-bit Interface

One of the best solutions for devices that require visualizing the data is the “smart” Liquid Crystal Display (LCD). This type of display consists of 7x5 dot segments arranged in rows. One row can consist of 8, 16, 20, or 40 segments, and LCD display can have 1, 2, or 4 rows.

LCD connects to microcontroller via 4-bit or 8-bit bus (4 or 8 lines). R/W signal is on the ground, because communication is one-way (toward LCD). Some displays have built-in backlight that can be turned on with RD1 pin via PNP transistor BC557.

Our following example prints text on LCD via 4-bit interface. Assumed pin configuration is default.

program LCD_default_test

dim Text as char[20]

main:

TRISB = 0                    ' PORTB is output
LCD_Init(PORTB)              ' Initialize LCD at PORTB
LCD_Cmd(LCD_CURSOR_OFF)      ' Turn off cursor
Text = "mElektronikos"
LCD_Out(1, 1, Text)          ' Print text at LCD

end.

Our second example prints text on LCD via 8-bit interface, with custom pin configuration.

program Lcd8_default_test
dim text as char[20]

main:
  TRISB = 0                   ' PORTB is output
  TRISD = 0                   ' PORTD is output
  Lcd8_Init(PORTB, PORTD)     ' Initialize LCD at PORTB and PORTD
  Lcd8_Cmd(Lcd_CURSOR_OFF)    ' Turn off cursor
  text = "mElektronikos"
  Lcd8_Out(1, 1, text)        ' Print text at LCD
end.

7.4 Graphical LCD (PIC18 only)

Most commonly used Graphical LCD (GLCD) has screen resolution of 128x64 pixels. This allows creating more elaborate visual messages than usual LCD can provide, involving drawings and bitmaps.

The following figure shows GLCD HW connection by default initialization (using GLCD_LCD_Init routine); if you need different pin settings, refer to GLCD_LCD_Config.

BASIC offers a comprehensive library for GLCD – refer to Chapter 5: Built-in and Library Routines for more information. Our following example demonstrates the possibilities of GLCD and the mentioned library. Note that the library works with PIC18 only.

program GLCD_test
 ' For PIC18

include "GLCD_128x64.pbas"    ' You need to include GLCD_128x64 library

dim text as string[25]


main:

  PORTC = 0
  PORTB = 0
  PORTD = 0
  TRISC = 0
  TRISD = 0
  TRISB = 0

  GLCD_LCD_Init(PORTC, PORTD)   ' default settings
  GLCD_Set_Font(FONT_NORMAL1)

  while true
      GLCD_Clear_Screen

      ' Draw Circles
      GLCD_Clear_Screen
      text = "Circle"
      GLCD_Put_Text(0, 7, text, NONINVERTED_TEXT)
      GLCD_Circle(63,31,10)

      Delay_Ms(4000)

     ' Draw Rectangles
      GLCD_Clear_Screen
      text = "Rectangle"
      GLCD_Put_Text(0, 7, text, NONINVERTED_TEXT)


      GLCD_Rectangle(10, 0, 30, 35)

      Delay_Ms(4000)
      GLCD_Clear_Screen

      ' Draw Lines
      GLCD_Clear_Screen
      text = "Line"
      GLCD_Put_Text(55, 7, text, NONINVERTED_TEXT)

      GLCD_Line(0, 0, 127, 50)

      GLCD_Line(0,63, 50, 0)

      Delay_Ms(5000)

      ' Fonts Demo
      GLCD_Clear_Screen
      text = "Fonts DEMO"
      GLCD_Set_Font(FONT_TINY)
      GLCD_Put_Text(0, 4, text, NONINVERTED_TEXT)
      GLCD_Put_Text(0, 5, text, INVERTED_TEXT)
      GLCD_Set_Font(FONT_BIG)
      GLCD_Put_Text(0, 6, text, NONINVERTED_TEXT)
      GLCD_Put_Text(0, 7, text, INVERTED_TEXT)
      Delay_ms(5000)
    wend
end.

7.5 Sound Signalization

Some applications require sound signalization in addition to visual or instead of it. It is commonly used to alert or announce the termination of some long, time-consuming process. The information presented by such means is fairly simple, but relieves the user from having to constantly look into displays and dials.

BASIC’s Sound library facilitates generating sound signals and output on specified port. We will present a simple demonstration using piezzo speaker connected to microcontroller’s port.

program Sound

' The following three tones are calculated for 4MHz crystal
sub procedure Tone1
  Sound_Play(200, 200)    ' Period = 2ms <=> 500Hz, Duration = 200 periods
end sub

sub procedure Tone2
  Sound_Play(180, 200)    ' Period = 1.8ms <=> 555Hz
end sub

sub procedure Tone3
  Sound_Play(160, 200)    ' Period = 1.6ms <=> 625Hz
end sub

sub procedure Melody      ' Plays the melody "Yellow house"

  Tone1
  Tone2
  Tone3
  Tone3

  Tone1
  Tone2
  Tone3
  Tone3

  Tone1
  Tone2
  Tone3

  Tone1
  Tone2
  Tone3
  Tone3

  Tone1
  Tone2
  Tone3

  Tone3
  Tone3
  Tone2
  Tone1
end sub

main:
  TRISB = $F0

  Sound_Init(PORTB, 2)                     ' Connect speaker on pins RB2 and GND
  Sound_Play(50, 100)

  while true
      if Button(PORTB,7,1,1) then          ' RB7 plays Tone1
        Tone1
      end if
      while TestBit(PORTB,7) = 1           ' Wait for button to be released
        nop
      wend

      if Button(PORTB,6,1,1) then          ' RB6 plays Tone2
        Tone2
      end if
      while TestBit(PORTB,6) = 1           ' Wait for button to be released
        nop
      wend

      if Button(PORTB,5,1,1) then          ' RB5 plays Tone3
        Tone3
      end if
      while TestBit(PORTB,5) = 1           ' Wait for button to be released
        nop
      wend

      if Button(PORTB,4,1,1) then          ' RB4 plays Melody
        Melody
      end if
      while TestBit(PORTB,4) = 1           ' Wait for button to be released
        nop
      wend
  wend
end.