Skip to content

use i2c_master interface #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
set(COMPONENT_ADD_INCLUDEDIRS include)
set(COMPONENT_SRCDIRS ".")
set(COMPONENT_PRIV_REQUIRES "esp32-smbus")
set(COMPONENT_PRIV_REQUIRES "esp_driver_i2c")
register_component()

5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

This component provides useful access functions for the I2C-LCD1602 device, which is compatible with the HD44780 LCD controller. It uses a PCF8574A Remote 8-bit I/O Expander over the I2C bus, allowing the controller to be programmed via I2C using 4-bit mode.

It is written and tested for the [ESP-IDF](https://github.com/espressif/esp-idf) environment, version 2.1, using the xtensa-esp32-elf toolchain (gcc version 5.2.0).
It is written and tested for the [ESP-IDF](https://github.com/espressif/esp-idf) environment, version 5.5, using the xtensa-esp32-elf toolchain (gcc version 14.2.0).

## Dependencies

Requires [esp32-smbus](https://github.com/DavidAntliff/esp32-smbus).
Requires [esp_driver_i2c] (provided with esp-idf)

## Example

Expand Down Expand Up @@ -49,7 +49,6 @@ The code in this project is licensed under the MIT license - see LICENSE for det
## Acknowledgements

* Inspired by the Arduino [DFRobot sample code](http://www.dfrobot.com/image/data/DFR0154/LiquidCrystal_I2Cv1-1.rar).
* "SMBus" is a trademark of Intel Corporation.
* "I2C" is a registered trademark of Phillips Corporation.

## Roadmap
Expand Down
26 changes: 14 additions & 12 deletions i2c-lcd1602.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@ static esp_err_t _write_to_expander(const i2c_lcd1602_info_t * i2c_lcd1602_info,
{
// backlight flag must be included with every write to maintain backlight state
ESP_LOGD(TAG, "_write_to_expander 0x%02x", data | i2c_lcd1602_info->backlight_flag);
return smbus_send_byte(i2c_lcd1602_info->smbus_info, data | i2c_lcd1602_info->backlight_flag);
data |= i2c_lcd1602_info->backlight_flag;
return i2c_master_transmit(*i2c_lcd1602_info->i2c_info->dev_handle, &data, 1, i2c_lcd1602_info->i2c_info->i2c_transfer_timeout_ms / portTICK_PERIOD_MS);
}

// IMPORTANT - for the display to stay "in sync" it is important that errors do not interrupt the
Expand All @@ -208,9 +209,9 @@ static esp_err_t _write_to_expander(const i2c_lcd1602_info_t * i2c_lcd1602_info,
static esp_err_t _strobe_enable(const i2c_lcd1602_info_t * i2c_lcd1602_info, uint8_t data)
{
esp_err_t err1 = _write_to_expander(i2c_lcd1602_info, data | FLAG_ENABLE);
ets_delay_us(DELAY_ENABLE_PULSE_WIDTH);
esp_rom_delay_us(DELAY_ENABLE_PULSE_WIDTH);
esp_err_t err2 = _write_to_expander(i2c_lcd1602_info, data & ~FLAG_ENABLE);
ets_delay_us(DELAY_ENABLE_PULSE_SETTLE);
esp_rom_delay_us(DELAY_ENABLE_PULSE_SETTLE);
return err1 ? err1 : err2;
}

Expand Down Expand Up @@ -278,13 +279,14 @@ void i2c_lcd1602_free(i2c_lcd1602_info_t ** i2c_lcd1602_info)
}
}

esp_err_t i2c_lcd1602_init(i2c_lcd1602_info_t * i2c_lcd1602_info, smbus_info_t * smbus_info,
esp_err_t i2c_lcd1602_init(i2c_lcd1602_info_t * i2c_lcd1602_info, struct i2c_info_t * i2c_info,
bool backlight, uint8_t num_rows, uint8_t num_columns, uint8_t num_visible_columns)
{
esp_err_t err = ESP_FAIL;
if (i2c_lcd1602_info != NULL)
{
i2c_lcd1602_info->smbus_info = smbus_info;
i2c_lcd1602_info->i2c_info = i2c_info;

i2c_lcd1602_info->backlight_flag = backlight ? FLAG_BACKLIGHT_ON : FLAG_BACKLIGHT_OFF;
i2c_lcd1602_info->num_rows = num_rows;
i2c_lcd1602_info->num_columns = num_columns;
Expand All @@ -301,7 +303,7 @@ esp_err_t i2c_lcd1602_init(i2c_lcd1602_info_t * i2c_lcd1602_info, smbus_info_t *
// See page 45/46 of HD44780 data sheet for the initialisation procedure.

// Wait at least 40ms after power rises above 2.7V before sending commands.
ets_delay_us(DELAY_POWER_ON);
esp_rom_delay_us(DELAY_POWER_ON);

err = i2c_lcd1602_reset(i2c_lcd1602_info);
}
Expand All @@ -326,7 +328,7 @@ esp_err_t i2c_lcd1602_reset(const i2c_lcd1602_info_t * i2c_lcd1602_info)
ESP_LOGE(TAG, "reset: _write_to_expander 1 failed: %d", last_err);
}

ets_delay_us(1000);
esp_rom_delay_us(1000);

// select 4-bit mode on LCD controller - see datasheet page 46, figure 24.
if ((last_err = _write_top_nibble(i2c_lcd1602_info, 0x03 << 4)) != ESP_OK)
Expand All @@ -336,7 +338,7 @@ esp_err_t i2c_lcd1602_reset(const i2c_lcd1602_info_t * i2c_lcd1602_info)
ESP_LOGE(TAG, "reset: _write_top_nibble 1 failed: %d", last_err);
}

ets_delay_us(DELAY_INIT_1);
esp_rom_delay_us(DELAY_INIT_1);

// repeat
if ((last_err = _write_top_nibble(i2c_lcd1602_info, 0x03 << 4)) != ESP_OK)
Expand All @@ -346,7 +348,7 @@ esp_err_t i2c_lcd1602_reset(const i2c_lcd1602_info_t * i2c_lcd1602_info)
ESP_LOGE(TAG, "reset: _write_top_nibble 2 failed: %d", last_err);
}

ets_delay_us(DELAY_INIT_2);
esp_rom_delay_us(DELAY_INIT_2);

// repeat
if ((last_err = _write_top_nibble(i2c_lcd1602_info, 0x03 << 4)) != ESP_OK)
Expand All @@ -356,7 +358,7 @@ esp_err_t i2c_lcd1602_reset(const i2c_lcd1602_info_t * i2c_lcd1602_info)
ESP_LOGE(TAG, "reset: _write_top_nibble 3 failed: %d", last_err);
}

ets_delay_us(DELAY_INIT_3);
esp_rom_delay_us(DELAY_INIT_3);

// select 4-bit mode
if ((last_err = _write_top_nibble(i2c_lcd1602_info, 0x02 << 4)) != ESP_OK)
Expand Down Expand Up @@ -413,7 +415,7 @@ esp_err_t i2c_lcd1602_clear(const i2c_lcd1602_info_t * i2c_lcd1602_info)
err = _write_command(i2c_lcd1602_info, COMMAND_CLEAR_DISPLAY);
if (err == ESP_OK)
{
ets_delay_us(DELAY_CLEAR_DISPLAY);
esp_rom_delay_us(DELAY_CLEAR_DISPLAY);
}
}
return err;
Expand All @@ -427,7 +429,7 @@ esp_err_t i2c_lcd1602_home(const i2c_lcd1602_info_t * i2c_lcd1602_info)
err = _write_command(i2c_lcd1602_info, COMMAND_RETURN_HOME);
if (err == ESP_OK)
{
ets_delay_us(DELAY_RETURN_HOME);
esp_rom_delay_us(DELAY_RETURN_HOME);
}
}
return err;
Expand Down
22 changes: 15 additions & 7 deletions include/i2c-lcd1602.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,36 @@
* @brief Interface definitions for the ESP32-compatible I2C LCD1602 component.
*
* This component provides structures and functions that are useful for communicating with the device.
*
* Technically, the LCD1602 device is an I2C not SMBus device, however some SMBus protocols can be used
* to communicate with the device, so it makes sense to use an SMBus interface to manage communication.
*/

#ifndef I2C_LCD1602_H
#define I2C_LCD1602_H

#include <stdbool.h>
#include "smbus.h"
#include "driver/i2c_master.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Structure containing information related to the I2C master device.
*/

struct i2c_info_t {
i2c_master_bus_handle_t *bus_handle;
i2c_master_dev_handle_t *dev_handle;
int i2c_transfer_timeout_ms;
};


/**
* @brief Structure containing information related to the I2C-LCD1602 device.
*/
typedef struct
{
bool init; ///< True if struct has been initialised, otherwise false
smbus_info_t * smbus_info; ///< Pointer to associated SMBus info
struct i2c_info_t *i2c_info; ///< Pointer to associated I2C master info
uint8_t backlight_flag; ///< Non-zero if backlight is to be enabled, otherwise zero
uint8_t num_rows; ///< Number of configured columns
uint8_t num_columns; ///< Number of configured columns, including offscreen columns
Expand Down Expand Up @@ -126,14 +134,14 @@ void i2c_lcd1602_free(i2c_lcd1602_info_t ** tsl2561_info);
* @brief Initialise a I2C-LCD1602 info instance with the specified SMBus information.
*
* @param[in] i2c_lcd1602_info Pointer to I2C-LCD1602 info instance.
* @param[in] smbus_info Pointer to SMBus info instance.
* @param[in] i2c_info Pointer to i2c master info instance.
* @param[in] backlight Initial backlight state.
* @param[in] num_rows Maximum number of supported rows for this device. Typical values include 2 (1602) or 4 (2004).
* @param[in] num_columns Maximum number of supported columns for this device. Typical values include 40 (1602, 2004).
* @param[in] num_visible_columns Number of columns visible at any one time. Typical values include 16 (1602) or 20 (2004).
* @return ESP_OK if successful, otherwise an error constant.
*/
esp_err_t i2c_lcd1602_init(i2c_lcd1602_info_t * i2c_lcd1602_info, smbus_info_t * smbus_info,
esp_err_t i2c_lcd1602_init(i2c_lcd1602_info_t * i2c_lcd1602_info, struct i2c_info_t * i2c_info,
bool backlight, uint8_t num_rows, uint8_t num_columns, uint8_t num_visible_columns);

/**
Expand Down