Unlocking the Potential of Serial LCDs

Things used in this project

Software apps and online services

1- STMicroelectronics STM32CubeMX

2- STMicroelectronics STM32CubeIDE

3- Proteus 8

Efficient Integration of Serial LCDs with STM32 Microcontrollers via UART Communication

Serial LCDs, also known as serially-interfaced LCDs, offer a streamlined solution for displaying information in electronic projects. With their simplified interface and versatile compatibility, they have become a staple in hobbyist and professional circuits alike. Let’s delve into the world of Serial LCDs and explore their capabilities, configurations, and applications.

Introducing Serial LCDs:

Serial LCDs come in various configurations, each tailored to specific needs. Among the products available are the BPK LCD Driver Board and a range of compatible Serial LCD modules:

  • BPK LCD Driver Board, Part Number 6-201
  • Serial LCD 2×16, Part Number 6-111
  • Serial LCD 2×20, Part Number 6-121
  • Serial LCD 2×40, Part Number 6-131
  • Serial LCD 4×20, Part Number 6-141

Compatibility and Connectivity:

The BPK Serial Driver Board serves as a bridge between LCD modules and microcontrollers, converting standard parallel interfaces to a manageable serial interface. Compatible with Hitachi 44780 controllers or equivalents, it supports up to 80 on-screen characters. However, it’s worth noting that it’s not suitable for 4×40 displays. Connecting the BPK Board to LCDs is a straightforward process, requiring careful consideration of pin arrangements, especially for backlit LCDs. The board ensures compatibility with both non-backlit and backlit displays, making it adaptable to various setups.

Programming and Operation:

Programming Serial LCDs involves sending text serially at either 2400 or 9600 baud, adhering to specific formatting requirements. Preceding instruction commands with the byte <254> transitions the LCD into Instruction mode temporarily. It’s important to note that LCDs require a brief settling period after power-up, and certain commands may necessitate additional pauses for optimal operation.

Furthermore, Serial LCDs seamlessly integrate with STM32 microcontrollers via UART communication, significantly enhancing their compatibility and utility. Leveraging the UART capabilities of STM32 devices facilitates effortless interfacing with Serial LCD modules, empowering developers to enhance the display capabilities of their applications.

To commence this project, we will initiate the configuration of UART (Universal Asynchronous Receiver-Transmitter), a pivotal step in establishing communication between the microcontroller and the Serial LCD. Subsequently, we will embark on the development of a dedicated driver module, denoted as ‘LCD_UART’, meticulously crafted to facilitate seamless interaction between the microcontroller and the Serial LCD module.

STM32CubeMX Configuration:

  • Open CubeMX & Create New Project Choose The Target MCU STM32F103C6 & Double-Click Its Name
  • Go To The Clock Configuration & Set The System Clock To 8MHz

Configuration for the UART Mode:

    • Enable USART1 Module (Asynchronous Mode)
    • Set the USART1 communication parameters (baud rate = 115200, parity=NON, stop bits =1, and word length =8bits)
    • Generate The Initialization Code & Open The Project In CubeIDE

STM32CubeIDE Configuration:

    • Write The Application Layer Code
    • Serial-LCD.h & Serial-LCD.c
    • main.c

/*
 * Serial-LCD.h
 * Author: Marwen Maghrebi
 */

#ifndef INC_SERIAL_LCD_H_
#define INC_SERIAL_LCD_H_

#include "main.h"
#include <stdio.h>
#include <string.h>

extern UART_HandleTypeDef huart1;

// Function prototypes
void Delay_10ms(void);
void Delay_500ms(void);
void LCD_UART_Transmit(uint8_t *pData, uint16_t Size);
void LCD_sendCommand(uint8_t command);
void LCD_sendData(uint8_t data);
void LCD_setCursor(uint8_t row, uint8_t col);
void LCD_scroll_left(uint8_t num_chars);
void LCD_scroll_right(uint8_t num_chars);
void LCD_home();
void LCD_blinking_cursor_ON();
void LCD_blinking_cursor_OFF();
void LCD_clearScreen(void);
void Move_cursor_left(uint8_t num_chars);
void Move_cursor_right(uint8_t num_chars);
void LCD_underline_cursor_ON();
void LCD_Blank();
void LCD_print(const char *str);
void LCD_printInt(int value);
void LCD_printFloat(float value, int decimalPlaces);

#endif /* INC_SERIAL_LCD_H_ */

/*
 * Serial-LCD.c
 * Author: Marwen Maghrebi
 */
#include"Serial-LCD.h"

//wait for LCD to power up
void Delay_500ms(void)
{
  HAL_Delay(500);
}

//wait for the clear commande to settle
void Delay_10ms(void)
{
  HAL_Delay(10);
}

// Transmit data via UART
void LCD_UART_Transmit(uint8_t *pData, uint16_t Size) {
  HAL_UART_Transmit(&huart1, pData, Size, HAL_MAX_DELAY);
}

// Send command to the LCD
void LCD_sendCommand(uint8_t command) {
  LCD_UART_Transmit(&command, 1);
}

// Send data to the LCD
void LCD_sendData(uint8_t data) {
  LCD_UART_Transmit(&data, 1);
}

// Set cursor position on the LCD !!
void LCD_setCursor(uint8_t row, uint8_t col) {
  LCD_sendCommand(0xFE); // Command to set the cursor position
  LCD_sendCommand(0x80 + (row * 0x40) + col); // Calculate position based on row and column
}

