All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.devices.hitachi.I2cHd44780.kt Maven / Gradle / Ivy

@file:Suppress("SpellCheckingInspection", "unused")

package ch.softappeal.konapi.devices.hitachi

import ch.softappeal.konapi.Closeable
import ch.softappeal.konapi.I2cDevice
import ch.softappeal.konapi.sleepMs

/*
    Dot Matrix Liquid Crystal Display Controller/Driver

    The https://www.sparkfun.com/datasheets/LCD/HD44780.pdf is connected
    to an 8-bit I/O expander for I2C-bus (like https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf).
    Only the bus lines DB4 to DB7 are connected, DB0 to DB3 are unconnected.
    Therefore, the HD44780 must be in 4-bit interface mode, and it's not possible read data (busy flag, counters).
    The control pins are connected as below:
 */
private const val RS_DATA: UByte = 0x01U        // register select: data register
private const val RS_INSTRUCTION: UByte = 0x00U // register select: instruction register
private const val RW_READ: UByte = 0x02U
private const val RW_WRITE: UByte = 0x00U
private const val E_TRUE: UByte = 0x04U  // enable high
private const val E_FALSE: UByte = 0x00U // enable low
private const val BACKLIGHT_ON: UByte = 0x08U
private const val BACKLIGHT_OFF: UByte = 0x00U
private const val DB4_DB7: UByte = 0xF0U

// instructions:
private const val CLEAR_DISPLAY: UByte = 0x01U
private const val ENTRY_MODE_SET: UByte = 0x04U
private const val DISPLAY_CONTROL: UByte = 0x08U
private const val FUNCTION_SET: UByte = 0x20U
private const val SET_DDRAM_ADDR: UByte = 0x80U

// ENTRY_MODE_SET:
private const val INCREMENT: UByte = 0x02U
private const val DECREMENT: UByte = 0x00U
private const val SHIFT_ON: UByte = 0x01U
private const val SHIFT_OFF: UByte = 0x00U

// DISPLAY_CONTROL:
private const val DISPLAY_ON: UByte = 0x04U
private const val DISPLAY_OFF: UByte = 0x00U
private const val CURSOR_ON: UByte = 0x02U
private const val CURSOR_OFF: UByte = 0x00U
private const val BLINK_ON: UByte = 0x01U
private const val BLINK_OFF: UByte = 0x00U

// FUNCTION_SET:
private const val MODE_8BIT: UByte = 0x10U
private const val MODE_4BIT: UByte = 0x00U
private const val LINES_2: UByte = 0x08U
private const val LINES_1: UByte = 0x00U
private const val FONT_5x10: UByte = 0x04U
private const val FONT_5x8: UByte = 0x00U

public class I2cHd44780(private val device: I2cDevice, public val config: Config) : Closeable {
    public enum class Font { Dots5x8, Dots5x10 }

    public data class Config(
        public val lines: Int,
        public val columns: Int,
        public val font: Font,
    ) {
        init {
            require(lines == 1 || lines == 2 || lines == 4) { "lines must be 1, 2 or 4" }
            require(columns > 0) { "colums must be > 0" }
        }

        // https://web.alfredstate.edu/faculty/weimandn/lcd/lcd_addressing/lcd_addressing_index.html
        internal fun lineOffset(line: Int) = when (line) {
            0 -> 0x00
            1 -> 0x40
            2 -> columns
            3 -> 0x40 + columns
            else -> error("invalid line")
        }

        internal fun requireCursorPosition(line: Int, column: Int) {
            require(line in 0.. LINES_1
                    else -> LINES_2
                } or
                when (config.font) {
                    Font.Dots5x8 -> FONT_5x8
                    Font.Dots5x10 -> FONT_5x10
                }
        )
        write4Bit(ENTRY_MODE_SET or INCREMENT or SHIFT_OFF)
        setDisplayControl()
        clear()
    }

    public fun clear() {
        write4Bit(CLEAR_DISPLAY)
        sleepMs(2)
    }

    public fun displayString(s: String) {
        s.forEach { char -> write4Bit(char.code.toUByte(), dataRegister = true) }
    }

    public fun setCursorPosition(line: Int, column: Int) {
        config.requireCursorPosition(line, column)
        write4Bit(SET_DDRAM_ADDR or (config.lineOffset(line) + column).toUByte())
    }

    public fun setBlink(value: Boolean) {
        if (blink == value) return
        blink = value
        setDisplayControl()
    }

    public fun showCursor(value: Boolean) {
        if (cursor == value) return
        cursor = value
        setDisplayControl()
    }

    public fun showDisplay(value: Boolean) {
        if (on == value) return
        on = value
        setDisplayControl()
    }

    public fun setBacklight(value: Boolean) {
        if (backlight == value) return
        backlight = value
        setDisplayControl()
    }

    override fun close() {
        backlight = false
        on = false
        setDisplayControl()
    }
}

public fun i2cLcd1602(i2cDevice: I2cDevice): I2cHd44780 = I2cHd44780(i2cDevice, I2cHd44780.Config(2, 16, I2cHd44780.Font.Dots5x8))




© 2015 - 2024 Weber Informatics LLC | Privacy Policy