Pages

Thursday, January 10, 2013

MCP23008 IO Expander - Interrupt control





Posted by Picasa

I still found the MCP230xx interrupt thing rather confusing.  I thought I could clear interrupt by disabling the interrupt bits, and then enabling them again.  But I found it not working.  So I went to read the Application Note AN1043.  Then I found my idea was wrong.   I need to use another method to clear interrupt.  One thing I am not sure is whether the on change from previous value is similar to edge triggering or not.  Or if there is no such idea as edge triggering in MCP230xx.


Microchip Application Note AN1043 Unique Features of the MCP23X08/17 GPIO Expanders

Interrupt Conditions

There are several configurable interrupt conditions which allow flexible configurations.

INTERRUPT-ON-PIN-CHANGE

Pins configured for interrupt-on-pin-change will cause an interrupt to occur if a pin changes to the opposite state. The default state is reset after an interrupt is serviced. For example, an interrupt occurs by an input changing from 1 to 0. The interrupt is then serviced while the pin state is still 0 by reading GPIO or INTCAP register.

The new initial state for the pin is a logic 0. Likewise, if the pin is toggled back to a logic 1 before servicing the interrupt, the new default state is a logic 1.

The interrupt condition is cleared by reading either INTCAP or GPIO register. The new pin state default is set when the interrupt is cleared.

INTERRUPT-ON-CHANGE FROM DEFVAL REGISTER VALUE

Pins configured for interrupt-on-change from register value will cause an interrupt to occur if the corresponding input pin differs from the register bit. The interrupt condition will remain as long as the condition exists, regardless if the INTCAP or GPIO is read.

For example, if DEFVAL<b0> = 0. An interrupt will occur if the pin changes to a logic 1 and the interrupt will remain as long as the pin remains a logic 1. The interrupt condition will clear if the pin changes back to a logic 0 and INTCAP or GPIO is read.

.END

# *****************************************************************************
# *** tkp44.py ***
# Program  - Test MCP23008 IO Expander based decimal keypad
# Version  - 4.4
# Date     - 2012nov30
# Update   - 2013jan08
# Author   - tlfong01
# File     - tkp44_2013jan10.py
# Blog     -
http://tlfong01.blogspot.hk/
# Purpose  - test decimal keypad
# License  - GNU GPLv3
# Hardware - Raspberry Pi Model B Revsion 2.0 [2012oct/nov/dec]
# Software - Raspbian Wheezy (2012sep15), Python 2.7.3, GPIO 0.4.1a
# Wiring   - RPi Board Numbering
#              P1-02 5V, P1-04 5V, P1-06 Gnd
#              P1-01 3V3, P1-03 I2C SDA1, P1-05 I2C SCL1
#              P1-08 UART TxD (MCP23017 Reset)
#              P1-10 UART RxD (MCP23017 INTB)
#              P1-12 RPi GPIO_GEN1 (BCM18) LED (P1-12 > LED > 330R > Gnd)
#              P1-14 Gnd
#              P1-16 GPIO_GEN4 - Buzzer, 3V3 5mA (P1-16 > Buzzer > Gnd)
#              P1-18 GPIO_GEN5 Button (3V3 > 10K > Contact 1/2 > 330R > Gnd)
#              P1-20 Gnd
#              P1-22 GPIO_GEN6 - MCP23008 INT / MCP23017 INTA             

# *****************************************************************************
# *** Import Python modules ***

import smbus
import sys
import RPi.GPIO as GPIO
from time import sleep
import select # interrupt module

# *****************************************************************************
# * Rpi GPIO pin assignments for LED, buzzer, and button *
GPIO.setmode(GPIO.BOARD) # Use RPi GPIO numbering, Not BCM numbering
GPIO.setwarnings(False)  # Disable linux's "pin already in use warning"

