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