diff --git a/drivers/hd44780/Makefile b/drivers/hd44780/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/hd44780/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/hd44780/hd44780.c b/drivers/hd44780/hd44780.c new file mode 100644 index 0000000000..8b8331120d --- /dev/null +++ b/drivers/hd44780/hd44780.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2017 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_hd44780 + * + * @{ + * @file + * @brief Driver for the HD44780 LCD + * + * @note The display is also known as LCM1602C from Arduino kits + * + * @author Sebastian Meiling + */ + +#include +#include + +#include "log.h" +#include "periph/gpio.h" +#include "xtimer.h" + +#include "hd44780.h" +#include "hd44780_internal.h" + +static inline void _command(hd44780_t *dev, uint8_t value); +static void _pulse(hd44780_t *dev); +static void _send(hd44780_t *dev, uint8_t value, uint8_t mode); +static void _write_bits(hd44780_t *dev, uint8_t bits, uint8_t value); + +/** + * @brief Send a command to the display + * + * @param[in] dev device descriptor of display to initialize + * @param[in] value the command + */ +static inline void _command(hd44780_t *dev, uint8_t value) +{ + _send(dev, value, HD44780_OFF); +} + +/** + * @brief Send a pulse to the display to enable new state + * + * @param[in] dev device descriptor of display to initialize + */ +static void _pulse(hd44780_t *dev) +{ + gpio_clear(dev->p.enable); + xtimer_usleep(HD44780_PULSE_WAIT_SHORT); + gpio_set(dev->p.enable); + xtimer_usleep(HD44780_PULSE_WAIT_SHORT); + gpio_clear(dev->p.enable); + xtimer_usleep(HD44780_PULSE_WAIT_LONG); +} + +/** + * @brief Send a data value to the display + * + * @param[in] dev device descriptor of display to initialize + * @param[in] value the data value, either char or command + * @param[in] state send state, to distinguish chars and commands + */ +static void _send(hd44780_t *dev, uint8_t value, hd44780_state_t state) +{ + (state == HD44780_ON) ? gpio_set(dev->p.rs) : gpio_clear(dev->p.rs); + /* if RW pin is available, set it to LOW */ + if (dev->p.rw != HD44780_RW_OFF) { + gpio_clear(dev->p.rw); + } + /* write data in 8Bit or 4Bit mode */ + if (dev->flag & HD44780_8BITMODE) { + _write_bits(dev, 8, value); + } + else { + _write_bits(dev, 4, value>>4); + _write_bits(dev, 4, value); + } +} + +static void _write_bits(hd44780_t *dev, uint8_t bits, uint8_t value) +{ + for (unsigned i = 0; i < bits; ++i) { + if ((value >> i) & 0x01) { + gpio_set(dev->p.data[i]); + } + else { + gpio_clear(dev->p.data[i]); + } + } + _pulse(dev); +} + +int hd44780_init(hd44780_t *dev, const hd44780_params_t *params) +{ + /* write config params to device descriptor */ + memcpy(&dev->p, params, sizeof(hd44780_params_t)); + /* verify cols and rows */ + if ((dev->p.cols > HD44780_MAX_COLS) || (dev->p.rows > HD44780_MAX_ROWS) + || (dev->p.rows * dev->p.cols > 80)) { + LOG_ERROR("hd44780_init: invalid LCD size!\n"); + return -1; + } + uint8_t count_pins = 0; + /* check which pins are used */ + for (unsigned i = 0; i < HD44780_MAX_PINS; ++i) { + if (dev->p.data[i] != HD44780_RW_OFF) { + ++count_pins; + } + } + /* set mode depending on configured pins */ + if (count_pins < HD44780_MAX_PINS) { + dev->flag |= HD44780_4BITMODE; + } + else { + dev->flag |= HD44780_8BITMODE; + } + /* set flag for 1 or 2 row mode, 4 rows are 2 rows split half */ + if (dev->p.rows > 1) { + dev->flag |= HD44780_2LINE; + } + else { + dev->flag |= HD44780_1LINE; + } + /* set char size to 5x8 as default, 5x10 is hardly used by LCDs anyway */ + dev->flag |= HD44780_5x8DOTS; + /* calc and set row offsets, depending on number of columns */ + dev->roff[0] = 0x00; + dev->roff[1] = 0x40; + dev->roff[2] = 0x00 + dev->p.cols; + dev->roff[3] = 0x40 + dev->p.cols; + + gpio_init(dev->p.rs, GPIO_OUT); + /* RW (read/write) of LCD not required, set it to HD44780_RW_OFF (255) */ + if (dev->p.rw != HD44780_RW_OFF) { + gpio_init(dev->p.rw, GPIO_OUT); + } + gpio_init(dev->p.enable, GPIO_OUT); + /* configure all data pins as output */ + for (int i = 0; i < ((dev->flag & HD44780_8BITMODE) ? 8 : 4); ++i) { + gpio_init(dev->p.data[i], GPIO_OUT); + } + /* see hitachi HD44780 datasheet pages 45/46 for init specs */ + xtimer_usleep(HD44780_INIT_WAIT_XXL); + gpio_clear(dev->p.rs); + gpio_clear(dev->p.enable); + if (dev->p.rw != HD44780_RW_OFF) { + gpio_clear(dev->p.rw); + } + /* put the LCD into 4 bit or 8 bit mode */ + if (!(dev->flag & HD44780_8BITMODE)) { + /* see hitachi HD44780 datasheet figure 24, pg 46 */ + _write_bits(dev, 4, 0x03); + xtimer_usleep(HD44780_INIT_WAIT_LONG); + + _write_bits(dev, 4, 0x03); + xtimer_usleep(HD44780_INIT_WAIT_LONG); + + _write_bits(dev, 4, 0x03); + xtimer_usleep(HD44780_INIT_WAIT_SHORT); + + _write_bits(dev, 4, 0x02); + } else { + /* see hitachi HD44780 datasheet page 45 figure 23 */ + _command(dev, HD44780_FUNCTIONSET | dev->flag); + xtimer_usleep(HD44780_INIT_WAIT_LONG); // wait more than 4.1ms + + _command(dev, HD44780_FUNCTIONSET | dev->flag); + xtimer_usleep(HD44780_INIT_WAIT_SHORT); + + _command(dev, HD44780_FUNCTIONSET | dev->flag); + } + _command(dev, HD44780_FUNCTIONSET | dev->flag); + + /* turn the display on with no cursor or blinking default, and clear */ + dev->ctrl = HD44780_DISPLAYON | HD44780_CURSOROFF | HD44780_BLINKOFF; + hd44780_display(dev, HD44780_ON); + hd44780_clear(dev); + /* Initialize to default text direction for western languages */ + dev->mode = HD44780_ENTRYLEFT | HD44780_ENTRYSHIFTDECREMENT; + _command(dev, HD44780_ENTRYMODESET | dev->mode); + + return 0; +} + +void hd44780_clear(hd44780_t *dev) +{ + _command(dev, HD44780_CLEARDISPLAY); + xtimer_usleep(HD44780_CMD_WAIT); +} + +void hd44780_home(hd44780_t *dev) +{ + _command(dev, HD44780_RETURNHOME); + xtimer_usleep(HD44780_CMD_WAIT); +} + +void hd44780_set_cursor(hd44780_t *dev, uint8_t col, uint8_t row) +{ + if (row >= dev->p.rows) { + row = dev->p.rows - 1; + } + _command(dev, HD44780_SETDDRAMADDR | (col + dev->roff[row])); +} + +void hd44780_display(hd44780_t *dev, hd44780_state_t state) +{ + if (state == HD44780_ON) { + dev->ctrl |= HD44780_DISPLAYON; + } + else { + dev->ctrl &= ~HD44780_DISPLAYON; + } + _command(dev, HD44780_DISPLAYCONTROL | dev->ctrl); +} + +void hd44780_cursor(hd44780_t *dev, hd44780_state_t state) +{ + if (state == HD44780_ON) { + dev->ctrl |= HD44780_CURSORON; + } + else { + dev->ctrl &= ~HD44780_CURSORON; + } + _command(dev, HD44780_DISPLAYCONTROL | dev->ctrl); +} + +void hd44780_blink(hd44780_t *dev, hd44780_state_t state) +{ + if (state == HD44780_ON) { + dev->ctrl |= HD44780_BLINKON; + } + else { + dev->ctrl &= ~HD44780_BLINKON; + } + _command(dev, HD44780_DISPLAYCONTROL | dev->ctrl); +} + +void hd44780_scroll_left(hd44780_t *dev) +{ + _command(dev, HD44780_CURSORSHIFT | HD44780_DISPLAYMOVE | HD44780_MOVELEFT); +} + +void hd44780_scroll_right(hd44780_t *dev) { + _command(dev, HD44780_CURSORSHIFT | HD44780_DISPLAYMOVE | HD44780_MOVERIGHT); +} + +void hd44780_left2right(hd44780_t *dev) +{ + dev->mode |= HD44780_ENTRYLEFT; + _command(dev, HD44780_ENTRYMODESET | dev->mode); +} + +void hd44780_right2left(hd44780_t *dev) +{ + dev->mode &= ~HD44780_ENTRYLEFT; + _command(dev, HD44780_ENTRYMODESET | dev->mode); +} + +void hd44780_autoscroll(hd44780_t *dev, hd44780_state_t state) +{ + if (state == HD44780_ON) { + dev->mode |= HD44780_ENTRYSHIFTINCREMENT; + } + else { + dev->mode &= ~HD44780_ENTRYSHIFTINCREMENT; + } + _command(dev, HD44780_ENTRYMODESET | dev->mode); +} + +void hd44780_create_char(hd44780_t *dev, uint8_t location, uint8_t charmap[]) +{ + location &= 0x7; /* 8 locations (0-7) possible */ + _command(dev, HD44780_SETCGRAMADDR | (location << 3)); + for (unsigned i = 0; i < HD44780_CGRAM_SIZE; ++i) { + hd44780_write(dev, charmap[i]); + } +} + +void hd44780_write(hd44780_t *dev, uint8_t value) +{ + _send(dev, value, HD44780_ON); +} + +void hd44780_print(hd44780_t *dev, const char *data ) +{ + while (*data != '\0') { + hd44780_write(dev, *data++); + } +} diff --git a/drivers/hd44780/include/hd44780_internal.h b/drivers/hd44780/include/hd44780_internal.h new file mode 100644 index 0000000000..2a4de1541c --- /dev/null +++ b/drivers/hd44780/include/hd44780_internal.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2017 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_hd44780 + * + * @{ + * @file + * @brief Internal config and parameters for the HD44780 display + * + * @author Sebastian Meiling + */ + +#ifndef HD44780_INTERNAL_H +#define HD44780_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief HD44780 LCD commands + * @{ + */ +#define HD44780_CLEARDISPLAY (0x01) +#define HD44780_RETURNHOME (0x02) +#define HD44780_ENTRYMODESET (0x04) +#define HD44780_DISPLAYCONTROL (0x08) +#define HD44780_CURSORSHIFT (0x10) +#define HD44780_FUNCTIONSET (0x20) +#define HD44780_SETCGRAMADDR (0x40) +#define HD44780_SETDDRAMADDR (0x80) +/**@}*/ + +/** + * @brief HD44780 LCD entry modes flags + * @{ + */ +#define HD44780_ENTRYRIGHT (0x00) +#define HD44780_ENTRYLEFT (0x02) +#define HD44780_ENTRYSHIFTINCREMENT (0x01) +#define HD44780_ENTRYSHIFTDECREMENT (0x00) +/**@}*/ + +/** + * @brief HD44780 LCD control flags + * @{ + */ +#define HD44780_DISPLAYON (0x04) +#define HD44780_DISPLAYOFF (0x00) +#define HD44780_CURSORON (0x02) +#define HD44780_CURSOROFF (0x00) +#define HD44780_BLINKON (0x01) +#define HD44780_BLINKOFF (0x00) +/**@}*/ + +/** + * @brief HD44780 display and cursor shift flags + * @{ + */ +#define HD44780_DISPLAYMOVE (0x08) +#define HD44780_CURSORMOVE (0x00) +#define HD44780_MOVERIGHT (0x04) +#define HD44780_MOVELEFT (0x00) +/**@}*/ + +/** + * @brief HD44780 LCD functional flags + * @{ + */ +#define HD44780_8BITMODE (0x10) +#define HD44780_4BITMODE (0x00) +#define HD44780_2LINE (0x08) +#define HD44780_1LINE (0x00) +#define HD44780_5x10DOTS (0x04) +#define HD44780_5x8DOTS (0x00) +/**@}*/ + +/** + * @brief HD44780 LCD timings + * @{ + */ +#define HD44780_CMD_WAIT (2000U) +#define HD44780_INIT_WAIT_XXL (50000U) +#define HD44780_INIT_WAIT_LONG (4500U) +#define HD44780_INIT_WAIT_SHORT (150U) +#define HD44780_PULSE_WAIT_SHORT (1U) +#define HD44780_PULSE_WAIT_LONG (100U) +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* HD44780_INTERNAL_H */ diff --git a/drivers/include/hd44780.h b/drivers/include/hd44780.h new file mode 100644 index 0000000000..f2286d8038 --- /dev/null +++ b/drivers/include/hd44780.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2017 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + + /** + * @defgroup drivers_hd44780 HD44780 LCD driver + * @ingroup drivers_displays + * @brief Driver for the Hitachi HD44780 LCD driver + * + * @note The driver currently supports direct addressing, no I2C + * + * @{ + * + * @file + * @brief Interface definition for the HD44780 LCD driver + * + * @author Sebastian Meiling + */ +#ifndef HD44780_H +#define HD44780_H + +#include + +#include "periph/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Maximal number of columns supported by the driver + */ +#define HD44780_MAX_COLS (40U) + +/** + * @brief Maximal number of rows supported by the driver + */ +#define HD44780_MAX_ROWS (4U) + +/** + * @brief Number of data pins for communication 4 or 8. + */ +#define HD44780_MAX_PINS (8U) + +/** + * @brief Specific value to turn rw pin off, if unused. + */ +#define HD44780_RW_OFF (255U) + +/** + * @brief Size of RAM for custom chars + * + * Generally the driver could support 8 chars of size 5x8 or 4 of size 5x10. + * However, most displays only use the former, which is (hard wired) default. + */ +#define HD44780_CGRAM_SIZE (8U) + +/** + * @brief Parameters needed for device initialization + */ +typedef struct { + uint8_t cols; /**< number of LCD cols */ + uint8_t rows; /**< number of LCD rows */ + gpio_t rs; /**< rs gpio pin */ + gpio_t rw; /**< rw gpio pin */ + gpio_t enable; /**< enable gpio pin */ + gpio_t data[8]; /**< data gpio pins */ +} hd44780_params_t; + +/** + * @brief Device descriptor for HD44780 LCD + */ +typedef struct { + hd44780_params_t p; /**< LCD config parameters */ + uint8_t flag; /**< LCD functional flags */ + uint8_t ctrl; /**< LCD control flags */ + uint8_t mode; /**< LCD mode flags */ + uint8_t roff[HD44780_MAX_ROWS]; /**< offsets for LCD rows */ +} hd44780_t; + +/** + * @brief Simple state values + */ +typedef enum { + HD44780_OFF, /**< disable feature */ + HD44780_ON /**< enable feature */ +} hd44780_state_t; + +/** + * @brief Initialize the given driver + * + * @param[out] dev device descriptor of display to initialize + * @param[in] params configuration parameters + * + * @return 0 on success, otherwise error + */ +int hd44780_init(hd44780_t *dev, const hd44780_params_t *params); + +/** + * @brief Clear display, delete all chars + * + * @param[in] dev device descriptor of LCD + */ +void hd44780_clear(hd44780_t *dev); + +/** + * @brief Reset cursor to row 0 and column 0 + * + * @param[in] dev device descriptor of LCD + */ +void hd44780_home(hd44780_t *dev); + +/** + * @brief Set cursor to specific position in column and row + * + * @param[in] dev device descriptor of LCD + * @param[in] col column position + * @param[in] row row position + */ +void hd44780_set_cursor(hd44780_t *dev, uint8_t col, uint8_t row); + +/** + * @brief Turn display on or off + * + * @param[in] dev device descriptor of LCD + * @param[in] state display on or off + */ +void hd44780_display(hd44780_t *dev, hd44780_state_t state); + +/** + * @brief Show cursor, on or off + * + * @param[in] dev device descriptor of LCD + * @param[in] state cursor on or off + */ +void hd44780_cursor(hd44780_t *dev, hd44780_state_t state); + +/** + * @brief Blink cursor, on or off + * + * @param[in] dev device descriptor of LCD + * @param[in] state blink on or off + */ +void hd44780_blink(hd44780_t *dev, hd44780_state_t state); + +/** + * @brief Enable left scrolling + * + * @param[in] dev device descriptor of LCD + */ +void hd44780_scroll_left(hd44780_t *dev); + +/** + * @brief Enable right scrolling + * + * @param[in] dev device descriptor of LCD + */ +void hd44780_scroll_right(hd44780_t *dev); + +/** + * @brief set display direction left to right + * + * @param[in] dev device descriptor of LCD + */ +void hd44780_left2right(hd44780_t *dev); + +/** + * @brief set display direction right to left + * + * @param[in] dev device descriptor of LCD + */ +void hd44780_right2left(hd44780_t *dev); + +/** + * @brief display autoscroll on or off + * + * @param[in] dev device descriptor of LCD + * @param[in] state scroll on or off + */ +void hd44780_autoscroll(hd44780_t *dev, hd44780_state_t state); + +/** + * @brief Create and store a custom character on display memory + * + * @param[in] dev device descriptor of LCD + * @param[in] location memory location + * @param[in] charmap character bitmap + * + * @pre charmap has to be of size HD44780_CGRAM_SIZE, which is 8 by default + */ +void hd44780_create_char(hd44780_t *dev, uint8_t location, uint8_t charmap[]); + +/** + * @brief Write a single character on the LCD + * + * @param[in] dev device descriptor of LCD + * @param[in] value the character to print, i.e., memory location + */ +void hd44780_write(hd44780_t *dev, uint8_t value); + +/** + * @brief Write a string on the LCD + * + * @param[in] dev device descriptor of LCD + * @param[in] data the string to print + */ +void hd44780_print(hd44780_t *dev, const char *data); + +#ifdef __cplusplus +} +#endif + +#endif /* HD44780_H */ +/** @} */