tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/hd44780i2c/hd44780i2c.go (about) 1 // Package hd44780i2c implements a driver for the Hitachi HD44780 LCD display module 2 // with an I2C adapter. 3 // 4 // Datasheet: https://www.sparkfun.com/datasheets/LCD/HD44780.pdf 5 package hd44780i2c 6 7 import ( 8 "errors" 9 "time" 10 11 "tinygo.org/x/drivers" 12 ) 13 14 // Device wraps an I2C connection to a HD44780 I2C LCD with related data. 15 type Device struct { 16 bus drivers.I2C 17 addr uint8 18 width uint8 19 height uint8 20 cursor cursor 21 backlight uint8 22 displayfunction uint8 23 displaycontrol uint8 24 displaymode uint8 25 } 26 27 type cursor struct { 28 x, y uint8 29 } 30 31 // Config for HD44780 I2C LCD. 32 type Config struct { 33 Width uint8 34 Height uint8 35 Font uint8 36 CursorOn bool 37 CursorBlink bool 38 } 39 40 // New creates a new HD44780 I2C LCD connection. The I2C bus must already be 41 // configured. 42 // 43 // This function only creates the Device object, it does not touch the device. 44 func New(bus drivers.I2C, addr uint8) Device { 45 if addr == 0 { 46 addr = 0x27 47 } 48 return Device{ 49 bus: bus, 50 addr: addr, 51 } 52 } 53 54 // Configure sets up the display. Display itself and backlight is default on. 55 func (d *Device) Configure(cfg Config) error { 56 57 if cfg.Width == 0 || cfg.Height == 0 { 58 return errors.New("width and height must be set") 59 } 60 d.width = uint8(cfg.Width) 61 d.height = uint8(cfg.Height) 62 63 delayms(50) 64 65 d.backlight = BACKLIGHT_ON 66 d.expanderWrite(0) 67 delayms(1000) 68 69 d.write4bits(0x03 << 4) 70 delayus(4500) 71 d.write4bits(0x03 << 4) 72 delayus(4500) 73 d.write4bits(0x03 << 4) 74 delayus(150) 75 d.write4bits(0x02 << 4) 76 77 d.displayfunction = DATA_LENGTH_4BIT | ONE_LINE | FONT_5X8 78 if d.height > 1 { 79 d.displayfunction |= TWO_LINE 80 } 81 if cfg.Font != 0 && d.height == 1 { 82 d.displayfunction |= FONT_5X10 83 } 84 d.sendCommand(FUNCTION_MODE | d.displayfunction) 85 86 d.displaycontrol = DISPLAY_ON | CURSOR_OFF | CURSOR_BLINK_OFF 87 if cfg.CursorOn { 88 d.displaycontrol |= CURSOR_ON 89 } 90 if cfg.CursorBlink { 91 d.displaycontrol |= CURSOR_BLINK_ON 92 } 93 d.sendCommand(DISPLAY_ON_OFF | d.displaycontrol) 94 d.ClearDisplay() 95 96 d.displaymode = CURSOR_INCREASE | DISPLAY_NO_SHIFT 97 d.sendCommand(ENTRY_MODE | d.displaymode) 98 d.Home() 99 100 return nil 101 } 102 103 // ClearDisplay clears all texts on the display and sets the cursor back to position (0, 0). 104 func (d *Device) ClearDisplay() { 105 d.sendCommand(DISPLAY_CLEAR) 106 d.cursor.x = 0 107 d.cursor.y = 0 108 delayus(2000) 109 } 110 111 // Home sets the cursor back to position (0, 0). 112 func (d *Device) Home() { 113 d.sendCommand(CURSOR_HOME) 114 d.cursor.x = 0 115 d.cursor.y = 0 116 delayus(2000) 117 } 118 119 // SetCursor sets the cursor to a specific position (x, y). 120 // 121 // For example, on 16x2 LCDs the range of x (column) is 0~15 and y (row) is 0~1. 122 // if y is larger than actual rows, it would be set to 0 (restart from first row). 123 func (d *Device) SetCursor(x, y uint8) { 124 rowOffset := []uint8{0x0, 0x40, 0x14, 0x54} 125 if y > (d.height - 1) { 126 y = 0 127 } 128 d.cursor.x = x 129 d.cursor.y = y 130 d.sendCommand(DDRAM_SET | (x + (rowOffset[y]))) 131 } 132 133 // Print prints text on the display (started from current cursor position). 134 // 135 // It would automatically break to new line when the text is too long. 136 // You can also use \n as line breakers. 137 func (d *Device) Print(data []byte) { 138 for _, chr := range data { 139 if chr == '\n' { 140 d.newLine() 141 } else { 142 if d.cursor.x >= d.width { 143 d.newLine() 144 } 145 d.sendData(uint8(rune(chr))) 146 d.cursor.x++ 147 } 148 } 149 } 150 151 // CreateCharacter crates custom characters (using data parameter) 152 // and stores it under CGRAM address (using cgramAddr, 0x0-0x7). 153 func (d *Device) CreateCharacter(cgramAddr uint8, data []byte) { 154 cgramAddr &= 0x7 155 d.sendCommand(CGRAM_SET | cgramAddr<<3) 156 for _, dd := range data { 157 d.sendData(dd) 158 } 159 d.SetCursor(d.cursor.x, d.cursor.y) 160 } 161 162 // DisplayOn turns on/off the display. 163 func (d *Device) DisplayOn(option bool) { 164 if option { 165 d.displaycontrol |= DISPLAY_ON 166 } else { 167 d.displaycontrol &= ^uint8(DISPLAY_ON) 168 } 169 d.sendCommand(DISPLAY_ON_OFF | d.displaycontrol) 170 } 171 172 // CursorOn display/hides the cursor. 173 func (d *Device) CursorOn(option bool) { 174 if option { 175 d.displaycontrol |= CURSOR_ON 176 } else { 177 d.displaycontrol &= ^uint8(CURSOR_ON) 178 } 179 d.sendCommand(DISPLAY_ON_OFF | d.displaycontrol) 180 } 181 182 // CursorBlink turns on/off the blinking cursor mode. 183 func (d *Device) CursorBlink(option bool) { 184 if option { 185 d.displaycontrol |= CURSOR_BLINK_ON 186 } else { 187 d.displaycontrol &= ^uint8(CURSOR_BLINK_ON) 188 } 189 d.sendCommand(DISPLAY_ON_OFF | d.displaycontrol) 190 } 191 192 // BacklightOn turns on/off the display backlight. 193 func (d *Device) BacklightOn(option bool) { 194 if option { 195 d.backlight = BACKLIGHT_ON 196 } else { 197 d.backlight = BACKLIGHT_OFF 198 } 199 d.expanderWrite(0) 200 } 201 202 func (d *Device) newLine() { 203 d.cursor.x = 0 204 d.cursor.y++ 205 d.SetCursor(d.cursor.x, d.cursor.y) 206 } 207 208 func delayms(t uint16) { 209 time.Sleep(time.Millisecond * time.Duration(t)) 210 } 211 212 func delayus(t uint16) { 213 time.Sleep(time.Microsecond * time.Duration(t)) 214 } 215 216 func (d *Device) expanderWrite(value uint8) { 217 d.bus.Tx(uint16(d.addr), []uint8{value | d.backlight}, nil) 218 } 219 220 func (d *Device) pulseEnable(value uint8) { 221 d.expanderWrite(value | En) 222 delayus(1) 223 d.expanderWrite(value & ^uint8(En)) 224 delayus(50) 225 } 226 227 func (d *Device) write4bits(value uint8) { 228 d.expanderWrite(value) 229 d.pulseEnable(value) 230 } 231 232 func (d *Device) write(value uint8, mode uint8) { 233 d.write4bits(uint8(value&0xf0) | mode) 234 d.write4bits(uint8((value<<4)&0xf0) | mode) 235 } 236 237 func (d *Device) sendCommand(value uint8) { 238 d.write(value, 0) 239 } 240 241 func (d *Device) sendData(value uint8) { 242 d.write(value, Rs) 243 }