ESP-IDF and 2x16 LCD display

chuledeco
Posts: 7
Joined: Thu Feb 13, 2020 3:22 pm

ESP-IDF and 2x16 LCD display

Postby chuledeco » Sun Mar 15, 2020 1:36 pm

Hello,

I was looking and looking and I can't find a library to handle a standard 2x16 display using the RS, R/W, E, D4, D5, D6, and D7 pins.

It seems that there are only solutions for the I2C driver.

Arduino has the LiquidCrystal.h library but is written in C++.

It is my last chance before starting to write it myself but I think it is very strange that it does not exist, I'm sure I'm not seeing it.

Can anyone help me guiding my to the resource?

Thanks in advance, Julian.

ESP_Sprite
Posts: 9582
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP-IDF and 2x16 LCD display

Postby ESP_Sprite » Sun Mar 15, 2020 4:29 pm

C++ should not be an issue on esp-idf. You can even import the Arduino base libraries if you want to use Arduino stuff.

Kaisha
Posts: 42
Joined: Thu Mar 05, 2020 8:59 pm

Re: ESP-IDF and 2x16 LCD display

Postby Kaisha » Sun Mar 15, 2020 6:43 pm

The ESP-IDF handles C++ just fine. Here's what I'm using, feel free to copy/paste:

Code: Select all

// **************************************************************************************************************************
//	SumpRelayModule\HD44780U.h
//
//		HD44780U LCD control
//		- the HD44780U internal clock runs at 270kHz (typically) and so timings are all based on 270kHz
//		- HD44780U requires a 37 us delay between commands, and a 41 us delay between character writes
//
//																			Ryan Boghean
//																			August 2017
//																			July 2018
//																			January 2020
//
// **************************************************************************************************************************

# pragma once

# include <cstdint>
# include <cassert>
# include <utility>

# include <freertos/FreeRTOS.h>
# include <freertos/task.h>
# include <esp_err.h>
# include <esp_log.h>
# include <driver/gpio.h>


// -------------------------------------------------------------------------------------------------
//	Display namespace
// -------------------------------------------------------------------------------------------------