# * P1 pins numbering *
RPiGPIOgen1 = 12 # Brown  (P1-12, BCM GPIO 18) LED
RPiGPIOgen4 = 16 # Yellow (P1-16, BCM GPIO 23) Buzzer
RPiGPIOgen5 = 18 # Green  (P1-18, BCM GPIO 24) Button
RPiGPIOgen6 = 22 # Blue   (P1-22, BCM GPIO 25) IOx Interrupt

RPiTxD = 8 # Orange (P1-08) UART TxD
RPiRxD = 10 # Yellow (P1-10) UART RxD

# * IO device pins assignment *
LEDpin = RPiGPIOgen1
BuzzerPin = RPiGPIOgen4
ButtonPin = RPiGPIOgen5
InterruptPin = RPiGPIOgen6
TxDpin = RPiTxD
RxDpin = RPiRxD

# * IO pins list *

OutputPinList = [LEDpin, BuzzerPin, TxDpin]
InputPinWithNoPullUpList = [ButtonPin, RxDpin]
InputPinWithPullUpList = [InterruptPin]

# *****************************************************************************
# * Global constants *
# * Loop counters *
TwoTimes = 2
FourTimes = 4
EightTimes = 8
TenTimes = 10
TwentyTimes = 20
FiftyTimes = 50
OneHundredTimes = 100
TwoHundredTimess = 200
FourHundredTimes = 400

# * Elapse times *
TwentyMilliSeconds = 0.02
FiftyMilliSeconds = 0.05
TenthSecond = 0.1
QuarterSecond = 0.25
HalfSecond = 0.5
OneSecond = 1
OneAndHalfSeconds = 1.5
TwoSeconds = 2

OnTime = TenthSecond
OffTime = QuarterSecond
ButtonDebouncingTime = QuarterSecond
TestTime = FiftyMilliSeconds

# * Nibble naming *
LowNibble = 0
HighNibble = 1
BothNibble = 2 # full byte of 8 bits

# * Nibble constants *
HighNibble1LowNibble0 = 0xf0
HighNibble0LowNibble1 = 0x0f

# * LED and buzzer states *
Off = False
On = True

# * Button states *
ButtonPressed = False
ButonReleased = True

# *****************************************************************************
# * GPIO functions *
# Note: 5V0 max = 50 mA, 3V3 max = 300 mA, per GPIO pin max = 17mA source, 12mA sink
# * Setup, read, write GPIO pins *
setupOutputPin = lambda oPin: GPIO.setup(oPin, GPIO.OUT) # set GPIO pin as output
setupInputPinWithNoPullUp = lambda iPin: GPIO.setup(iPin, GPIO.IN, pull_up_down=GPIO.PUD_OFF) # set GPIO pin as input, no pull up
setupInputPinWithPullUp = lambda iPin: GPIO.setup(iPin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # set GPIO pin as input, with pull up
writeOutputPin = lambda oPin, oValue: GPIO.output(oPin, oValue) # write value to output pin
setupWriteOutputPin = lambda oPin, oValue: (setupOutputPin(oPin), writeOutputPin(oPin, oValue)) # set and write
readInputPin = lambda iPin: GPIO.input(ButtonPin) # read value from input pin
def SetupGPIOpins(outputPinList, inputPinWithNoPullUpList, inputPinWithPullUpList): # set up GPIO pins in InputPinList and OutputPinList
    for oPin in outputPinList:
       setupWriteOutputPin(oPin, Off)
    for iPin in inputPinWithNoPullUpList:
        setupInputPinWithPullUp(iPin)
    for iPin in inputPinWithPullUpList:
        setupInputPinWithPullUp(iPin)

def SetupGPIO(): # set up GPIO pins
     SetupGPIOpins(OutputPinList, InputPinWithNoPullUpList, InputPinWithPullUpList )

# * pulse and echo functions *
def pulsePin(oPin, onTime, offTime): # blink LED or beep buzzer
    writeOutputPin(oPin, On)
    sleep(onTime)
    writeOutputPin(oPin, Off)   
    sleep(offTime)

def echoPin(iPin, oPin): # echo input pin to output pin, e.g. button to LED or buzzer
    while True:
        if readInputPin(iPin) == ButonReleased:
            pass
        else:
            pulsePin(oPin, OnTime, OffTime)
            break
        continue

def togglePin(oPin, toggleTime): # toggle pin
    writeOutputPin(oPin, On)
    sleep(toggleTime)
    writeOutputPin(oPin, Off)   
    sleep(toggleTime)

# * Test Buzzer, LED, Button functions *
def TestBuzzer(): # beep 4 times
    SetupGPIO()
    for i in range (FourTimes):
        pulsePin(BuzzerPin, OnTime, OffTime)

def TestLED(): # blink 8 times
    SetupGPIO()
    for i in range (EightTimes):
        pulsePin(LEDpin, OnTime, OffTime)

def TestButtonEchoBuzzer(): #
    SetupGPIO()
    for i in range (TenTimes):
        echoPin(ButtonPin, BuzzerPin)         

def TestButtonEchoLED(): #
    SetupGPIO()
    for i in range (TenTimes):
        echoPin(ButtonPin, LEDpin)

def TestToggleTxDpin():
    while True:
        togglePin(TxDpin, TwoSeconds)


# GPIO Interrupt function !!! not yet tested !!!
def TestInterruptPinFallingEdgeDetection(): # !!! Not tested !!!
    GPIO.cleanup() # set all input pins no pull up, disable all interutp detection setting
    SetupGPIO()  
    GPIO.set_low_event(InterruptPin) # set up low level detection

    for i in range(30):
        if GPIO.event_detected(InterruptPin):
     break
        else:
            print "No interrupt detected.", i
            sleep(1)
     continue

    GPIO.set_low_event(InterruptPin, enable = False)  # disable detection
    print "End of test, or interrupt detected"

# *****************************************************************************
#  * Beep functions *
def Beep(count):
    for i in range(count):
        pulsePin(BuzzerPin, OnTime, OffTime)
   
def StartBeep():
    Beep(TwoTimes)
    sleep(1)

def EndBeep():
    Beep(FourTimes)

def OneBeep():
    Beep(1)

def FourBeeps():
    Beep(4)

# *****************************************************************************
# * MCP23008 / MCP23017 IO Expander *
# * Bash script using i2cTools's i2cset command to toggle GPIO pins *
#!/bin/bash
# i2cset -y 1 0x20 0x00 0x00
# count=0
# while [ $count -lt 10 ];
# do
#  i2cset -y 1 0x20 0x0a 0x00
#  sleep 0.5
#  i2cset -y 1 0x20 0x0a 0xff
#  sleep 0.5
#  let count++
# done

# To run i2c-X commands in user mode: sudo chmod 666 /dev/i2c-X
# sudo chmod 666 /dev/i2c-1

# To detect I2C device base addresses
# sudo i2cdetect -y 1

# * Setup SMBus *
I2C_BUS_NUMBER = 1 # P1-03 = SDA1, P1-05 = SCL1
smBus1 = smbus.SMBus(I2C_BUS_NUMBER) # global variable, cannot be set by a function

# * Register base addresses *
MCP23017BaseAddress1 = 0x22 # 1 LED, 1 button
MCP23008BaseAddress1 = 0x24 # 2 unipolar stepping motors
MCP23008BaseAddress2 = 0x25 # 1 decimal keypad

# * Port type *
PortA = 0
PortB = 1

# * Byte patterns *
# * Data bytes *
AllOutput = 0x00
AllHigh = 0xff
AllLow =  0x00
AlternateHighLow = 0xaa
AlternateLowHigh = 0x55

# * Direction setting bytes *
HalfHighHalfLow = 0xf0
HalfLowHalfHigh = 0x0f
Nibble1HighNibble2Low = 0xf0
Nibble1LowNibble2High = 0x0f

HighNibbleInputLowNibbleOutput = 0xf0
# * MCP23008/MCP23017 register address offsets *
InputOutputDirection = 0
InputPolarity = 1
InterruptOnChangeEnable = 2
InterruptOnChangeDefaultValue = 3
InterruptOnChangeMode = 4
Configuration = 5
PushPullOpenDrain = 6
InterruptFlag = 7
InterruptCapture = 8
PortStatus = 9
OutputLatch = 10

# * MCP23008/MCP23017 Band0/Band1 register address offset arrays *
RegisterAddressOffsetArray0 = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,          
     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a]
              
