Interrupts do it better :-)
Moderators: adafruit_support_bill, adafruit

Forum rules
Talk about Adafruit Raspberry Pi® accessories! Please do not ask for Linux support, this is for Adafruit products only! For Raspberry Pi help please visit: http://www.raspberrypi.org/phpBB3/

Interrupts do it better :-)

by JumpZero on Sun Apr 14, 2013 8:46 pm

Hello,

Not sure to post on the right place. Anyway...
I am the happy owner of an Adafruit RGB 16x2 LCD+Keypad Kit, when reading MCP23017 datasheet, I noticed the chip can generates interrupts. On the other hand the latest RPIO (0.10.0) can handle Raspberry pi GPIO interrupts, so it was just a matter of linking them. I have coded a python program based on Adafruit_CharLCDPlate.py and it works perfectly :-)
So I would like to share it, how can I do?
It doesn't seem possible to give public (world) access to a bitbucket repository.
This may interest others user and it's educational to use the interrupts as well.
Thanks
--
Jmp0

The README:

This program runs on Raspberry pi plus the Adafruit RGB 16x2 LCD+Keypad Kit
The purpose of this program is to use the interrupts instead of constantly polling the
Buttons. This is a modifiation of Adafruit_CharLCDPlate.py. We enable the interrupts on
both the MCP23017 AND the Raspberry Pi GPIO. Then we electrically link them:

The MCP23017 INTA pin is connected to Raspberry pi GPIO 23 through a resistor divider, to reduce the MCP23017 5V to an acceptable level for the pi.

+----------------------+
| | +---------+ +-----------+
| MCP23017 20|+------->| 680 Ohms|+--+--->| 820 Ohms |<-------+ GND Ohhh Ugly no monospace font available here :-(
| | +---------+ | +-----------+ This drawing doesn't mean anything anymore
+----------------------+ v
GPIO 23

To enable interrupts on Raspberry pi,latest RPIO package must be install:
$ sudo apt-get install python-setuptools
$ sudo easy_install -U RPIO

Then run this software:
$ sudo python UseInterrupt.py

and press the buttons..
Interrupts do it better:-)
JumpZero
 
Posts: 21
Joined: Thu Feb 21, 2013 8:32 pm

Re: Interrupts do it better :-)

by tldr on Sun Apr 14, 2013 9:07 pm

JumpZero wrote: Ohhh Ugly no monospace font available here :-(


just place code tags around your ascii art. i'd kind of like to see it.
"If I had known it was harmless, I would have killed it myself." - Phillip K. Dick, A Scanner Darkly
User avatar
tldr
 
Posts: 466
Joined: Thu Aug 30, 2012 1:34 am

Re: Interrupts do it better :-)

by JumpZero on Sun Apr 14, 2013 9:52 pm

Code: Select all | TOGGLE FULL SIZE

+  +----------------------+
+  |                      |         +---------+        +-----------+
+  |  MCP23017          20|+------->| 680 Ohms|+--+--->| 820 Ohms  |<-------+ GND
+  |                      |         +---------+   |    +-----------+                     
+  +----------------------+                       v
+                                              GPIO 23


You're right it was that simple..
JumpZero
 
Posts: 21
Joined: Thu Feb 21, 2013 8:32 pm

Re: Interrupts do it better :-)

by tldr on Mon Apr 15, 2013 9:28 am

i guess the voltage divider on the output is because the plate had to run at 5v to power the lcd, eh?
"If I had known it was harmless, I would have killed it myself." - Phillip K. Dick, A Scanner Darkly
User avatar
tldr
 
Posts: 466
Joined: Thu Aug 30, 2012 1:34 am

Re: Interrupts do it better :-)

by JumpZero on Mon Apr 15, 2013 9:40 am

Yes, on Adafruit RGB 16x2 LCD+Keypad Kit, MCP23017 runs at 5V and thus its outputs are 5V, Raspberry pi GPIO are 3.3V and known not to be voltage tolerant.

