Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python
- # Based on Adafruit Adafruit_CharLCDPlate.py
- # ==========================================
- # This script enable the MCP23017 Interruptions mode on port A
- # That means an interruption will be generated each time a button is pressed on Adafruit CharLCDPlate
- # Then if correctly hardwired to a Raspberry Pi GPIO,
- # this interrupt will in turn generates an interrupt on the Raspberry Pi itself,
- # allowing us to launch something...
- # With this method we dont have to constantly poll the buttons (we save CPU time)
- # ==========================================
- # Guy / April 2013 / Version RPi.GPIO != RPIO
- # Python library for Adafruit RGB-backlit LCD plate for Raspberry Pi.
- # Written by Adafruit Industries. M.I.T. license.
- # This is essentially a complete rewrite, but the calling syntax
- # and constants are based on code from lrvick and LiquidCrystal.
- # lrvic - https://github.com/lrvick/raspi-hd44780/blob/master/hd44780.py
- # LiquidCrystal - https://github.com/arduino/Arduino/blob/master/libraries/LiquidCrystal/LiquidCrystal.cpp
- from Adafruit_I2C import Adafruit_I2C
- from time import sleep, strftime, time
- from sys import exit
- import RPi.GPIO as GPIO
- #import os
- class Adafruit_CharLCDPlate(Adafruit_I2C):
- # ----------------------------------------------------------------------
- # Constants
- # Port expander registers
- MCP23017_IOCON_BANK0 = 0x0A # IOCON when Bank 0 active
- MCP23017_IOCON_BANK1 = 0x15 # IOCON when Bank 1 active
- # These are register addresses when in Bank 1 only:
- MCP23017_GPIOA = 0x09
- MCP23017_IODIRB = 0x10
- MCP23017_GPIOB = 0x19
- MCP23017_GPINTENA = 0x02
- MCP23017_GPINTENB = 0x12
- MCP23017_DEFVALA = 0x03
- MCP23017_DEFVALB = 0x13
- MCP23017_INTCONA = 0x04
- MCP23017_INTCONB = 0x14
- MCP23017_INTCAPA = 0x08
- MCP23017_INTCAPB = 0x18
- # Port expander input pin definitions
- SELECT = 0
- RIGHT = 1
- DOWN = 2
- UP = 3
- LEFT = 4
- # LED colors
- # global OFF, RED, GREEN, BLUE, YELLOW, TEAL, VIOLET, WHITE, ON
- OFF = 0x00
- RED = 0x01
- GREEN = 0x02
- BLUE = 0x04
- YELLOW = RED + GREEN
- TEAL = GREEN + BLUE
- VIOLET = RED + BLUE
- WHITE = RED + GREEN + BLUE
- ON = RED + GREEN + BLUE
- # LCD Commands
- LCD_CLEARDISPLAY = 0x01
- LCD_RETURNHOME = 0x02
- LCD_ENTRYMODESET = 0x04
- LCD_DISPLAYCONTROL = 0x08
- LCD_CURSORSHIFT = 0x10
- LCD_FUNCTIONSET = 0x20
- LCD_SETCGRAMADDR = 0x40
- LCD_SETDDRAMADDR = 0x80
- # Flags for display on/off control
- LCD_DISPLAYON = 0x04
- LCD_DISPLAYOFF = 0x00
- LCD_CURSORON = 0x02
- LCD_CURSOROFF = 0x00
- LCD_BLINKON = 0x01
- LCD_BLINKOFF = 0x00
- # Flags for display entry mode
- LCD_ENTRYRIGHT = 0x00
- LCD_ENTRYLEFT = 0x02
- LCD_ENTRYSHIFTINCREMENT = 0x01
- LCD_ENTRYSHIFTDECREMENT = 0x00
- # Flags for display/cursor shift
- LCD_DISPLAYMOVE = 0x08
- LCD_CURSORMOVE = 0x00
- LCD_MOVERIGHT = 0x04
- LCD_MOVELEFT = 0x00
- # ----------------------------------------------------------------------
- # Constructor
- def __init__(self, busnum=-1, addr=0x20, debug=False):
- self.i2c = Adafruit_I2C(addr, busnum, debug)
- # I2C is relatively slow. MCP output port states are cached
- # so we don't need to constantly poll-and-change bit states.
- self.porta, self.portb, self.ddrb = 0, 0, 0b00010000
- # Set MCP23017 IOCON register to Bank 0 with sequential operation.
- # If chip is already set for Bank 0, this will just write to OLATB,
- # which won't seriously bother anything on the plate right now
- # (blue backlight LED will come on, but that's done in the next
- # step anyway).
- self.i2c.bus.write_byte_data(
- self.i2c.address, self.MCP23017_IOCON_BANK1, 0)
- # Brute force reload ALL registers to known state. This also
- # sets up all the input pins, pull-ups, etc. for the Pi Plate.
- self.i2c.bus.write_i2c_block_data(
- self.i2c.address, 0,
- [ 0b00111111, # IODIRA R+G LEDs=outputs, buttons=inputs
- self.ddrb , # IODIRB LCD D7=input, Blue LED=output
- 0b00111111, # IPOLA Invert polarity on button inputs
- 0b00000000, # IPOLB
- 0b00011111, # GPINTENA ENABLE interrupt-on-change MODIFICATION GUY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- 0b00000000, # GPINTENB
- 0b00000000, # DEFVALA MODIF GUY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- 0b00000000, # DEFVALB
- 0b00000000, # INTCONA MODIF GUY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- 0b00000000, # INTCONB
- 0b00000000, # IOCON MODIF GUY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- 0b00000000, # IOCON MODIF GUY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- 0b00111111, # GPPUA Enable pull-ups on buttons
- 0b00000000, # GPPUB
- 0b00000000, # INTFA
- 0b00000000, # INTFB
- 0b00000000, # INTCAPA
- 0b00000000, # INTCAPB
- self.porta, # GPIOA
- self.portb, # GPIOB
- self.porta, # OLATA 0 on all outputs; side effect of
- self.portb ]) # OLATB turning on R+G+B backlight LEDs.
- # Switch to Bank 1 and disable sequential operation.
- # From this point forward, the register addresses do NOT match
- # the list immediately above. Instead, use the constants defined
- # at the start of the class. Also, the address register will no
- # longer increment automatically after this -- multi-byte
- # operations must be broken down into single-byte calls.
- self.i2c.bus.write_byte_data(
- self.i2c.address, self.MCP23017_IOCON_BANK0, 0b10100000)
- self.displayshift = (self.LCD_CURSORMOVE |
- self.LCD_MOVERIGHT)
- self.displaymode = (self.LCD_ENTRYLEFT |
- self.LCD_ENTRYSHIFTDECREMENT)
- self.displaycontrol = (self.LCD_DISPLAYON |
- self.LCD_CURSOROFF |
- self.LCD_BLINKOFF)
- self.write(0x33) # Init
- self.write(0x32) # Init
- self.write(0x28) # 2 line 5x8 matrix
- self.write(self.LCD_CLEARDISPLAY)
- self.write(self.LCD_CURSORSHIFT | self.displayshift)
- self.write(self.LCD_ENTRYMODESET | self.displaymode)
- self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
- self.write(self.LCD_RETURNHOME)
- # ----------------------------------------------------------------------
- # Write operations
- # The LCD data pins (D4-D7) connect to MCP pins 12-9 (PORTB4-1), in
- # that order. Because this sequence is 'reversed,' a direct shift
- # won't work. This table remaps 4-bit data values to MCP PORTB
- # outputs, incorporating both the reverse and shift.
- flip = ( 0b00000000, 0b00010000, 0b00001000, 0b00011000,
- 0b00000100, 0b00010100, 0b00001100, 0b00011100,
- 0b00000010, 0b00010010, 0b00001010, 0b00011010,
- 0b00000110, 0b00010110, 0b00001110, 0b00011110 )
- # Low-level 4-bit interface for LCD output. This doesn't actually
- # write data, just returns a byte array of the PORTB state over time.
- # Can concatenate the output of multiple calls (up to 8) for more
- # efficient batch write.
- def out4(self, bitmask, value):
- hi = bitmask | self.flip[value >> 4]
- lo = bitmask | self.flip[value & 0x0F]
- return [hi | 0b00100000, hi, lo | 0b00100000, lo]
- # The speed of LCD accesses is inherently limited by I2C through the
- # port expander. A 'well behaved program' is expected to poll the
- # LCD to know that a prior instruction completed. But the timing of
- # most instructions is a known uniform 37 mS. The enable strobe
- # can't even be twiddled that fast through I2C, so it's a safe bet
- # with these instructions to not waste time polling (which requires
- # several I2C transfers for reconfiguring the port direction).
- # The D7 pin is set as input when a potentially time-consuming
- # instruction has been issued (e.g. screen clear), as well as on
- # startup, and polling will then occur before more commands or data
- # are issued.
- pollables = ( LCD_CLEARDISPLAY, LCD_RETURNHOME )
- # Write byte, list or string value to LCD
- def write(self, value, char_mode=False):
- """ Send command/data to LCD """
- # If pin D7 is in input state, poll LCD busy flag until clear.
- if self.ddrb & 0b00010000:
- lo = (self.portb & 0b00000001) | 0b01000000
- hi = lo | 0b00100000 # E=1 (strobe)
- self.i2c.bus.write_byte_data(
- self.i2c.address, self.MCP23017_GPIOB, lo)
- while True:
- # Strobe high (enable)
- self.i2c.bus.write_byte(self.i2c.address, hi)
- # First nybble contains busy state
- bits = self.i2c.bus.read_byte(self.i2c.address)
- # Strobe low, high, low. Second nybble (A3) is ignored.
- self.i2c.bus.write_i2c_block_data(
- self.i2c.address, self.MCP23017_GPIOB, [lo, hi, lo])
- if (bits & 0b00000010) == 0: break # D7=0, not busy
- self.portb = lo
- # Polling complete, change D7 pin to output
- self.ddrb &= 0b11101111
- self.i2c.bus.write_byte_data(self.i2c.address,
- self.MCP23017_IODIRB, self.ddrb)
- bitmask = self.portb & 0b00000001 # Mask out PORTB LCD control bits
- if char_mode: bitmask |= 0b10000000 # Set data bit if not a command
- # If string or list, iterate through multiple write ops
- if isinstance(value, str):
- last = len(value) - 1 # Last character in string
- data = [] # Start with blank list
- for i, v in enumerate(value): # For each character...
- # Append 4 bytes to list representing PORTB over time.
- # First the high 4 data bits with strobe (enable) set
- # and unset, then same with low 4 data bits (strobe 1/0).
- data.extend(self.out4(bitmask, ord(v)))
- # I2C block data write is limited to 32 bytes max.
- # If limit reached, write data so far and clear.
- # Also do this on last byte if not otherwise handled.
- if (len(data) >= 32) or (i == last):
- self.i2c.bus.write_i2c_block_data(
- self.i2c.address, self.MCP23017_GPIOB, data)
- self.portb = data[-1] # Save state of last byte out
- data = [] # Clear list for next iteration
- elif isinstance(value, list):
- # Same as above, but for list instead of string
- last = len(value) - 1
- data = []
- for i, v in enumerate(value):
- data.extend(self.out4(bitmask, v))
- if (len(data) >= 32) or (i == last):
- self.i2c.bus.write_i2c_block_data(
- self.i2c.address, self.MCP23017_GPIOB, data)
- self.portb = data[-1]
- data = []
- else:
- # Single byte
- data = self.out4(bitmask, value)
- self.i2c.bus.write_i2c_block_data(
- self.i2c.address, self.MCP23017_GPIOB, data)
- self.portb = data[-1]
- # If a poll-worthy instruction was issued, reconfigure D7
- # pin as input to indicate need for polling on next call.
- if (not char_mode) and (value in self.pollables):
- self.ddrb |= 0b00010000
- self.i2c.bus.write_byte_data(self.i2c.address,
- self.MCP23017_IODIRB, self.ddrb)
- # ----------------------------------------------------------------------
- # Utility methods
- def begin(self, cols, lines):
- self.currline = 0
- self.numlines = lines
- self.clear()
- # Puts the MCP23017 back in Bank 0 + sequential write mode so
- # that other code using the 'classic' library can still work.
- # Any code using this newer version of the library should
- # consider adding an atexit() handler that calls this.
- def stop(self):
- self.porta = 0b11000000 # Turn off LEDs on the way out
- self.portb = 0b00000001
- sleep(0.0015)
- self.i2c.bus.write_byte_data(
- self.i2c.address, self.MCP23017_IOCON_BANK1, 0)
- self.i2c.bus.write_i2c_block_data(
- self.i2c.address, 0,
- [ 0b00111111, # IODIRA
- self.ddrb , # IODIRB
- 0b00000000, # IPOLA
- 0b00000000, # IPOLB
- 0b00000000, # GPINTENA
- 0b00000000, # GPINTENB
- 0b00000000, # DEFVALA
- 0b00000000, # DEFVALB
- 0b00000000, # INTCONA
- 0b00000000, # INTCONB
- 0b00000000, # IOCON
- 0b00000000, # IOCON
- 0b00111111, # GPPUA
- 0b00000000, # GPPUB
- 0b00000000, # INTFA
- 0b00000000, # INTFB
- 0b00000000, # INTCAPA
- 0b00000000, # INTCAPB
- self.porta, # GPIOA
- self.portb, # GPIOB
- self.porta, # OLATA
- self.portb ]) # OLATB
- def clear(self):
- self.write(self.LCD_CLEARDISPLAY)
- def message(self, text):
- """ Send string to LCD. Newline wraps to second line"""
- lines = text.split('\n') # Split at newline(s)
- for line in lines: # Render each substring...
- self.write(line, True)
- if len(lines) > 1: # If newline(s),
- self.write(0xC0) # set DDRAM address to second line
- def backlight(self, color):
- c = ~color
- self.porta = (self.porta & 0b00111111) | ((c & 0b011) << 6)
- self.portb = (self.portb & 0b11111110) | ((c & 0b100) >> 2)
- # Has to be done as two writes because sequential operation is off.
- self.i2c.bus.write_byte_data(
- self.i2c.address, self.MCP23017_GPIOA, self.porta)
- self.i2c.bus.write_byte_data(
- self.i2c.address, self.MCP23017_GPIOB, self.portb)
- def buttonPressed(self, b):
- return (self.i2c.readU8(self.MCP23017_GPIOA) >> b) & 1
- def ReadGPINTENA(self):
- return (self.i2c.readU8(self.MCP23017_GPINTENA))
- def ReadDEFVALA(self):
- return (self.i2c.readU8(self.MCP23017_DEFVALA))
- def ReadINTCONA(self):
- return (self.i2c.readU8(self.MCP23017_INTCONA))
- def ReadINTCAPA(self):
- return (self.i2c.readU8(self.MCP23017_INTCAPA) & 0b00011111)
- def gpio_callback(self, channel):
- # print("gpio %s: %s" % (gpio_id, val))
- sleep(0.3) # wait button release Not very good to try by reading GPIO # not needed since debounce # debounce doesn't give expected results
- global BUTTON
- BUTTON = lcd.ReadINTCAPA()
- def RunMenu(self):
- global STATE
- if STATE == 1: # Display menu
- TEXT = m[MENU] + "\nSELECT UP/DOWN"
- # print TEXT
- lcd.clear()
- lcd.message(TEXT)
- elif STATE == 2: # Select has been pressed, a menu item choosen
- if MENU == 0: # cpu temp
- f = open('/sys/class/thermal/thermal_zone0/temp', 'r')
- TEMP = f.read()
- LIST = list(TEMP)
- LIST.insert(2, '.')
- TEMP = "".join(LIST)
- TEXT = "Temp CPU:" + TEMP # + "\nBUTTON = " + str(BUTTON)
- lcd.clear()
- lcd.message(TEXT)
- elif MENU == 1: # gpu temp
- TEXT = "Not available :("
- lcd.clear()
- lcd.message(TEXT)
- elif MENU == 2: # cpu frequency
- f = open('/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq', 'r')
- FREQ = f.read()
- TEXT = "Freq.=" + FREQ
- lcd.clear()
- lcd.message(TEXT)
- elif MENU == 3: # time
- TEXT = strftime('%H:%M %d-%b-%y')
- lcd.clear()
- lcd.message(TEXT)
- elif MENU == 4: # exit
- lcd.clear()
- lcd.backlight(lcd.OFF)
- GPIO.remove_event_detect(ENTRY)
- exit()
- STATE = 0
- # ----------------------------------------------------------------------
- # Test code
- if __name__ == '__main__':
- lcd = Adafruit_CharLCDPlate()
- col = (lcd.RED, lcd.YELLOW, lcd.GREEN, lcd.TEAL, lcd.BLUE, lcd.VIOLET, lcd.OFF, lcd.ON)
- m = ("Temp CPU", "Temp GPU", "Frequecy", "Time", "Exit program")
- COLOR = 6 # COLOR varies 0 to 7 starts with off
- STATE = 0
- BUTTON = 0
- MENU = 0
- FirstCall = True
- TimeMax = 30 # lcd off if no button pressed after TimeMax seconds
- GPIO.setmode(GPIO.BCM)
- GPIO.setwarnings(False)
- ENTRY = 23
- GPIO.setup(ENTRY, GPIO.IN)
- lcd.begin(16, 2)
- lcd.clear()
- lcd.message("Initialization\n......")
- sleep(3)
- lcd.ReadINTCAPA() # Reset MCP23017 Interrupt by reading INTCAPA then we are ready to generate another Interrupt
- lcd.backlight(lcd.OFF)
- lcd.clear()
- lcd.message("Init done\nInterrupt enable")
- # GPIO.wait_for_interrupts(threaded=True)
- # GPIO.add_interrupt_callback(23, lcd.gpio_callback, edge='falling', threaded_callback=True, debounce_timeout_ms=150)
- # with debounce sometimes the progran freeze... Back to sleep(0.3) in gpio_callback
- # GPIO.add_interrupt_callback(23, lcd.gpio_callback, edge='falling', threaded_callback=True)
- GPIO.add_event_detect(ENTRY, GPIO.FALLING, lcd.gpio_callback)
- while True:
- if BUTTON != 0: # A button has been pressed?
- # We go through this loop once only each time a BUTTON is pressed
- StartTime = time()
- if FirstCall == True:
- FirstCall = False
- lcd.clear()
- lcd.message("LEFT/RIGHT=COLOR\nUP/DOWN= MENU")
- else:
- if BUTTON == 1:
- STATE = 2
- lcd.RunMenu()
- elif BUTTON == 8: # Menu UP
- MENU = MENU + 1
- if MENU > 4: # Menu varies 0 to 4
- MENU = 0
- STATE = 1
- lcd.RunMenu()
- elif BUTTON == 4: # Menu DOWN
- MENU = MENU - 1
- if MENU < 0: # Menu varies 0 to 4
- MENU = 4
- STATE = 1
- lcd.RunMenu()
- elif BUTTON == 16: # Color down
- COLOR = COLOR - 1
- if COLOR < 0:
- COLOR = 7
- lcd.backlight(col[COLOR])
- elif BUTTON == 2: # Color up
- COLOR = COLOR + 1
- if COLOR > 7:
- COLOR = 0
- lcd.backlight(col[COLOR])
- BUTTON = 0 # BUTTON will be set by gpio_callback if an interrupt occurs i.e. a button is pressed
- if FirstCall == False:
- elapsed = (time() - StartTime)
- if elapsed > TimeMax:
- lcd.clear()
- lcd.backlight(lcd.OFF)
- FirstCall = True
- sleep(0.1) # Without this python goes in the loop so fast that it uses all CPU ressources: top reports python uses 98% of CPU.
- # I don't like that. And we use interrupt to minimize the use of cpu!.... ;-)
- print "I never come HERE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement