Pages

Thursday, January 31, 2013

HC573 / ULN2803 Buffer Board




Now I am assembling a HC573 / ULN2803 buffer board.  Earlier I worried that it was a mistake to let the RPi's GPIO pin to drive the buzzer, because when switching off the buffer, there would be a induction coil fly back current which would damage the GPIO pin switching the current.

However, when I dismantled the buzzer, I found that there is a CS9014, a diode and a 330R resistor.  I think it is an oscillator switching on/off the coil attracting and releasing the spring metal plate, making the buzzing sound.

In other words, it is the CS9014 switching the current, NOT the RPi GPIO pin, which only switching on off the 5V0 power to the buzzer.  So I guess the fly back current would only damage the CS9014, not the RPi's GPIO pin.

.END

Wednesday, January 30, 2013

HC573 pinout








LVC245A testing notes

Now I have written a function to read the MCP23017 Port B every second, while I by hand input data to the port.

TestReadMCP23017SystemB1()

def TestReadMCP23017SystemB1():
    while True:
        dataByte = ReadDataByte(MCP23017BaseAddressSystemB1, RegisterAddressOffsetArray0, PortStatus, PortB)
        PrintEightBitPattern("Data byte read = ", dataByte)
        sleep(1)
 



So if I set Pin 3 to ground, I got the following print thing.
 

Data byte read =  11110111

So the basic things all look good.  I don't have much confidence playing with LVC254A.  I think perhaps I can install two HC573 buffers, one for MCP23017 output Port A, and one for input Port B.  The good thing with HC537 is that I can latch data, or isolate data from data bus.




.END



# *****************************************************************************
# !/usr/bin/python2.7
#
# Program
#   FPLl204.py - 2013jan30
# Author
#   tlfong01 <http://tlfong01.blogspot.hk/>
# Configuration
#   Raspberry Pi Bv2 512MB, Raspbian Wheezy, Python 2.7.3, GPIO 0.4.1a
# License
#   GNU GPLv3
# Warranty
#   For hobbist only.  Use at your own risk.  There is not  any warranty,
#   not even implied warranty of merchantability or fitness for any
#   particular purpose
# System development methodologies / Programming paradims
#   Software prototyping, Test-driven (TDD), Iterative and incremental (IID),
#   Agile / Functional Programming (FP), Object Oriented Programming (OOP)
# Specifications summary
#   5V0max 50mA, 3V3max 300mA, PerPinMax 17mA source 12mA sink
# References
#   IO Expander
#     1. Mcirochip Application Notes AN1043 (GPIO Expander)
#   Matrix keypad
#     2. Microchip Application Notes AN1081 (Matrix Keypad)
#   LCD1602
#     3. ShenZhen YaJingDa Electronics YJD1602A-1 datasheet (2007-09-08)
#     4. PowerTip PC-1602F datasheet (11/10/2004)
#     5. Sitronix ST7066U Dot Matrix LCD Controller/Driver datasheet (01/03/01)
# Raspberry Pi communities
#   eLinux -  http://elinux.org/RPi_Community
#   element14 - http://www.element14.com/community/groups/raspberry-pi
# *****************************************************************************

# 0. Contents ******************************************************************
#
#  1. Python imports
#  2. RPi GPIO pin assignment
#  3. Global constants
#  4. GPIO Functions
#  5. Debugging and documentation functions (beep, print bit/byte, message)
#  6. IO Expander MCP23008, MCP23017
#  7. Unipolar Stepping Motor 28BYJ48/NPM-PF35/PX245
#  8. Decimal keypad
#  9. LCD1602
# 10. Demultiplexor
# 11. Main test functions


# 1. Python imports ***********************************************************

import smbus
import sys
import RPi.GPIO as GPIO
from time import sleep
import select # interrupt module, not tested yet

# from __future__ import print_function       # import Python 3 print function so it can be used


# 2. RPi GPIO pin assignment and MCP230xx register base addresses ************

# * MCP230xx Register base addresses *

# RPi System A *

MCP23017BaseAddress1 = 0x22 # LED, button
MCP23008BaseAddress1 = 0x24 # stepping motors
MCP23008BaseAddress2 = 0x25 # keypad
MCP23008BaseAddress3 = 0x26 # LCD1602

# RPi System B *

MCP23017BaseAddressSystemB1 = 0x20

# * RPi GPIO pin numbering *

GPIO.setmode(GPIO.BOARD) # Use RPi GPIO numbering, Not BCM numbering
GPIO.setwarnings(False)  # Disable linux's "pin already in use warning"

# * RPi GPIO pin 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  

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/keypad interrupt

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

# * peripherals pins assignment *

LEDpin = RPiGPIOgen1
BuzzerPin = RPiGPIOgen4
ButtonPin = RPiGPIOgen5
TxDpin = RPiTxD
RxDpin = RPiRxD
KeypadInterruptPin = ButtonPin

# * GPIO input/output pins list *

OutputPinList = [LEDpin, BuzzerPin, TxDpin]
InputPinWithNoPullUpList = [ButtonPin, KeypadInterruptPin, RPiGPIOgen6]
InputPinWithPullUpList = []


# 3. 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
OneHundredMilliSeconds = 0.1
TwoHundredMilliSeconds = 0.2
TenthSecond = 0.1
QuarterSecond = 0.25
HalfSecond = 0.5
OneSecond = 1
OneAndHalfSeconds = 1.5
TwoSeconds = 2

# * On/Off times *
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

# * Interrupt states *
Low = False
High = True

# 4. GPIO Functions ***********************************************************
#
# * 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/echo/toggle 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 *
def TestBuzzer(): # beep 4 times
    SetupGPIO()
    for i in range (FourTimes):
        pulsePin(BuzzerPin, OnTime, OffTime)

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

def TestButtonEchoBuzzer(): #
    SetupGPIO()
    print "\n", "Press button 4 times.", "\n"
    for i in range (FourTimes):
        echoPin(ButtonPin, BuzzerPin)         

def TestButtonEchoLED(): #
    SetupGPIO()
    print "\n", "Press button 4 times.", "\n"   
    for i in range (FourTimes):
        echoPin(ButtonPin, LEDpin)

# 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"


# 5. Debugging and documentation functions ************************************

# * Data bit processing and printing *

def SetDataBit(dataByte, bitIndex):
    setDataByte = 0x01 << bitIndex
    dataByte = dataByte | setDataByte
    return dataByte

def ResetDataBit(dataByte, bitIndex):
    resetDataByte = ~(0x01 << bitIndex)
    dataByte = dataByte & resetDataByte
    return dataByte

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 ConvertIntegerToEightPattern(integer):
    EightBitPattern = [""]
    for k in range(8):
        EightBitPattern = [i+j for i in ['0','1'] for j in EightBitPattern]
    return EightBitPattern[integer]

def PrintEightBitPattern(message, dataByte):
    eightBitPattern = ConvertIntegerToEightPattern(dataByte)
    print message, eightBitPattern

OnTime = 0.1
OffTime = 0.25

def Beep(count):
    for i in range(count):
        pulsePin(BuzzerPin, OnTime, OffTime)
   
def StartBeep():
    Beep(4)
    sleep(1)

def EndBeep():
    Beep(4)

def OneBeep():
    Beep(1)

def FourBeeps():
    Beep(4)

def StartProgram(message):
    SetupGPIO()
    StartBeep()
    print "\n", message, "\n"

def EndProgram():
    EndBeep()
    endMessage = "*** End of program.***"
    print "\n", endMessage, "\n"

# 6. IO Expander MCP23008, MCP23017 *******************************************

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

# * i2cdetect/i2cset commands *
# To detect I2C device base addresses
#   sudo i2cdetect -y 1
# * To toggle GPIO pin *
#  !/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

# * Port type *
PortA = 0
PortB = 1

# * Data constant 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 input/output ports *

def SetupMCP23008PortAllOutput(registerBaseAddress):
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InputOutputDirection, PortA, AllOutput)

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

# * Read/write 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

def ReadUpperDataNibble(registerBaseAddress, registerAddressArray, dataRegisterIndex, portType):
    dataByte = ReadDataByte(registerBaseAddress, registerAddressArray, dataRegisterIndex, portType)
    upperDataNibble = dataByte >> 4
    return upperDataNibble

