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  }