Tutorial 7 : LiquidCrystal library - sudeshmoreyos/Morey_os-demo-1.0 GitHub Wiki

Home <<Prev 7 Next>>

In this tutorial, we will explore the LiquidCrystal Library. Before we begin, let us first take a brief introduction to an Alphanumeric LCD.

1. Introduction to Alphanumeric LCD

Wikipedia defines a Liquid Crystal Display as "A liquid-crystal display (LCD) is a flat-panel display or other electronically modulated optical device that uses the light-modulating properties of liquid crystals combined with polarizers. Liquid crystals do not emit light directly, instead using a backlight or reflector to produce images in color or monochrome." An alphanumeric LCD can display only alphanumeric characters and cannot display images. It is also a monochrome display. A typical 16x2 LCD is shown below:

The term 16x2 means that the LCD can display up to 32 alphanumeric characters, arranged in 2 rows and 16 columns. Many other variants are also available, such as 8x1, 8x2, 16x1, 20x4, etc. As shown above, the LCD has 16 pins listed below:

  • pin-1 : GND
  • pin-2 : VCC
  • pin-3 : Contrast
  • pin-4 : RS
  • pin-5 : RW
  • pin-6 : EN
  • pin-7 : Data0
  • pin-8 : Data1
  • pin-9 : Data2
  • pin-10 : Data3
  • pin-11 : Data4
  • pin-12 : Data5
  • pin-13 : Data6
  • pin-14 : Data7
  • pin-15 : Backlight+
  • pin-16 : Backlight-

The VCC and GND pins are used to power the LCD. The Contrast pin is used to control the visibility of the displayed text. RS, RW, and EN are control pins, while Data0 to Data7 are used to transfer data to the LCD. Backlight+ and Backlight- are used to power the optional LCD backlight.

2. LiquidCrystal Wiring Connections

To display text on an LCD, we must first connect it to the controller or development board. As discussed earlier, the LCD provides three control pins and eight data pins. In the standard interface mode, all control pins and all data pins must be connected, requiring a total of 11 controller pins. This consumes a significant number of I/O pins. Therefore, a more commonly used interface mode requires only 6 pins: RS, EN, Data4, Data5, Data6, and Data7. Please note that the RW pin must be permanently connected to GND in this mode. The LiquidCrystal Library currently supports only this 6-pin mode. A typical wiring connection with an Arduino Uno is shown below:

As shown above, Pin13 to Pin8 of the Arduino Uno are connected to RS, EN, Data4, Data5, Data6, and Data7 of the LCD, respectively. The RW pin of the LCD is connected to GND, while VCC and GND are connected to the power supply. Data0 to Data3 are left unconnected. The backlight pins may optionally be connected to the +5 V supply to enable the LCD backlight.

3. LiquidCrystal Library

To use Liquid Crystal Library, we need to include following header :

#include "lib/LiquidCrystal.h"

Source files can be accessed here LiquidCrystal Lib. This Library supports following initialization and functions :

3.1 Liquid Crystal Struct initialization

The first step is to initialize a LiquidCrystal_t structure, which holds the configuration information required by the LiquidCrystal Library. Initialization is done as follows:

LiquidCrystal_t lcd={pin13,pin12,pin11,pin10,pin9,pin8};

In the above declaration, we create a structure of type LiquidCrystal_t and assign the digital pins connected to the LCD. The pins must be specified in the following order: RS, EN, Data4, Data5, Data6, and Data7. This structure should be declared above the setup() function.

3.2 LiquidCrystal_begin

Declaration:

void lcd_begin(LiquidCrystal_t * lcd_struct, mos_uint8_t cols, mos_uint8_t rows);

After initializing the LiquidCrystal_t structure, the first function that must be called is LiquidCrystal_begin(). This function takes three input parameters:

  1. A pointer to a LiquidCrystal_t structure.
  2. The number of columns supported by the LCD.
  3. The number of rows supported by the LCD.

For example, a 16x2 LCD has 16 columns and 2 rows. This function initializes the LCD and configures it according to the specified dimensions. It is typically called inside the setup() function, as shown below:

void setup()
{
    LiquidCrystal_begin(&lcd, 16, 2);
}

3.3 LiquidCrystal_clear