# * Config interrupts *
def EnableInterruptOnChangeHighNibble(registerBaseAddress):
    EnableInterruptHighNibble = 0xf0
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InterruptOnChangeEnable, PortA, EnableInterruptHighNibble)

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

def SetInterruptOnChangeDefaultHighNibble(registerBaseAddress):
    DefaultValueByte = 0xf0
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InterruptOnChangeDefaultValue, PortA, DefaultValueByte)

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)

# * Poll interrupts *
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 DetectOneInterrupt(registerBaseAddress): # !!! NOT tested !!!  
    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)

# * Test port toggling, poll interrupts *

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)

def TestDetectInterrupt(count): # !!! NOT Tested !!!
    RegisterBaseAddress =  MCP23008BaseAddress2
    SetupKeypadInterruptHighNibbleCompareDefaultValueOpenDrainOutput(RegisterBaseAddress)
    for i in range(count):
        DetectOneInterrupt(RegisterBaseAddress)

# * System B functions *

def TestToggleMCP23017SystemB1(): # 2013jan30
    ToggleTime = 0.5
    ToggleCount = 4
    ToggleMCP23017GP(MCP23017BaseAddressSystemB1,  ToggleTime, ToggleCount)

def TestReadMCP23017SystemB1():
    while True:
        dataByte = ReadDataByte(MCP23017BaseAddressSystemB1, RegisterAddressOffsetArray0, PortStatus, PortB)
        PrintEightBitPattern("Data byte read = ", dataByte)
        sleep(1)

# 7. 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, OneHundredMilliSeconds)
    OneBeep()
    WriteMotorWindingFullStepSequence13232414(MCP23008BaseAddress1, HighNibble, TwentyTimes, OneHundredMilliSeconds)

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


# 8. Decimal keypad ***********************************************************

# * Function descriptions *

# SetupKeypad(registerBaseAddress)
# Assign GP0~3 as output (Col0~2, Col3 n.c), GP4~7 input (Row0~3)

# WriteAllColumnsLow(registerBaseAddress)
# Set all column outputs (GP0~2 actually, GP3 dummy)

# LoopUntilGpioPortHighNibbleChange(registerBaseAddress)
# Loop until GPIO register's GP4~7 not all High (some key pressed)

# LoopUntilInterruptCaptureHighNibbleChange(registerBaseAddress)
# Loop until INTCAP register's ICP4~7 not all High (some key pressed)

# 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

PollGpioRegister = 0
PollKeypadInterruptPin = 1

# * Set up keypad matrix GPIO and interrupts *

def SetupKeypad(registerBaseAddress):
    SetupKeypadGPIO(registerBaseAddress)
    SetupKeypadInterrupt(registerBaseAddress)
    ClearKeypadInterrupt(registerBaseAddress)

def SetupKeypadGPIO(registerBaseAddress):
    UpperNibbleInputLowerNibbleOutput = 0xf0 # (GP4~7, Row0~3) = input, lower nibble (GP0~3, Column0~2, 3 = no coonect) = output
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InputOutputDirection, PortA, UpperNibbleInputLowerNibbleOutput)
    WriteAllColumnsLow(registerBaseAddress)

def SetupKeypadInterrupt(registerBaseAddress):
    EnableInterruptOnChangeHighNibble(registerBaseAddress) # Enable interrupt on upper nibble (GP4~7)
    SetInterruptOnChangeDefaultHighNibble(registerBaseAddress) # Interrupt default values = all high (GP4~7 0b1111)
    SetInterruptOnChangeCompareDefaultHighNibble(registerBaseAddress) # select compare default value interrutpt
    SetInterruptOutputOpenDrain(registerBaseAddress) # set interrupt output (INT) open drain

def EnableInterruptOnChangeHighNibble(registerBaseAddress):
    EnableInterruptHighNibble = 0xf0
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InterruptOnChangeEnable, PortA, EnableInterruptHighNibble)

def SetInterruptOnChangeDefaultHighNibble(registerBaseAddress):
    DefaultValueByte = 0xf0
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InterruptOnChangeDefaultValue, PortA, DefaultValueByte)

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)

# * Clear and poll interrupts *

def ClearKeypadInterrupt(registerBaseAddress):
    WriteAllColumnsLow(registerBaseAddress)
    sleep(0.5)
    dummyRead = ReadInterruptCaptureHighNibble(registerBaseAddress)

def ReadKeypadInterruptPin(interruptPin):
    interruptStatus = readInputPin(interruptPin)
    return interruptStatus

# * Write columns *

def WriteColumns(registerBaseAddress, columnDataNibble):
    columnDataByte = columnDataNibble | 0x00
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, columnDataByte)

def WriteAllColumnsLow(registerBaseAddress):
    AllColumnsLowNibble = 0x0
    WriteColumns(registerBaseAddress, AllColumnsLowNibble)

def WriteColumn0Low(registerBaseAddress):
    Column0LowNibble = 0xe
    WriteColumns(registerBaseAddress, Column0LowNibble)

def WriteColumn1Low(registerBaseAddress):
    Column1LowNibble = 0xd
    WriteColumns(registerBaseAddress, Column1LowNibble)

def WriteColumn2Low(registerBaseAddress):
    Column2LowNibble = 0xb
    WriteColumns(registerBaseAddress, Column2LowNibble)

# * Read rows *

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

def GetRowDataNibble(registerBaseAddress):
    rowDataByte = ReadDataByte(registerBaseAddress, RegisterAddressOffsetArray0, PortStatus, PortA)
    rowDataNibble = rowDataByte >> 4
    return rowDataNibble

# * Get row number, column number, and key number *

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):
    RowsAllHigh = 0xf

    WriteColumn0Low(registerBaseAddress)
    rowDataNibble = ReadUpperDataNibble(registerBaseAddress, RegisterAddressOffsetArray0, PortStatus, PortA)
    if rowDataNibble != RowsAllHigh:
        columnNumber = 0
        return columnNumber

    WriteColumn1Low(registerBaseAddress)
    rowDataNibble = ReadUpperDataNibble(registerBaseAddress, RegisterAddressOffsetArray0, PortStatus, PortA)
    if rowDataNibble != RowsAllHigh:
        columnNumber = 1
        return columnNumber

    WriteColumn2Low(registerBaseAddress)
    rowDataNibble = ReadUpperDataNibble(registerBaseAddress, RegisterAddressOffsetArray0, PortStatus, PortA)
    if rowDataNibble != RowsAllHigh:
        columnNumber = 2
        return columnNumber

def GetKeyNumber(rowNumber, columnNumber):
    # print "Row Number = ", rowNumber
    # print "Column Number = ", columnNumber
    keyNumber = (rowNumber * 3) + (columnNumber + 1)
    return keyNumber

# * Get one key and a sequence of keys *

def GetKey(registerBaseAddress, pollType):
    PollGpioRegister = 0
    PollKeypadInterruptPin = 1

    if (pollType == PollGpioRegister):
        # print "Polling GpioRegister"
        LoopUntilGpioPortHighNibbleChange(registerBaseAddress)

    elif (pollType == PollKeypadInterruptPin):
        # print "Polling Interrupt Pin"
        LoopUntilKeypadInterruptPinLow(registerBaseAddress, KeypadInterruptPin)        

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

def GetKeyString(registerBaseAddress, pollType, count):
    for i in range (count):       
        OneBeep()
    keyNumber = GetKey(registerBaseAddress, pollType)   
        print "Key", i + 1, " = ", keyNumber

# * Loop until row data changes *

def LoopUntilGpioPortHighNibbleChange(registerBaseAddress):
    WriteAllColumnsLow(registerBaseAddress)
    NoKeyPressedNibble = 0xf
    rowDataNibble = NoKeyPressedNibble
    while (rowDataNibble == NoKeyPressedNibble):
        rowDataNibble = GetRowDataNibble(registerBaseAddress)
    sleep(0.05) # debouncing time 50mS

# * Loop until interrupt key low *

def LoopUntilKeypadInterruptPinLow(registerBaseAddress, interruptPin):
    ClearKeypadInterrupt(registerBaseAddress)
    interruptPinState = readInputPin(interruptPin)
    #print "interruptPinState = ", interruptPinState
    while (interruptPinState == True):
       interruptPinState = readInputPin(interruptPin)
       #print "interruptPinState = ", interruptPinState
       #sleep(1)
    #print "interruptPinState = ", interruptPinState
    sleep(0.05) # debouncing time 50mS

# * Test keypad *

def TestKeypad(registerBaseAddress, pollType, count):
    SetupKeypad(registerBaseAddress)
    if (pollType == PollKeypadInterruptPin):
        SetupKeypadInterrupt(registerBaseAddress)
        ClearKeypadInterrupt(registerBaseAddress)
    print "*** Start testing keypad. ***" 
    print "Press keypad ", count, "times."
    GetKeyString(registerBaseAddress, pollType, count)
    ClearKeypadInterrupt(registerBaseAddress)
    print "*** Stop testing keypad. ***\n"

def TestPollingKeypad():
    TestKeypad(KeypadRegisterBaseAddress, PollGpioRegister, count = 4)

def TestInterruptKeypad():
    TestKeypad(KeypadRegisterBaseAddress, PollKeypadInterruptPin, count = 4)


# * LCD1602 *******************************************************************

LcdRegisterBaseAddress = MCP23008BaseAddress3 # 0x26

# * LCD1602A-1 16-pin connector (MCP23008 GPIO 6-pin connector) Pin numbering *

# Pin 04 (Pin 1) RegisterSelect
# Pin 06 (Pin 2) WriteEnable 
# Pin 11 (Pin 3) DataBit4 = 2
# Pin 12 (Pin 4) DataBit5 = 3
# Pin 13 (Pin 5) DataBit6 = 4
# Pin 14 (Pin 6) DataBit7 = 5

# LCD1602 software initialization procedure

# Part 1 - Pseudo 8 bit instructions to config 4 bit instruction mode (0x30, 0x30, 0x30, 0x20)

# 1. Power on, wait 20mS (> 15mS)
# 2. Write 0x3, wait 5mS (> 4.1mS) # set 8 bit interface
# 3. Write 0x3, wait 1mS (> 100uS) # set 8 bit interface
# 4. Write 0x3, wait 1mS (> 37uS)  # set 8 bit interface
# 5. Write 0x2, wait 1mS (> 37uS)  # set 4 bit interface

# Part 2- 4 bit instructions to config display characteristics (0x28, 0x08, 0x01, 0x06, 0x02)

# 6. Write 0x2, wait 1mS (> 37uS) 0x8 wait 1mS (> 37uS) (2 lines, 5x8 dot)
# 7. Write 0x0, wait 1mS (> 37uS) 0x8 wait 1mS (> 37uS) (cursor on, no blinking, blank screen)
# 8. Write 0x0, wait 1mS (> 37uS) 0x1 wait 2mS (> 1.52mS)(clear display)
# 9. Write 0x0, wait 1mS (> 37uS) 0xe (DisplayOnCursorOnCursorBlinkOff)
# a. Write 0x0, wait 1mS (> 37uS) 0x2 wait 2mS (cursor home)

# * LCD1602A-1 16-pin connector pin numbering *

RegisterSelect = 0 # 0 = instruction register, 1 = data register
WriteEnable = 1 # 1 = enable, 0 = disable
DataBit4 = 2
DataBit5 = 3
DataBit6 = 4
DataBit7 = 5

# * Constant data bytes *

AllZeroDataByte = 0x00
AllOneDataByte = 0xff

# * Write Enable/Disable, Select Data/Instruction masks *

EnableWriteMask  = 0x02 # 0b 0000 0010
DisableWriteMask = 0xfd # 0b 1111 1101

SelectDataRegisterMask        = 0x01 # 0b 0000 0001
SelectInstructionRegisterMask = 0xfe # 0b 1111 1110

# Register selection constant *

InstructionRegister = 0
DataRegister = 1

# * LCD1602A-1 control instructions *

# * Instruction codes *

EightBitInstructionMode = 0x3
FourBitInstructionMode  = 0x2

TwoLineFiveTimesEightDot1 = 0x2
TwoLineFiveTimesEightDot2 = 0x8

DisplayOnCursorOnCursorBlinkOff1 = 0x0
DisplayOnCursorOnCursorBlinkOff2 = 0xe

ClearDisplay1 = 0x0
ClearDisplay2 = 0x1

CursorIncrementDisplayNoShift1 = 0x0
CursorIncrementDisplayNoShift2 = 0x6

CursorHome1 = 0x0
CursorHome2 = 0x2

# * Instruction timing *

PowerOnResetDelay      = 0.05    # 50mS
VeryLongOperationDelay = 0.005   # 5mS
LongOperationDelay     = 0.002   # 2mS
ShortOperationDelay    = 0.00005 # 50uS
WritePulseLength       = 0.00005 # 50uS

# * RPi/MCP23008 Python functions *

def LcdGpioSetup(registerBaseAddress):
    SetupMCP23008PortAllOutput(registerBaseAddress)

def LcdConfiguration(registerBaseAddress):
    dataControlByte = AllZeroDataByte
    # PrintEightBitPattern("dataControlByte01 = ", dataControlByte)
    LcdDisableWrite(registerBaseAddress, dataControlByte)
    sleep(PowerOnResetDelay)

    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, EightBitInstructionMode, InstructionRegister, VeryLongOperationDelay)
    # PrintEightBitPattern("dataControlByte02 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, EightBitInstructionMode, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte03 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, EightBitInstructionMode, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte04 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, FourBitInstructionMode, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte05 = ", dataControlByte)

    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, TwoLineFiveTimesEightDot1, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte06 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, TwoLineFiveTimesEightDot2, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte07 = ", dataControlByte)

    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, DisplayOnCursorOnCursorBlinkOff1, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte08 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, DisplayOnCursorOnCursorBlinkOff2, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte09 = ", dataControlByte)

    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, ClearDisplay1, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte10 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, ClearDisplay2, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte11 = ", dataControlByte)

    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, CursorIncrementDisplayNoShift1, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte12 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, CursorIncrementDisplayNoShift2, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte13 = ", dataControlByte)

    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, CursorHome1, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte14 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, CursorHome2, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte15 = ", dataControlByte)

    return dataControlByte

def LcdSelectDataRegister(registerBaseAddress, dataByte):
    dataByte = dataByte | SelectDataRegisterMask
    LcdWriteDataByte(registerBaseAddress, dataByte) 
    return dataByte

def LcdSelectInstructionRegister(registerBaseAddress, dataByte):
    dataByte = dataByte & SelectInstructionRegisterMask
    LcdWriteDataByte(registerBaseAddress, dataByte)
    return dataByte

def LcdEnableWrite(registerBaseAddress, dataByte):
    dataByte = dataByte | EnableWriteMask
    LcdWriteDataByte(registerBaseAddress, dataByte)
    return dataByte

def LcdDisableWrite(registerBaseAddress, dataByte):
    dataByte = dataByte & DisableWriteMask
    LcdWriteDataByte(registerBaseAddress, dataByte)
    return dataByte

def LcdWritePulse(registerBaseAddress, dataByte):
    LcdEnableWrite(registerBaseAddress, dataByte)
    sleep(WritePulseLength)
    LcdDisableWrite(registerBaseAddress, dataByte)
    return dataByte

def LcdWriteDataByte(registerBaseAddress, dataByte):
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, dataByte)
    return dataByte

def LcdWriteDataNibble(registerBaseAddress, dataByte, dataNibble, registerType, operationDelay):
    dataNibble = dataNibble << 2
    dataByte = dataByte & 0b11000011
    dataByte = dataByte | dataNibble
    if (registerType == InstructionRegister):
       dataByte = LcdSelectInstructionRegister(registerBaseAddress, dataByte)
    elif (registerType == DataRegister):
       dataByte = LcdSelectDataRegister(registerBaseAddress, dataByte)
    LcdDisableWrite(registerBaseAddress, dataByte)
    dataByte = LcdWriteDataByte(registerBaseAddress, dataByte)
    LcdWritePulse(registerBaseAddress, dataByte)
    sleep(operationDelay)
    return dataByte

