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  }