Small Printed Circuit Board
THIS SECTION IS UNDER CONSTRUCTION

W.Bot

LCD Driver

LCD type

The LCD module used on W.Bot is the L2012.

The L2012 LCD display has 2 rows, 20 characters per row.

The LCD module is connected to the Devices board.

LCD Driver

LCDLib.h file
/** * @package Generic LCD Driver Library * @processor PIC16F1825, PIC18F2553 * @lcd L2012 (HD44780) * @author WizLab.it * @version 20161211.056 */ #ifndef LCD_H #define LCD_H #include "commons.h" /* * LCD Configuration structure * - *portCtrl - pointer to PORT where Control Pins are attached to * - *portData - pointer to PORT where Data Pins are attached to * - rs - pin connected to LCD RS signal * - en - pin connected to LCD EN signal * - db4 - DB4 Data line * - db5 - DB5 Data line * - db6 - DB6 Data line * - db7 - DB7 Data line */ volatile struct { volatile uint8_t* portCtrl; volatile uint8_t* portData; uint8_t rs; uint8_t en; uint8_t db4; uint8_t db5; uint8_t db6; uint8_t db7; } LCDLib_config; //Functions void LCDLib_Init(volatile uint8_t* portCtrl, uint8_t rs, uint8_t en, volatile uint8_t* portData, uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7); void LCDLib_Send(uint8_t c, bool isChar); void LCDLib_EnableCycle(bool isChar); void LCDLib_Clear(); void LCDLib_PrintString(const uint8_t* string, uint8_t length); void LCDLib_PrintChar(uint8_t c); void LCDLib_ShiftRight(); void LCDLib_ShiftLeft(); void LCDLib_SetCursor(uint8_t lineAndColumn); #endif
LCDLib.c file
/** * @package Generic LCD Driver Library * @processor PIC16F1825, PIC18F2553 * @lcd L2012 (HD44780) * @author WizLab.it * @version 20161220.058 */ #include "LCDLib.h" /*============================================================================== * Initialize LCD * - Set pins * - Send init commands * - Clear LCD *============================================================================*/ void LCDLib_Init(volatile uint8_t* portCtrl, uint8_t rs, uint8_t en, volatile uint8_t* portData, uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7) { //Set LCD parameters LCDLib_config.portCtrl = portCtrl; LCDLib_config.rs = rs; LCDLib_config.en = en; LCDLib_config.portData = portData; LCDLib_config.db4 = db4; LCDLib_config.db5 = db5; LCDLib_config.db6 = db6; LCDLib_config.db7 = db7; //Require attention from LCD __delay_ms(15); __delay_ms(15); __delay_ms(15); LCDLib_Send(0x03, false); __delay_ms(5); LCDLib_Send(0x03, false); __delay_ms(1); LCDLib_Send(0x03, false); __delay_ms(4); //Init display LCDLib_Send(0x02, false); //Set data length to 4 bit LCDLib_Send(0x28, false); //Confirm data length to 4 bit, and set 5x7 font LCDLib_Send(0x0C, false); //Display ON, cursor OFF LCDLib_Send(0x06, false); //Move cursor on the right after write, display not shifted LCDLib_Clear(); //Clear the display } /*============================================================================== * Send byte (command or data) to LCD * See L2012 LCD documentation *============================================================================*/ void LCDLib_Send(uint8_t c, bool isChar) { //Prepare mask volatile uint8_t mask=0x00; mask |= (1<<LCDLib_config.db4); mask |= (1<<LCDLib_config.db5); mask |= (1<<LCDLib_config.db6); mask |= (1<<LCDLib_config.db7); mask = ~mask; // Send high nibble. *(LCDLib_config.portData) &= mask; if((c & 0b00010000) != 0) *(LCDLib_config.portData) |= (1<<LCDLib_config.db4); if((c & 0b00100000) != 0) *(LCDLib_config.portData) |= (1<<LCDLib_config.db5); if((c & 0b01000000) != 0) *(LCDLib_config.portData) |= (1<<LCDLib_config.db6); if((c & 0b10000000) != 0) *(LCDLib_config.portData) |= (1<<LCDLib_config.db7); LCDLib_EnableCycle(isChar); // Send low nibble. *(LCDLib_config.portData) &= mask; if((c & 0b00000001) != 0) *(LCDLib_config.portData) |= (1<<LCDLib_config.db4); if((c & 0b00000010) != 0) *(LCDLib_config.portData) |= (1<<LCDLib_config.db5); if((c & 0b00000100) != 0) *(LCDLib_config.portData) |= (1<<LCDLib_config.db6); if((c & 0b00001000) != 0) *(LCDLib_config.portData) |= (1<<LCDLib_config.db7); LCDLib_EnableCycle(isChar); //Required delay __delay_us(45); } /*============================================================================== * Execute an "Enable cycle" * "Enable cycle" structure depends by type of data to send (command or data) *============================================================================*/ void LCDLib_EnableCycle(bool isChar) { *(LCDLib_config.portCtrl) &= ~(1 << LCDLib_config.rs); if(isChar) *(LCDLib_config.portCtrl) |= (1 << LCDLib_config.rs); *(LCDLib_config.portCtrl) |= (1 << LCDLib_config.en); __delay_ms(4); *(LCDLib_config.portCtrl) &= ~(1 << LCDLib_config.en); } /*============================================================================== * Clear the LCD * LCD screen is emptied and cursor set to position 0,0 *============================================================================*/ void LCDLib_Clear() { LCDLib_Send(0x01, false); } /*============================================================================== * Print a character on the LCD at current position *============================================================================*/ void LCDLib_PrintChar(uint8_t c) { LCDLib_Send(c, true); } /*============================================================================== * Print a string on the LCD at current position *============================================================================*/ void LCDLib_PrintString(const uint8_t* string, uint8_t length) { uint8_t i; for(i=0; i<length; i++) { LCDLib_PrintChar(string[i]); } } /*============================================================================== * Shift the display content on the right *============================================================================*/ void LCDLib_ShiftRight() { LCDLib_Send(0x1C, false); } /*============================================================================== * Shift the display content on the left *============================================================================*/ void LCDLib_ShiftLeft() { LCDLib_Send(0x18, false); } /*============================================================================== * Set the cursor's position *============================================================================*/ void LCDLib_SetCursor(uint8_t lineAndColumn) { lineAndColumn &= 0b10111111; if((lineAndColumn & 0x80) == 0x80) lineAndColumn |= 0b01000000; lineAndColumn |= 0b10000000; LCDLib_Send(lineAndColumn, false); }
LCD Driver usage