def LcdWriteCharFourBitMode(registerBaseAddress, dataControlByte, asciiChar, registerType):
    charUpperNibble = ord(asciiChar) >> 4
    charLowerNibble = ord(asciiChar) & 0x0f
    LcdWriteCharTwoNibbles(registerBaseAddress, dataControlByte, charUpperNibble, charLowerNibble, registerType)
    return dataControlByte

def LcdWriteDataByteFourBitMode(registerBaseAddress, dataControlByte, dataByte, registerType):
    upperNibble = dataByte >> 4
    lowerNibble = dataByte & 0x0f
    LcdWriteCharTwoNibbles(registerBaseAddress, dataControlByte, upperNibble, lowerNibble, registerType)
    return dataControlByte

def LcdWriteCharTwoNibbles(registerBaseAddress, dataControlByte, upperNibble, lowerNibble, registerType):
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, upperNibble, registerType, ShortOperationDelay)
    # PrintEightBitPattern("dataControlByte01 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, lowerNibble, registerType, ShortOperationDelay)
    # PrintEightBitPattern("dataControlByte02 = ", dataControlByte)
    return dataControlByte

def LcdWriteCharString(registerBaseAddress, dataControlByte, string):
    for asciiChar in (string):
        LcdWriteCharFourBitMode(registerBaseAddress, dataControlByte, asciiChar, DataRegister)
    return dataControlByte

def LcdMoveCursor(registerBaseAddress, dataControlByte, rowNumber, columnNumber):
    if (rowNumber == 1):
       cursorByte = 0x80 | (0x00 + (columnNumber - 1)) # command = 0x80, row 1 DDRAM start address = 0x10
    elif (rowNumber == 2):
       cursorByte = 0x80 | (0x40 + (columnNumber - 1)) # row 2 DDRAM start address = 0x40
    LcdWriteDataByteFourBitMode(registerBaseAddress, dataControlByte, cursorByte, InstructionRegister)

    # charUpperNibble = cursorByte >> 4
    # charLowerNibble = cursorByte & 0x0f
    # dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, charUpperNibble, InstructionRegister, ShortOperationDelay)
    # dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, charLowerNibble, InstructionRegister, ShortOperationDelay)
    return dataControlByte

# * Test LCD1602 functions *

def TestLcd(registerBaseAddress):
    LcdGpioSetup(registerBaseAddress)
    dataControlByte = LcdConfiguration(registerBaseAddress)
    dataControlByte = LcdMoveCursor(registerBaseAddress, dataControlByte, 1, 2)
    dataControlByte = LcdWriteCharString(registerBaseAddress, dataControlByte, "Raspberry Pi")
    dataControlByte = LcdMoveCursor(registerBaseAddress, dataControlByte, 2, 1)
    dataControlByte = LcdWriteCharString(registerBaseAddress, dataControlByte, "2013-01-25 v1141")

# * Demultiplexer *************************************************************

DemuxRegisterBaseAddress = KeypadRegisterBaseAddress

def DemuxGpioSetup(registerBaseAddress):
    UpperNibbleInputLowerNibbleOutput = 0xf0 # Input (GP4~7, Row0~3), Output (GP0~3, Column0~2, 3 reserved)
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InputOutputDirection, PortA, UpperNibbleInputLowerNibbleOutput)
    InitialControlDateByte = 0x00
    return InitialControlDateByte

def DemuxAddressLatch(controlDataByte, latchAction):
    DemuxAddressLatchMask = 0b00001000
    Enable = 1
    Disable = 0
    if (latchAction == Enable):
        controlDataByte = controlDataByte | DemuxAddressLatchMask
    elif (latchAction == Disable):
       controlDataByte = controlDataByte & ~(DemuxAddressLatchMask)
    return controlDataByte

def SetDemuxChannel(registerBaseAddress, controlDataByte, addressNibble):
    Enable = 1
    Disable = 0
    controlDataByte = controlDataByte & 0b11110000
    addressNibble = addressNibble & 0b00000111
    controlDataByte = controlDataByte | addressNibble
    controlDataByte = DemuxAddressLatch(controlDataByte, Disable)
    PrintEightBitPattern("demuxDataByte = ", controlDataByte)
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, controlDataByte)
    controlDataByte = DemuxAddressLatch(controlDataByte, Enable)
    PrintEightBitPattern("demuxDataByte = ", controlDataByte)
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, controlDataByte)
    return controlDataByte

def TestDemux(demuxRegisterBaseAddress, channelNumber): 
    controlDataByte = DemuxGpioSetup(demuxRegisterBaseAddress)
    controlDataByte = SetDemuxChannel(demuxRegisterBaseAddress, controlDataByte, channelNumber)

def TestDemuxLcd(demuxRegisterBaseAddress, lcdRegisterBaseAddress, channelNumber1, channelNumber2):
    Row1 = 1
    Row2 = 2
    Row3 = 3
    Row4 = 4
    Column1 = 1

    # Setup one MCP23008 for Demultiplexor, another MCP23008 for LCD1602/LCD2004
    controlDataByte = DemuxGpioSetup(demuxRegisterBaseAddress)
    LcdGpioSetup(lcdRegisterBaseAddress)

    # Select demux channelNumber1
    controlDataByte = SetDemuxChannel(demuxRegisterBaseAddress, controlDataByte, channelNumber1)

    # Display two lines in the LCD at this channel
    dataControlByte = LcdConfiguration(lcdRegisterBaseAddress)
    dataControlByte = LcdMoveCursor(lcdRegisterBaseAddress, dataControlByte, Row1, Column1)
    dataControlByte = LcdWriteCharString(lcdRegisterBaseAddress, dataControlByte, "Channel 1 01")
    dataControlByte = LcdMoveCursor(lcdRegisterBaseAddress, dataControlByte, Row2, Column1)
    dataControlByte = LcdWriteCharString(lcdRegisterBaseAddress, dataControlByte, "2013-01-28 v201")

    sleep(2)

    # Select demux channelNumber2
    controlDataByte = SetDemuxChannel(demuxRegisterBaseAddress, controlDataByte, channelNumber2)

    # Display two lines in the LCD at this channel
    dataControlByte = LcdConfiguration(lcdRegisterBaseAddress)
    dataControlByte = LcdMoveCursor(lcdRegisterBaseAddress, dataControlByte, Row1, Column1)
    dataControlByte = LcdWriteCharString(lcdRegisterBaseAddress, dataControlByte, "Channel 2 01")
    dataControlByte = LcdMoveCursor(lcdRegisterBaseAddress, dataControlByte, Row2, Column1)
    dataControlByte = LcdWriteCharString(lcdRegisterBaseAddress, dataControlByte, "2013-01-30 v204")

    sleep(2)

# 11. Main test functions *****************************************************
StartProgram("*** Test Demultiplexer v204 TL Fong 2013jan30hkt1513 ***")

# RPi System A Tests *

# TestBuzzer() # beep buzzer 4 times
# TestLED() # blink LED 4 tmes
# TestButtonEchoBuzzer() # echo buton with buzzer 4 times
# TestButtonEchoLED() # echo button with LED 4 times
# TestToggleMCP23017GP() # toggle MCP23017 #1 output ports (connected to only 1 LED)
# TestToggleMCP23008GP() # toggle MCP23008 #1 output port (connected to stepping motors)
# TestMotor() # move two stepping motors
# TestPollingKeypad() # read 4 keys by polling IO Expander GPIO port of rows (MCP23008 #2)
# TestInterruptKeypad() # read 4 keys by polling IO Expander INT pin # !!! not tested !!!
# TestLcd(LcdRegisterBaseAddress)
# TestDemux(DemuxRegisterBaseAddress, 4) 
# TestDemuxLcd(DemuxRegisterBaseAddress, LcdRegisterBaseAddress, channelNumber1 = 0, channelNumber2 = 1)

# RPi System B Tests *

# TestBuzzer() # beep buzzer 4 times
# TestLED() # blink LED 4 tmes
# TestButtonEchoBuzzer() # echo buton with buzzer 4 times
# TestButtonEchoLED() # echo button with LED 4 times

TestToggleMCP23017SystemB1() # toggle MCP23017 System B1