Declaration:

void lcd_clear(LiquidCrystal_t *lcd_struct);

This function is used to clear all text currently displayed on the LCD. It takes a single input parameter, which is a pointer to a LiquidCrystal_t structure. After clearing the display, the cursor is automatically moved to the origin position (0,0).

3.4 LiquidCrystal_write

Declaration:

void lcd_write(LiquidCrystal_t *lcd_struct, char data);

This function is used to display a single character on the LCD. It takes two input parameters: a pointer to a LiquidCrystal_t structure and the character to be displayed. The character is printed at the current cursor position, after which the cursor is automatically advanced to the next position.

3.5 LiquidCrystal_print

Declaration:

void lcd_print(LiquidCrystal_t *lcd_struct, char *data_string);

This function is used to display a text string on the LCD. It takes two input parameters: a pointer to a LiquidCrystal_t structure and a string to be displayed. If the text reaches the end of a row, printing automatically continues on the next row. If the text reaches the end of the last row, printing automatically wraps around to the first row.

3.6 LiquidCrystal_setCursor

Declaration:

void lcd_setCursor(LiquidCrystal_t *lcd_struct, mos_uint8_t x, mos_uint8_t y);

This function is used to position the cursor at any location on the LCD. It is typically used before calling lcd_write() or lcd_print(). The function takes three input parameters:

  1. A pointer to a LiquidCrystal_t structure.
  2. The column (x) position.
  3. The row (y) position.

The cursor position can range from (0,0) to (cols-1, rows-1), where cols and rows represent the number of columns and rows supported by the LCD. For example, for a 16x2 LCD, cols = 16 and rows = 2. The possible cursor positions for a 16x2 LCD are illustrated below:

4. LiquidCrystal lib Example

Let us now check an example code named lcd-demo.c that can be found in example folder (examples/board-examples/arduino-uno/lcd-demo). The code is as follows :

// Declare here all header files used in the code.h , OS related files are included by default
#include "morey_os.h"
#include "lib/LiquidCrystal.h"
#include "Digital.h"

LiquidCrystal_t lcd={pin13,pin12,pin11,pin10,pin9,pin8};

// Declare all initialization functions of controller peripherals in the setup function below
void setup(void)
{    
	lcd_begin(&lcd,16,2);
	Digital.pinmode(pina0,OUTPUT);
}

// Delcare all processes here
TASK_CREATE(lcd_test,"lcd_test");
TASK_CREATE(led,"led");

// Delcare autostart  processes here. Atleast one process must be autostarted;
TASK_AUTOSTART(&lcd_test,&led);

TASK_RUN(lcd_test)
{
  // Declare all variables here, please read documentation to understand 
  // which variables should be declared as static variables 
	static int i = 0;

  // Process starts here  
  BEGIN();

  while(1)
  {
	for(i=0;i<16;i++)
	{
		lcd_clear(&lcd);
		lcd_setCursor(&lcd,i,0);
		lcd_print(&lcd,"Morey_os");
		DELAY_SEC_PRECISE(1); 
	}
	
	for(i=0;i<16;i++)
	{
		lcd_clear(&lcd);
		lcd_setCursor(&lcd,i,1);
		lcd_print(&lcd,"Morey_os");
		DELAY_SEC_PRECISE(1); 
	}
	  
	for(i=0;i<16;i++)
	{
		lcd_clear(&lcd);
		lcd_setCursor(&lcd,i,0);
		lcd_write(&lcd,'A');
		DELAY_SEC_PRECISE(1); 
	}
	
	for(i=0;i<16;i++)
	{
		lcd_clear(&lcd);
		lcd_setCursor(&lcd,i,1);
		lcd_write(&lcd,'A');
		DELAY_SEC_PRECISE(1); 
	}
	
  }
  
  // process ends here
  END();
}

TASK_RUN(led)
{
  // Declare all variables here, please read documentation to understand 
  // which variables should be declared as static variables

  // Process starts here  
  BEGIN();

  while(1)
  {
	Digital.write(pinA0,HIGH);
	DELAY_SEC(0.1);	
	Digital.write(pinA0,LOW);
	DELAY_SEC(0.1);
  }
  
  // process ends here
  END();
}

Code Explanation

  1. Header Files