Before to start sending text to the LCD module, it has to be initialized.

The function LCDLib_Init() is used to initialize the LCD module: basically, it defines the PIC PORTs and PINs used to drive the LCD.

void LCDLib_Init(volatile uint8_t* portCtrl, uint8_t rs, uint8_t en, volatile uint8_t* portData, uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7);

Parameters:

  • volatile uint8_t* portCtrl: PORT used for the control bits
  • uint8_t rs: number of the pin from portCtrl used for the RS signal
  • uint8_t en: number of the pin from portCtrl used for the EN signal
  • volatile uint8_t* portData: PORT used for the data bits
  • uint8_t db4: number of the pin from portData used for the DB4 signal
  • uint8_t db5: number of the pin from portData used for the DB5 signal
  • uint8_t db6: number of the pin from portData used for the DB6 signal
  • uint8_t db7: number of the pin from portData used for the DB7 signal

Example:

LCDLib_Init(&PORTA, 3, 5, &PORTB, 2, 5, 3, 4);

In this example, the RS signal is connected to port RA3, EN to RA5, DB4 to RB2, DB5 to RB5, DB6 to RB3, and DB7 to RB4.

Once the LCD module is initiated, it can receive texts and commands:

LCDLib_Clear(); //Clear the display LCDLib_SetCursor(0x05); //Move the cursor to 1st line, 5th column LCDLib_SetCursor(0x84); //Move the cursor to 2nd line, 4th column LCDLib_PrintString("* W.BOT ", 9); //Print a string LCDLib_ShiftRight(); //Shift LCD content of 1 position on the right (both rows)

See the cursor position paragraph to understand how the cursor positioning works.

I2C Commands

LCD module can be driven from remote peripherals sending commands via the I2C bus.

Here it is the commands sub-set from the Devices board to drive the LCD module.

Byte 0 (Command) Extra bytes Description
0x12 - LCD Clear
0x15
Byte 2+: text
Print text at defined position
0x16 - LCD text left shift
0x19 - LCD text right shift

Cursor position

The L2012 LCD display has 2 rows, 20 characters per row.

Character addressing uses 1 byte:

b7 b6 b5 b4 b3 b2 b1 b0
Row selection
0: first row
1: second row
-
Column selection
Valid values from 0d (0x00) to 19d (0x13)