gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/jhd1313m1_driver.go (about) 1 package i2c 2 3 import ( 4 "fmt" 5 "strconv" 6 "time" 7 8 "gobot.io/x/gobot/v2" 9 ) 10 11 const ( 12 REG_RED = 0x04 13 REG_GREEN = 0x03 14 REG_BLUE = 0x02 15 16 LCD_CLEARDISPLAY = 0x01 17 LCD_RETURNHOME = 0x02 18 LCD_ENTRYMODESET = 0x04 19 LCD_DISPLAYCONTROL = 0x08 20 LCD_CURSORSHIFT = 0x10 21 LCD_FUNCTIONSET = 0x20 22 LCD_SETCGRAMADDR = 0x40 23 LCD_SETDDRAMADDR = 0x80 24 LCD_ENTRYRIGHT = 0x00 25 LCD_ENTRYLEFT = 0x02 26 LCD_ENTRYSHIFTINCREMENT = 0x01 27 LCD_ENTRYSHIFTDECREMENT = 0x00 28 LCD_DISPLAYON = 0x04 29 LCD_DISPLAYOFF = 0x00 30 LCD_CURSORON = 0x02 31 LCD_CURSOROFF = 0x00 32 LCD_BLINKON = 0x01 33 LCD_BLINKOFF = 0x00 34 LCD_DISPLAYMOVE = 0x08 35 LCD_CURSORMOVE = 0x00 36 LCD_MOVERIGHT = 0x04 37 LCD_MOVELEFT = 0x00 38 LCD_2LINE = 0x08 39 LCD_CMD = 0x80 40 LCD_DATA = 0x40 41 42 LCD_2NDLINEOFFSET = 0x40 43 ) 44 45 // CustomLCDChars is a map of CGRAM characters that can be loaded 46 // into a LCD screen to display custom characters. Some LCD screens such 47 // as the Grove screen (jhd1313m1) isn't loaded with latin 1 characters. 48 // It's up to the developer to load the set up to 8 custom characters and 49 // update the input text so the character is swapped by a byte reflecting 50 // the position of the custom character to use. 51 // See SetCustomChar 52 var CustomLCDChars = map[string][8]byte{ 53 "é": {130, 132, 142, 145, 159, 144, 142, 128}, 54 "è": {136, 132, 142, 145, 159, 144, 142, 128}, 55 "ê": {132, 138, 142, 145, 159, 144, 142, 128}, 56 "à": {136, 134, 128, 142, 145, 147, 141, 128}, 57 "â": {132, 138, 128, 142, 145, 147, 141, 128}, 58 "á": {2, 4, 14, 1, 15, 17, 15, 0}, 59 "î": {132, 138, 128, 140, 132, 132, 142, 128}, 60 "í": {2, 4, 12, 4, 4, 4, 14, 0}, 61 "û": {132, 138, 128, 145, 145, 147, 141, 128}, 62 "ù": {136, 134, 128, 145, 145, 147, 141, 128}, 63 "ñ": {14, 0, 22, 25, 17, 17, 17, 0}, 64 "ó": {2, 4, 14, 17, 17, 17, 14, 0}, 65 "heart": {0, 10, 31, 31, 31, 14, 4, 0}, 66 "smiley": {0, 0, 10, 0, 0, 17, 14, 0}, 67 "frowney": {0, 0, 10, 0, 0, 0, 14, 17}, 68 } 69 70 var jhd1313m1ErrInvalidPosition = fmt.Errorf("Invalid position value") 71 72 // JHD1313M1Driver is a driver for the Jhd1313m1 LCD display which has two i2c addreses, 73 // one belongs to a controller and the other controls solely the backlight. 74 // This module was tested with the Seed Grove LCD RGB Backlight v2.0 display which requires 5V to operate. 75 // http://www.seeedstudio.com/wiki/Grove_-_LCD_RGB_Backlight 76 type JHD1313M1Driver struct { 77 name string 78 connector Connector 79 Config 80 gobot.Commander 81 lcdAddress int 82 lcdConnection Connection 83 rgbAddress int 84 rgbConnection Connection 85 } 86 87 // NewJHD1313M1Driver creates a new driver with specified i2c interface. 88 // Params: 89 // 90 // conn Connector - the Adaptor to use with this Driver 91 // 92 // Optional params: 93 // 94 // i2c.WithBus(int): bus to use with this driver 95 func NewJHD1313M1Driver(a Connector, options ...func(Config)) *JHD1313M1Driver { 96 j := &JHD1313M1Driver{ 97 name: gobot.DefaultName("JHD1313M1"), 98 connector: a, 99 Config: NewConfig(), 100 Commander: gobot.NewCommander(), 101 lcdAddress: 0x3E, 102 rgbAddress: 0x62, 103 } 104 105 for _, option := range options { 106 option(j) 107 } 108 109 j.AddCommand("SetRGB", func(params map[string]interface{}) interface{} { 110 r, _ := strconv.Atoi(params["r"].(string)) 111 g, _ := strconv.Atoi(params["g"].(string)) 112 b, _ := strconv.Atoi(params["b"].(string)) 113 return j.SetRGB(r, g, b) 114 }) 115 j.AddCommand("Clear", func(params map[string]interface{}) interface{} { 116 return j.Clear() 117 }) 118 j.AddCommand("Home", func(params map[string]interface{}) interface{} { 119 return j.Home() 120 }) 121 j.AddCommand("Write", func(params map[string]interface{}) interface{} { 122 msg := params["msg"].(string) 123 return j.Write(msg) 124 }) 125 j.AddCommand("SetPosition", func(params map[string]interface{}) interface{} { 126 pos, _ := strconv.Atoi(params["pos"].(string)) 127 return j.SetPosition(pos) 128 }) 129 j.AddCommand("Scroll", func(params map[string]interface{}) interface{} { 130 lr, _ := strconv.ParseBool(params["lr"].(string)) 131 return j.Scroll(lr) 132 }) 133 134 return j 135 } 136 137 // Name returns the name the JHD1313M1 Driver was given when created. 138 func (h *JHD1313M1Driver) Name() string { return h.name } 139 140 // SetName sets the name for the JHD1313M1 Driver. 141 func (h *JHD1313M1Driver) SetName(n string) { h.name = n } 142 143 // Connection returns the driver connection to the device. 144 func (h *JHD1313M1Driver) Connection() gobot.Connection { 145 return h.connector.(gobot.Connection) 146 } 147 148 // Start starts the backlit and the screen and initializes the states. 149 func (h *JHD1313M1Driver) Start() (err error) { 150 bus := h.GetBusOrDefault(h.connector.DefaultI2cBus()) 151 152 if h.lcdConnection, err = h.connector.GetI2cConnection(h.lcdAddress, bus); err != nil { 153 return err 154 } 155 156 if h.rgbConnection, err = h.connector.GetI2cConnection(h.rgbAddress, bus); err != nil { 157 return err 158 } 159 160 // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! 161 // according to datasheet, we need at least 40ms after power rises above 2.7V 162 // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 163 time.Sleep(50 * time.Millisecond) 164 165 // this is according to the hitachi HD44780 datasheet 166 // page 45 figure 23 167 // Send function set command sequence 168 init_payload := []byte{LCD_CMD, LCD_FUNCTIONSET | LCD_2LINE} 169 if _, err := h.lcdConnection.Write(init_payload); err != nil { 170 return err 171 } 172 173 // wait more than 4.1ms 174 time.Sleep(4500 * time.Microsecond) 175 // second try 176 if _, err := h.lcdConnection.Write(init_payload); err != nil { 177 return err 178 } 179 180 time.Sleep(150 * time.Microsecond) 181 // third go 182 if _, err := h.lcdConnection.Write(init_payload); err != nil { 183 return err 184 } 185 186 if _, err := h.lcdConnection.Write([]byte{LCD_CMD, LCD_DISPLAYCONTROL | LCD_DISPLAYON}); err != nil { 187 return err 188 } 189 190 time.Sleep(100 * time.Microsecond) 191 if err := h.Clear(); err != nil { 192 return err 193 } 194 195 if _, err := h.lcdConnection.Write([]byte{LCD_CMD, LCD_ENTRYMODESET | LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT}); err != nil { 196 return err 197 } 198 199 if err := h.setReg(0, 0); err != nil { 200 return err 201 } 202 if err := h.setReg(1, 0); err != nil { 203 return err 204 } 205 if err := h.setReg(0x08, 0xAA); err != nil { 206 return err 207 } 208 209 if err := h.SetRGB(255, 255, 255); err != nil { 210 return err 211 } 212 213 return nil 214 } 215 216 // SetRGB sets the Red Green Blue value of backlit. 217 func (h *JHD1313M1Driver) SetRGB(r, g, b int) error { 218 if err := h.setReg(REG_RED, r); err != nil { 219 return err 220 } 221 if err := h.setReg(REG_GREEN, g); err != nil { 222 return err 223 } 224 return h.setReg(REG_BLUE, b) 225 } 226 227 // Clear clears the text on the lCD display. 228 func (h *JHD1313M1Driver) Clear() error { 229 err := h.command([]byte{LCD_CLEARDISPLAY}) 230 return err 231 } 232 233 // Home sets the cursor to the origin position on the display. 234 func (h *JHD1313M1Driver) Home() error { 235 err := h.command([]byte{LCD_RETURNHOME}) 236 // This wait fixes a race condition when calling home and clear back to back. 237 time.Sleep(2 * time.Millisecond) 238 return err 239 } 240 241 // Write displays the passed message on the screen. 242 func (h *JHD1313M1Driver) Write(message string) error { 243 // This wait fixes an odd bug where the clear function doesn't always work properly. 244 time.Sleep(1 * time.Millisecond) 245 for _, val := range message { 246 if val == '\n' { 247 if err := h.SetPosition(16); err != nil { 248 return err 249 } 250 continue 251 } 252 if _, err := h.lcdConnection.Write([]byte{LCD_DATA, byte(val)}); err != nil { 253 return err 254 } 255 } 256 return nil 257 } 258 259 // SetPosition sets the cursor and the data display to pos. 260 // 0..15 are the positions in the first display line. 261 // 16..32 are the positions in the second display line. 262 func (h *JHD1313M1Driver) SetPosition(pos int) (err error) { 263 if pos < 0 || pos > 31 { 264 err = jhd1313m1ErrInvalidPosition 265 return 266 } 267 offset := byte(pos) 268 if pos >= 16 { 269 offset -= 16 270 offset |= LCD_2NDLINEOFFSET 271 } 272 err = h.command([]byte{LCD_SETDDRAMADDR | offset}) 273 return 274 } 275 276 // Scroll sets the scrolling direction for the display, either left to right, or 277 // right to left. 278 func (h *JHD1313M1Driver) Scroll(lr bool) error { 279 if lr { 280 _, err := h.lcdConnection.Write([]byte{LCD_CMD, LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT}) 281 return err 282 } 283 284 _, err := h.lcdConnection.Write([]byte{LCD_CMD, LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT}) 285 return err 286 } 287 288 // Halt is a noop function. 289 func (h *JHD1313M1Driver) Halt() error { return nil } 290 291 // SetCustomChar sets one of the 8 CGRAM locations with a custom character. 292 // The custom character can be used by writing a byte of value 0 to 7. 293 // When you are using LCD as 5x8 dots in function set then you can define a total of 8 user defined patterns 294 // (1 Byte for each row and 8 rows for each pattern). 295 // Use http://www.8051projects.net/lcd-interfacing/lcd-custom-character.php to create your own 296 // characters. 297 // To use a custom character, write byte value of the custom character position as a string after 298 // having setup the custom character. 299 func (h *JHD1313M1Driver) SetCustomChar(pos int, charMap [8]byte) error { 300 if pos > 7 { 301 return fmt.Errorf("can't set a custom character at a position greater than 7") 302 } 303 location := uint8(pos) 304 if err := h.command([]byte{LCD_SETCGRAMADDR | (location << 3)}); err != nil { 305 return err 306 } 307 _, err := h.lcdConnection.Write(append([]byte{LCD_DATA}, charMap[:]...)) 308 return err 309 } 310 311 func (h *JHD1313M1Driver) setReg(command int, data int) error { 312 _, err := h.rgbConnection.Write([]byte{byte(command), byte(data)}) 313 return err 314 } 315 316 func (h *JHD1313M1Driver) command(buf []byte) error { 317 _, err := h.lcdConnection.Write(append([]byte{LCD_CMD}, buf...)) 318 return err 319 }