RegisterAddressOffsetArray1 = [0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x11, 0x13, 0x15, 0x17, 0x19,          
     0x01, 0x03, 0x05, 0x07, 0x09, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a]

# * Setup MCP23008/MCP23017 ports all output *
def SetupMCP23008PortAllOutput(registerBaseAddress):
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InputOutputDirection, PortA, AllOutput)

def SetupMCP23017BothPortAllOutput(registerBaseAddress):
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InputOutputDirection, PortA, AllOutput)
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InputOutputDirection, PortB, AllOutput)

# * Write/Read MCP23008/MCP23017 registers *
def WriteDataByte(registerBaseAddress, registerAddressArray, dataRegisterIndex, portType, dataByte):
    if (portType == PortA) | (portType == PortA):
       addressOffset = registerAddressArray[dataRegisterIndex]
    if (portType == PortB):
       addressOffset = registerAddressArray[dataRegisterIndex + 11]
    smBus1.write_byte_data(registerBaseAddress, addressOffset, dataByte)

def ReadDataByte(registerBaseAddress, registerAddressArray, dataRegisterIndex, portType):
    if (portType == PortA):
       addressOffset = registerAddressArray[dataRegisterIndex]
    if (portType == PortB):
       addressOffset = registerAddressArray[dataRegisterIndex + 11]
    dataByte = smBus1.read_byte_data(registerBaseAddress, addressOffset)
    return dataByte

# * Test MCP23008/MCP23017 toggle port functions *
def ToggleMCP23008GP(registerBaseAddress, toggleTime, toggleCount):
    SetupMCP23008PortAllOutput(registerBaseAddress)
    for i in range(toggleCount):
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, AllHigh)
 sleep(toggleTime)
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, AllLow)
  sleep(toggleTime)

def ToggleMCP23017GP(registerBaseAddress, toggleTime, toggleCount):
    SetupMCP23017BothPortAllOutput(registerBaseAddress)
    for i in range(toggleCount):
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, AllHigh)
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortB, AllHigh)
 sleep(toggleTime)
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, AllLow)
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortB, AllLow)
  sleep(toggleTime)

def TestToggleMCP23008GP():
    ToggleTime = 0.5
    ToggleCount = 4
    ToggleMCP23008GP(MCP23008BaseAddress1, ToggleTime, ToggleCount)

def TestToggleMCP23017GP():
    ToggleTime = 0.5
    ToggleCount = 4
    ToggleMCP23008GP(MCP23017BaseAddress1,  ToggleTime, ToggleCount)

# * MCP23008/MCP23017 Interrupt functions *
# * MCP23008 Interrupt setting *
def EnableInterruptOnChangeHighNibble(registerBaseAddress):
    EnableInterruptHighNibble = 0xf0
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InterruptOnChangeEnable, PortA, EnableInterruptHighNibble)

def DisableInterruptOnChangeHighNibble(registerBaseAddress):
    DisableInterruptHighNibble = 0x00
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InterruptOnChangeEnable, PortA, DisableInterruptHighNibble)

def ClearInterruptOnChangeHighNibble(registerBaseAddress):
    DisableInterruptOnChangeHighNibble(registerBaseAddress)
    EnableInterruptOnChangeHighNibble(registerBaseAddress)

def SetInterruptOnChangeDefaultHighNibble(registerBaseAddress):
    DefaultValueHighNibble = 0xf0 # GP4~7 (keypad rows input) interrupt default value = High
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InterruptOnChangeDefaultValue, PortA, DefaultValueHighNibble)

