MSP430 - LCD Terminal

Here is my MSP430G2553 LCD Terminal program.
Where would I use something like this?

1) If I am debugging a system and would like to see some internal results I can send ASCII text to a dumb terminal. Can I not use the IDE debugger to trace the code? Many times I am debugging in "real time" and am not allowed to stop the code with a break point.

Thus I only need my code to spit out text via a single UART TXD pin on the fly.

2) With low pin count MCU I may not have 7 pins to spare in order to interface with an LCD module. With one TXD pin I can interface with the LCD module.

3) In addition, you can have a cluster of LCD modules on one display panel all interfaced to the single TXD pin. All you need is to make each LCD module addressable using a message packet containing the module ID address.


Code:
//************************************************
// MSP430G2553 LCD Dumb Terminal
//************************************************

/************************************************

2013.07.01 - MrChips
     
************************************************/

#include "io430.h"

//---------------------------------------------------
// Global Variables
//---------------------------------------------------

short FirstChar, Posn;

//---------------------------------------------------
// Defines
//---------------------------------------------------

#define title1 "MrChips Terminal"
#define title2 "Version 1.00"

#define TRUE 1
#define FALSE 0

// ASCII codes recognized by this terminal

#define HOME 0x01
#define LINE2 0x02
#define CLR 0x03
#define LF 0x0A
#define CR 0x0D
#define BS 0x08
#define DEL1 0x09
#define DEL2 0x7F
#define SP 0x20

//---------------------------------------------------
// LCD Interface
//---------------------------------------------------

void LCD_init(void);
void LCD_ready(void);
void LCD_ir(char ch);
void LCD_dr(char ch);
void LCD_txt(char *s);
void LCD_Home(void);
void LCD_Clear(void);
void LCD_Line1(void);
void LCD_Line2(void);
void LCD_Line3(void);
void LCD_Line4(void);
void LCD_Posn(char n);

// define LCD interface on PORT2
#define LCD_OUT P2OUT
#define LCD_IN P2IN
#define LCD_DDR P2DIR
#define LCD_RS P2OUT_bit.P4
#define LCD_RW P2OUT_bit.P5
#define LCD_E P2OUT_bit.P6

void LCD_init(void)
{
  LCD_ir(0x28); // dual line, 4 bits
  LCD_ir(0x06); // increment mode
  LCD_ir(0x0C); // cursor turned off
  LCD_ir(0x10); // cursor right
  LCD_ir(0x01); // clear display
}

void LCD_Clear(void)
{
  Posn = 0;
  LCD_ir(0x01);
}

void LCD_Home(void)
{
  Posn = 0;
  LCD_ir(0x02);
}

void LCD_Line1(void)
{
  unsigned char i;
  Posn = 0;
  LCD_ir(0x80);
  for (i = 0; i < 16; i++)
  {
    LCD_dr(SP);
  }
  LCD_ir(0x80);
}

void LCD_Line2(void)
{
  unsigned char i;
  Posn = 40;
  LCD_ir(0xC0);
  for (i = 0; i < 16; i++)
  {
    LCD_dr(SP);
  }
  LCD_ir(0xC0);
}

void LCD_Line3(void)
{
  LCD_ir(0x94);
}

void LCD_Line4(void)
{
  LCD_ir(0xD4);
}

void LCD_Posn(char n)
// direct positioning where n = 0-79
{
  char ch;
  Posn = ch = n;
  if (ch >= 40) ch += 24;
  LCD_ir(ch | 0x80);
}

void LCD_ready(void)
{
  char busy;
  LCD_DDR = 0xF0;
  LCD_RW = 1;
  LCD_RS = 0;
  do
  {
    LCD_E = 1;
    // high 4 bits read first
    // busy flag is D7 of LCD = bit 3 of P2IN
    busy = (LCD_IN & 0x08);
    LCD_E = 0;
    LCD_E = 1;
    LCD_E = 0;
  } while (busy);
  LCD_OUT = 0;
  LCD_DDR = 0xFF;
}

void LCD_ir(char ch)
{
  LCD_ready();
  //LCD_RS = 0; // not needed // select instruction register
  //LCD_RW = 0; // not needed
  LCD_OUT |= ((ch >> 4) & 0x0F);
  LCD_E = 1;
  LCD_E = 0;
  LCD_OUT = 0;
  LCD_OUT |= (ch & 0x0F);
  LCD_E = 1;
  LCD_E = 0;
}

void LCD_dr(char ch)
{
  Posn = ++Posn % 80;
  LCD_ready();
  LCD_RS = 1; // select data register
  //LCD_RW = 0; // not needed
  LCD_OUT |= ((ch >> 4) & 0x0F);
  LCD_E = 1;
  LCD_E = 0;
  LCD_OUT = 0;
  LCD_RS = 1;
  LCD_OUT |= (ch & 0x0F);
  LCD_E = 1;
  LCD_E = 0;
}