The morey_os.h header file must be included in all applications. Since this example uses the LiquidCrystal Library, we also include lib/LiquidCrystal.h.

#include "morey_os.h"
#include "lib/LiquidCrystal.h"

A structure of type LiquidCrystal_t, named lcd, is initialized and the corresponding digital pins of the board are assigned to it.

LiquidCrystal_t lcd={pin13,pin12,pin11,pin10,pin9,pin8};
  1. LCD Initialization

The LCD is initialized as a 16-column, 2-row display inside the setup() function.

void setup(void)
{
    lcd_begin(&lcd,16,2);
    Digital.pinmode(pinA0,OUTPUT);
}
  1. Task Creation

Two tasks, lcd_test and led, are created using the TASK_CREATE macro. Please note that the TASK_CREATE macro takes two inputs. The first input is the task name, while the second input is a string used during debugging. We will discuss code debugging in more detail in subsequent tutorials.

TASK_CREATE(lcd_test,"lcd_test");
TASK_CREATE(led,"led");
  1. Task Autostart

Both tasks are configured to start automatically when the controller boots. Tasks can also be started manually from within other tasks. Starting and terminating tasks will be discussed in later tutorials. For now, both tasks are configured to start automatically at controller startup.

TASK_AUTOSTART(&lcd_test,&led);
  1. LCD Task Implementation

Next, we implement TASK_RUN() for both tasks. The lcd_test task demonstrates all five functions provided by the LiquidCrystal Library:

  • LiquidCrystal_begin()
  • LiquidCrystal_clear()
  • LiquidCrystal_write()
  • LiquidCrystal_print()
  • LiquidCrystal_setCursor()

In TASK_RUN(lcd_test), a static integer variable i is declared. Please note that all variables in Morey_os must be declared as static. The reason for this requirement will be discussed in subsequent tutorials. After the BEGIN() macro, the code enters an infinite while(1) loop. Inside this loop, four different for loops are used to demonstrate various LCD operations. The first for loop displays a moving message from position (0,0) to (15,0). You will notice that when the text reaches the end of the first row, the overflow automatically continues on the second row. During each iteration:

  1. lcd_clear() clears the display.
  2. lcd_setCursor() selects the cursor position.
  3. lcd_print() displays the text.
  4. DELAY_SEC_PRECISE(1) introduces a one-second delay.

The second for loop displays the same moving message on the second row.

The third for loop displays a moving single character 'A' on the first row using lcd_write().

The fourth for loop displays a moving single character 'A' on the second row.

To create delays in Morey_os, we can use DELAY_SEC() or DELAY_SEC_PRECISE() macros. A detailed discussion of these macros will be provided in subsequent tutorials. It is also important to note that Morey_os uses cooperative threading. Therefore, every infinite while(1) loop must contain at least one DELAY_SEC() or DELAY_SEC_PRECISE() macro. In this example, DELAY_SEC_PRECISE(1) is used inside the for loops, satisfying this requirement.

TASK_RUN(lcd_test)
{
    static int i = 0;

    BEGIN();

    while(1)
    {
        ...
    }

    END();
}
  1. LED Task Implementation

The led task simply toggles pinA0 ON and OFF every 0.1 second.

TASK_RUN(led)
{
    BEGIN();

    while(1)
    {
        Digital.write(pinA0,HIGH);
        DELAY_SEC(0.1);

        Digital.write(pinA0,LOW);
        DELAY_SEC(0.1);
    }

    END();
}

This task runs independently of the LCD task and demonstrates the multitasking capability of Morey_os.

  1. Content of Makefile is as follows :
PROJECT = lcd-demo
MOREY_OS_PATH = ../../../..
BOARD = ARDUINO_UNO
include $(MOREY_OS_PATH)/Makefile.include

PROJECT Macro will hold name of the project file name. MOREY_OS_PATH holds the location of Morey_os root directory. BOARD Macro should hold ARDUINO_UNO since we are programming for Arduino_uno board. And last line should remain the same as mentioned above.

  1. This code is simple and requires no special configurations. Hence config.h file content will remain same as below :
#ifndef CONFIG_H
#define CONFIG_H
#define COMPILER AVR_GCC
#endif

Home <<Prev 7 Next>>