namespace Display {


// -------------------------------------------------------------------------------------------------
//	HD44780U_4bit
//		- uses a 4 bit data interface (which means 6 lines in total required)
// -------------------------------------------------------------------------------------------------

// ----- HD44780U_4bit -----
template <
	gpio_num_t rs_, gpio_num_t e_, gpio_num_t d4_, gpio_num_t d5_, gpio_num_t d6_, gpio_num_t d7_, 
	uint8_t cols_, uint8_t rows_, bool font_mode_
	> class HD44780U_4bit {

	static_assert(cols_ > 0, "Error: display cannot have 0 columns.");
	static_assert(rows_ == 1 || rows_ == 2 || rows_ == 4, "Error: only supports 1, 2, or 4 row displays.");

	private:
		// pin constants
		static constexpr gpio_num_t pin_rs = rs_;
		static constexpr gpio_num_t pin_e = e_;

		static constexpr gpio_num_t d4 = d4_;
		static constexpr gpio_num_t d5 = d5_;
		static constexpr gpio_num_t d6 = d6_;
		static constexpr gpio_num_t d7 = d7_;

		// display constants
		static constexpr bool font_mode = font_mode_;							// false = 5x8 font, true = 5x10 font
		static constexpr bool display_lines = (rows_ == 4) ? true : false;		// false = 1 and 2 display lines, true = 4 display lines
		static constexpr bool data_length = false;								// false = 4 bits, true = 8 bits

		static constexpr uint8_t cols = cols_;
		static constexpr uint8_t rows = rows_;
	
		// internal helpers
		static void SendInit(uint8_t cmd);				// sends an initialization command
		static void SendCommand(uint8_t cmd);			// sends a command
		static void SendChar(char);						// sends a character

	public:
		HD44780U_4bit();

		// info
		static constexpr uint8_t RowCount() { return rows; }
		static constexpr uint8_t ColumnCount() { return cols; }

		// access
		void Init();																				// must be called prior to using 
		void Clear();																				// clears whole display
		void ClearLn(uint8_t row);																	// clears a single line
		void SetCursor(uint8_t row, uint8_t col);													// set the cursor position
		void PrintChar(char c);																		// print a single character at current cursor position, increments cursor
		void PrintLn(uint8_t row, const char*);														// prints the string to the line
		template<class... TArgs> void PrintLn(uint8_t row, const char* fmt, TArgs&& ... args);		// prints a full line using printf formatting, any character off the edge will be truncated, control characters (/n /t etc...) should not be used
	};


// ----- convenience macros -----
# define HD_TEMPLATE template<gpio_num_t rs_, gpio_num_t e_, gpio_num_t d4_, gpio_num_t d5_, gpio_num_t d6_, gpio_num_t d7_, uint8_t cols_, uint8_t rows_, bool font_mode_>
# define HD_CLASS HD44780U_4bit<rs_, e_, d4_, d5_, d6_, d7_, cols_, rows_, font_mode_>


// ----- standard member functions -----
HD_TEMPLATE HD_CLASS::HD44780U_4bit() {}


// ----- internal helpers -----
HD_TEMPLATE void HD_CLASS::SendInit(uint8_t cmd) {

	gpio_set_level(pin_rs, 0);
	gpio_set_level(pin_e, 1);

	// 4-bit init commands only send the high nibble
	gpio_set_level(d4, (cmd & 0b00010000) != 0);
	gpio_set_level(d5, (cmd & 0b00100000) != 0);
	gpio_set_level(d6, (cmd & 0b01000000) != 0);
	gpio_set_level(d7, (cmd & 0b10000000) != 0);

	ets_delay_us(37);
	gpio_set_level(pin_e, 0);
	ets_delay_us(37);
	gpio_set_level(pin_e, 1);
	}

HD_TEMPLATE void HD_CLASS::SendCommand(uint8_t cmd) {

	gpio_set_level(pin_rs, 0);

	// send high nibble
	gpio_set_level(d4, (cmd & 0b00010000) != 0);
	gpio_set_level(d5, (cmd & 0b00100000) != 0);
	gpio_set_level(d6, (cmd & 0b01000000) != 0);
	gpio_set_level(d7, (cmd & 0b10000000) != 0);

	ets_delay_us(37);
	gpio_set_level(pin_e, 0);
	ets_delay_us(37);
	gpio_set_level(pin_e, 1);

	// send low nibble
	gpio_set_level(d4, (cmd & 0b00000001) != 0);
	gpio_set_level(d5, (cmd & 0b00000010) != 0);
	gpio_set_level(d6, (cmd & 0b00000100) != 0);
	gpio_set_level(d7, (cmd & 0b00001000) != 0);

	ets_delay_us(37);
	gpio_set_level(pin_e, 0);
	ets_delay_us(37);
	gpio_set_level(pin_e, 1);
	}

HD_TEMPLATE void HD_CLASS::SendChar(char c) {

	gpio_set_level(pin_rs, 1);

	// send high nibble
	gpio_set_level(d4, (c & 0b00010000) != 0);
	gpio_set_level(d5, (c & 0b00100000) != 0);
	gpio_set_level(d6, (c & 0b01000000) != 0);
	gpio_set_level(d7, (c & 0b10000000) != 0);

	ets_delay_us(41);
	gpio_set_level(pin_e, 0);
	ets_delay_us(41);
	gpio_set_level(pin_e, 1);

	// send low nibble
	gpio_set_level(d4, (c & 0b00000001) != 0);
	gpio_set_level(d5, (c & 0b00000010) != 0);
	gpio_set_level(d6, (c & 0b00000100) != 0);
	gpio_set_level(d7, (c & 0b00001000) != 0);

	ets_delay_us(41);
	gpio_set_level(pin_e, 0);
	ets_delay_us(41);
	gpio_set_level(pin_e, 1);
	}


// ----- access -----
HD_TEMPLATE void HD_CLASS::Init() {

	constexpr uint8_t cmd_por = 0x30;
	constexpr uint8_t cmd_4bit_mode = 0x20;
	constexpr uint8_t cmd_8bit_mode = 0x30;

	// config GPIO pins
	gpio_config_t io_conf = {};
	io_conf.pin_bit_mask =
		(1ULL << static_cast<uint64_t>(pin_rs)) |
		(1ULL << static_cast<uint64_t>(pin_e)) |
		(1ULL << static_cast<uint64_t>(d4)) |
		(1ULL << static_cast<uint64_t>(d5)) |
		(1ULL << static_cast<uint64_t>(d6)) |
		(1ULL << static_cast<uint64_t>(d7));
	io_conf.mode = GPIO_MODE_OUTPUT;
	io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
	io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
	io_conf.intr_type = GPIO_INTR_DISABLE;

	ESP_ERROR_CHECK(gpio_config(&io_conf));

	gpio_set_level(pin_rs, 0);
	gpio_set_level(pin_e, 1);
	gpio_set_level(d4, 0);
	gpio_set_level(d5, 0);
	gpio_set_level(d6, 0);
	gpio_set_level(d7, 0);

	// power on reset
	ets_delay_us(100000);							// wait 15ms (other docs claim 40ms, some as high as 100) after Vcc rises past 4.5v (2.7v)
	SendInit(cmd_por);
	ets_delay_us(5000);								// wait more than 4.1ms
	SendInit(cmd_por);
	ets_delay_us(1000);								// wait more than 100us
	SendInit(cmd_por);
	ets_delay_us(1000);								// no delay specified here in docs, but other libraries have it
	if (data_length) SendInit(cmd_8bit_mode);
	else SendInit(cmd_4bit_mode);

	// initialize
	uint8_t cmd_fs = 0x20;					// 'function set' command
	if (font_mode) cmd_fs |= 0x04;
	if (display_lines) cmd_fs |= 0x08;
	if (data_length) cmd_fs |= 0x10;
	SendCommand(cmd_fs);
	SendCommand(0x0C);					// turn display on, cursor/blink off
	SendCommand(0x01);					// clear display
	ets_delay_us(20000);						// clear display requires a long wait before new commands

	// ready to go
	Clear();
	}

HD_TEMPLATE void HD_CLASS::Clear() {
	for (uint8_t i = 0; i < rows; ++i) {
		ClearLn(i);
		}
	}

HD_TEMPLATE void HD_CLASS::ClearLn(uint8_t row) {
	SetCursor(row, 0);
	for (uint8_t i = 0; i < cols; ++i) {
		SendChar(32);
		}
	}

HD_TEMPLATE void HD_CLASS::SetCursor(uint8_t row, uint8_t col) {
	static constexpr uint8_t row_offsets[4] = { 0x00, 0x40, 0x14, 0x54 };
	assert(row < rows);
	assert(col < cols);

	SendCommand(0x80 + row_offsets[row] + col);
	}

HD_TEMPLATE void HD_CLASS::PrintChar(char c) {
	SendChar(c);
	}

void HD_CLASS::PrintLn(uint8_t row, const char* s) {
	if (row > rows) return;				// sanity check

	// set cursor
	SetCursor(row, 0);

	// print string
	size_t n = 0;
	while (n < cols && *s) {
		SendChar(*s++);
		++n;
		}

	while (n < cols) {
		SendChar(' ');
		++n;
		}
	}

HD_TEMPLATE template<class... TArgs> void HD_CLASS::PrintLn(uint8_t row, const char* fmt, TArgs&& ... args) {
	if (row > rows) return;				// sanity check

	// print to buffer
	char bfr[cols + 1];
	int n = vsnprintf(bfr, cols + 1, fmt, std::forward<TArgs>(args)...);
	while (n < cols) {
		bfr[n] = 32;
		++n;
		}

	// set cursor
	SetCursor(row, 0);

	// send data
	for (uint8_t i = 0; i < cols; ++i) SendChar(bfr[i]);
	}


// ----- clean up macros -----
# undef HD_CLASS
# undef HD_TEMPLATE


// -------------------------------------------------------------------------------------------------
//	End 
// -------------------------------------------------------------------------------------------------

}			// end of Display namespace

Who is online

Users browsing this forum: zilizii and 117 guests