/* * Copyright (C) 2020 Freie Universität Berlin * * 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_aip31068 * @brief Device driver for AIP31068 * @author Hendrik van Essen * @file * @{ */ #include #include #include "xtimer.h" #include "aip31068.h" #include "aip31068_regs.h" #include "aip31068_internal.h" #define ENABLE_DEBUG 0 #include "debug.h" /** * @brief Write a data byte to the device. * * @param[in] dev Device descriptor of the AIP31068 * @param[in] value Data byte to write * * @return 0 on success * @return -1 if acquiring of I2C bus fails * @return -EIO When slave device doesn't ACK the byte * @return -ENXIO When no devices respond on the address sent on the bus * @return -ETIMEDOUT When timeout occurs before device's response * @return -EINVAL When an invalid argument is given * @return -EOPNOTSUPP When MCU driver doesn't support the flag operation * @return -EAGAIN When a lost bus arbitration occurs */ static inline int _data(aip31068_t *dev, uint8_t value); /** * @brief Write a command byte with it's arguments (modified bits) to the device. * * @param[in] dev Device descriptor of the AIP31068 * @param[in] value Command byte to write * * @return 0 on success * @return -1 if acquiring of I2C bus fails * @return -EIO When slave device doesn't ACK the byte * @return -ENXIO When no devices respond on the address sent on the bus * @return -ETIMEDOUT When timeout occurs before device's response * @return -EINVAL When an invalid argument is given * @return -EOPNOTSUPP When MCU driver doesn't support the flag operation * @return -EAGAIN When a lost bus arbitration occurs */ static inline int _command(aip31068_t *dev, uint8_t value); /** * @brief Write a command or data byte to the device. * * @param[in] dev Device descriptor of the AIP31068 * @param[in] data_byte Byte to write * @param[in] is_cmd Whether byte should be interpreted as data or command * * @return 0 on success * @return -1 if acquiring of I2C bus fails * @return -EIO When slave device doesn't ACK the byte * @return -ENXIO When no devices respond on the address sent on the bus * @return -ETIMEDOUT When timeout occurs before device's response * @return -EINVAL When an invalid argument is given * @return -EOPNOTSUPP When MCU driver doesn't support the flag operation * @return -EAGAIN When a lost bus arbitration occurs */ static inline int _write(aip31068_t *dev, uint8_t data_byte, bool is_cmd); /** * @brief Write data to the device. * * @param[in] dev Device descriptor of the AIP31068 * @param[in] data Data to write * @param[in] len Length of the data * * @return 0 on success * @return -1 if acquiring of I2C bus fails * @return -EIO When slave device doesn't ACK the byte * @return -ENXIO When no devices respond on the address sent on the bus * @return -ETIMEDOUT When timeout occurs before device's response * @return -EINVAL When an invalid argument is given * @return -EOPNOTSUPP When MCU driver doesn't support the flag operation * @return -EAGAIN When a lost bus arbitration occurs */ static int _device_write(aip31068_t *dev, uint8_t *data, uint8_t len); int aip31068_init(aip31068_t *dev, const aip31068_params_t *params) { assert(dev); assert(params); /* displays with more than 4 lines are not supported * (see aip31068_set_cursor_position) */ assert(params->row_count <= 4); dev->params = *params; dev->_curr_display_control = 0; dev->_curr_entry_mode_set = 0; uint8_t _function_set = 0; /* configure bit mode */ if (params->bit_mode == BITMODE_8_BIT) { SETBIT(_function_set, AIP31068_BIT_FUNCTION_SET_BITMODE); } /* configure line count */ if (params->row_count >= 2) { SETBIT(_function_set, AIP31068_BIT_FUNCTION_SET_LINECOUNT); } /* configure character size */ if (params->font_size == FONT_SIZE_5x10) { SETBIT(_function_set, AIP31068_BIT_FUNCTION_SET_FONTSIZE); } /* Begin of the initialization sequence (page 20 in the datasheet). * The initialization sequence here is writing AIP31068_CMD_FUNCTION_SET * three times with different sleep periods in between according to the * data sheet of the HD44780. The AIP31068 is forwarding commands to the * HD44780 hidden behind it so to be sure that it works for all kinds of * HD44780 we follow the initialization sequence of the HD44780, even though * it might be unnecessary for others. */ xtimer_msleep(50); int rc = 0; /* send function set command sequence */ rc = _command(dev, AIP31068_CMD_FUNCTION_SET | _function_set); if (rc < 0) { return rc; } /* wait after the first try */ xtimer_msleep(5); /* second try */ rc = _command(dev, AIP31068_CMD_FUNCTION_SET | _function_set); if (rc < 0) { return rc; } /* wait after the second try */ xtimer_usleep(500); /* third go */ rc = _command(dev, AIP31068_CMD_FUNCTION_SET | _function_set); if (rc < 0) { return rc; } rc = aip31068_turn_off(dev); if (rc < 0) { return rc; } rc = aip31068_clear(dev); if (rc < 0) { return rc; } rc = aip31068_set_text_insertion_mode(dev, LEFT_TO_RIGHT); if (rc < 0) { return rc; } /* End of the initialization sequence. */ return 0; } int aip31068_turn_on(aip31068_t *dev) { SETBIT(dev->_curr_display_control, AIP31068_BIT_DISPLAY_CONTROL_DISPLAY); return _command(dev, AIP31068_CMD_DISPLAY_CONTROL | dev->_curr_display_control); } int aip31068_turn_off(aip31068_t *dev) { CLRBIT(dev->_curr_display_control, AIP31068_BIT_DISPLAY_CONTROL_DISPLAY); return _command(dev, AIP31068_CMD_DISPLAY_CONTROL | dev->_curr_display_control); } int aip31068_clear(aip31068_t *dev) { int rc = _command(dev, AIP31068_CMD_CLEAR_DISPLAY); xtimer_usleep(AIP31068_EXECUTION_TIME_MAX); return rc; } int aip31068_return_home(aip31068_t *dev) { int rc = _command(dev, AIP31068_CMD_RETURN_HOME); xtimer_usleep(AIP31068_EXECUTION_TIME_MAX); return rc; } int aip31068_set_auto_scroll_enabled(aip31068_t *dev, bool enabled) { if (enabled) { SETBIT(dev->_curr_entry_mode_set, AIP31068_BIT_ENTRY_MODE_AUTOINCREMENT); } else { CLRBIT(dev->_curr_entry_mode_set, AIP31068_BIT_ENTRY_MODE_AUTOINCREMENT); } return _command(dev, AIP31068_CMD_ENTRY_MODE_SET | dev->_curr_entry_mode_set); } int aip31068_set_cursor_blinking_enabled(aip31068_t *dev, bool enabled) { if (enabled) { SETBIT(dev->_curr_display_control, AIP31068_BIT_DISPLAY_CONTROL_CURSOR_BLINKING); } else { CLRBIT(dev->_curr_display_control, AIP31068_BIT_DISPLAY_CONTROL_CURSOR_BLINKING); } return _command(dev, AIP31068_CMD_DISPLAY_CONTROL | dev->_curr_display_control); } int aip31068_set_cursor_visible(aip31068_t *dev, bool visible) { if (visible) { SETBIT(dev->_curr_display_control, AIP31068_BIT_DISPLAY_CONTROL_CURSOR); } else { CLRBIT(dev->_curr_display_control, AIP31068_BIT_DISPLAY_CONTROL_CURSOR); } return _command(dev, AIP31068_CMD_DISPLAY_CONTROL | dev->_curr_display_control); } int aip31068_set_cursor_position(aip31068_t *dev, uint8_t row, uint8_t col) { uint8_t row_offsets[4]; row_offsets[0] = 0x00; row_offsets[1] = 0x40; row_offsets[2] = 0x00 + dev->params.col_count; row_offsets[3] = 0x40 + dev->params.col_count; if (row >= dev->params.row_count) { row = dev->params.row_count - 1; } return _command(dev, AIP31068_CMD_SET_DDRAM_ADDR | (col | row_offsets[row])); } int aip31068_set_text_insertion_mode(aip31068_t *dev, aip31068_text_insertion_mode_t mode) { if (mode == LEFT_TO_RIGHT) { SETBIT(dev->_curr_entry_mode_set, AIP31068_BIT_ENTRY_MODE_INCREMENT); } else { CLRBIT(dev->_curr_entry_mode_set, AIP31068_BIT_ENTRY_MODE_INCREMENT); } return _command(dev, AIP31068_CMD_ENTRY_MODE_SET | dev->_curr_entry_mode_set); } int aip31068_move_cursor_left(aip31068_t *dev) { uint8_t cmd = AIP31068_CMD_CURSOR_DISPLAY_SHIFT; CLRBIT(cmd, AIP31068_BIT_CURSOR_DISPLAY_SHIFT_DIRECTION); return _command(dev, cmd); } int aip31068_move_cursor_right(aip31068_t *dev) { uint8_t cmd = AIP31068_CMD_CURSOR_DISPLAY_SHIFT; SETBIT(cmd, AIP31068_BIT_CURSOR_DISPLAY_SHIFT_DIRECTION); return _command(dev, cmd); } int aip31068_scroll_display_left(aip31068_t *dev) { uint8_t cmd = AIP31068_CMD_CURSOR_DISPLAY_SHIFT; SETBIT(cmd, AIP31068_BIT_CURSOR_DISPLAY_SHIFT_SELECTION); CLRBIT(cmd, AIP31068_BIT_CURSOR_DISPLAY_SHIFT_DIRECTION); return _command(dev, cmd); } int aip31068_scroll_display_right(aip31068_t *dev) { uint8_t cmd = AIP31068_CMD_CURSOR_DISPLAY_SHIFT; SETBIT(cmd, AIP31068_BIT_CURSOR_DISPLAY_SHIFT_SELECTION); SETBIT(cmd, AIP31068_BIT_CURSOR_DISPLAY_SHIFT_DIRECTION); return _command(dev, cmd); } int aip31068_set_custom_symbol(aip31068_t *dev, aip31068_custom_symbol_t custom_symbol, const uint8_t charmap[]) { /* Bits 0-2 define the row address of a custom character in CGRAM. * Bits 3-5 define the base address of a custom character in CGRAM. */ uint8_t location = custom_symbol << 3; int rc = _command(dev, AIP31068_CMD_SET_CGRAM_ADDR | location); if (rc < 0) { return rc; } /* How many rows are necessary for a complete character for given font? */ int row_count = dev->params.font_size == FONT_SIZE_5x8 ? 8 : 10; for (int i = 0; i < row_count; i++) { rc = _data(dev, charmap[i]); if (rc < 0) { return rc; } } return _command(dev, AIP31068_CMD_SET_DDRAM_ADDR); } int aip31068_print_custom_symbol(aip31068_t *dev, aip31068_custom_symbol_t custom_symbol) { return _data(dev, (uint8_t) custom_symbol); } int aip31068_print(aip31068_t *dev, const char *data) { while (*data != '\0') { int rc; if ((rc = _data(dev, *data)) != 0) { return rc; } data++; } return 0; } int aip31068_print_char(aip31068_t *dev, char c) { return _data(dev, c); } static inline int _data(aip31068_t *dev, uint8_t value) { int rc = _write(dev, value, false); xtimer_usleep(AIP31068_EXECUTION_TIME_DEFAULT); return rc; } static inline int _command(aip31068_t *dev, uint8_t value) { int rc = _write(dev, value, true); xtimer_usleep(AIP31068_EXECUTION_TIME_DEFAULT); return rc; } static inline int _write(aip31068_t *dev, uint8_t data_byte, bool is_cmd) { uint8_t control_byte = 0; if (!is_cmd) { SETBIT(control_byte, AIP31068_BIT_CONTROL_BYTE_RS); } uint8_t data[] = { control_byte, data_byte }; return _device_write(dev, data, sizeof(data)); } static int _device_write(aip31068_t* dev, uint8_t *data, uint8_t len) { i2c_t i2c_dev = dev->params.i2c_dev; i2c_acquire(i2c_dev); int rc = i2c_write_bytes(i2c_dev, dev->params.i2c_addr, data, len, 0); i2c_release(i2c_dev); return rc; }