Skip to content

Commit de1dfa8

Browse files
committed
first commit
0 parents  commit de1dfa8

16 files changed

+4608
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.vscode/
2+
build/
3+
junk/
4+
dependencies.lock

CMakeLists.txt

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# For more information about build system see
2+
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
3+
# The following five lines of boilerplate have to be in your project's
4+
# CMakeLists in this exact order for cmake to work correctly
5+
cmake_minimum_required(VERSION 3.16)
6+
7+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
8+
project(esp32_i2c_lcd1602_2004_demo_cpp)

components/i2clcd/CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
idf_component_register(SRCS "i2clcd.cpp" "lcdapi.cpp"
2+
INCLUDE_DIRS "include"
3+
REQUIRES esp_driver_i2c esp_driver_gpio)

components/i2clcd/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
placeholder

components/i2clcd/component.mk

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Use defaults

components/i2clcd/i2clcd.cpp

+204
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
#include "i2clcd.h"
2+
#include <esp_log.h>
3+
#include "freertos/FreeRTOS.h"
4+
#include "freertos/task.h"
5+
6+
#define PIN_NUM_SDA GPIO_NUM_21
7+
#define PIN_NUM_SCL GPIO_NUM_22
8+
#define I2C_ADDR 0x27 // 0x3f // 0x27
9+
#define I2C_MASTER_FREQ_HZ 10000
10+
#define I2C_TOOL_TIMEOUT_VALUE_MS 50
11+
#define _write(__data) _writeTo(__data, sizeof(__data))
12+
13+
static const char TAG[] = "i2clcd";
14+
15+
/// @brief Create i2clcd interface
16+
/// @param sda gpio pin for sda
17+
/// @param scl gpio pin for scl
18+
/// @param i2cAddr bus address (e.g 0x27 most common)
19+
/// @param i2cFreq typically 10000hz
20+
/// @param num_lines number of lines (2 or 4)
21+
/// @param num_columns number columns (16 or 20)
22+
I2CLCD::I2CLCD(const gpio_num_t sda,
23+
const gpio_num_t scl,
24+
const uint8_t i2cAddr,
25+
const uint32_t i2cFreq,
26+
const int num_lines,
27+
const int num_columns) : LcdApi(num_lines, num_columns)
28+
{
29+
_init(sda, scl);
30+
_addDevice(i2cAddr, i2cFreq);
31+
const uint8_t data1[]{0};
32+
_writeTo(data1, sizeof(data1));
33+
_delay(20); // Allow LCD time to powerup
34+
35+
// Send reset 3 times
36+
_hal_write_init_nibble(LCD_FUNCTION_RESET);
37+
_delay(5); // Need to delay at least 4.1 msec
38+
_hal_write_init_nibble(LCD_FUNCTION_RESET);
39+
_delay(1);
40+
_hal_write_init_nibble(LCD_FUNCTION_RESET);
41+
_delay(1);
42+
// Put LCD into 4-bit mode
43+
_hal_write_init_nibble(LCD_FUNCTION);
44+
_delay(1);
45+
LcdApi::postInit();
46+
const uint8_t cmd = num_lines > 1 ? LCD_FUNCTION | LCD_FUNCTION_2LINES : LCD_FUNCTION;
47+
_hal_write_command(cmd);
48+
}
49+
50+
I2CLCD::~I2CLCD()
51+
{
52+
_removeDevice();
53+
}
54+
55+
/// @brief Writes an initialization nibble to the LCD.
56+
/// @param nibble
57+
void I2CLCD::_hal_write_init_nibble(uint8_t nibble)
58+
{
59+
// This particular function is only used during initialization.
60+
const uint8_t byte = ((nibble >> 4) & 0x0f) << SHIFT_DATA;
61+
const uint8_t data1[]{static_cast<uint8_t>(byte | MASK_E)};
62+
_write(data1);
63+
const uint8_t data2[]{byte};
64+
_write(data2);
65+
}
66+
67+
/// @brief Write a command to the LCD. Data is latched on the falling edge of E.
68+
/// @param cmd
69+
void I2CLCD::_hal_write_command(uint8_t cmd)
70+
{
71+
uint8_t byte = ((m_backlight << SHIFT_BACKLIGHT) |
72+
(((cmd >> 4) & 0x0f) << SHIFT_DATA));
73+
const uint8_t data1[]{static_cast<uint8_t>(byte | MASK_E)};
74+
_write(data1);
75+
const uint8_t data2[]{byte};
76+
_write(data2);
77+
78+
byte = ((m_backlight << SHIFT_BACKLIGHT) |
79+
((cmd & 0x0f) << SHIFT_DATA));
80+
const uint8_t data3[]{static_cast<uint8_t>(byte | MASK_E)};
81+
_write(data3);
82+
const uint8_t data4[]{byte};
83+
_write(data4);
84+
if (cmd <= 3)
85+
{
86+
// The home and clear commands require a worst case delay of 4.1 msec
87+
_delay(5);
88+
}
89+
}
90+
91+
/// @brief Write data to the LCD. Data is latched on the falling edge of E.
92+
/// @param data
93+
void I2CLCD::_hal_write_data(uint8_t data)
94+
{
95+
uint8_t byte = (MASK_RS |
96+
(m_backlight << SHIFT_BACKLIGHT) |
97+
(((data >> 4) & 0x0f) << SHIFT_DATA));
98+
const uint8_t data1[]{static_cast<uint8_t>(byte | MASK_E)};
99+
_write(data1);
100+
const uint8_t data2[]{byte};
101+
_write(data2);
102+
103+
byte = (MASK_RS |
104+
(m_backlight << SHIFT_BACKLIGHT) |
105+
((data & 0x0f) << SHIFT_DATA));
106+
const uint8_t data3[]{static_cast<uint8_t>(byte | MASK_E)};
107+
_write(data3);
108+
const uint8_t data4[]{byte};
109+
_write(data4);
110+
}
111+
112+
/// @brief Allows the hal layer to turn the backlight on
113+
void I2CLCD::_hal_backlight_on()
114+
{
115+
const uint8_t data1[]{1 << SHIFT_BACKLIGHT};
116+
_write(data1);
117+
}
118+
119+
/// @brief Allows the hal layer to turn the backlight off
120+
void I2CLCD::_hal_backlight_off()
121+
{
122+
const uint8_t data1[]{0};
123+
_write(data1);
124+
}
125+
126+
/// @brief Delay execution for a few miliseconds
127+
/// @param msec
128+
void I2CLCD::_delay(const int msec)
129+
{
130+
vTaskDelay(msec / portTICK_PERIOD_MS);
131+
}
132+
133+
/// @brief Wrote to 12c slave device
134+
/// @param data data to be written
135+
/// @param size number of bytes
136+
/// @return true if successful
137+
bool I2CLCD::_writeTo(const uint8_t *data, const size_t size)
138+
{
139+
const esp_err_t ret = i2c_master_transmit(m_dev_handle, data, size, I2C_TOOL_TIMEOUT_VALUE_MS);
140+
if (ret == ESP_OK)
141+
{
142+
return true;
143+
}
144+
else if (ret == ESP_ERR_TIMEOUT)
145+
{
146+
ESP_LOGW(TAG, "Bus is busy");
147+
return false;
148+
}
149+
else
150+
{
151+
ESP_LOGW(TAG, "Write Failed");
152+
return false;
153+
}
154+
}
155+
156+
/// @brief add 12c slave device to the bus
157+
/// @return true if succesful
158+
bool I2CLCD::_addDevice(const uint8_t i2cAddr,
159+
const uint32_t i2cFreq)
160+
{
161+
i2c_device_config_t i2c_dev_conf = {
162+
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
163+
.device_address = i2cAddr, // I2C_ADDR,
164+
.scl_speed_hz = i2cFreq, // I2C_MASTER_FREQ_HZ,
165+
.scl_wait_us = 0,
166+
.flags{.disable_ack_check = 0},
167+
};
168+
if (i2c_master_bus_add_device(m_i2c_bus, &i2c_dev_conf, &m_dev_handle) != ESP_OK)
169+
{
170+
ESP_LOGE(TAG, "Adding device has failed");
171+
return false;
172+
}
173+
ESP_LOGI(TAG, "Adding device has not failed");
174+
return true;
175+
}
176+
177+
/// @brief remove 12c slave device to the bus
178+
/// @return true if succesful
179+
bool I2CLCD::_removeDevice()
180+
{
181+
if (i2c_master_bus_rm_device(m_dev_handle) != ESP_OK)
182+
{
183+
ESP_LOGE(TAG, "Removing device has failed");
184+
return false;
185+
}
186+
ESP_LOGI(TAG, "Removing device has not failed");
187+
return true;
188+
}
189+
190+
/// @brief initialize 12c bus master
191+
void I2CLCD::_init(const gpio_num_t sda, const gpio_num_t scl)
192+
{
193+
i2c_master_bus_config_t bus_config = {
194+
.i2c_port = -1, // I2C_NUM_0,
195+
.sda_io_num = sda, // PIN_NUM_SDA,
196+
.scl_io_num = scl, // PIN_NUM_SCL,
197+
.clk_source = I2C_CLK_SRC_DEFAULT,
198+
.glitch_ignore_cnt = 7,
199+
.intr_priority = 0,
200+
.trans_queue_depth = 0,
201+
.flags{.enable_internal_pullup = false},
202+
};
203+
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &m_i2c_bus));
204+
}