TestReadMCP23017SystemB1()

EndProgram()

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

LVC245A testing notes





Posted by Picasa

http://www.blogger.com/blogger.g?blogID=1840459474737789074#editor/target=post;postID=8693921551839077666

I have soldered the LVC245A to MCP23017.  I also did the off line hands on test of 5V/0V input by hand and see that the LVC245 A side port and MCP23008 B port reflects the 5V/0V to 3V3/0V.

I also found that I made a mistake in the direction last time.  This new summary is a correction.

.END


SN74 LVC245A MCP23017 interface


Posted by Picasa

http://www.blogger.com/blogger.g?blogID=1840459474737789074#editor/target=post;postID=7074160684462416023

Now I am carefully reading the SN74LVC245A datasheet before connecting it to the MCP23017 port B.

.END  

MCP23017 Programming Notes

Now I began testing MCP23017's GPIO ports.  I ran the old test programs to blink the LEDs connected to Port 1, but nothing happens.  Then I remembered that I have not connected the 5V0 power supply.  After connecting the power supply, the LEDs blink happily.


# 11. Main test functions *****************************************************
StartProgram("*** Test Demultiplexer v202 TL Fong 2013jan29hkt2153 ***")

...

 
# RPi System B Tests *

# TestBuzzer() # beep buzzer 4 times
# TestLED() # blink LED 4 tmes
# TestButtonEchoBuzzer() # echo buton with buzzer 4 times
# TestButtonEchoLED() # echo button with LED 4 times

TestToggleMCP23017SystemB1() # toggle MCP23017 System B1


EndProgram()


.END



# *****************************************************************************
# !/usr/bin/python2.7
#
# Program
#   FPLl203.py - 2013jan30
# Author
#   tlfong01 <http://tlfong01.blogspot.hk/>
# Configuration
#   Raspberry Pi Bv2 512MB, Raspbian Wheezy, Python 2.7.3, GPIO 0.4.1a
# License
#   GNU GPLv3
# Warranty
#   For hobbist only.  Use at your own risk.  There is not  any warranty,
#   not even implied warranty of merchantability or fitness for any
#   particular purpose
# System development methodologies / Programming paradims
#   Software prototyping, Test-driven (TDD), Iterative and incremental (IID),
#   Agile / Functional Programming (FP), Object Oriented Programming (OOP)
# Specifications summary
#   5V0max 50mA, 3V3max 300mA, PerPinMax 17mA source 12mA sink
# References
#   IO Expander
#     1. Mcirochip Application Notes AN1043 (GPIO Expander)
#   Matrix keypad
#     2. Microchip Application Notes AN1081 (Matrix Keypad)
#   LCD1602
#     3. ShenZhen YaJingDa Electronics YJD1602A-1 datasheet (2007-09-08)
#     4. PowerTip PC-1602F datasheet (11/10/2004)
#     5. Sitronix ST7066U Dot Matrix LCD Controller/Driver datasheet (01/03/01)
# Raspberry Pi communities
#   eLinux -  http://elinux.org/RPi_Community
#   element14 - http://www.element14.com/community/groups/raspberry-pi
# *****************************************************************************

# 0. Contents ******************************************************************
#
#  1. Python imports
#  2. RPi GPIO pin assignment
#  3. Global constants
#  4. GPIO Functions
#  5. Debugging and documentation functions (beep, print bit/byte, message)
#  6. IO Expander MCP23008, MCP23017
#  7. Unipolar Stepping Motor 28BYJ48/NPM-PF35/PX245
#  8. Decimal keypad
#  9. LCD1602
# 10. Demultiplexor
# 11. Main test functions


# 1. Python imports ***********************************************************

import smbus
import sys
import RPi.GPIO as GPIO
from time import sleep
import select # interrupt module, not tested yet

# from __future__ import print_function       # import Python 3 print function so it can be used


# 2. RPi GPIO pin assignment and MCP230xx register base addresses ************

# * MCP230xx Register base addresses *

# RPi System A *

MCP23017BaseAddress1 = 0x22 # LED, button
MCP23008BaseAddress1 = 0x24 # stepping motors
MCP23008BaseAddress2 = 0x25 # keypad
MCP23008BaseAddress3 = 0x26 # LCD1602

# RPi System B *

MCP23017BaseAddressSystemB1 = 0x20

# * RPi GPIO pin numbering *

GPIO.setmode(GPIO.BOARD) # Use RPi GPIO numbering, Not BCM numbering
GPIO.setwarnings(False)  # Disable linux's "pin already in use warning"

# * RPi GPIO pin 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  

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/keypad interrupt

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

# * peripherals pins assignment *

LEDpin = RPiGPIOgen1
BuzzerPin = RPiGPIOgen4
ButtonPin = RPiGPIOgen5
TxDpin = RPiTxD
RxDpin = RPiRxD
KeypadInterruptPin = ButtonPin

# * GPIO input/output pins list *

OutputPinList = [LEDpin, BuzzerPin, TxDpin]
InputPinWithNoPullUpList = [ButtonPin, KeypadInterruptPin, RPiGPIOgen6]
InputPinWithPullUpList = []


# 3. 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
OneHundredMilliSeconds = 0.1
TwoHundredMilliSeconds = 0.2
TenthSecond = 0.1
QuarterSecond = 0.25
HalfSecond = 0.5
OneSecond = 1
OneAndHalfSeconds = 1.5
TwoSeconds = 2

# * On/Off times *
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

# * Interrupt states *
Low = False
High = True

# 4. GPIO Functions ***********************************************************
#
# * 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/echo/toggle 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 *
def TestBuzzer(): # beep 4 times
    SetupGPIO()
    for i in range (FourTimes):
        pulsePin(BuzzerPin, OnTime, OffTime)

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

def TestButtonEchoBuzzer(): #
    SetupGPIO()
    print "\n", "Press button 4 times.", "\n"
    for i in range (FourTimes):
        echoPin(ButtonPin, BuzzerPin)         

def TestButtonEchoLED(): #
    SetupGPIO()
    print "\n", "Press button 4 times.", "\n"   
    for i in range (FourTimes):
        echoPin(ButtonPin, LEDpin)

# 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"


# 5. Debugging and documentation functions ************************************

# * Data bit processing and printing *

def SetDataBit(dataByte, bitIndex):
    setDataByte = 0x01 << bitIndex
    dataByte = dataByte | setDataByte
    return dataByte

def ResetDataBit(dataByte, bitIndex):
    resetDataByte = ~(0x01 << bitIndex)
    dataByte = dataByte & resetDataByte
    return dataByte

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 ConvertIntegerToEightPattern(integer):
    EightBitPattern = [""]
    for k in range(8):
        EightBitPattern = [i+j for i in ['0','1'] for j in EightBitPattern]
    return EightBitPattern[integer]

def PrintEightBitPattern(message, dataByte):
    eightBitPattern = ConvertIntegerToEightPattern(dataByte)
    print message, eightBitPattern

OnTime = 0.1
OffTime = 0.25

def Beep(count):
    for i in range(count):
        pulsePin(BuzzerPin, OnTime, OffTime)
   
def StartBeep():
    Beep(4)
    sleep(1)

def EndBeep():
    Beep(4)

def OneBeep():
    Beep(1)

def FourBeeps():
    Beep(4)

def StartProgram(message):
    SetupGPIO()
    StartBeep()
    print "\n", message, "\n"

def EndProgram():
    EndBeep()
    endMessage = "*** End of program.***"
    print "\n", endMessage, "\n"

# 6. IO Expander MCP23008, MCP23017 *******************************************

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

# * i2cdetect/i2cset commands *
# To detect I2C device base addresses
#   sudo i2cdetect -y 1
# * To toggle GPIO pin *
#  !/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

# * Port type *
PortA = 0
PortB = 1

# * Data constant 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 input/output ports *

def SetupMCP23008PortAllOutput(registerBaseAddress):
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InputOutputDirection, PortA, AllOutput)

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

# * Read/write 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

def ReadUpperDataNibble(registerBaseAddress, registerAddressArray, dataRegisterIndex, portType):
    dataByte = ReadDataByte(registerBaseAddress, registerAddressArray, dataRegisterIndex, portType)
    upperDataNibble = dataByte >> 4
    return upperDataNibble