void LCD_BS(void)
{
  if (Posn > 0)
  {
    LCD_Posn(--Posn);
  }
}

void LCD_txt(char *s)
{
  while(*s) LCD_dr(*s++);
}

//---------------------------------------------------
// Hardware Initialization
//---------------------------------------------------

void init(void)
{
   // Stop watchdog timer to prevent time out reset
  WDTCTL = WDTPW + WDTHOLD;
  
  // set DCO - Digitally-Controlled Oscillator frequency
  // change from 1MHz to 16MHz
  BCSCTL1 = CALBC1_8MHZ;
  DCOCTL = CALDCO_8MHZ;

  P2DIR = 0xFF;
  P2SEL = 0x00; //select for I/O function
  P2OUT = 0x00;
}

//---------------------------------------------------
// software delay
//---------------------------------------------------

void delay(unsigned long d)
{
  unsigned long i;
  for (i = 0; i < d; i++);
}

//-----------------------------------
// Initialize UART
//-----------------------------------

void init_UART(void)
{
  // set baud rate generator (16-bit prescaler)
  // for 9600 baud
  UCA0BR1 = 3;
  UCA0BR0 = 56; // fine tuned for 9600 baud
  UCA0MCTL = UCBRS0;
  
  UCA0CTL0 = 0x00; // init UART in default mode
  UCA0CTL1 |= UCSSEL_2;
  UCA0CTL1_bit.UCSWRST = 0; // enable USCI
  
  P1DIR_bit.P1 = 0; // enable P1.1 for RXD input
  P1DIR_bit.P2 = 1; // enable P1.2 for TXD output
  P1SEL2_bit.P1 = 1; // select RXD
  P1SEL_bit.P1 = 1; // select RXD
  P1SEL2_bit.P2 = 1; // select TXD
  P1SEL_bit.P2 = 1; // select TXD
}

void putc(char ch)
{
  while (UCA0STAT_bit.UCBUSY);
  UCA0TXBUF = ch;
}

void puts(char *s)
{
    while(*s) putc(*s++);
}

char getc(void)
{
  // wait for character received
  while (!IFG2_bit.UCA0RXIFG);
  return (UCA0RXBUF & 0x7F);
}

//-----------------------------------
// Main
//-----------------------------------

void main(void)
{
  char ch;
  init();
  init_UART();
  LCD_init();
  
  LCD_txt(title1);
  LCD_Line2();
  LCD_txt(title2);
  FirstChar = TRUE;

  while(1)
  {

    ch = getc();
    if (FirstChar)
    {
      FirstChar = FALSE;
      LCD_Clear();
    }

    switch (ch)
    {
       case CLR:
         LCD_Clear();
       break;
       case HOME:
         LCD_Home();
       break;
       case LINE2:
         LCD_Line2();
       break;
       case BS:
       break;
       case DEL1:
       case DEL2:
         LCD_BS();
         LCD_dr(SP);
         LCD_BS();
       break;
       case CR:
         if (Posn < 40)
         {
           LCD_Posn(0);
         }
         else
         {
           LCD_Posn(40);
         }
       break;
       case LF:
         if (Posn < 40)
         {
           LCD_Posn(Posn + 40);
         }
         else
         {
           LCD_Posn(Posn - 40);
         }
       break;
       default:
         LCD_dr(ch);
       break;
    } // end of switch   
  } // end of while
} // end of main

//-----------------------------------
// end of Main
//-----------------------------------
Notes:

1) Note that the RXD input is on pin-3 of the MSP430G2553.

2) For testing, you need to send some ASCII characters via TXD (pin-4) from a source such as another MSP430G2553.

3) This took me some time to figure out that the MSP430G2553 clock set to a default frequency of 1MHz was too slow for the MCU to respond to back to back received characters when using one stop bit. The simple solution was to increase the clock speed to 8MHz.

4) I fine tuned the UART clock prescaler in order to get exactly 9600 baud.

5) I chose to use RX data polling instead of interrupts just to keep things simple.

6) You may notice in main( ) the first use of the switch - case statement in this MSP430 series of lessons. This is simply a cleaner way instead of using a huge sequence of if-else statements.

7) The code is initially written for a 16 x 2 LCD module. It would require some adjustments in order to make it compatible with 20 x 4 displays.

Coming Next

Now that I have done the LCD display and the UART TX and RX examples, I will next present an IR decoder, i.e. how to receive the codes from your TV/VCR/DVD remote controller.

Blog entry information

Author
MrChips
Views
1,660
Last update

More entries in General

More entries from MrChips

Share this entry

Top