def SetInterruptOnChangeCompareDefaultHighNibble(registerBaseAddress):
    InterruptOnChangeDefaultValueHighNibble = 0xf0 # GP4~7 change from default value will cause interrupt
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InterruptOnChangeMode, PortA, InterruptOnChangeDefaultValueHighNibble)

def SetInterruptOutputPushPull(registerBaseAddress): # interrupt pin open drain, no auto add inc, no slew rate
    PushPull  = 0b00111010 # 0x32, seq/slew disabled, interrupt output drive drive (push pull) active High
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, PushPullOpenDrain, PortA, PushPull)

def SetInterruptOutputOpenDrain(registerBaseAddress): # interrupt pin open drain, no auto add inc, no slew rate
    OpenDrain = 0b00111000 # 0x34, seq/slew disabled, interrupt output open drain (don't care active High or Low)
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, PushPullOpenDrain, PortA, OpenDrain)

def SetupInterruptHighNibbleCompareDefaultValueOpenDrainOutput(registerBaseAddress):
    EnableInterruptOnChangeHighNibble(registerBaseAddress)
    SetInterruptOnChangeDefaultHighNibble(registerBaseAddress)
    SetInterruptOutputOpenDrain(registerBaseAddress)

# * Port A Interrupt checking *

def ReadInterruptFlagHighNibble(registerBaseAddress):
    interruptFlagByte = ReadDataByte(registerBaseAddress,  RegisterAddressOffsetArray0, InterruptFlag, PortA) 
    interruptFlagNibble = interruptFlagByte >> 4
    return interruptFlagNibble  
   
def ReadInterruptCaptureHighNibble(registerBaseAddress):
    interruptCaptureByte = ReadDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InterruptCapture, PortA)   
    interruptCaptureNibble = interruptCaptureByte >> 4
    return interruptCaptureNibble 

def ConvertIntegerToFourBitPattern(integer):
    FourBitPattern = [""]
    for k in range(4):
        FourBitPattern = [i+j for i in ['0','1'] for j in FourBitPattern]
    return FourBitPattern[integer]

def DetectOneInterrupt(registerBaseAddress):   
    interruptFlagNibble = ReadInterruptFlagHighNibble(registerBaseAddress)
    interruptCaptureNibble = ReadInterruptCaptureHighNibble(registerBaseAddress)
    print "Interrupt capture high nibble = ", ConvertIntegerToFourBitPattern(interruptCaptureNibble)
    print "Interrupt capture high nibble = ", ConvertIntegerToFourBitPattern(interruptCaptureNibble)
    ClearInterruptOnChangeHighNibble(registerBaseAddress) # !!! clear interrupt !!!
    sleep(2)

def TestDetectInterrupt(count):
    RegisterBaseAddress =  MCP23008BaseAddress2
    SetupInterruptHighNibbleCompareDefaultValueOpenDrainOutput(RegisterBaseAddress)
    for i in range(count):
        DetectOneInterrupt(RegisterBaseAddress)

# *****************************************************************************
# * Unipolar Stepping Motor 28BYJ48/NPM-PF35/PX245 *
# Unipolar Stepping Motor Switching Sequence
# 1. Wave sequence = 1 - 3 - 2 - 4 (A-, B-, A+, B+)
# 2. Full step sequence = 13 - 14 - 24 - 23 (A-B-, A-B+, A+B+, A+B-)
# 3. Half step sequence  = 13 - 1 - 14 - 4 - 24 - 2 - 23 - 3
# 4. One step swing = 1 - 3 - 1 - 3 (A-, B-, A-, B-)
# Winding        A-(1)    A+(2)    B-(3)   B+(4)    COM
# NPM PF35       Black    Yellow   Brown   Orange   Red
# 28BYJ48        Blue     Pink     Yellow  Orange   Red 
# PX245          Black    Green    Blue    Red      Yelow/White

# * Convert decimal pin number to hex *
def convert1PinToHex(p): # convert 1 of 8 high pin to hex
    hexString = 0x01
    for count in range(p-1):
    hexString = hexString << 1
    return hexString