# * Config interrupts *
def EnableInterruptOnChangeHighNibble(registerBaseAddress):
    EnableInterruptHighNibble = 0xf0
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InterruptOnChangeEnable, PortA, EnableInterruptHighNibble)

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

def SetInterruptOnChangeDefaultHighNibble(registerBaseAddress):
    DefaultValueByte = 0xf0
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InterruptOnChangeDefaultValue, PortA, DefaultValueByte)

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)

# * Poll interrupts *
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 DetectOneInterrupt(registerBaseAddress): # !!! NOT tested !!!  
    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)

# * Test port toggling, poll interrupts *

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)

def TestDetectInterrupt(count): # !!! NOT Tested !!!
    RegisterBaseAddress =  MCP23008BaseAddress2
    SetupKeypadInterruptHighNibbleCompareDefaultValueOpenDrainOutput(RegisterBaseAddress)
    for i in range(count):
        DetectOneInterrupt(RegisterBaseAddress)

def TestToggleMCP23017SystemB1(): # 2013jan30
    ToggleTime = 0.5
    ToggleCount = 4
    ToggleMCP23017GP(MCP23017BaseAddressSystemB1,  ToggleTime, ToggleCount)

# 7. 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, OneHundredMilliSeconds)
    OneBeep()
    WriteMotorWindingFullStepSequence13232414(MCP23008BaseAddress1, HighNibble, TwentyTimes, OneHundredMilliSeconds)

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


# 8. Decimal keypad ***********************************************************

# * Function descriptions *

# SetupKeypad(registerBaseAddress)
# Assign GP0~3 as output (Col0~2, Col3 n.c), GP4~7 input (Row0~3)

# WriteAllColumnsLow(registerBaseAddress)
# Set all column outputs (GP0~2 actually, GP3 dummy)

# LoopUntilGpioPortHighNibbleChange(registerBaseAddress)
# Loop until GPIO register's GP4~7 not all High (some key pressed)

# LoopUntilInterruptCaptureHighNibbleChange(registerBaseAddress)
# Loop until INTCAP register's ICP4~7 not all High (some key pressed)

# 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

PollGpioRegister = 0
PollKeypadInterruptPin = 1

# * Set up keypad matrix GPIO and interrupts *

def SetupKeypad(registerBaseAddress):
    SetupKeypadGPIO(registerBaseAddress)
    SetupKeypadInterrupt(registerBaseAddress)
    ClearKeypadInterrupt(registerBaseAddress)

def SetupKeypadGPIO(registerBaseAddress):
    UpperNibbleInputLowerNibbleOutput = 0xf0 # (GP4~7, Row0~3) = input, lower nibble (GP0~3, Column0~2, 3 = no coonect) = output
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InputOutputDirection, PortA, UpperNibbleInputLowerNibbleOutput)
    WriteAllColumnsLow(registerBaseAddress)

def SetupKeypadInterrupt(registerBaseAddress):
    EnableInterruptOnChangeHighNibble(registerBaseAddress) # Enable interrupt on upper nibble (GP4~7)
    SetInterruptOnChangeDefaultHighNibble(registerBaseAddress) # Interrupt default values = all high (GP4~7 0b1111)
    SetInterruptOnChangeCompareDefaultHighNibble(registerBaseAddress) # select compare default value interrutpt
    SetInterruptOutputOpenDrain(registerBaseAddress) # set interrupt output (INT) open drain

def EnableInterruptOnChangeHighNibble(registerBaseAddress):
    EnableInterruptHighNibble = 0xf0
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InterruptOnChangeEnable, PortA, EnableInterruptHighNibble)

def SetInterruptOnChangeDefaultHighNibble(registerBaseAddress):
    DefaultValueByte = 0xf0
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InterruptOnChangeDefaultValue, PortA, DefaultValueByte)

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)

# * Clear and poll interrupts *

def ClearKeypadInterrupt(registerBaseAddress):
    WriteAllColumnsLow(registerBaseAddress)
    sleep(0.5)
    dummyRead = ReadInterruptCaptureHighNibble(registerBaseAddress)

def ReadKeypadInterruptPin(interruptPin):
    interruptStatus = readInputPin(interruptPin)
    return interruptStatus

# * Write columns *

def WriteColumns(registerBaseAddress, columnDataNibble):
    columnDataByte = columnDataNibble | 0x00
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, columnDataByte)

def WriteAllColumnsLow(registerBaseAddress):
    AllColumnsLowNibble = 0x0
    WriteColumns(registerBaseAddress, AllColumnsLowNibble)

def WriteColumn0Low(registerBaseAddress):
    Column0LowNibble = 0xe
    WriteColumns(registerBaseAddress, Column0LowNibble)

def WriteColumn1Low(registerBaseAddress):
    Column1LowNibble = 0xd
    WriteColumns(registerBaseAddress, Column1LowNibble)

def WriteColumn2Low(registerBaseAddress):
    Column2LowNibble = 0xb
    WriteColumns(registerBaseAddress, Column2LowNibble)

# * Read rows *

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

def GetRowDataNibble(registerBaseAddress):
    rowDataByte = ReadDataByte(registerBaseAddress, RegisterAddressOffsetArray0, PortStatus, PortA)
    rowDataNibble = rowDataByte >> 4
    return rowDataNibble

# * Get row number, column number, and key number *

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):
    RowsAllHigh = 0xf

    WriteColumn0Low(registerBaseAddress)
    rowDataNibble = ReadUpperDataNibble(registerBaseAddress, RegisterAddressOffsetArray0, PortStatus, PortA)
    if rowDataNibble != RowsAllHigh:
        columnNumber = 0
        return columnNumber

    WriteColumn1Low(registerBaseAddress)
    rowDataNibble = ReadUpperDataNibble(registerBaseAddress, RegisterAddressOffsetArray0, PortStatus, PortA)
    if rowDataNibble != RowsAllHigh:
        columnNumber = 1
        return columnNumber

    WriteColumn2Low(registerBaseAddress)
    rowDataNibble = ReadUpperDataNibble(registerBaseAddress, RegisterAddressOffsetArray0, PortStatus, PortA)
    if rowDataNibble != RowsAllHigh:
        columnNumber = 2
        return columnNumber

def GetKeyNumber(rowNumber, columnNumber):
    # print "Row Number = ", rowNumber
    # print "Column Number = ", columnNumber
    keyNumber = (rowNumber * 3) + (columnNumber + 1)
    return keyNumber

# * Get one key and a sequence of keys *

def GetKey(registerBaseAddress, pollType):
    PollGpioRegister = 0
    PollKeypadInterruptPin = 1

    if (pollType == PollGpioRegister):
        # print "Polling GpioRegister"
        LoopUntilGpioPortHighNibbleChange(registerBaseAddress)

    elif (pollType == PollKeypadInterruptPin):
        # print "Polling Interrupt Pin"
        LoopUntilKeypadInterruptPinLow(registerBaseAddress, KeypadInterruptPin)        

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

def GetKeyString(registerBaseAddress, pollType, count):
    for i in range (count):       
        OneBeep()
    keyNumber = GetKey(registerBaseAddress, pollType)   
        print "Key", i + 1, " = ", keyNumber

# * Loop until row data changes *

def LoopUntilGpioPortHighNibbleChange(registerBaseAddress):
    WriteAllColumnsLow(registerBaseAddress)
    NoKeyPressedNibble = 0xf
    rowDataNibble = NoKeyPressedNibble
    while (rowDataNibble == NoKeyPressedNibble):
        rowDataNibble = GetRowDataNibble(registerBaseAddress)
    sleep(0.05) # debouncing time 50mS

# * Loop until interrupt key low *

def LoopUntilKeypadInterruptPinLow(registerBaseAddress, interruptPin):
    ClearKeypadInterrupt(registerBaseAddress)
    interruptPinState = readInputPin(interruptPin)
    #print "interruptPinState = ", interruptPinState
    while (interruptPinState == True):
       interruptPinState = readInputPin(interruptPin)
       #print "interruptPinState = ", interruptPinState
       #sleep(1)
    #print "interruptPinState = ", interruptPinState
    sleep(0.05) # debouncing time 50mS