components/i2clcd/idf_component.yml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-version: "1.0.0"
2+
description: ESP IDF i2c implementation of the LCD driver for 1602 and 2004
3+
url: https://github.com/cfrankb/esp32-lcd1602_2004-cpp
4+
dependencies:
5+
## Required IDF version
6+
idf:
7+
version: ">=5.3"
8+

components/i2clcd/include/i2clcd.h

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#ifndef __i2CLCD__H
2+
#define __i2CLCD__H
3+
4+
#include <driver/i2c_master.h>
5+
#include "lcdapi.h"
6+
7+
class I2CLCD : public LcdApi
8+
{
9+
public:
10+
I2CLCD(const gpio_num_t sda,
11+
const gpio_num_t scl,
12+
const uint8_t i2cAddr,
13+
const uint32_t i2cFreq,
14+
const int num_lines,
15+
const int num_columns);
16+
~I2CLCD();
17+
18+
private:
19+
enum : uint8_t
20+
{
21+
// # PCF8574 pin definitions
22+
MASK_RS = 0x01, // # P0
23+
MASK_RW = 0x02, // # P1
24+
MASK_E = 0x04, // # P2
25+
SHIFT_BACKLIGHT = 3, // # P3
26+
SHIFT_DATA = 4, // # P4-P7
27+
};
28+
29+
void _init(const gpio_num_t sda, const gpio_num_t scl);
30+
bool _addDevice(const uint8_t i2cAddr,
31+
const uint32_t i2cFreq);
32+
bool _removeDevice();
33+
bool _writeTo(const uint8_t *data, const size_t size);
34+
void _delay(const int msec);
35+
void _hal_write_command(uint8_t cmd) override;
36+
void _hal_write_data(uint8_t data);
37+
void _hal_write_init_nibble(uint8_t nibble);
38+
void _hal_backlight_on() override;
39+
void _hal_backlight_off() override;
40+
i2c_master_bus_handle_t m_i2c_bus = nullptr;
41+
i2c_master_dev_handle_t m_dev_handle = nullptr;
42+
};
43+
44+
#endif