def convert2PinToHex(p1, p2):  # convert 2 of 8 high pins to hex
    return (convert1PinToHex(p1) | convert1PinToHex(p2))

def convert2PinToHighNibble(p1, p2):  # convert 2 of 8 high pins to high nibble
    lowNibble = convert1PinToHex(p1) | convert1PinToHex(p2)
    highNibble = lowNibble << 4
    return highNibble

# * Move unipolar stepping motor *
def WriteMotorWindingWaveSequence1324(RegisterBaseAddress, NibbleType, StepCount, StepTime): # move motor using 13-24 sequence 
    # Set port all output
    WriteDataByte(MCP23008BaseAddress1, RegisterAddressOffsetArray0, InputOutputDirection, PortA, AllOutput)

    if NibbleType == LowNibble:
 hexString1 =  convert2PinToHex(1, 3)
   hexString2 =  convert2PinToHex(2, 4)
    else:
 hexString1 = convert2PinToHighNibble(1, 3)
 hexString2 = convert2PinToHighNibble(2, 4)
    for i in range(StepCount):
        WriteDataByte(MCP23008BaseAddress1, RegisterAddressOffsetArray0, OutputLatch, PortA, hexString1)
    sleep(StepTime)
        WriteDataByte(MCP23008BaseAddress1, RegisterAddressOffsetArray0, OutputLatch, PortA, hexString2)
        sleep(StepTime)

def WriteMotorWindingFullStepSequence13232414(RegisterBaseAddress, NibbleType, StepCount, StepTime): #move motor using 13-23-24-14 sequence
    # Set port all output
    WriteDataByte(MCP23008BaseAddress1, RegisterAddressOffsetArray0, InputOutputDirection, PortA, AllOutput)

    if NibbleType == LowNibble:
     motorWindingActivationPatternArray = (0x05, 0x06, 0x0a, 0x09)
    else:
 motorWindingActivationPatternArray = (0x50, 0x60, 0xa0, 0x90)
    for i in range(StepCount):
 for pattern in motorWindingActivationPatternArray:
            WriteDataByte(MCP23008BaseAddress1, RegisterAddressOffsetArray0, OutputLatch, PortA, pattern)
     sleep(StepTime)

def TestConvert1PinToHex(): # test convert 1 high pin to hex
    print "*** Testing 1 pin number decimal 0 ~ 7 converted to hexdecimal 0x01 ~ 0x80"
    for d in range(8):
    print hex(convert1PinToHex(d))

def TestConvert2PinToHex(p1, p2): # test convert 2 high pins to hex
    print "*** Testing 2 pin numbers decimal 0 ~ 7 converted to hexdecimal"
    print "Pin 1 = ", p1, "Pin 2 = ", p2
    print "Hex = ", hex(convert2PinToHex(p1, p2))

def TestConvert2PinToHighNibble(p1, p2): # test convert 2 of 8 high pins to high nibble
    print "*** Testing 2 pin numbers decimal 0 ~ 7 converted to high nibble"
    print "Pin 1 = ", p1, "Pin 2 = ", p2
    print "HighNibble = ", hex(convert2PinToHighNibble(p1, p2))

def MoveTwoMotors(RegisterBaseAddress): 
 OneBeep()
 WriteMotorWindingWaveSequence1324(MCP23008BaseAddress1, LowNibble, TwentyTimes, FiftyMilliSeconds)
 OneBeep()
 WriteMotorWindingWaveSequence1324(MCP23008BaseAddress1, HighNibble, TwentyTimes, FiftyMilliSeconds)
 # OneBeep()
 # WriteMotorWindingFullStepSequence13232414(MCP23008BaseAddress1, LowNibble, TwentyTimes, FiftyMilliSeconds)
 # OneBeep()
 # WriteMotorWindingFullStepSequence13232414(MCP23008BaseAddress1, HighNibble, TwentyTimes, FiftyMilliSeconds)