# * Test keypad *

def TestKeypad(registerBaseAddress, pollType, count):
    SetupKeypad(registerBaseAddress)
    if (pollType == PollKeypadInterruptPin):
        SetupKeypadInterrupt(registerBaseAddress)
        ClearKeypadInterrupt(registerBaseAddress)
    print "*** Start testing keypad. ***" 
    print "Press keypad ", count, "times."
    GetKeyString(registerBaseAddress, pollType, count)
    ClearKeypadInterrupt(registerBaseAddress)
    print "*** Stop testing keypad. ***\n"

def TestPollingKeypad():
    TestKeypad(KeypadRegisterBaseAddress, PollGpioRegister, count = 4)

def TestInterruptKeypad():
    TestKeypad(KeypadRegisterBaseAddress, PollKeypadInterruptPin, count = 4)


# * LCD1602 *******************************************************************

LcdRegisterBaseAddress = MCP23008BaseAddress3 # 0x26

# * LCD1602A-1 16-pin connector (MCP23008 GPIO 6-pin connector) Pin numbering *

# Pin 04 (Pin 1) RegisterSelect
# Pin 06 (Pin 2) WriteEnable 
# Pin 11 (Pin 3) DataBit4 = 2
# Pin 12 (Pin 4) DataBit5 = 3
# Pin 13 (Pin 5) DataBit6 = 4
# Pin 14 (Pin 6) DataBit7 = 5

# LCD1602 software initialization procedure

# Part 1 - Pseudo 8 bit instructions to config 4 bit instruction mode (0x30, 0x30, 0x30, 0x20)

# 1. Power on, wait 20mS (> 15mS)
# 2. Write 0x3, wait 5mS (> 4.1mS) # set 8 bit interface
# 3. Write 0x3, wait 1mS (> 100uS) # set 8 bit interface
# 4. Write 0x3, wait 1mS (> 37uS)  # set 8 bit interface
# 5. Write 0x2, wait 1mS (> 37uS)  # set 4 bit interface

# Part 2- 4 bit instructions to config display characteristics (0x28, 0x08, 0x01, 0x06, 0x02)

# 6. Write 0x2, wait 1mS (> 37uS) 0x8 wait 1mS (> 37uS) (2 lines, 5x8 dot)
# 7. Write 0x0, wait 1mS (> 37uS) 0x8 wait 1mS (> 37uS) (cursor on, no blinking, blank screen)
# 8. Write 0x0, wait 1mS (> 37uS) 0x1 wait 2mS (> 1.52mS)(clear display)
# 9. Write 0x0, wait 1mS (> 37uS) 0xe (DisplayOnCursorOnCursorBlinkOff)
# a. Write 0x0, wait 1mS (> 37uS) 0x2 wait 2mS (cursor home)

# * LCD1602A-1 16-pin connector pin numbering *

RegisterSelect = 0 # 0 = instruction register, 1 = data register
WriteEnable = 1 # 1 = enable, 0 = disable
DataBit4 = 2
DataBit5 = 3
DataBit6 = 4
DataBit7 = 5

# * Constant data bytes *

AllZeroDataByte = 0x00
AllOneDataByte = 0xff

# * Write Enable/Disable, Select Data/Instruction masks *

EnableWriteMask  = 0x02 # 0b 0000 0010
DisableWriteMask = 0xfd # 0b 1111 1101

SelectDataRegisterMask        = 0x01 # 0b 0000 0001
SelectInstructionRegisterMask = 0xfe # 0b 1111 1110

# Register selection constant *

InstructionRegister = 0
DataRegister = 1

# * LCD1602A-1 control instructions *

# * Instruction codes *

EightBitInstructionMode = 0x3
FourBitInstructionMode  = 0x2

TwoLineFiveTimesEightDot1 = 0x2
TwoLineFiveTimesEightDot2 = 0x8

DisplayOnCursorOnCursorBlinkOff1 = 0x0
DisplayOnCursorOnCursorBlinkOff2 = 0xe

ClearDisplay1 = 0x0
ClearDisplay2 = 0x1

CursorIncrementDisplayNoShift1 = 0x0
CursorIncrementDisplayNoShift2 = 0x6

CursorHome1 = 0x0
CursorHome2 = 0x2

# * Instruction timing *

PowerOnResetDelay      = 0.05    # 50mS
VeryLongOperationDelay = 0.005   # 5mS
LongOperationDelay     = 0.002   # 2mS
ShortOperationDelay    = 0.00005 # 50uS
WritePulseLength       = 0.00005 # 50uS

# * RPi/MCP23008 Python functions *

def LcdGpioSetup(registerBaseAddress):
    SetupMCP23008PortAllOutput(registerBaseAddress)

def LcdConfiguration(registerBaseAddress):
    dataControlByte = AllZeroDataByte
    # PrintEightBitPattern("dataControlByte01 = ", dataControlByte)
    LcdDisableWrite(registerBaseAddress, dataControlByte)
    sleep(PowerOnResetDelay)

    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, EightBitInstructionMode, InstructionRegister, VeryLongOperationDelay)
    # PrintEightBitPattern("dataControlByte02 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, EightBitInstructionMode, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte03 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, EightBitInstructionMode, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte04 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, FourBitInstructionMode, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte05 = ", dataControlByte)

    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, TwoLineFiveTimesEightDot1, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte06 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, TwoLineFiveTimesEightDot2, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte07 = ", dataControlByte)

    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, DisplayOnCursorOnCursorBlinkOff1, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte08 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, DisplayOnCursorOnCursorBlinkOff2, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte09 = ", dataControlByte)

    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, ClearDisplay1, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte10 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, ClearDisplay2, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte11 = ", dataControlByte)

    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, CursorIncrementDisplayNoShift1, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte12 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, CursorIncrementDisplayNoShift2, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte13 = ", dataControlByte)

    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, CursorHome1, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte14 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, CursorHome2, InstructionRegister, LongOperationDelay)
    # PrintEightBitPattern("dataControlByte15 = ", dataControlByte)

    return dataControlByte

def LcdSelectDataRegister(registerBaseAddress, dataByte):
    dataByte = dataByte | SelectDataRegisterMask
    LcdWriteDataByte(registerBaseAddress, dataByte) 
    return dataByte

def LcdSelectInstructionRegister(registerBaseAddress, dataByte):
    dataByte = dataByte & SelectInstructionRegisterMask
    LcdWriteDataByte(registerBaseAddress, dataByte)
    return dataByte

def LcdEnableWrite(registerBaseAddress, dataByte):
    dataByte = dataByte | EnableWriteMask
    LcdWriteDataByte(registerBaseAddress, dataByte)
    return dataByte

def LcdDisableWrite(registerBaseAddress, dataByte):
    dataByte = dataByte & DisableWriteMask
    LcdWriteDataByte(registerBaseAddress, dataByte)
    return dataByte

def LcdWritePulse(registerBaseAddress, dataByte):
    LcdEnableWrite(registerBaseAddress, dataByte)
    sleep(WritePulseLength)
    LcdDisableWrite(registerBaseAddress, dataByte)
    return dataByte

def LcdWriteDataByte(registerBaseAddress, dataByte):
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, dataByte)
    return dataByte

def LcdWriteDataNibble(registerBaseAddress, dataByte, dataNibble, registerType, operationDelay):
    dataNibble = dataNibble << 2
    dataByte = dataByte & 0b11000011
    dataByte = dataByte | dataNibble
    if (registerType == InstructionRegister):
       dataByte = LcdSelectInstructionRegister(registerBaseAddress, dataByte)
    elif (registerType == DataRegister):
       dataByte = LcdSelectDataRegister(registerBaseAddress, dataByte)
    LcdDisableWrite(registerBaseAddress, dataByte)
    dataByte = LcdWriteDataByte(registerBaseAddress, dataByte)
    LcdWritePulse(registerBaseAddress, dataByte)
    sleep(operationDelay)
    return dataByte

