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-IDF and 2x16 LCD display
-
- Posts: 9719
- Joined: Thu Nov 26, 2015 4:08 am
Re: ESP-IDF and 2x16 LCD display
C++ should not be an issue on esp-idf. You can even import the Arduino base libraries if you want to use Arduino stuff.
Re: ESP-IDF and 2x16 LCD display
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: No registered users and 69 guests