def TestMotor():
    MotorRegisterBaseAddress = MCP23008BaseAddress1
    MoveTwoMotors(MotorRegisterBaseAddress)

# *****************************************************************************
# * Decimal keypad *

# Keypad scanning procedure version 1.0
# 1. Set 3 column ports GP0, GP1, GP2 as output (GP3 don't care)
# 2. Set 4 row ports GP4, GP5, GP6, GP7 as input
# 3. Write Low to all column ports
# 4. Wait until any key pressed (pooling or interrupt)
# 5. Find rowNumber and column number
# 6. Calculate key number

# * keypad base address and smBus setting *
keypadRegisterBaseAddress = MCP23008BaseAddress2
keypadSmBus = smBus1

def SetupKeypad(registerBaseAddress):
   HalfInputHalfOutput = 0xf0
   WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InputOutputDirection, PortA, HalfInputHalfOutput)

def LoopUntilGpioPortHighNibbleChange(registerBaseAddress):
    NoKeyPressed = 0xf0
    rowDataByte = NoKeyPressed
    AllLowByte = 0x00
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, AllLowByte)
    while (rowDataByte == NoKeyPressed):
        rowDataByte = GetRowDataByte(registerBaseAddress)
    sleep(0.05) # debouncing time 50mS

def LoopUntilInterruptCaptureHighNibbleChange(registerBaseAddress):
    NoKeyPressedInterruptCaptureHighNibble = 0xf
    interruptCaptureHighNibble = ReadInterruptCaptureHighNibble(registerBaseAddress)
    print "Interrupt capture high nibble = ", ConvertIntegerToFourBitPattern(interruptCaptureHighNibble)
    while (interruptCaptureHighNibble == NoKeyPressedInterruptCaptureHighNibble):
       interruptCaptureHighNibble = ReadInterruptCaptureHighNibble(registerBaseAddress)
    sleep(1) # !!! clear interrupt !!!
    ClearInterruptOnChangeHighNibble(registerBaseAddress) # !!! clear interrupt !!!
    print "Interrupt capture high nibble = ", ConvertIntegerToFourBitPattern(interruptCaptureHighNibble)
    sleep(0.05) # debouncing time 50mS

def GetRowDataByte(registerBaseAddress):
    rowDataByte = ReadDataByte(registerBaseAddress, RegisterAddressOffsetArray0, PortStatus, PortA)
    return rowDataByte

def GetRowNumber(registerBaseAddress):
    rowDataByte = ReadDataByte(registerBaseAddress, RegisterAddressOffsetArray0, PortStatus, PortA)
    rowDataNibble = rowDataByte >> 4
    if rowDataNibble == 0b1110:
        rowNumber = 0
        return rowNumber
    elif rowDataNibble == 0b1101:
        rowNumber = 1
        return rowNumber
    elif rowDataNibble == 0b1011:
        rowNumber = 2
        return rowNumber
    elif rowDataNibble == 0b0111:
        rowNumber = 3
        return rowNumber
    else:
        rowNumber = 99 
 return rowNumber

def GetColumnNumber(registerBaseAddress):
    oldRowDataByte = ReadDataByte(registerBaseAddress, RegisterAddressOffsetArray0, PortStatus, PortA)
    oldRowDataNibble = oldRowDataByte >> 4

    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, 0xf6)
    newRowDataByte = ReadDataByte(registerBaseAddress, RegisterAddressOffsetArray0, PortStatus, PortA)
    newDataNibble = newRowDataByte >> 4
    if newDataNibble == oldRowDataNibble:
        columnNumber = 0
        return columnNumber
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, 0xf5)
    newRowDataByte = ReadDataByte(registerBaseAddress, RegisterAddressOffsetArray0, PortStatus, PortA)
    newDataNibble = newRowDataByte >> 4
    if newDataNibble == oldRowDataNibble:
        columnNumber = 1
        return columnNumber
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, 0xf3)
    newRowDataByte = ReadDataByte(registerBaseAddress, RegisterAddressOffsetArray0, PortStatus, PortA)
    newDataNibble = newRowDataByte >> 4
    if newDataNibble == oldRowDataNibble:
        columnNumber = 2
        return columnNumber
    columnNumber = 99
    return columnNumber

