gobot.io/x/gobot/v2@v2.1.0/drivers/gpio/hd44780_driver.go (about) 1 package gpio 2 3 import ( 4 "errors" 5 "fmt" 6 "sync" 7 "time" 8 9 "gobot.io/x/gobot/v2" 10 ) 11 12 // Commands for the driver 13 const ( 14 HD44780_CLEARDISPLAY = 0x01 15 HD44780_RETURNHOME = 0x02 16 HD44780_ENTRYMODESET = 0x04 17 HD44780_DISPLAYCONTROL = 0x08 18 HD44780_CURSORSHIFT = 0x10 19 HD44780_FUNCTIONSET = 0x20 20 HD44780_SETCGRAMADDR = 0x40 21 HD44780_SETDDRAMADDR = 0x80 22 HD44780_ENTRYRIGHT = 0x00 23 HD44780_ENTRYLEFT = 0x02 24 HD44780_ENTRYSHIFTINCREMENT = 0x01 25 HD44780_ENTRYSHIFTDECREMENT = 0x00 26 HD44780_DISPLAYON = 0x04 27 HD44780_DISPLAYOFF = 0x00 28 HD44780_CURSORON = 0x02 29 HD44780_CURSOROFF = 0x00 30 HD44780_BLINKON = 0x01 31 HD44780_BLINKOFF = 0x00 32 HD44780_DISPLAYMOVE = 0x08 33 HD44780_CURSORMOVE = 0x00 34 HD44780_MOVERIGHT = 0x04 35 HD44780_MOVELEFT = 0x00 36 HD44780_1LINE = 0x00 37 HD44780_2LINE = 0x08 38 HD44780_5x8DOTS = 0x00 39 HD44780_5x10DOTS = 0x04 40 HD44780_4BITBUS = 0x00 41 HD44780_8BITBUS = 0x10 42 ) 43 44 // Some useful constants for the driver 45 const ( 46 HD44780_2NDLINEOFFSET = 0x40 47 ) 48 49 // HD44780BusMode is the data bus mode 50 type HD44780BusMode int 51 52 // Bus modes of the driver 53 const ( 54 HD44780_4BITMODE HD44780BusMode = iota + 1 55 HD44780_8BITMODE 56 ) 57 58 // HD44780DataPin are the data bit pins 59 type HD44780DataPin struct { 60 D0 string // not used if 4Bit mode 61 D1 string // not used if 4Bit mode 62 D2 string // not used if 4Bit mode 63 D3 string // not used if 4Bit mode 64 D4 string 65 D5 string 66 D6 string 67 D7 string 68 } 69 70 // HD44780Driver is the gobot driver for the HD44780 LCD controller 71 // Datasheet: https://www.sparkfun.com/datasheets/LCD/HD44780.pdf 72 type HD44780Driver struct { 73 name string 74 cols int 75 rows int 76 rowOffsets [4]int 77 busMode HD44780BusMode 78 pinRS *DirectPinDriver 79 pinEN *DirectPinDriver 80 pinRW *DirectPinDriver 81 pinDataBits []*DirectPinDriver 82 displayCtrl int 83 displayFunc int 84 displayMode int 85 connection gobot.Connection 86 gobot.Commander 87 mutex *sync.Mutex // mutex is needed for sequences, like CreateChar(), Write(), Start(), Halt() 88 } 89 90 // NewHD44780Driver return a new HD44780Driver 91 // a: gobot.Connection 92 // cols: lcd columns 93 // rows: lcd rows 94 // busMode: 4Bit or 8Bit 95 // pinRS: register select pin 96 // pinEN: clock enable pin 97 // pinDataBits: databit pins 98 func NewHD44780Driver(a gobot.Connection, cols int, rows int, busMode HD44780BusMode, pinRS string, pinEN string, pinDataBits HD44780DataPin) *HD44780Driver { 99 h := &HD44780Driver{ 100 name: "HD44780Driver", 101 cols: cols, 102 rows: rows, 103 busMode: busMode, 104 pinRS: NewDirectPinDriver(a, pinRS), 105 pinEN: NewDirectPinDriver(a, pinEN), 106 connection: a, 107 Commander: gobot.NewCommander(), 108 mutex: &sync.Mutex{}, 109 } 110 111 if h.busMode == HD44780_4BITMODE { 112 h.pinDataBits = make([]*DirectPinDriver, 4) 113 h.pinDataBits[0] = NewDirectPinDriver(a, pinDataBits.D4) 114 h.pinDataBits[1] = NewDirectPinDriver(a, pinDataBits.D5) 115 h.pinDataBits[2] = NewDirectPinDriver(a, pinDataBits.D6) 116 h.pinDataBits[3] = NewDirectPinDriver(a, pinDataBits.D7) 117 } else { 118 h.pinDataBits = make([]*DirectPinDriver, 8) 119 h.pinDataBits[0] = NewDirectPinDriver(a, pinDataBits.D0) 120 h.pinDataBits[1] = NewDirectPinDriver(a, pinDataBits.D1) 121 h.pinDataBits[2] = NewDirectPinDriver(a, pinDataBits.D2) 122 h.pinDataBits[3] = NewDirectPinDriver(a, pinDataBits.D3) 123 h.pinDataBits[4] = NewDirectPinDriver(a, pinDataBits.D4) 124 h.pinDataBits[5] = NewDirectPinDriver(a, pinDataBits.D5) 125 h.pinDataBits[6] = NewDirectPinDriver(a, pinDataBits.D6) 126 h.pinDataBits[7] = NewDirectPinDriver(a, pinDataBits.D7) 127 } 128 129 h.rowOffsets[0] = 0x00 130 h.rowOffsets[1] = HD44780_2NDLINEOFFSET 131 h.rowOffsets[2] = 0x00 + cols 132 h.rowOffsets[3] = HD44780_2NDLINEOFFSET + cols 133 134 /* TODO : Add commands */ 135 136 return h 137 } 138 139 // SetRWPin initializes the RW pin 140 func (h *HD44780Driver) SetRWPin(pinRW string) { 141 h.mutex.Lock() 142 defer h.mutex.Unlock() 143 144 h.pinRW = NewDirectPinDriver(h.connection, pinRW) 145 } 146 147 // Name returns the HD44780Driver name 148 func (h *HD44780Driver) Name() string { return h.name } 149 150 // SetName sets the HD44780Driver name 151 func (h *HD44780Driver) SetName(n string) { h.name = n } 152 153 // Connection returns the HD44780Driver Connection 154 func (h *HD44780Driver) Connection() gobot.Connection { 155 return h.connection 156 } 157 158 // Start initializes the HD44780 LCD controller 159 // refer to page 45/46 of Hitachi HD44780 datasheet 160 func (h *HD44780Driver) Start() (err error) { 161 h.mutex.Lock() 162 defer h.mutex.Unlock() 163 164 for _, bitPin := range h.pinDataBits { 165 if bitPin.Pin() == "" { 166 return errors.New("Initialization error") 167 } 168 } 169 170 time.Sleep(50 * time.Millisecond) 171 172 if err := h.activateWriteMode(); err != nil { 173 return err 174 } 175 176 // for initialization refer to documentation, page 45 and 46 177 if h.busMode == HD44780_4BITMODE { 178 if err := h.writeDataPins(0x03); err != nil { 179 return err 180 } 181 time.Sleep(5 * time.Millisecond) 182 if err := h.writeDataPins(0x03); err != nil { 183 return err 184 } 185 time.Sleep(100 * time.Microsecond) 186 if err := h.writeDataPins(0x03); err != nil { 187 return err 188 } 189 // no additional delay is necessary now 190 if err := h.writeDataPins(0x02); err != nil { 191 return err 192 } 193 } else { 194 if err := h.sendCommand(0x30); err != nil { 195 return err 196 } 197 time.Sleep(5 * time.Millisecond) 198 if err := h.sendCommand(0x30); err != nil { 199 return err 200 } 201 time.Sleep(100 * time.Microsecond) 202 if err := h.sendCommand(0x30); err != nil { 203 return err 204 } 205 // no additional delay is necessary now 206 } 207 208 if h.busMode == HD44780_4BITMODE { 209 h.displayFunc |= HD44780_4BITBUS 210 } else { 211 h.displayFunc |= HD44780_8BITBUS 212 } 213 214 if h.rows > 1 { 215 h.displayFunc |= HD44780_2LINE 216 } else { 217 h.displayFunc |= HD44780_1LINE 218 } 219 220 h.displayFunc |= HD44780_5x8DOTS 221 h.displayCtrl = HD44780_DISPLAYON | HD44780_BLINKOFF | HD44780_CURSOROFF 222 h.displayMode = HD44780_ENTRYLEFT | HD44780_ENTRYSHIFTDECREMENT 223 224 if err := h.sendCommand(HD44780_FUNCTIONSET | h.displayFunc); err != nil { 225 return err 226 } 227 228 if err := h.sendCommand(HD44780_DISPLAYCONTROL | h.displayCtrl); err != nil { 229 return err 230 } 231 232 if err := h.clear(); err != nil { 233 return err 234 } 235 236 if err := h.sendCommand(HD44780_ENTRYMODESET | h.displayMode); err != nil { 237 return err 238 } 239 240 // see documentation, page 45, 46: the busy flag can't be checked before 241 return nil 242 } 243 244 // Halt implements the Driver interface 245 func (h *HD44780Driver) Halt() error { 246 // mutex: bad characters and device locking can be prevented 247 // if the last action is finished before return 248 h.mutex.Lock() 249 defer h.mutex.Unlock() 250 251 return nil 252 } 253 254 // Write output text to the display 255 func (h *HD44780Driver) Write(message string) (err error) { 256 h.mutex.Lock() 257 defer h.mutex.Unlock() 258 259 col := 0 260 if (h.displayMode & HD44780_ENTRYLEFT) == 0 { 261 col = h.cols - 1 262 } 263 264 row := 0 265 for _, c := range message { 266 if c == '\n' { 267 row++ 268 if err := h.setCursor(col, row); err != nil { 269 return err 270 } 271 continue 272 } 273 if err := h.writeChar(int(c)); err != nil { 274 return err 275 } 276 } 277 278 return nil 279 } 280 281 // Clear clear the display 282 func (h *HD44780Driver) Clear() (err error) { 283 h.mutex.Lock() 284 defer h.mutex.Unlock() 285 286 return h.clear() 287 } 288 289 // Home return cursor to home 290 func (h *HD44780Driver) Home() (err error) { 291 h.mutex.Lock() 292 defer h.mutex.Unlock() 293 294 if err := h.sendCommand(HD44780_RETURNHOME); err != nil { 295 return err 296 } 297 time.Sleep(2 * time.Millisecond) 298 299 return nil 300 } 301 302 // SetCursor move the cursor to the specified position 303 func (h *HD44780Driver) SetCursor(col int, row int) (err error) { 304 h.mutex.Lock() 305 defer h.mutex.Unlock() 306 307 return h.setCursor(col, row) 308 } 309 310 // Display turn the display on and off 311 func (h *HD44780Driver) Display(on bool) (err error) { 312 h.mutex.Lock() 313 defer h.mutex.Unlock() 314 315 if on { 316 h.displayCtrl |= HD44780_DISPLAYON 317 } else { 318 h.displayCtrl &= ^HD44780_DISPLAYON 319 } 320 321 return h.sendCommand(HD44780_DISPLAYCONTROL | h.displayCtrl) 322 } 323 324 // Cursor turn the cursor on and off 325 func (h *HD44780Driver) Cursor(on bool) (err error) { 326 h.mutex.Lock() 327 defer h.mutex.Unlock() 328 329 if on { 330 h.displayCtrl |= HD44780_CURSORON 331 } else { 332 h.displayCtrl &= ^HD44780_CURSORON 333 } 334 335 return h.sendCommand(HD44780_DISPLAYCONTROL | h.displayCtrl) 336 } 337 338 // Blink turn the blink on and off 339 func (h *HD44780Driver) Blink(on bool) (err error) { 340 h.mutex.Lock() 341 defer h.mutex.Unlock() 342 343 if on { 344 h.displayCtrl |= HD44780_BLINKON 345 } else { 346 h.displayCtrl &= ^HD44780_BLINKON 347 } 348 349 return h.sendCommand(HD44780_DISPLAYCONTROL | h.displayCtrl) 350 } 351 352 // ScrollLeft scroll text left 353 func (h *HD44780Driver) ScrollLeft() (err error) { 354 h.mutex.Lock() 355 defer h.mutex.Unlock() 356 357 return h.sendCommand(HD44780_CURSORSHIFT | HD44780_DISPLAYMOVE | HD44780_MOVELEFT) 358 } 359 360 // ScrollRight scroll text right 361 func (h *HD44780Driver) ScrollRight() (err error) { 362 h.mutex.Lock() 363 defer h.mutex.Unlock() 364 365 return h.sendCommand(HD44780_CURSORSHIFT | HD44780_DISPLAYMOVE | HD44780_MOVERIGHT) 366 } 367 368 // LeftToRight display text from left to right 369 func (h *HD44780Driver) LeftToRight() (err error) { 370 h.mutex.Lock() 371 defer h.mutex.Unlock() 372 373 h.displayMode |= HD44780_ENTRYLEFT 374 return h.sendCommand(HD44780_ENTRYMODESET | h.displayMode) 375 } 376 377 // RightToLeft display text from right to left 378 func (h *HD44780Driver) RightToLeft() (err error) { 379 h.mutex.Lock() 380 defer h.mutex.Unlock() 381 382 h.displayMode &= ^HD44780_ENTRYLEFT 383 return h.sendCommand(HD44780_ENTRYMODESET | h.displayMode) 384 } 385 386 // SendCommand send control command 387 func (h *HD44780Driver) SendCommand(data int) (err error) { 388 h.mutex.Lock() 389 defer h.mutex.Unlock() 390 391 return h.sendCommand(data) 392 } 393 394 // WriteChar output a character to the display 395 func (h *HD44780Driver) WriteChar(data int) (err error) { 396 h.mutex.Lock() 397 defer h.mutex.Unlock() 398 399 return h.writeChar(data) 400 } 401 402 // CreateChar create custom character 403 func (h *HD44780Driver) CreateChar(pos int, charMap [8]byte) (err error) { 404 h.mutex.Lock() 405 defer h.mutex.Unlock() 406 407 if pos > 7 { 408 return errors.New("can't set a custom character at a position greater than 7") 409 } 410 411 if err := h.sendCommand(HD44780_SETCGRAMADDR | (pos << 3)); err != nil { 412 return err 413 } 414 415 for i := range charMap { 416 if err := h.writeChar(int(charMap[i])); err != nil { 417 return err 418 } 419 } 420 421 return nil 422 } 423 424 func (h *HD44780Driver) sendCommand(data int) (err error) { 425 if err := h.activateWriteMode(); err != nil { 426 return err 427 } 428 if err := h.pinRS.Off(); err != nil { 429 return err 430 } 431 if h.busMode == HD44780_4BITMODE { 432 if err := h.writeDataPins(data >> 4); err != nil { 433 return err 434 } 435 } 436 437 return h.writeDataPins(data) 438 } 439 440 func (h *HD44780Driver) writeChar(data int) (err error) { 441 if err := h.activateWriteMode(); err != nil { 442 return err 443 } 444 445 if err := h.pinRS.On(); err != nil { 446 return err 447 } 448 if h.busMode == HD44780_4BITMODE { 449 if err := h.writeDataPins(data >> 4); err != nil { 450 return err 451 } 452 } 453 454 return h.writeDataPins(data) 455 } 456 457 func (h *HD44780Driver) clear() (err error) { 458 if err := h.sendCommand(HD44780_CLEARDISPLAY); err != nil { 459 return err 460 } 461 462 // clear is time consuming, see documentation for JHD1313 463 // for lower clock speed it takes more time 464 time.Sleep(4 * time.Millisecond) 465 466 return nil 467 } 468 469 func (h *HD44780Driver) setCursor(col int, row int) (err error) { 470 if col < 0 || row < 0 || col >= h.cols || row >= h.rows { 471 return fmt.Errorf("Invalid position value (%d, %d), range (%d, %d)", col, row, h.cols-1, h.rows-1) 472 } 473 474 return h.sendCommand(HD44780_SETDDRAMADDR | col + h.rowOffsets[row]) 475 } 476 477 func (h *HD44780Driver) writeDataPins(data int) (err error) { 478 for i, pin := range h.pinDataBits { 479 if ((data >> i) & 0x01) == 0x01 { 480 if err := pin.On(); err != nil { 481 return err 482 } 483 } else { 484 if err := pin.Off(); err != nil { 485 return err 486 } 487 } 488 } 489 return h.fallingEdge() 490 } 491 492 // fallingEdge creates falling edge to trigger data transmission 493 func (h *HD44780Driver) fallingEdge() (err error) { 494 if err := h.pinEN.On(); err != nil { 495 return err 496 } 497 time.Sleep(1 * time.Microsecond) 498 499 if err := h.pinEN.Off(); err != nil { 500 return err 501 } 502 // fastest write operation at 190kHz mode takes 53 us 503 time.Sleep(60 * time.Microsecond) 504 505 return nil 506 } 507 508 func (h *HD44780Driver) activateWriteMode() (err error) { 509 if h.pinRW == nil { 510 return 511 } 512 return h.pinRW.Off() 513 }