drivers: auxdisplay: Add Hitachi HD44780 driver
Adds an auxiliary display driver for Hitachi HD44780-based (and compatible) LCD displays. Signed-off-by: Jamie McCrae <spam@helper3000.net>
This commit is contained in:
parent
71c727e92e
commit
212a4857ba
|
@ -1,5 +1,6 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
zephyr_library_sources_ifdef(CONFIG_AUXDISPLAY_HD44780 auxdisplay_hd44780.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_AUXDISPLAY_ITRON auxdisplay_itron.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_AUXDISPLAY_JHD1313 auxdisplay_jhd1313.c)
|
||||
|
|
|
@ -20,6 +20,7 @@ module = AUXDISPLAY
|
|||
module-str = auxdisplay
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
source "drivers/auxdisplay/Kconfig.hd44780"
|
||||
source "drivers/auxdisplay/Kconfig.itron"
|
||||
source "drivers/auxdisplay/Kconfig.jhd1313"
|
||||
|
||||
|
|
9
drivers/auxdisplay/Kconfig.hd44780
Normal file
9
drivers/auxdisplay/Kconfig.hd44780
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Copyright (c) 2023 Jamie McCrae
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config AUXDISPLAY_HD44780
|
||||
bool "Hitachi HD44780 LCD driver"
|
||||
default y
|
||||
depends on DT_HAS_HIT_HD44780_ENABLED
|
||||
help
|
||||
Enable driver for Hitachi HD44780 and compatible LCDs.
|
594
drivers/auxdisplay/auxdisplay_hd44780.c
Normal file
594
drivers/auxdisplay/auxdisplay_hd44780.c
Normal file
|
@ -0,0 +1,594 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Jamie McCrae
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT hit_hd44780
|
||||
|
||||
#include <string.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/drivers/auxdisplay.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(auxdisplay_hd44780, CONFIG_AUXDISPLAY_LOG_LEVEL);
|
||||
|
||||
#define AUXDISPLAY_HD44780_BACKLIGHT_MIN 0
|
||||
#define AUXDISPLAY_HD44780_BACKLIGHT_MAX 1
|
||||
|
||||
#define AUXDISPLAY_HD44780_CUSTOM_CHARACTERS 8
|
||||
#define AUXDISPLAY_HD44780_CUSTOM_CHARACTER_WIDTH 5
|
||||
#define AUXDISPLAY_HD44780_CUSTOM_CHARACTER_HEIGHT 8
|
||||
|
||||
enum {
|
||||
AUXDISPLAY_HD44780_MODE_4_BIT = 0,
|
||||
AUXDISPLAY_HD44780_MODE_8_BIT = 1,
|
||||
|
||||
/* Reserved for internal driver use only */
|
||||
AUXDISPLAY_HD44780_MODE_4_BIT_ONCE,
|
||||
};
|
||||
|
||||
/* Display commands */
|
||||
#define AUXDISPLAY_HD44780_CMD_CLEAR 0x01
|
||||
#define AUXDISPLAY_HD44780_CMD_ENTRY_MODE 0x04
|
||||
#define AUXDISPLAY_HD44780_CMD_DISPLAY_MODE 0x08
|
||||
#define AUXDISPLAY_HD44780_CMD_CGRAM_SET 0x40
|
||||
#define AUXDISPLAY_HD44780_CMD_POSITION_SET 0x80
|
||||
#define AUXDISPLAY_HD44780_CMD_SETUP 0x20
|
||||
|
||||
#define AUXDISPLAY_HD44780_8_BIT_CONFIG 0x10
|
||||
#define AUXDISPLAY_HD44780_2_LINE_CONFIG 0x08
|
||||
|
||||
#define AUXDISPLAY_HD44780_POSITION_BLINK_ENABLED 0x01
|
||||
#define AUXDISPLAY_HD44780_CURSOR_ENABLED 0x02
|
||||
#define AUXDISPLAY_HD44780_DISPLAY_ENABLED 0x04
|
||||
|
||||
#define AUXDISPLAY_HD44780_DISPLAY_SHIFT 0x01
|
||||
#define AUXDISPLAY_HD44780_CURSOR_MOVE_RIGHT 0x02
|
||||
|
||||
struct auxdisplay_hd44780_data {
|
||||
uint16_t character_x;
|
||||
uint16_t character_y;
|
||||
bool cursor_enabled;
|
||||
bool position_blink_enabled;
|
||||
uint8_t direction;
|
||||
bool display_shift;
|
||||
bool backlight_state;
|
||||
};
|
||||
|
||||
struct auxdisplay_hd44780_config {
|
||||
struct auxdisplay_capabilities capabilities;
|
||||
struct gpio_dt_spec rs_gpio;
|
||||
struct gpio_dt_spec rw_gpio;
|
||||
struct gpio_dt_spec e_gpio;
|
||||
struct gpio_dt_spec db_gpios[8];
|
||||
struct gpio_dt_spec backlight_gpio;
|
||||
uint8_t line_addresses[4];
|
||||
uint16_t enable_line_rise_delay;
|
||||
uint16_t enable_line_fall_delay;
|
||||
uint16_t clear_delay;
|
||||
uint16_t boot_delay;
|
||||
};
|
||||
|
||||
static void auxdisplay_hd44780_set_entry_mode(const struct device *dev);
|
||||
static void auxdisplay_hd44780_set_display_mode(const struct device *dev, bool enabled);
|
||||
|
||||
static void auxdisplay_hd44780_command(const struct device *dev, bool rs, uint8_t cmd,
|
||||
uint8_t mode)
|
||||
{
|
||||
const struct auxdisplay_hd44780_config *config = dev->config;
|
||||
int8_t i = 7;
|
||||
|
||||
if (mode == AUXDISPLAY_HD44780_MODE_8_BIT) {
|
||||
while (i >= 0) {
|
||||
gpio_pin_set_dt(&config->db_gpios[i], ((cmd & BIT(i)) ? 1 : 0));
|
||||
--i;
|
||||
}
|
||||
} else {
|
||||
while (i >= 4) {
|
||||
gpio_pin_set_dt(&config->db_gpios[i], ((cmd & BIT(i)) ? 1 : 0));
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
gpio_pin_set_dt(&config->rs_gpio, rs);
|
||||
|
||||
if (config->rw_gpio.port) {
|
||||
gpio_pin_set_dt(&config->rw_gpio, 0);
|
||||
}
|
||||
|
||||
gpio_pin_set_dt(&config->e_gpio, 1);
|
||||
k_sleep(K_USEC(config->enable_line_rise_delay));
|
||||
gpio_pin_set_dt(&config->e_gpio, 0);
|
||||
k_sleep(K_USEC(config->enable_line_fall_delay));
|
||||
|
||||
if (mode == AUXDISPLAY_HD44780_MODE_4_BIT) {
|
||||
while (i >= 0) {
|
||||
gpio_pin_set_dt(&config->db_gpios[(i + 4)], ((cmd & BIT(i)) ? 1 : 0));
|
||||
--i;
|
||||
}
|
||||
|
||||
gpio_pin_set_dt(&config->e_gpio, 1);
|
||||
k_sleep(K_USEC(config->enable_line_rise_delay));
|
||||
gpio_pin_set_dt(&config->e_gpio, 0);
|
||||
k_sleep(K_USEC(config->enable_line_fall_delay));
|
||||
}
|
||||
}
|
||||
|
||||
static int auxdisplay_hd44780_init(const struct device *dev)
|
||||
{
|
||||
const struct auxdisplay_hd44780_config *config = dev->config;
|
||||
struct auxdisplay_hd44780_data *data = dev->data;
|
||||
int rc;
|
||||
uint8_t i = 0;
|
||||
uint8_t cmd = AUXDISPLAY_HD44780_CMD_SETUP | AUXDISPLAY_HD44780_8_BIT_CONFIG;
|
||||
|
||||
if (config->capabilities.mode > AUXDISPLAY_HD44780_MODE_8_BIT) {
|
||||
/* This index is reserved for internal driver usage */
|
||||
LOG_ERR("HD44780 mode must be 4 or 8-bit");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Configure and set GPIOs */
|
||||
rc = gpio_pin_configure_dt(&config->rs_gpio, GPIO_OUTPUT);
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Configuration of RS GPIO failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (config->rw_gpio.port) {
|
||||
rc = gpio_pin_configure_dt(&config->rw_gpio, GPIO_OUTPUT);
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Configuration of RW GPIO failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
rc = gpio_pin_configure_dt(&config->e_gpio, GPIO_OUTPUT);
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Configuration of E GPIO failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (config->capabilities.mode == AUXDISPLAY_HD44780_MODE_4_BIT) {
|
||||
i = 4;
|
||||
}
|
||||
|
||||
while (i < 8) {
|
||||
if (config->db_gpios[i].port) {
|
||||
rc = gpio_pin_configure_dt(&config->db_gpios[i], GPIO_OUTPUT);
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Configuration of DB%d GPIO failed: %d", i, rc);
|
||||
return rc;
|
||||
}
|
||||
} else if (config->capabilities.mode == AUXDISPLAY_HD44780_MODE_4_BIT && i > 3) {
|
||||
/* Required pin missing */
|
||||
LOG_ERR("Required DB%d pin missing (DB4-DB7 needed for 4-bit mode)", i);
|
||||
return -EINVAL;
|
||||
} else if (config->capabilities.mode == AUXDISPLAY_HD44780_MODE_8_BIT) {
|
||||
/* Required pin missing */
|
||||
LOG_ERR("Required DB%d pin missing", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
if (config->backlight_gpio.port) {
|
||||
rc = gpio_pin_configure_dt(&config->backlight_gpio, GPIO_OUTPUT);
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Configuration of backlight GPIO failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
gpio_pin_set_dt(&config->backlight_gpio, 0);
|
||||
}
|
||||
|
||||
data->character_x = 0;
|
||||
data->character_y = 0;
|
||||
data->backlight_state = false;
|
||||
data->cursor_enabled = false;
|
||||
data->position_blink_enabled = false;
|
||||
data->direction = AUXDISPLAY_DIRECTION_RIGHT;
|
||||
|
||||
if (config->boot_delay != 0) {
|
||||
/* Boot delay is set, wait for a period of time for the LCD to become ready to
|
||||
* accept commands
|
||||
*/
|
||||
k_sleep(K_MSEC(config->boot_delay));
|
||||
}
|
||||
|
||||
if (config->capabilities.mode == AUXDISPLAY_HD44780_MODE_4_BIT) {
|
||||
/* Reset display to known state in 8-bit mode */
|
||||
auxdisplay_hd44780_command(dev, false, cmd, AUXDISPLAY_HD44780_MODE_4_BIT_ONCE);
|
||||
auxdisplay_hd44780_command(dev, false, cmd, AUXDISPLAY_HD44780_MODE_4_BIT_ONCE);
|
||||
|
||||
/* Put display into 4-bit mode */
|
||||
cmd = AUXDISPLAY_HD44780_CMD_SETUP;
|
||||
auxdisplay_hd44780_command(dev, false, cmd, AUXDISPLAY_HD44780_MODE_4_BIT_ONCE);
|
||||
}
|
||||
|
||||
if (config->capabilities.rows > 1) {
|
||||
cmd |= AUXDISPLAY_HD44780_2_LINE_CONFIG;
|
||||
}
|
||||
|
||||
/* Configure display */
|
||||
auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
|
||||
auxdisplay_hd44780_set_display_mode(dev, true);
|
||||
auxdisplay_hd44780_set_entry_mode(dev);
|
||||
auxdisplay_hd44780_command(dev, false, AUXDISPLAY_HD44780_CMD_CLEAR,
|
||||
config->capabilities.mode);
|
||||
|
||||
k_sleep(K_USEC(config->clear_delay));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auxdisplay_hd44780_capabilities_get(const struct device *dev,
|
||||
struct auxdisplay_capabilities *capabilities)
|
||||
{
|
||||
const struct auxdisplay_hd44780_config *config = dev->config;
|
||||
|
||||
memcpy(capabilities, &config->capabilities, sizeof(struct auxdisplay_capabilities));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auxdisplay_hd44780_clear(const struct device *dev)
|
||||
{
|
||||
const struct auxdisplay_hd44780_config *config = dev->config;
|
||||
struct auxdisplay_hd44780_data *data = dev->data;
|
||||
|
||||
auxdisplay_hd44780_command(dev, false, AUXDISPLAY_HD44780_CMD_CLEAR,
|
||||
config->capabilities.mode);
|
||||
|
||||
data->character_x = 0;
|
||||
data->character_y = 0;
|
||||
|
||||
k_sleep(K_USEC(config->clear_delay));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void auxdisplay_hd44780_set_entry_mode(const struct device *dev)
|
||||
{
|
||||
const struct auxdisplay_hd44780_config *config = dev->config;
|
||||
struct auxdisplay_hd44780_data *data = dev->data;
|
||||
uint8_t cmd = AUXDISPLAY_HD44780_CMD_ENTRY_MODE;
|
||||
|
||||
if (data->direction == AUXDISPLAY_DIRECTION_RIGHT) {
|
||||
cmd |= AUXDISPLAY_HD44780_CURSOR_MOVE_RIGHT;
|
||||
}
|
||||
|
||||
if (data->display_shift) {
|
||||
cmd |= AUXDISPLAY_HD44780_DISPLAY_SHIFT;
|
||||
}
|
||||
|
||||
auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
|
||||
}
|
||||
|
||||
static void auxdisplay_hd44780_set_display_mode(const struct device *dev, bool enabled)
|
||||
{
|
||||
const struct auxdisplay_hd44780_config *config = dev->config;
|
||||
struct auxdisplay_hd44780_data *data = dev->data;
|
||||
uint8_t cmd = AUXDISPLAY_HD44780_CMD_DISPLAY_MODE;
|
||||
|
||||
if (data->cursor_enabled) {
|
||||
cmd |= AUXDISPLAY_HD44780_CURSOR_ENABLED;
|
||||
}
|
||||
|
||||
if (data->position_blink_enabled) {
|
||||
cmd |= AUXDISPLAY_HD44780_POSITION_BLINK_ENABLED;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
cmd |= AUXDISPLAY_HD44780_DISPLAY_ENABLED;
|
||||
}
|
||||
|
||||
auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
|
||||
}
|
||||
|
||||
static int auxdisplay_hd44780_display_on(const struct device *dev)
|
||||
{
|
||||
auxdisplay_hd44780_set_display_mode(dev, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auxdisplay_hd44780_display_off(const struct device *dev)
|
||||
{
|
||||
auxdisplay_hd44780_set_display_mode(dev, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auxdisplay_hd44780_cursor_set_enabled(const struct device *dev, bool enabled)
|
||||
{
|
||||
struct auxdisplay_hd44780_data *data = dev->data;
|
||||
|
||||
data->cursor_enabled = enabled;
|
||||
|
||||
auxdisplay_hd44780_set_display_mode(dev, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auxdisplay_hd44780_position_blinking_set_enabled(const struct device *dev, bool enabled)
|
||||
{
|
||||
struct auxdisplay_hd44780_data *data = dev->data;
|
||||
|
||||
data->position_blink_enabled = enabled;
|
||||
|
||||
auxdisplay_hd44780_set_display_mode(dev, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auxdisplay_hd44780_cursor_shift_set(const struct device *dev, uint8_t direction,
|
||||
bool display_shift)
|
||||
{
|
||||
struct auxdisplay_hd44780_data *data = dev->data;
|
||||
|
||||
if (display_shift) {
|
||||
/* Not currently supported */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->direction = direction;
|
||||
data->display_shift = (display_shift ? true : false);
|
||||
|
||||
auxdisplay_hd44780_set_entry_mode(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auxdisplay_hd44780_cursor_position_set(const struct device *dev,
|
||||
enum auxdisplay_position type, int16_t x,
|
||||
int16_t y)
|
||||
{
|
||||
const struct auxdisplay_hd44780_config *config = dev->config;
|
||||
struct auxdisplay_hd44780_data *data = dev->data;
|
||||
uint8_t cmd = AUXDISPLAY_HD44780_CMD_POSITION_SET;
|
||||
|
||||
if (type == AUXDISPLAY_POSITION_RELATIVE) {
|
||||
x += (int16_t)data->character_x;
|
||||
y += (int16_t)data->character_y;
|
||||
} else if (type == AUXDISPLAY_POSITION_RELATIVE_DIRECTION) {
|
||||
if (data->direction == AUXDISPLAY_DIRECTION_RIGHT) {
|
||||
x += (int16_t)data->character_x;
|
||||
y += (int16_t)data->character_y;
|
||||
} else {
|
||||
x -= (int16_t)data->character_x;
|
||||
y -= (int16_t)data->character_y;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check position is valid before applying */
|
||||
if (x < 0 || y < 0) {
|
||||
return -EINVAL;
|
||||
} else if (x >= config->capabilities.columns || y >= config->capabilities.rows) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->character_x = (uint16_t)x;
|
||||
data->character_y = (uint16_t)y;
|
||||
cmd |= config->line_addresses[data->character_y] + data->character_x;
|
||||
|
||||
auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auxdisplay_hd44780_cursor_position_get(const struct device *dev, int16_t *x, int16_t *y)
|
||||
{
|
||||
struct auxdisplay_hd44780_data *data = dev->data;
|
||||
|
||||
*x = (int16_t)data->character_x;
|
||||
*y = (int16_t)data->character_y;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auxdisplay_hd44780_backlight_get(const struct device *dev, uint8_t *backlight)
|
||||
{
|
||||
const struct auxdisplay_hd44780_config *config = dev->config;
|
||||
struct auxdisplay_hd44780_data *data = dev->data;
|
||||
|
||||
if (!config->backlight_gpio.port) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
*backlight = (data->backlight_state == true ? 1 : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auxdisplay_hd44780_backlight_set(const struct device *dev, uint8_t backlight)
|
||||
{
|
||||
const struct auxdisplay_hd44780_config *config = dev->config;
|
||||
struct auxdisplay_hd44780_data *data = dev->data;
|
||||
|
||||
if (!config->backlight_gpio.port) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
data->backlight_state = (bool)backlight;
|
||||
|
||||
gpio_pin_set_dt(&config->backlight_gpio, (uint8_t)data->backlight_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auxdisplay_hd44780_custom_character_set(const struct device *dev,
|
||||
struct auxdisplay_character *character)
|
||||
{
|
||||
const struct auxdisplay_hd44780_config *config = dev->config;
|
||||
struct auxdisplay_hd44780_data *data = dev->data;
|
||||
uint8_t i = 0;
|
||||
uint8_t cmd = AUXDISPLAY_HD44780_CMD_CGRAM_SET | (character->index << 3);
|
||||
|
||||
auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
|
||||
|
||||
/* HD44780 accepts 5x8 font but needs 8x8 data to be sent, mask off top 3 bits
|
||||
* for each line sent
|
||||
*/
|
||||
while (i < 8) {
|
||||
uint8_t l = 0;
|
||||
|
||||
cmd = 0;
|
||||
|
||||
while (l < 5) {
|
||||
if (character->data[(i * 5) + (4 - l)]) {
|
||||
cmd |= BIT(l);
|
||||
}
|
||||
|
||||
++l;
|
||||
}
|
||||
|
||||
auxdisplay_hd44780_command(dev, true, cmd, config->capabilities.mode);
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
character->character_code = character->index;
|
||||
|
||||
/* Send last known address to switch back to DDRAM entry mode */
|
||||
cmd = AUXDISPLAY_HD44780_CMD_POSITION_SET |
|
||||
(config->line_addresses[data->character_y] +
|
||||
data->character_x);
|
||||
|
||||
auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auxdisplay_hd44780_write(const struct device *dev, const uint8_t *text, uint16_t len)
|
||||
{
|
||||
const struct auxdisplay_hd44780_config *config = dev->config;
|
||||
struct auxdisplay_hd44780_data *data = dev->data;
|
||||
uint16_t i = 0;
|
||||
|
||||
while (i < len) {
|
||||
auxdisplay_hd44780_command(dev, true, text[i], config->capabilities.mode);
|
||||
++i;
|
||||
|
||||
if (data->direction == AUXDISPLAY_DIRECTION_RIGHT) {
|
||||
/* Increment */
|
||||
++data->character_x;
|
||||
|
||||
if (data->character_x == config->capabilities.columns) {
|
||||
data->character_x = 0;
|
||||
++data->character_y;
|
||||
|
||||
if (data->character_y == config->capabilities.rows) {
|
||||
data->character_y = 0;
|
||||
}
|
||||
|
||||
/* Send command to set position */
|
||||
uint8_t cmd = AUXDISPLAY_HD44780_CMD_POSITION_SET |
|
||||
config->line_addresses[data->character_y];
|
||||
auxdisplay_hd44780_command(dev, false, cmd,
|
||||
config->capabilities.mode);
|
||||
}
|
||||
} else {
|
||||
/* Decrement */
|
||||
if (data->character_x == 0) {
|
||||
data->character_x = config->capabilities.columns - 1;
|
||||
|
||||
if (data->character_y == 0) {
|
||||
data->character_y = config->capabilities.rows - 1;
|
||||
} else {
|
||||
--data->character_y;
|
||||
}
|
||||
|
||||
/* Send command to set position */
|
||||
uint8_t cmd = AUXDISPLAY_HD44780_CMD_POSITION_SET |
|
||||
(config->line_addresses[data->character_y] +
|
||||
data->character_x);
|
||||
auxdisplay_hd44780_command(dev, false, cmd,
|
||||
config->capabilities.mode);
|
||||
} else {
|
||||
--data->character_x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct auxdisplay_driver_api auxdisplay_hd44780_auxdisplay_api = {
|
||||
.display_on = auxdisplay_hd44780_display_on,
|
||||
.display_off = auxdisplay_hd44780_display_off,
|
||||
.cursor_set_enabled = auxdisplay_hd44780_cursor_set_enabled,
|
||||
.position_blinking_set_enabled = auxdisplay_hd44780_position_blinking_set_enabled,
|
||||
.cursor_shift_set = auxdisplay_hd44780_cursor_shift_set,
|
||||
.cursor_position_set = auxdisplay_hd44780_cursor_position_set,
|
||||
.cursor_position_get = auxdisplay_hd44780_cursor_position_get,
|
||||
.capabilities_get = auxdisplay_hd44780_capabilities_get,
|
||||
.clear = auxdisplay_hd44780_clear,
|
||||
.backlight_get = auxdisplay_hd44780_backlight_get,
|
||||
.backlight_set = auxdisplay_hd44780_backlight_set,
|
||||
.custom_character_set = auxdisplay_hd44780_custom_character_set,
|
||||
.write = auxdisplay_hd44780_write,
|
||||
};
|
||||
|
||||
/* Returns desired value if backlight is enabled, otherwise returns not supported value */
|
||||
#define BACKLIGHT_CHECK(inst, value) \
|
||||
COND_CODE_1(DT_PROP_HAS_IDX(DT_DRV_INST(inst), backlight_gpios, 0), (value), \
|
||||
(AUXDISPLAY_LIGHT_NOT_SUPPORTED))
|
||||
|
||||
#define AUXDISPLAY_HD44780_DEVICE(inst) \
|
||||
static struct auxdisplay_hd44780_data auxdisplay_hd44780_data_##inst; \
|
||||
static const struct auxdisplay_hd44780_config auxdisplay_hd44780_config_##inst = { \
|
||||
.capabilities = { \
|
||||
.columns = DT_INST_PROP(inst, columns), \
|
||||
.rows = DT_INST_PROP(inst, rows), \
|
||||
.mode = DT_INST_ENUM_IDX(inst, mode), \
|
||||
.brightness.minimum = AUXDISPLAY_LIGHT_NOT_SUPPORTED, \
|
||||
.brightness.maximum = AUXDISPLAY_LIGHT_NOT_SUPPORTED, \
|
||||
.backlight.minimum = BACKLIGHT_CHECK(inst, \
|
||||
AUXDISPLAY_HD44780_BACKLIGHT_MIN), \
|
||||
.backlight.maximum = BACKLIGHT_CHECK(inst, \
|
||||
AUXDISPLAY_HD44780_BACKLIGHT_MAX), \
|
||||
.custom_characters = AUXDISPLAY_HD44780_CUSTOM_CHARACTERS, \
|
||||
.custom_character_width = AUXDISPLAY_HD44780_CUSTOM_CHARACTER_WIDTH, \
|
||||
.custom_character_height = AUXDISPLAY_HD44780_CUSTOM_CHARACTER_HEIGHT, \
|
||||
}, \
|
||||
.rs_gpio = GPIO_DT_SPEC_INST_GET(inst, register_select_gpios), \
|
||||
.rw_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, read_write_gpios, {0}), \
|
||||
.e_gpio = GPIO_DT_SPEC_INST_GET(inst, enable_gpios), \
|
||||
.db_gpios[0] = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, data_bus_gpios, 0, {0}), \
|
||||
.db_gpios[1] = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, data_bus_gpios, 1, {0}), \
|
||||
.db_gpios[2] = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, data_bus_gpios, 2, {0}), \
|
||||
.db_gpios[3] = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, data_bus_gpios, 3, {0}), \
|
||||
.db_gpios[4] = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, data_bus_gpios, 4), \
|
||||
.db_gpios[5] = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, data_bus_gpios, 5), \
|
||||
.db_gpios[6] = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, data_bus_gpios, 6), \
|
||||
.db_gpios[7] = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, data_bus_gpios, 7), \
|
||||
.line_addresses[0] = DT_INST_PROP_BY_IDX(inst, line_addresses, 0), \
|
||||
.line_addresses[1] = DT_INST_PROP_BY_IDX(inst, line_addresses, 1), \
|
||||
.line_addresses[2] = DT_INST_PROP_BY_IDX(inst, line_addresses, 2), \
|
||||
.line_addresses[3] = DT_INST_PROP_BY_IDX(inst, line_addresses, 3), \
|
||||
.backlight_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, backlight_gpios, {0}), \
|
||||
.enable_line_rise_delay = DT_INST_PROP(inst, enable_line_rise_delay_us), \
|
||||
.enable_line_fall_delay = DT_INST_PROP(inst, enable_line_fall_delay_us), \
|
||||
.clear_delay = DT_INST_PROP(inst, clear_command_delay_us), \
|
||||
.boot_delay = DT_INST_PROP(inst, boot_delay_ms), \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(inst, \
|
||||
&auxdisplay_hd44780_init, \
|
||||
NULL, \
|
||||
&auxdisplay_hd44780_data_##inst, \
|
||||
&auxdisplay_hd44780_config_##inst, \
|
||||
POST_KERNEL, \
|
||||
CONFIG_AUXDISPLAY_INIT_PRIORITY, \
|
||||
&auxdisplay_hd44780_auxdisplay_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(AUXDISPLAY_HD44780_DEVICE)
|
88
dts/bindings/auxdisplay/hit,hd44780.yaml
Normal file
88
dts/bindings/auxdisplay/hit,hd44780.yaml
Normal file
|
@ -0,0 +1,88 @@
|
|||
#
|
||||
# Copyright (c) 2023 Jamie McCrae
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
description: Hitachi HD44780 LCD
|
||||
|
||||
compatible: "hit,hd44780"
|
||||
|
||||
include: [auxdisplay-device.yaml]
|
||||
|
||||
properties:
|
||||
mode:
|
||||
type: int
|
||||
required: true
|
||||
description: Operating mode of display, 8-bit or 4 for 4-bit mode
|
||||
enum:
|
||||
- 4
|
||||
- 8
|
||||
|
||||
register-select-gpios:
|
||||
type: phandle-array
|
||||
required: true
|
||||
description: GPIO connected to Register Select (RS) of LCD
|
||||
|
||||
read-write-gpios:
|
||||
type: phandle-array
|
||||
description: Optional GPIO used for selecting read or write mode
|
||||
|
||||
enable-gpios:
|
||||
type: phandle-array
|
||||
required: true
|
||||
description: GPIO used for enabling commands to be sent
|
||||
|
||||
data-bus-gpios:
|
||||
type: phandle-array
|
||||
required: true
|
||||
description: |
|
||||
Array of GPIOs connected to the DB lines of the display, this must
|
||||
contain 8 entries ascending from DB0 to DB7, for 4-bit interface
|
||||
displays, the first 4 must be set as `<0>`
|
||||
|
||||
brightness-gpios:
|
||||
type: phandle-array
|
||||
description: Optional GPIO used for controlling the brightness (contrast)
|
||||
|
||||
backlight-gpios:
|
||||
type: phandle-array
|
||||
description: Optional GPIO used for enabling the backlight
|
||||
|
||||
line-addresses:
|
||||
type: uint8-array
|
||||
default: [0x00, 0x40, 0x14, 0x54]
|
||||
description: |
|
||||
Array of addresses for each row, will use defaults if not provided.
|
||||
Default is as per Hitachi HD44780 specification.
|
||||
|
||||
enable-line-rise-delay-us:
|
||||
type: int
|
||||
default: 800
|
||||
description: |
|
||||
Delay time (in us) to wait after enable line rises before setting low.
|
||||
Default is as per Hitachi HD44780 specification.
|
||||
|
||||
enable-line-fall-delay-us:
|
||||
type: int
|
||||
default: 100
|
||||
description: |
|
||||
Delay time (in us) to wait after enable line falls before sending
|
||||
another command. Default is as per Hitachi HD44780 specification.
|
||||
|
||||
clear-command-delay-us:
|
||||
type: int
|
||||
default: 5000
|
||||
description: |
|
||||
Delay time (in us) to wait after issuing a clear command before sending
|
||||
another command. Default is as per Hitachi HD44780 specification.
|
||||
|
||||
boot-delay-ms:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Delay time (in ms) to wait at boot time before sending a command (note:
|
||||
this will delay startup of the whole application by this time, this
|
||||
should only be used when time is needed for the display device to be
|
||||
ready before it can be configured which without any delay would cause
|
||||
the display to not function properly).
|
Loading…
Reference in a new issue