// Scroll LCD content left
void LCD_scroll_left(uint8_t num_chars) {
  for (uint8_t i = 0; i < num_chars; i++) {
    LCD_sendCommand(0xFE); // Command to set the cursor position
    LCD_sendCommand(0x18); // Command to scroll one character to the left
    HAL_Delay(100);
  }
}

// Scroll LCD content right
void LCD_scroll_right(uint8_t num_chars) {
  for (uint8_t i = 0; i < num_chars; i++) {
    LCD_sendCommand(0xFE); // Command to set the cursor position
    LCD_sendCommand(0x1C); // Command to scroll one character to the right
    HAL_Delay(100);
  }
}

// Return cursor to the home position
void LCD_home() {
  LCD_sendCommand(0xFE); // Command to clear the screen
  LCD_sendCommand(0x00);
}

// Enable blinking cursor
void LCD_blinking_cursor_ON() {
  LCD_sendCommand(0xFE); // Command to clear the screen
  LCD_sendCommand(0x0D);
}

// Disable blinking cursor
void LCD_blinking_cursor_OFF() {
  LCD_sendCommand(0xFE); // Command to clear the screen
  LCD_sendCommand(0x0C);
}

// Clear the LCD screen
void LCD_clearScreen(void) {
  LCD_sendCommand(0xFE); // Command to clear the screen
  LCD_sendCommand(0x01);
}

// Move cursor left by num_chars characters
void Move_cursor_left(uint8_t num_chars) {
  for (uint8_t i = 0; i < num_chars; i++) {
    LCD_sendCommand(0xFE); // Command to clear the screen
    LCD_sendCommand(0x10);
    HAL_Delay(100);
  }
}

// Move cursor right by num_chars characters
void Move_cursor_right(uint8_t num_chars) {
  for (uint8_t i = 0; i < num_chars; i++) {
    LCD_sendCommand(0xFE); // Command to clear the screen
    LCD_sendCommand(0x14);
    HAL_Delay(100);
  }
}

// Enable underline cursor
void LCD_underline_cursor_ON() {
  LCD_sendCommand(0xFE); // Command to clear the screen
  LCD_sendCommand(0x0E);
}

// Turn off cursor and blinking
void LCD_Blank() {
  LCD_sendCommand(0xFE); // Command to clear the screen
  LCD_sendCommand(0x08);
}

// Print a string to the LCD
void LCD_print(const char *str) {
  while (*str) {
    LCD_sendData(*str++);
  }
}

// Print an integer to the LCD
void LCD_printInt(int value) {
  char buffer[20];  // Adjust the buffer size based on your needs
  sprintf(buffer, "%d", value);
  LCD_print(buffer);
}

// Print a floating-point number to the LCD with given decimal places
void LCD_printFloat(float value, int decimalPlaces) {
    char buffer[20];
    sprintf(buffer, "%.*f", decimalPlaces, value);
    // code to display the buffer on the LCD4
    LCD_print(buffer);

}


/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include"Serial-LCD.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  Delay_500ms();   // Wait for LCD to power up
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    LCD_clearScreen();
    Delay_10ms();
    // Enable blinking cursor
    LCD_blinking_cursor_ON();
    Delay_10ms();

    // Move cursor left and right
    Move_cursor_left(50);
    Move_cursor_right(50);
    Delay_10ms();

    // Enable underline cursor
    LCD_underline_cursor_ON();
    Delay_10ms();

    // Disable blinking cursor
    LCD_blinking_cursor_OFF();
    Delay_10ms();

    // Clear the screen
    LCD_clearScreen();
    Delay_10ms();

    // Print messages with scrolling effect
    LCD_setCursor(0, 0);
    LCD_print("THE EMBEDDED THINGS!");
    LCD_setCursor(1, 0);
    LCD_print("FOR MORE VIDEOS");
    LCD_setCursor(2, 0);
    LCD_print("LIKE & SUBSCRIBE");
    LCD_setCursor(3, 0);
    LCD_print("THANK YOU ^_^");

    LCD_scroll_left(20);
    Delay_10ms();
    LCD_scroll_right(20);

    // Clear the screen
    LCD_clearScreen();
    Delay_10ms();

    // Print integer and float values
    int x = 200;
    float y = 10.23;
    LCD_setCursor(0, 0);
    LCD_print("INTEGER VAL: ");
    LCD_setCursor(0, 12);
    LCD_printInt(x);

    LCD_setCursor(1, 0);
    LCD_print("FLOAT VAL:");
    LCD_setCursor(1,12);
    LCD_printFloat(y, 2);

    Delay_10ms();
    }
  /* USER CODE END 3 */
}

Proteus Configuration :

  • Open Proteus & Create New Project and click next

  • Click on Pick Device
  • Search for STM32F103C6MILFORD-2X20-BKP & MILFORD-4X20-BKP & MILFORD-2X16-BKP
  • Click on Terminal Mode then choose (DEFAULT & POWER &GROUND)
  • finally make the circuit below and start the simulation

That’s all!

If you have any questions or suggestions don’t hesitate to leave a comment below

Related posts

STM32-Driven LED Bar: Integration with 74HC595 Shift Register

How to Interface STM32 Microcontrollers with ADC128S102 via SPI

HC-SR04 Ultrasonic Sensor: Integrating with STM32 Microcontrollers Using TIMER