Pages

Wednesday, January 09, 2013

MCP23008 IO Expander - Interrupt control


I have made yet another cleaning up, this time making things more consistant.



Program to test decimal keypad - v1.0  TL Fong 2013jan08
Press keypad  4 times.
Key pressed =  01
Key pressed =  04
Key pressed =  07
Key pressed =  10
Interrupt flag nibble =  0111
Interrupt flag nibble =  1111
Interrupt flag nibble =  1111
Interrupt flag nibble =  1111
Interrupt flag nibble =  1111
Interrupt flag nibble =  1111
Interrupt flag nibble =  1111
Interrupt flag nibble =  1111
Interrupt flag nibble =  1111
Interrupt flag nibble =  1111
Interrupt flag nibble =  1111
Interrupt flag nibble =  1110
Interrupt flag nibble =  1110
Interrupt flag nibble =  1101
Interrupt flag nibble =  1011
Interrupt flag nibble =  0111
Interrupt flag nibble =  0111
Interrupt flag nibble =  0111


The main test functions look neat and tidy.


# *****************************************************************************

# * Main test functions *

# * Setup RPi GPIO *

SetupGPIO()
StartMessage("Program to test decimal keypad - v1.0  TL Fong 2013jan08")

# * 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 stepping motor *

# TestMotor() # move two stepping motors

# * Part 3 - Test decimal keypad *

TestKeypad() # input 4 keys from keypad
TestDetectInterrupt() 

EndMessage("End of program")


The critical input a key function is also neat and tidy.

def GetKey(registerBaseAddress): 

    LoopUntilRowDataByteChange(registerBaseAddress) 
    rowNumber = GetRowNumber(registerBaseAddress)
    columnNumber = GetColumnNumber(registerBaseAddress)
    keyNumber = GetKeyNumber(rowNumber, columnNumber)
    return keyNumber


Next step is to replace the loop polling row data byte function by a loop polling interrupt capture byte function.


# *****************************************************************************
# *** tiox40.py ***
# Program  - Test MCP23008 and MCP23017
# Version  - 4.0
# Date     - 2012nov30
# Update   - 2013jan08
# Author   - tlfong01
# File     - tiox40_2013jan08.py
# Blog     - http://tlfong01.blogspot.hk/ 
# Purpose  - test basics of Raspberry Pi GPIO
# 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 http://pypi.python.org/pypi/RPi.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 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 DetectInterrupt(registerBaseAddress):
    while True:        
        interruptFlagNibble = ReadInterruptFlagHighNibble(registerBaseAddress)
        interruptCaptureNibble = ReadInterruptCaptureHighNibble(registerBaseAddress)
        print "Interrupt flag nibble = ", ConvertIntegerToFourBitPattern(interruptCaptureNibble) 
  sleep(2)

def TestDetectInterrupt():
    RegisterBaseAddress =  MCP23008BaseAddress2
    SetupInterruptHighNibbleCompareDefaultValueOpenDrainOutput(RegisterBaseAddress)
    DetectInterrupt(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 LoopUntilRowDataByteChange(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 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): 
    LoopUntilRowDataByteChange(registerBaseAddress) 
    rowNumber = GetRowNumber(registerBaseAddress)
    columnNumber = GetColumnNumber(registerBaseAddress)
    keyNumber = GetKeyNumber(rowNumber, columnNumber)
    return keyNumber

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

def TestKeypad():
    KeypadRegisterBaseAddress = MCP23008BaseAddress2
    Count = 4
    print "Press keypad ", Count, "times."
    SetupKeypad(KeypadRegisterBaseAddress)
    GetKeyString(KeypadRegisterBaseAddress, 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 - v1.0  TL Fong 2013jan08")

# * 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 stepping motor *

# TestMotor() # move two stepping motors

# * Part 3 - Test decimal keypad *

TestKeypad() # input 4 keys from keypad
TestDetectInterrupt() 

EndMessage("End of program")

# *****************************************************************************
# End of Program
# *****************************************************************************

.END

No comments:

Post a Comment