def GetKeyNumber(rowNumber, columnNumber):
    keyNumber = (rowNumber * 3) + (columnNumber + 1)
    return keyNumber

def GetKey(registerBaseAddress, pollData):
    PollGpioPortHighNibble = 0
    PollInterruptCaptureHighNibble = 1
    if (pollData == PollGpioPortHighNibble):
        print "PollGpioPortHighNibble"
        LoopUntilGpioPortHighNibbleChange(registerBaseAddress)
    elif (pollData == PollInterruptCaptureHighNibble):
        print "PollInterruptCaptureHighNibble"
        LoopUntilInterruptCaptureHighNibbleChange(registerBaseAddress)
    rowNumber = GetRowNumber(registerBaseAddress)
    columnNumber = GetColumnNumber(registerBaseAddress)
    keyNumber = GetKeyNumber(rowNumber, columnNumber)
    return keyNumber

def GetKeyString(registerBaseAddress, pollData, count):
    for i in range (count):       
        OneBeep()
 keyNumber = GetKey(registerBaseAddress, pollData)
        print "Key pressed = ", '{0:2}'.format(str(keyNumber).zfill(2).rjust(2))

def TestPollRowDataKeypad():
    KeypadRegisterBaseAddress = MCP23008BaseAddress2
    PollData = 0 # poll row data
    Count = 4
    print "Press keypad ", Count, "times."
    SetupKeypad(KeypadRegisterBaseAddress)
    GetKeyString(KeypadRegisterBaseAddress, PollData, Count)

def TestPollInterruptCaptureDataKeypad():
    KeypadRegisterBaseAddress = MCP23008BaseAddress2
    PollData = 1 # poll interrupt capture data
    Count = 4
    print "Press keypad ", Count, "times."
    SetupKeypad(KeypadRegisterBaseAddress)
    GetKeyString(KeypadRegisterBaseAddress, PollData, Count)

def TestKeypad(pollData, count):
    KeypadRegisterBaseAddress = MCP23008BaseAddress2
    SetupKeypad(KeypadRegisterBaseAddress)
    print "Press keypad ", count, "times."
    GetKeyString(KeypadRegisterBaseAddress, pollData, count)

# ****************************************************************************
# * Program messages *
def StartMessage(message):
    StartBeep()
    print message

def EndMessage(message):
    EndBeep
    print message

# *****************************************************************************
# * Main test functions *
# * Setup RPi GPIO *
SetupGPIO()
StartMessage("*** Program to test decimal keypad - v4.3  TL Fong 2013jan10 ***")

# * Part 1 - Test RPi GPIO *
# TestBuzzer() # beep buzzer 4 times
# TestLED() # blink LED 8 tmes
# TestButtonEchoBuzzer() # echo buton with buzzer 10 times
# TestButtonEchoLED() # echo button with LED 10 times
# TestToggleTxDpin() # toggle TxD pin every 2 seconds

# * Part 2 - Test MCP23008/MCP23017 output ports *
# TestToggleMCP23008GP() # toggle MCP23008 output port
# TestToggleMCP23017GP() # toggle MCP23017 output ports


# * Part 3 Test interrupt *
# TestDetectInterrupt()
# * Part 4 - Test stepping motor *
# TestMotor() # move two stepping motors
# * Part 5 - Test decimal keypad *
PollGpioPortHighNibble = 0
PollInterruptCaptureHighNibble = 1
KeyCount = 4
InterruptCount = 10

TestKeypad(PollGpioPortHighNibble, KeyCount)
TestDetectInterrupt(InterruptCount)
TestKeypad(PollInterruptCaptureHighNibble, KeyCount)

EndMessage("End of program")
# *****************************************************************************
# End of Program
# *****************************************************************************

.END

No comments:

Post a Comment