components/i2clcd/include/ilcdapi.h

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include "inttypes.h"
2+
3+
class ILcdApi
4+
{
5+
6+
public:
7+
ILcdApi() {};
8+
~ILcdApi() {};
9+
10+
protected:
11+
virtual void _hal_write_command(uint8_t cmd) = 0;
12+
virtual void _hal_write_data(uint8_t cmd) = 0;
13+
virtual void _hal_backlight_on() = 0;
14+
virtual void _hal_backlight_off() = 0;
15+
};

components/i2clcd/include/lcdapi.h

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#include "ilcdapi.h"
2+
3+
class LcdApi : public ILcdApi
4+
{
5+
public:
6+
LcdApi(int num_lines, int num_columns);
7+
~LcdApi();
8+
9+
void clear();
10+
void hide_cursor();
11+
void blink_cursor_on();
12+
void blink_cursor_off();
13+
void display_on();
14+
void display_off();
15+
void backlight_on();
16+
void backlight_off();
17+
void move_to(int cursor_x, int cursor_y);
18+
void putchar(char ch);
19+
void putstr(const char *s);
20+
void custom_char(int location, uint8_t charmap[8]);
21+
22+
enum
23+
{
24+
LCD_CLR = 0x01, // DB0: clear display
25+
LCD_HOME = 0x02, // DB1: return to home position
26+
LCD_ENTRY_MODE = 0x04, // DB2: set entry mode
27+
LCD_ENTRY_INC = 0x02, // --DB1: increment
28+
LCD_ENTRY_SHIFT = 0x01, // --DB0: shift
29+
30+
LCD_ON_CTRL = 0x08, // DB3: turn lcd/cursor on
31+
LCD_ON_DISPLAY = 0x04, // --DB2: turn display on
32+
LCD_ON_CURSOR = 0x02, // --DB1: turn cursor on
33+
LCD_ON_BLINK = 0x01, // --DB0: blinking cursor
34+
LCD_MOVE = 0x10, // DB4: move cursor/display
35+
LCD_MOVE_DISP = 0x08, // --DB3: move display (0-> move cursor)
36+
LCD_MOVE_RIGHT = 0x04, // --DB2: move right (0-> left)
37+
38+
LCD_FUNCTION = 0x20, // DB5: function set
39+
LCD_FUNCTION_8BIT = 0x10, // --DB4: set 8BIT mode (0->4BIT mode)
40+
LCD_FUNCTION_2LINES = 0x08, // --DB3: two lines (0->one line)
41+
LCD_FUNCTION_10DOTS = 0x04, // --DB2: 5x10 font (0->5x7 font)
42+
LCD_FUNCTION_RESET = 0x30, // See "Initializing by Instruction" section
43+
44+
LCD_CGRAM = 0x40, // DB6: set CG RAM address
45+
LCD_DDRAM = 0x80, // DB7: set DD RAM address
46+
LCD_RS_CMD = 0, //
47+
LCD_RS_DATA = 1, //
48+
49+
LCD_RW_WRITE = 0, //
50+
LCD_RW_READ = 1, //
51+
};
52+
53+
protected:
54+
int m_num_lines;
55+
int m_num_columns;
56+
int m_cursor_x;
57+
int m_cursor_y;
58+
bool m_backlight;
59+
bool m_implied_newline;
60+
61+
void postInit();
62+
void _hal_sleep_us(uint32_t usecs);
63+
64+
private:
65+
};

0 commit comments

Comments
 (0)