--
Jmp0
JumpZero
 
Posts: 21
Joined: Thu Feb 21, 2013 8:32 pm

Re: Interrupts do it better :-)

by wildtang3nt on Mon Apr 15, 2013 10:38 pm

I'd love to find a way to use interrupts with the LCD menu code I've been modifying since it was posted here.
Proud owner of (among other wonderful things):
Li-Poly MintyBoost
RGB LCD Pi Plate & Pi Dish
Ice Tube Clock & USBTinyISP
wildtang3nt
 
Posts: 32
Joined: Thu Aug 09, 2012 2:31 pm
Location: Hamilton, Ontario, Canada

Re: Interrupts do it better :-)

by JumpZero on Tue Apr 16, 2013 10:19 pm

Hello here is the code to have the buttons generating interrupts and drive the LCD, the menu is just an example, of course.
But for this to work you need to connect the pin 20 of MCP23017 to the GPIO 23 as per schematic, here under.

Have fun!
--
Jmp0

Code: Select all | TOGGLE FULL SIZE
This program runs on Raspberry pi plus the Adafruit RGB 16x2 LCD+Keypad Kit
The purpose of this program is to use the interrupts instead of constantly polling the
Buttons. This is a modifiation of Adafruit_CharLCDPlate.py. We enable the interrupts on
both the MCP23017 AND the Raspberry Pi GPIO. Then we electrically link them:

The MCP23017 INTA pin is connected to Raspberry pi GPIO 23 through a resistor divider,
to reduce the MCP23017 5V to an acceptable level for the pi.

  +----------------------+
  |                      |         +---------+        +-----------+
  |  MCP23017          20|+------->| 680 Ohms|+--+--->| 820 Ohms  |<-------+ GND
  |                      |         +---------+   |    +-----------+                     
  +----------------------+                       v
                                              GPIO 23

To enable interrupts on Raspberry pi,latest RPIO package must be install:
$ sudo apt-get install python-setuptools
$ sudo easy_install -U RPIO

Then run this software:
$ sudo python UseInterrupt.py

and press the buttons..
Interrupts do it better:-)



Code: Select all | TOGGLE FULL SIZE
#!/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

# 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 RPIO 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, gpio_id, val):
        # 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)
                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.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    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)
   
    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 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
JumpZero
 
Posts: 21
Joined: Thu Feb 21, 2013 8:32 pm

Re: Interrupts do it better :-)

by tldr on Tue Apr 16, 2013 11:12 pm

is there no way to suspend the program pending an interrupt? as it is, you're still in a polling loop.

i fear my knowledge of python is a tad shallow.
"If I had known it was harmless, I would have killed it myself." - Phillip K. Dick, A Scanner Darkly
User avatar
tldr
 
Posts: 466
Joined: Thu Aug 30, 2012 1:34 am

Re: Interrupts do it better :-)

by JumpZero on Wed Apr 17, 2013 8:43 am

Hi tldr,

Eh! You have read the code in detail to notice that, good ;-)

Yes there is a way to suspend the program until an interrupt appears: it's the python instruction
RPIO.wait_for_interrupts(threaded=True) see http://pythonhosted.org/RPIO/rpio_py.html

But I prefer my solution :-)
Because, no i'm not in a polling loop.
I'm in a loop; but not polling the buttons (and polling the buttons through i2C bus takes time)
And in this loop if I want I can do something else, coffee tea etc.., and stop when the interrupts appears, treat it, then go back to the loop.
It's more 'multitasking'!
In this script, the loop is doing nothing. When an interrupt appears the function gpio_callback will write in the variable BUTTON and then the main loop will do something because BUTTON !=0
This code is just to demonstrate what is possible.

This is also possible in bash: http://pythonhosted.org/RPIO/rpio_cmd.html

Have a good day
--
Jmp0
JumpZero
 
Posts: 21
Joined: Thu Feb 21, 2013 8:32 pm

Re: Interrupts do it better :-)