def LcdWriteCharFourBitMode(registerBaseAddress, dataControlByte, asciiChar, registerType):
    charUpperNibble = ord(asciiChar) >> 4
    charLowerNibble = ord(asciiChar) & 0x0f
    LcdWriteCharTwoNibbles(registerBaseAddress, dataControlByte, charUpperNibble, charLowerNibble, registerType)
    return dataControlByte

def LcdWriteDataByteFourBitMode(registerBaseAddress, dataControlByte, dataByte, registerType):
    upperNibble = dataByte >> 4
    lowerNibble = dataByte & 0x0f
    LcdWriteCharTwoNibbles(registerBaseAddress, dataControlByte, upperNibble, lowerNibble, registerType)
    return dataControlByte

def LcdWriteCharTwoNibbles(registerBaseAddress, dataControlByte, upperNibble, lowerNibble, registerType):
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, upperNibble, registerType, ShortOperationDelay)
    # PrintEightBitPattern("dataControlByte01 = ", dataControlByte)
    dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, lowerNibble, registerType, ShortOperationDelay)
    # PrintEightBitPattern("dataControlByte02 = ", dataControlByte)
    return dataControlByte

def LcdWriteCharString(registerBaseAddress, dataControlByte, string):
    for asciiChar in (string):
        LcdWriteCharFourBitMode(registerBaseAddress, dataControlByte, asciiChar, DataRegister)
    return dataControlByte

def LcdMoveCursor(registerBaseAddress, dataControlByte, rowNumber, columnNumber):
    if (rowNumber == 1):
       cursorByte = 0x80 | (0x00 + (columnNumber - 1)) # command = 0x80, row 1 DDRAM start address = 0x10
    elif (rowNumber == 2):
       cursorByte = 0x80 | (0x40 + (columnNumber - 1)) # row 2 DDRAM start address = 0x40
    LcdWriteDataByteFourBitMode(registerBaseAddress, dataControlByte, cursorByte, InstructionRegister)

    # charUpperNibble = cursorByte >> 4
    # charLowerNibble = cursorByte & 0x0f
    # dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, charUpperNibble, InstructionRegister, ShortOperationDelay)
    # dataControlByte = LcdWriteDataNibble(registerBaseAddress, dataControlByte, charLowerNibble, InstructionRegister, ShortOperationDelay)
    return dataControlByte

# * Test LCD1602 functions *

def TestLcd(registerBaseAddress):
    LcdGpioSetup(registerBaseAddress)
    dataControlByte = LcdConfiguration(registerBaseAddress)
    dataControlByte = LcdMoveCursor(registerBaseAddress, dataControlByte, 1, 2)
    dataControlByte = LcdWriteCharString(registerBaseAddress, dataControlByte, "Raspberry Pi")
    dataControlByte = LcdMoveCursor(registerBaseAddress, dataControlByte, 2, 1)
    dataControlByte = LcdWriteCharString(registerBaseAddress, dataControlByte, "2013-01-25 v1141")

# * Demultiplexer *************************************************************

DemuxRegisterBaseAddress = KeypadRegisterBaseAddress

def DemuxGpioSetup(registerBaseAddress):
    UpperNibbleInputLowerNibbleOutput = 0xf0 # Input (GP4~7, Row0~3), Output (GP0~3, Column0~2, 3 reserved)
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, InputOutputDirection, PortA, UpperNibbleInputLowerNibbleOutput)
    InitialControlDateByte = 0x00
    return InitialControlDateByte

def DemuxAddressLatch(controlDataByte, latchAction):
    DemuxAddressLatchMask = 0b00001000
    Enable = 1
    Disable = 0
    if (latchAction == Enable):
        controlDataByte = controlDataByte | DemuxAddressLatchMask
    elif (latchAction == Disable):
       controlDataByte = controlDataByte & ~(DemuxAddressLatchMask)
    return controlDataByte

def SetDemuxChannel(registerBaseAddress, controlDataByte, addressNibble):
    Enable = 1
    Disable = 0
    controlDataByte = controlDataByte & 0b11110000
    addressNibble = addressNibble & 0b00000111
    controlDataByte = controlDataByte | addressNibble
    controlDataByte = DemuxAddressLatch(controlDataByte, Disable)
    PrintEightBitPattern("demuxDataByte = ", controlDataByte)
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, controlDataByte)
    controlDataByte = DemuxAddressLatch(controlDataByte, Enable)
    PrintEightBitPattern("demuxDataByte = ", controlDataByte)
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArray0, OutputLatch, PortA, controlDataByte)
    return controlDataByte

def TestDemux(demuxRegisterBaseAddress, channelNumber): 
    controlDataByte = DemuxGpioSetup(demuxRegisterBaseAddress)
    controlDataByte = SetDemuxChannel(demuxRegisterBaseAddress, controlDataByte, channelNumber)

def TestDemuxLcd(demuxRegisterBaseAddress, lcdRegisterBaseAddress, channelNumber1, channelNumber2):
    Row1 = 1
    Row2 = 2
    Row3 = 3
    Row4 = 4
    Column1 = 1

    # Setup one MCP23008 for Demultiplexor, another MCP23008 for LCD1602/LCD2004
    controlDataByte = DemuxGpioSetup(demuxRegisterBaseAddress)
    LcdGpioSetup(lcdRegisterBaseAddress)

    # Select demux channelNumber1
    controlDataByte = SetDemuxChannel(demuxRegisterBaseAddress, controlDataByte, channelNumber1)

    # Display two lines in the LCD at this channel
    dataControlByte = LcdConfiguration(lcdRegisterBaseAddress)
    dataControlByte = LcdMoveCursor(lcdRegisterBaseAddress, dataControlByte, Row1, Column1)
    dataControlByte = LcdWriteCharString(lcdRegisterBaseAddress, dataControlByte, "Channel 1 01")
    dataControlByte = LcdMoveCursor(lcdRegisterBaseAddress, dataControlByte, Row2, Column1)
    dataControlByte = LcdWriteCharString(lcdRegisterBaseAddress, dataControlByte, "2013-01-28 v201")

    sleep(2)

    # Select demux channelNumber2
    controlDataByte = SetDemuxChannel(demuxRegisterBaseAddress, controlDataByte, channelNumber2)

    # Display two lines in the LCD at this channel
    dataControlByte = LcdConfiguration(lcdRegisterBaseAddress)
    dataControlByte = LcdMoveCursor(lcdRegisterBaseAddress, dataControlByte, Row1, Column1)
    dataControlByte = LcdWriteCharString(lcdRegisterBaseAddress, dataControlByte, "Channel 2 01")
    dataControlByte = LcdMoveCursor(lcdRegisterBaseAddress, dataControlByte, Row2, Column1)
    dataControlByte = LcdWriteCharString(lcdRegisterBaseAddress, dataControlByte, "2013-01-28 v201")

    sleep(2)

# 11. Main test functions *****************************************************
StartProgram("*** Test Demultiplexer v202 TL Fong 2013jan29hkt2153 ***")

# RPi System A Tests *

# TestBuzzer() # beep buzzer 4 times
# TestLED() # blink LED 4 tmes
# TestButtonEchoBuzzer() # echo buton with buzzer 4 times
# TestButtonEchoLED() # echo button with LED 4 times
# TestToggleMCP23017GP() # toggle MCP23017 #1 output ports (connected to only 1 LED)
# TestToggleMCP23008GP() # toggle MCP23008 #1 output port (connected to stepping motors)
# TestMotor() # move two stepping motors
# TestPollingKeypad() # read 4 keys by polling IO Expander GPIO port of rows (MCP23008 #2)
# TestInterruptKeypad() # read 4 keys by polling IO Expander INT pin # !!! not tested !!!
# TestLcd(LcdRegisterBaseAddress)
# TestDemux(DemuxRegisterBaseAddress, 4) 
# TestDemuxLcd(DemuxRegisterBaseAddress, LcdRegisterBaseAddress, channelNumber1 = 0, channelNumber2 = 1)

# RPi System B Tests *

# TestBuzzer() # beep buzzer 4 times
# TestLED() # blink LED 4 tmes
# TestButtonEchoBuzzer() # echo buton with buzzer 4 times
# TestButtonEchoLED() # echo button with LED 4 times

TestToggleMCP23017SystemB1() # toggle MCP23017 System B1

EndProgram()

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