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