by tldr on Wed Apr 17, 2013 9:18 am

this looks really cool, but there are apparently huge deficiencies in my software stack.

please enlighten as to what distribution you are running and which additional packages you've installed to get this functionality.

i'm running occidentalis 0.2 and did an update/upgrade just last night prior to installing pianobar.
"If I had known it was harmless, I would have killed it myself." - Phillip K. Dick, A Scanner Darkly
User avatar
tldr
 
Posts: 466
Joined: Thu Aug 30, 2012 1:34 am

Re: Interrupts do it better :-)

by JumpZero on Wed Apr 17, 2013 10:32 am

This run on Occidentalis (I use the latest one, but can't tell revision number I'm not in front of my pi right now)
And you need Adafruit additional software that is included in AdafruitCharLCDPlate https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code.git
including Adafruit_I2C
JumpZero
 
Posts: 21
Joined: Thu Feb 21, 2013 8:32 pm

Re: Interrupts do it better :-)

by JumpZero on Wed Apr 24, 2013 4:47 pm

Hello,

I have recently found that the classic module RPi.GPIO included in Occidentalis (maybe also in Raspbian) can handle interrupts as well, so this make things even more simple.
No need to install the RPIO module.

Code: Select all | TOGGLE FULL SIZE
This program runs on Raspberry pi plus the Adafruit RGB 16x2 LCD+Keypad Kit
The purpose of this program is to use the interrupts instead of constantly polling the
Buttons. This is a modifiation of Adafruit_CharLCDPlate.py. We enable the interrupts on
both the MCP23017 AND the Raspberry Pi GPIO. Then we electrically link them:

The MCP23017 INTA pin is connected to Raspberry pi GPIO 23 through a resistor divider,
to reduce the MCP23017 5V to an acceptable level for the pi.

  +----------------------+
  |                      |         +---------+        +-----------+
  |  MCP23017          20|+------->| 680 Ohms|+--+--->| 820 Ohms  |<-------+ GND
  |                      |         +---------+   |    +-----------+                     
  +----------------------+                       v
                                              GPIO 23

Just make sure you have the latest RPi.GPIO version   https://pypi.python.org/pypi/RPi.GPIO

Then run this software:
$ sudo python UseInterruptGPIO.py

and press the buttons..
Interrupts do it better:-)



Code: Select all | TOGGLE FULL SIZE
#!/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 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"

JumpZero
 
Posts: 21
Joined: Thu Feb 21, 2013 8:32 pm

Re: Interrupts do it better :-)

by igorlopez on Sat Aug 10, 2013 8:19 am

Thanks for this tip.
A small modification on the hw to get good sw behavior.

I was about to ask this question here but found the answer before asking.

B.R
igorlopez
 
Posts: 3
Joined: Tue Jun 11, 2013 2:27 pm

Re: Interrupts do it better :-)

by JumpZero on Sat Aug 10, 2013 8:58 am

Happy if it can help :-)
--
Jmp0
JumpZero
 
Posts: 21
Joined: Thu Feb 21, 2013 8:32 pm

Re: Interrupts do it better :-)

by phil.drummond on Sun Aug 18, 2013 9:14 pm

Quick question about interrupts on the Raspberry Pi.
I have a radiation detector manufactured by Aware Electronics. It generates pulses for each event and the pulses are quite short. The refresh time of the tube/power supply is very fast allowing for count rates something like 200,000 pulses per second. (normal background levels around where I live are between 9 and 13 pulses a second for comparison :)
I would love to own Aware Electronics' display device but it's in the neighborhood of $400. It consists of a display, a few buttons and a PIC of some sort as well as electronics to shape and buffer the signal from the detector. 400 dollars is a lot of money and the hardware sounds like a Raspberry Pi, an Adafruit 16x2 display and not much els would be required to get going for much much less money :)

Have any of you messed with this kind of input?
phil.drummond
 
Posts: 125
Joined: Sun Feb 08, 2009 4:57 pm