|
| 1 | +#!/usr/bin/pythonhttp://raspberrypi.local/editor |
| 2 | + |
| 3 | +# |
| 4 | +# based on code from lrvick and LiquidCrystal |
| 5 | +# lrvic - https://github.com/lrvick/raspi-hd44780/blob/master/hd44780.py |
| 6 | +# LiquidCrystal - https://github.com/arduino/Arduino/blob/master/libraries/LiquidCrystal/LiquidCrystal.cpp |
| 7 | + |
| 8 | + |
| 9 | +from time import sleep |
| 10 | +from Adafruit_I2C import Adafruit_I2C |
| 11 | +from Adafruit_MCP230xx import Adafruit_MCP230XX |
| 12 | +import smbus |
| 13 | + |
| 14 | + |
| 15 | +class Adafruit_CharLCDPlate: |
| 16 | + |
| 17 | + OUTPUT = 0 |
| 18 | + INPUT = 1 |
| 19 | + |
| 20 | + # LED colors |
| 21 | + RED = 0x01 |
| 22 | + GREEN = 0x02 |
| 23 | + BLUE = 0x04 |
| 24 | + YELLOW = 0x03 |
| 25 | + TEAL = 0x06 |
| 26 | + VIOLET = 0x05 |
| 27 | + ON = 0x07 |
| 28 | + OFF = 0x0 |
| 29 | + |
| 30 | + # buttons |
| 31 | + SELECT = 0 |
| 32 | + RIGHT = 1 |
| 33 | + DOWN = 2 |
| 34 | + UP = 3 |
| 35 | + LEFT = 4 |
| 36 | + |
| 37 | + # commands |
| 38 | + LCD_CLEARDISPLAY = 0x01 |
| 39 | + LCD_RETURNHOME = 0x02 |
| 40 | + LCD_ENTRYMODESET = 0x04 |
| 41 | + LCD_DISPLAYCONTROL = 0x08 |
| 42 | + LCD_CURSORSHIFT = 0x10 |
| 43 | + LCD_FUNCTIONSET = 0x20 |
| 44 | + LCD_SETCGRAMADDR = 0x40 |
| 45 | + LCD_SETDDRAMADDR = 0x80 |
| 46 | + |
| 47 | + # flags for display entry mode |
| 48 | + LCD_ENTRYRIGHT = 0x00 |
| 49 | + LCD_ENTRYLEFT = 0x02 |
| 50 | + LCD_ENTRYSHIFTINCREMENT = 0x01 |
| 51 | + LCD_ENTRYSHIFTDECREMENT = 0x00 |
| 52 | + |
| 53 | + # flags for display on/off control |
| 54 | + LCD_DISPLAYON = 0x04 |
| 55 | + LCD_DISPLAYOFF = 0x00 |
| 56 | + LCD_CURSORON = 0x02 |
| 57 | + LCD_CURSOROFF = 0x00 |
| 58 | + LCD_BLINKON = 0x01 |
| 59 | + LCD_BLINKOFF = 0x00 |
| 60 | + |
| 61 | + # flags for display/cursor shift |
| 62 | + LCD_DISPLAYMOVE = 0x08 |
| 63 | + LCD_CURSORMOVE = 0x00 |
| 64 | + |
| 65 | + # flags for display/cursor shift |
| 66 | + LCD_DISPLAYMOVE = 0x08 |
| 67 | + LCD_CURSORMOVE = 0x00 |
| 68 | + LCD_MOVERIGHT = 0x04 |
| 69 | + LCD_MOVELEFT = 0x00 |
| 70 | + |
| 71 | + # flags for function set |
| 72 | + LCD_8BITMODE = 0x10 |
| 73 | + LCD_4BITMODE = 0x00 |
| 74 | + LCD_2LINE = 0x08 |
| 75 | + LCD_1LINE = 0x00 |
| 76 | + LCD_5x10DOTS = 0x04 |
| 77 | + LCD_5x8DOTS = 0x00 |
| 78 | + |
| 79 | + |
| 80 | + |
| 81 | + |
| 82 | + def __init__(self, busnum=1, pin_rs=15, pin_e=13, pins_db=[12, 11, 10, 9], pin_rw=14): |
| 83 | + self.pin_rs = pin_rs |
| 84 | + self.pin_e = pin_e |
| 85 | + self.pin_rw = pin_rw |
| 86 | + self.pins_db = pins_db |
| 87 | + |
| 88 | + self.mcp = Adafruit_MCP230XX(busnum = busnum, address = 0x20, num_gpios = 16) |
| 89 | + |
| 90 | + self.mcp.config(self.pin_e, self.OUTPUT) |
| 91 | + self.mcp.config(self.pin_rs, self.OUTPUT) |
| 92 | + self.mcp.config(self.pin_rw, self.OUTPUT) |
| 93 | + self.mcp.output(self.pin_rw, 0) |
| 94 | + self.mcp.output(self.pin_e, 0) |
| 95 | + |
| 96 | + for pin in self.pins_db: |
| 97 | + self.mcp.config(pin, self.OUTPUT) |
| 98 | + |
| 99 | + self.write4bits(0x33) # initialization |
| 100 | + self.write4bits(0x32) # initialization |
| 101 | + self.write4bits(0x28) # 2 line 5x8 matrix |
| 102 | + self.write4bits(0x0C) # turn cursor off 0x0E to enable cursor |
| 103 | + self.write4bits(0x06) # set cursor incrementing and no shift of display |
| 104 | + |
| 105 | + self.displaycontrol = self.LCD_DISPLAYON | self.LCD_CURSOROFF | self.LCD_BLINKOFF |
| 106 | + |
| 107 | + self.displayfunction = self.LCD_4BITMODE | self.LCD_1LINE | self.LCD_5x8DOTS |
| 108 | + self.displayfunction |= self.LCD_2LINE |
| 109 | + |
| 110 | + """ Initialize to default text direction (for romance languages) """ |
| 111 | + self.displaymode = self.LCD_ENTRYLEFT | self.LCD_ENTRYSHIFTDECREMENT |
| 112 | + self.write4bits(self.LCD_ENTRYMODESET | self.displaymode) # set the entry mode |
| 113 | + |
| 114 | + # turn on backlights! |
| 115 | + self.mcp.config(6, self.mcp.OUTPUT) |
| 116 | + self.mcp.config(7, self.mcp.OUTPUT) |
| 117 | + self.mcp.config(8, self.mcp.OUTPUT) |
| 118 | + self.mcp.output(6, 0) # red |
| 119 | + self.mcp.output(7, 0) # green |
| 120 | + self.mcp.output(8, 0) # blue |
| 121 | + |
| 122 | + # turn on pullups |
| 123 | + self.mcp.pullup(self.SELECT, True) |
| 124 | + self.mcp.pullup(self.LEFT, True) |
| 125 | + self.mcp.pullup(self.RIGHT, True) |
| 126 | + self.mcp.pullup(self.UP, True) |
| 127 | + self.mcp.pullup(self.DOWN, True) |
| 128 | + self.mcp.config(self.SELECT, self.mcp.INPUT) |
| 129 | + self.mcp.config(self.LEFT, self.mcp.INPUT) |
| 130 | + self.mcp.config(self.RIGHT, self.mcp.INPUT) |
| 131 | + self.mcp.config(self.DOWN, self.mcp.INPUT) |
| 132 | + self.mcp.config(self.UP, self.mcp.INPUT) |
| 133 | + |
| 134 | + def begin(self, cols, lines): |
| 135 | + if (lines > 1): |
| 136 | + self.numlines = lines |
| 137 | + self.displayfunction |= self.LCD_2LINE |
| 138 | + self.currline = 0 |
| 139 | + self.clear() |
| 140 | + |
| 141 | + def home(self): |
| 142 | + self.write4bits(self.LCD_RETURNHOME) # set cursor position to zero |
| 143 | + self.waitBFlow() #wait for Busy flag low |
| 144 | + |
| 145 | + |
| 146 | + def clear(self): |
| 147 | + self.write4bits(self.LCD_CLEARDISPLAY) # command to clear display |
| 148 | + self.waitBFlow() #wait for Busy flag low |
| 149 | + |
| 150 | + def setCursor(self, col, row): |
| 151 | + self.row_offsets = [ 0x00, 0x40, 0x14, 0x54 ] |
| 152 | + if ( row > self.numlines ): |
| 153 | + row = self.numlines - 1 # we count rows starting w/0 |
| 154 | + self.write4bits(self.LCD_SETDDRAMADDR | (col + self.row_offsets[row])) |
| 155 | + |
| 156 | + def noDisplay(self): |
| 157 | + """ Turn the display off (quickly) """ |
| 158 | + self.displaycontrol &= ~self.LCD_DISPLAYON |
| 159 | + self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol) |
| 160 | + |
| 161 | + def display(self): |
| 162 | + """ Turn the display on (quickly) """ |
| 163 | + self.displaycontrol |= self.LCD_DISPLAYON |
| 164 | + self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol) |
| 165 | + |
| 166 | + def noCursor(self): |
| 167 | + """ underline cursor off """ |
| 168 | + self.displaycontrol &= ~self.LCD_CURSORON |
| 169 | + self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol) |
| 170 | + |
| 171 | + |
| 172 | + def cursor(self): |
| 173 | + """ underline Cursor On """ |
| 174 | + self.displaycontrol |= self.LCD_CURSORON |
| 175 | + self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol) |
| 176 | + |
| 177 | + def ToggleCursor(self): |
| 178 | + """ Toggles the underline cursor On/Off bb""" |
| 179 | + self.displaycontrol ^= self.LCD_CURSORON |
| 180 | + self.delayMicroseconds(200000) |
| 181 | + self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol) |
| 182 | + |
| 183 | + def noBlink(self): |
| 184 | + """ Turn off the blinking cursor """ |
| 185 | + self.displaycontrol &= ~self.LCD_BLINKON |
| 186 | + self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol) |
| 187 | + |
| 188 | + def blink(self): |
| 189 | + """ Turn on the blinking cursor""" |
| 190 | + self.displaycontrol |= self.LCD_BLINKON |
| 191 | + self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol) |
| 192 | + |
| 193 | + def ToggleBlink(self): |
| 194 | + """ Toggles the blinking cursor""" |
| 195 | + self.displaycontrol ^= self.LCD_BLINKON |
| 196 | + self.delayMicroseconds(200000) |
| 197 | + self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol) |
| 198 | + |
| 199 | + |
| 200 | + def DisplayLeft(self): |
| 201 | + """ These commands scroll the display without changing the RAM """ |
| 202 | + self.write4bits(self.LCD_CURSORSHIFT | self.LCD_DISPLAYMOVE | self.LCD_MOVELEFT) |
| 203 | + |
| 204 | + def scrollDisplayRight(self): |
| 205 | + """ These commands scroll the display without changing the RAM """ |
| 206 | + self.write4bits(self.LCD_CURSORSHIFT | self.LCD_DISPLAYMOVE | self.LCD_MOVERIGHT); |
| 207 | + |
| 208 | + def leftToRight(self): |
| 209 | + """ This is for text that flows Left to Right """ |
| 210 | + self.displaymode |= self.LCD_ENTRYLEFT |
| 211 | + self.write4bits(self.LCD_ENTRYMODESET | self.displaymode); |
| 212 | + |
| 213 | + def rightToLeft(self): |
| 214 | + """ This is for text that flows Right to Left """ |
| 215 | + self.displaymode &= ~self.LCD_ENTRYLEFT |
| 216 | + self.write4bits(self.LCD_ENTRYMODESET | self.displaymode) |
| 217 | + |
| 218 | + def autoscroll(self): |
| 219 | + """ This will 'right justify' text from the cursor """ |
| 220 | + self.displaymode |= self.LCD_ENTRYSHIFTINCREMENT |
| 221 | + self.write4bits(self.LCD_ENTRYMODESET | self.displaymode) |
| 222 | + |
| 223 | + def noAutoscroll(self): |
| 224 | + """ This will 'left justify' text from the cursor """ |
| 225 | + self.displaymode &= ~self.LCD_ENTRYSHIFTINCREMENT |
| 226 | + self.write4bits(self.LCD_ENTRYMODESET | self.displaymode) |
| 227 | + |
| 228 | + def write4bits(self, bits, char_mode=False): |
| 229 | + """ Send command to LCD """ |
| 230 | + #self.delayMicroseconds(1000) # 1000 microsecond sleep |
| 231 | + bits=bin(bits)[2:].zfill(8) |
| 232 | + self.mcp.output(self.pin_rs, char_mode) |
| 233 | + |
| 234 | + for i in range(4): |
| 235 | + if bits[i] == "1": |
| 236 | + self.mcp.output(self.pins_db[::-1][i], True) |
| 237 | + else: |
| 238 | + self.mcp.output(self.pins_db[::-1][i], False) |
| 239 | + self.pulseEnable() |
| 240 | + |
| 241 | + for i in range(4,8): |
| 242 | + if bits[i] == "1": |
| 243 | + self.mcp.output(self.pins_db[::-1][i-4], True) |
| 244 | + else: |
| 245 | + self.mcp.output(self.pins_db[::-1][i-4], False) |
| 246 | + self.pulseEnable() |
| 247 | + |
| 248 | + #See pg. 24 and 58 HD44780.pdf and http://www.avrbeginners.net/ Standard LCD bit mode |
| 249 | + def read4bits(self, char_mode=False): |
| 250 | + """ Get Data from LCD, when char_mode = 0 get Busy Flag and Address Counter, else get RAM data """ |
| 251 | +# print "First:", bin(self.mcp.direction)[2:].zfill(16) |
| 252 | + self.mcp.output(self.pin_rs, char_mode) # when char_mode = 0 get Busy Flag and Address Counter, else get RAM data |
| 253 | + self.mcp.output(self.pin_rw, 1) #set rw to 1 |
| 254 | +# Configure pins for input |
| 255 | + makeinput=True |
| 256 | + if makeinput: |
| 257 | + for pin in self.pins_db: |
| 258 | + self.mcp.config(pin, self.INPUT) |
| 259 | + self.mcp.output(self.pin_e, True) # set Enable high and keep while read first nibble |
| 260 | + bits = range(8) |
| 261 | + # get the pins values |
| 262 | + for i in range(4): |
| 263 | + bt = self.mcp.input(self.pins_db[::-1][i], makeinput) # if makeinput is False, pin direction checking is supressed in input |
| 264 | + bits[i]=bt |
| 265 | + #print i,bt,bits[i] |
| 266 | + self.mcp.output(self.pin_e, False) # set Enable low to finish first nibble |
| 267 | + self.mcp.output(self.pin_e, True) # set Enable high and keep while read 2nd nibble |
| 268 | + # get the pins values |
| 269 | + for i in range(4,8): |
| 270 | + bt = self.mcp.input(self.pins_db[::-1][i-4],makeinput) |
| 271 | + bits[i]=bt |
| 272 | + #print i,bt,bits[i] |
| 273 | + self.mcp.output(self.pin_e, False) # set Enable low to finish 2nd nibble |
| 274 | + # restore to pins to output and rw to 0 |
| 275 | + for pin in self.pins_db: |
| 276 | + self.mcp.config(pin, self.OUTPUT) |
| 277 | + self.mcp.output(self.pin_rw, 0) # return rw to 0 |
| 278 | +# print "Last :", bin(self.mcp.direction)[2:].zfill(16) |
| 279 | + return bits; |
| 280 | + |
| 281 | + def readBF(self): |
| 282 | + """ Get Data from LCD, when char_mode = 0 get Busy Flag and Address Counter, else get RAM data """ |
| 283 | +# print "First:", bin(self.mcp.direction)[2:].zfill(16) |
| 284 | + self.mcp.output(self.pin_rs, 0) # when char_mode = 0 get Busy Flag and Address Counter, else get RAM data |
| 285 | + self.mcp.output(self.pin_rw, 1) #set rw to 1 |
| 286 | +# Configure pins for input |
| 287 | + makeinput=True |
| 288 | + self.mcp.config(self.pins_db[::-1][1], self.INPUT) |
| 289 | + self.mcp.output(self.pin_e, True) # set Enable high and keep while read first nibble |
| 290 | + # get the pins values |
| 291 | + bt = self.mcp.input(self.pins_db[::-1][1], makeinput) # if makeinput is False, pin direction checking is supressed in input |
| 292 | + self.mcp.output(self.pin_e, False) # set Enable low to finish first nibble |
| 293 | + self.pulseEnable() # one more pulse to get (but ingore the 2nd nibble) |
| 294 | + # restore to pins to output and rw to 0 |
| 295 | + self.mcp.config(self.pins_db[::-1][1], self.OUTPUT) |
| 296 | + self.mcp.output(self.pin_rw, 0) # return rw to 0 |
| 297 | + return bt==1 |
| 298 | + |
| 299 | + def waitBFlow(self): |
| 300 | + for cnt in range(100000): |
| 301 | + if not self.readBF(): |
| 302 | + #print cnt |
| 303 | + return |
| 304 | + print "timed out of waitBFlow" |
| 305 | + |
| 306 | + def delayMicroseconds(self, microseconds): |
| 307 | + seconds = microseconds / 1000000 # divide microseconds by 1 million for seconds |
| 308 | + sleep(seconds) |
| 309 | + |
| 310 | + def pulseEnable(self): |
| 311 | + self.mcp.output(self.pin_e, True) |
| 312 | + self.delayMicroseconds(1) # 1 microsecond pause - enable pulse must be > 450ns |
| 313 | + self.mcp.output(self.pin_e, False) |
| 314 | + #self.delayMicroseconds(1) # commands need > 37us to settle |
| 315 | + |
| 316 | + def message(self, text): |
| 317 | + """ Send string to LCD. Newline wraps to second line""" |
| 318 | + for char in text: |
| 319 | + if char == '\n': |
| 320 | + self.write4bits(0xC0) # set DDRAM address 0x40 start of second line |
| 321 | + else: |
| 322 | + self.write4bits(ord(char),True) |
| 323 | + |
| 324 | + def backlight(self, color): |
| 325 | + self.mcp.output(6, not color & 0x01) |
| 326 | + self.mcp.output(7, not color & 0x02) |
| 327 | + self.mcp.output(8, not color & 0x04) |
| 328 | + |
| 329 | + def buttonPressed(self, buttonname): |
| 330 | + if (buttonname > self.LEFT): |
| 331 | + return false |
| 332 | + |
| 333 | + return not self.mcp.input(buttonname) |
| 334 | + |
| 335 | + |
| 336 | +if __name__ == '__main__': |
| 337 | + |
| 338 | + lcd = Adafruit_CharLCDPlate(busnum = 1) |
| 339 | + lcd.clear() |
| 340 | + lcd.message("Adafruit RGB LCD\nPlate w/Keypad!") |
| 341 | + sleep(1) |
| 342 | + |
| 343 | + print " Cycle thru backlight colors 3 times " |
| 344 | + |
| 345 | + for i in range(3): |
| 346 | + print "red" |
| 347 | + lcd.backlight(lcd.RED) |
| 348 | + sleep(1) |
| 349 | + print "yellow" |
| 350 | + lcd.backlight(lcd.YELLOW) |
| 351 | + sleep(1) |
| 352 | + print "green" |
| 353 | + lcd.backlight(lcd.GREEN) |
| 354 | + sleep(1) |
| 355 | + print "teal" |
| 356 | + lcd.backlight(lcd.TEAL) |
| 357 | + sleep(1) |
| 358 | + print "blue" |
| 359 | + lcd.backlight(lcd.BLUE) |
| 360 | + sleep(1) |
| 361 | + print "violet" |
| 362 | + lcd.backlight(lcd.VIOLET) |
| 363 | + sleep(1) |
| 364 | + print "off" |
| 365 | + lcd.backlight(lcd.OFF) |
| 366 | + sleep(1) |
| 367 | + print "on" |
| 368 | + lcd.backlight(lcd.ON) |
| 369 | + sleep(1) |
| 370 | + |
| 371 | + print " Try buttons on plate" |
| 372 | + |
| 373 | + while 1: |
| 374 | + if (lcd.buttonPressed(lcd.LEFT)): |
| 375 | + lcd.backlight(lcd.RED) |
| 376 | + |
| 377 | + if (lcd.buttonPressed(lcd.UP)): |
| 378 | + lcd.backlight(lcd.BLUE) |
| 379 | + |
| 380 | + if (lcd.buttonPressed(lcd.DOWN)): |
| 381 | + lcd.backlight(lcd.GREEN) |
| 382 | + |
| 383 | + if (lcd.buttonPressed(lcd.RIGHT)): |
| 384 | + lcd.backlight(lcd.VIOLET) |
| 385 | + |
| 386 | + if (lcd.buttonPressed(lcd.SELECT)): |
| 387 | + lcd.backlight(lcd.ON) |
| 388 | + |
| 389 | + |
0 commit comments