Pages

Thursday, February 21, 2013

Raspberry Pi seems damaged, could not boot




A while ago I was testing the following SPI function, which basically does 3 things: (1) Create a spi object, (2) open SPI0, CE1_N, (3) write to SPI0 600 times.

But then I found the RPi not responding, and I found the TP1/TP2 drops to below 4V and all on board indictaor LEDs went off.  To troubleshoot, I removed the hardware connected to the RPi, one by one, first SPI hardware, then I2C hardware, then the signal routing hardware, but no luck.  Then I removed ALL the connecting cables from the RPI, but the problem does not go away.  At last I removed the HDMI and Ethernet connectors, but still the RPI only tried to boot for about 30 seconds, then all LEDs off.

I guess part of the RPi is permanently damaged.  In other words, it is half dead!

I don't know what was the cause.  It is unlikely that the SPI test program kills it, because I have not yet connected any hardware device to it.  I only thought to use the scope to display the MOSI wave forms, ...

I think I need to swap another RPi.



def TestWiringPiSpi():
    print "\n" + "*** Start testing wiringPi SPI, ... ***" + "\n"

    spi = spidev.SpiDev() # create spidev object
    spi.open(0,1) # open SPI0, CE1_N
    for i in range(600):  
        TwoByteArray = spi.xfer2([0xf0, 0x0f]) # write and read 2 bytes
    time.sleep(0.01)

    print "\n" + "*** Stop testing wiringPi SPI, ... ***" + "\n"

.END


# *****************************************************************************
# !/usr/bin/python2.7
#
# Hardware/Software
#   FongLab Fpl4 - 2013feb
# Author
#   tlfong01  
# Configuration
#   Raspberry Pi Bv2 512MB, Raspbian Wheezy, Python 2.7.3, RPI.GPIO 0.4.1a,
#   PythonWiringPi 1.0.5
# License
#   GNU GPLv3
# Warranty 
#   For hobbist only.  Use at your own risk.  There is not any warranty.
# 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. Program title
#  2. Python imports
#  3. RPi GPIO pin assignment, MCP230xx register base assignment
#  4. Global constants
#  5. GPIO Functions 
#  6. Debugging and documentation functions (beep, print bit/byte, message)
#  7. IO Expander Mcp23008, Mcp23017
#  8. Decimal keypad
#  9. LCD - LCD1602. LCD2004
# 10. Stepping motor - unlpolar steppers 28BYJ48/NPM-PF35/PX245
# 11. Demultiplexor
# 12. Old test functions 
# 13. Main program

# 1. Program Title ************************************************************

ProgramTitle1 = "SPI428 20130221b"
ProgramTitle2 = "FPL428 20130221b"

# 2 Python imports ***********************************************************

import sys
import time
import select
import RPi.GPIO as GPIO
import smbus 
import spidev

smBus1 = smbus.SMBus(1) 

# 3. GPIO and IO Expander pin/address assignments *****************************

I2cBaseAddress0 = 0x20
I2cBaseAddress1 = 0x21
I2cBaseAddress2 = 0x22
I2cBaseAddress3 = 0x23
I2cBaseAddress4 = 0x24
I2cBaseAddress5 = 0x25
I2cBaseAddress6 = 0x26
I2cBaseAddress7 = 0x27

# System A I2C base address assignment *

Mcp23017BaseAddress1 = 0x22 # LED, button
Mcp23008BaseAddress1 = 0x24 # stepping motors
Mcp23008BaseAddress2 = 0x25 # keypad
Mcp23008BaseAddress3 = 0x26 # LCD1602

# System B I2C base address assignment *

Mcp23017BaseAddressSystemB1 = 0x20 
Mcp23008BaseAddressSystemB1 = 0x21 
Mcp23008BaseAddressSystemB1 = 0x21 

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

# * SCI GPIO pins *

RpiGpioGen10 = 19 # SPI_MOSI
RpiGpioGen9 = 21 # SPI_MISO
RpiGpioGen11 = 23 # SPI_SCLK
RpiGpioGen8 = 24 # SPI_CE0_N
RpiGpioGen7 = 26 # SPI_CE1_N

SpiClockPin = RpiGpioGen11
SpiMosiPin = RpiGpioGen10
SpiMisoPin = RpiGpioGen9
SpiSelect0Pin = RpiGpioGen8
SpiSelect1Pin = RpiGpioGen7

# * peripherals pins assignment *

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

# * GPIO input/output pins list *

OutputPinList = [LEDpin, BuzzerPin, TxDpin, SpiClockPin, SpiMosiPin, SpiSelect0Pin, SpiSelect1Pin]
InputPinWithNoPullUpList = []
InputPinWithPullUpList = [ButtonPin, RxDpin, RPiGPIOgen6, SpiMisoPin]

# 4. 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

OnTime = 0.1
OffTime = 0.25

# 5. GPIO Functions ***********************************************************
# * Local constants *

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

# * 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:
        setupInputPinWithNoPullUp(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)
    time.sleep(onTime)
    writeOutputPin(oPin, Off)    
    time.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)
    time.sleep(toggleTime)
    writeOutputPin(oPin, Off)    
    time.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 TestButtonEchoTxD(): #
    SetupGPIO()
    print "\n", "Press button 4 times.", "\n"
    for i in range (FourTimes):
        echoPin(ButtonPin, TxDpin)  

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

def TestRxdEchoTxD(): #
    SetupGPIO()
    print "\n", "RxD Echo TxD - Press button 4 times.", "\n"
    for i in range (FourTimes):
        echoPin(RxDpin, TxDpin) 

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

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

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

def TestTxDpinRxDpin1(): # blink, read 4 times
    SetupGPIO()
    for i in range (FourTimes): 
        writeOutputPin(TxDpin, On)
        if readInputPin(RxDpin) == High:
        print "RxD input = High"
        else:
        print "RxD input = Low"
        time.sleep(1)
        writeOutputPin(TxDpin, Off)   
    if readInputPin(RxDpin) == High:
        print "RxD input = High"
        else:
        print "RxD input = Low"
        time.sleep(1)

def TestTxDpinRxDpin2(): #
    SetupGPIO()
    print "\n", "Short to Ground RxDpin 4 times.", "\n"    
    for i in range (FourTimes):
        echoPin(RxDpin, TxDpin)

# 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
            time.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

def PrintFourBitPattern(message, dataByte):
    fourBitPattern = ConvertIntegerToFourBitPattern(dataByte)
    print message, fourBitPattern

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

def StopBeep():
    Beep(2)

def OneBeep():
    Beep(1)

def FourBeeps():
    Beep(4)

def StartProgram():
    message =  "\n" + "*** Start Program - " + ProgramTitle1 + " ***" + "\n"
    SetupGPIO()
    StartBeep()
    print message

def StopProgram():
    message =  "\n" + "*** Stop Program ***" + "\n"
    StopBeep()
    print message

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

# 7. IO Expander Mcp23008, Mcp23017 *******************************************

# * Local constants *

# * Port type *
PortA = 0
PortB = 1

# * Mcp23008/Mcp23017 register address offsets index Bank 0 *
InputOutputDirection = 0
InputPolarity = 1
InterruptEnable = 2
DefaultValue = 3  
CompareMode = 4
BankInterruptPinMode = 5
PullUp = 6
InterruptFlag = 7
InterruptCapture = 8
PortStatus = 9
OutputLatch = 10

# * Data constant bytes *

AllOutputByte = 0x00
AllInputByte = 0xff
AllHighByte = 0xff
AllLowByte =  0x00
AllPullUpByte = 0xff

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

# * Mcp23008/Mcp23017 Bank0/Bank1 register address offset arrays *
               
Mcp23008RegisterAddressOffsetArray = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a]

RegisterAddressOffsetArrayBank0 = [0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14,        
                                   0x01, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x1d, 0x0f, 0x11, 0x13, 0x15]

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

# * Mcp23008 Functions *

def SetupPortAllOutputMcp23008(registerBaseAddress):
    WriteDataByteMcp23008(registerBaseAddress, InputOutputDirection, AllOutputByte)

def SetupPortAllInputMcp23008(registerBaseAddress):
    WriteDataByteMcp23008(registerBaseAddress, InputOutputDirection, AllInputByte)

def WriteDataByteMcp23008(registerBaseAddress, dataRegisterIndex, dataByte):
    addressOffset = Mcp23008RegisterAddressOffsetArray[dataRegisterIndex]
    smBus1.write_byte_data(registerBaseAddress, addressOffset, dataByte)

def ReadDataByteMcp23008(registerBaseAddress, dataRegisterIndex):
    addressOffset = Mcp23008RegisterAddressOffsetArray[dataRegisterIndex]
    dataByte = smBus1.read_byte_data(registerBaseAddress, addressOffset)
    return dataByte

def WriteDataBitMcp23008(registerBaseAddress, dataRegisterIndex, bitNumber, dataBit):
    addressOffset = Mcp23008RegisterAddressOffsetArray[dataRegisterIndex + 11]
    dataByte = smBus1.read_byte_data(registerBaseAddress, addressOffset)
    bitMask = 0x01 << bitNumber
    if (dataBit == 1):
        dataByte = dataByte | bitMask
    else:
        dataByte = dataByte & (~bitMask)
    smBus1.write_byte_data(registerBaseAddress, addressOffset, dataByte)

def ToggleGpMcp23008(registerBaseAddress, toggleTime, toggleCount):
    SetupMcp23008PortAllOutput(registerBaseAddress)
    for i in range(toggleCount):
        WriteDataByteMcp23008(registerBaseAddress, OutputLatch, AllHighByte) 
    time.sleep(toggleTime)
        WriteDataByteMcp23008(registerBaseAddress, OutputLatch, AllLowByte) 
     time.sleep(toggleTime)

def ReadGpMcp23008(registerBaseAddress, count):
    SetupMcp23008PortAllInput(registerBaseAddress)    
    for i in range(count):
        dataByte = ReadDataByteMcp23008(registerBaseAddress, PortStatus)
        PrintEightBitPattern("Data byte read = ", dataByte)
        time.sleep(1)


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


# * Mcp23017 Functions *


def SetupMcp23017BothPortAllOutput(registerBaseAddress):
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, InputOutputDirection, PortA, AllOutputByte)
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, InputOutputDirection, PortB, AllOutputByte)


def SetupPortAoutputPortBinputPullUpMcp23017(registerBaseAddress):
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, InputOutputDirection, PortA, AllOutputByte)
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, InputOutputDirection, PortB, AllInputByte)
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, PullUp, PortB, AllPullUpByte)


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 WriteDataBit(registerBaseAddress, registerAddressArray, dataRegisterIndex, portType, bitNumber, dataBit):
    if (portType == PortA) | (portType == PortA):
       addressOffset = registerAddressArray[dataRegisterIndex]
    if (portType == PortB):
       addressOffset = registerAddressArray[dataRegisterIndex + 11]
    dataByte = smBus1.read_byte_data(registerBaseAddress, addressOffset)
    bitMask = 0x01 << bitNumber
    if (dataBit == 1):
        dataByte = dataByte | bitMask
    else:
        dataByte = dataByte & (~bitMask)
    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 ReadDataBit(registerBaseAddress, registerAddressArray, dataRegisterIndex, portType, bitNumber):
    if (portType == PortA):
       addressOffset = registerAddressArray[dataRegisterIndex]
    if (portType == PortB):
       addressOffset = registerAddressArray[dataRegisterIndex + 11]
    dataByte = smBus1.read_byte_data(registerBaseAddress, addressOffset)
    bitMask = 0x01 << bitNumber
    dataByte = dataByte & bitMask
    if (dataByte == 0):
      dataBit = 0
    else:
      dataBit = 1
    return dataBit


def ReadUpperNibble(registerBaseAddress, registerAddressArray, dataRegisterIndex, portType):
    dataByte = ReadDataByte(registerBaseAddress, registerAddressArray, dataRegisterIndex, portType)
    upperNibble = dataByte >> 4
    return upperNibble


def ReadLowerNibble(registerBaseAddress, registerAddressArray, dataRegisterIndex, portType):
    dataByte = ReadDataByte(registerBaseAddress, registerAddressArray, dataRegisterIndex, portType)
    lowerNibble = dataByte & 0x0f
    return lowerNibble


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


def DisableInterruptOnChangeHighNibble(registerBaseAddress): 
    DisableInterruptHighNibble = 0x00
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, InterruptEnable, PortA, DisableInterruptHighNibble)


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

def SetInterruptOnChangeCompareDefaultHighNibble(registerBaseAddress): 
    InterruptOnChangeDefaultValueHighNibble = 0xf0 # GP4~7 change from default value will cause interrupt
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, CompareMode, 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, RegisterAddressOffsetArrayBank0, 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, RegisterAddressOffsetArrayBank0, PushPullOpenDrain, PortA, OpenDrain)


# * Poll interrupts *
def ReadInterruptFlagHighNibble(registerBaseAddress): 
    interruptFlagByte = ReadDataByte(registerBaseAddress,  RegisterAddressOffsetArrayBank0, InterruptFlag, PortA)  
    interruptFlagNbble = interruptFlagByte >> 4
    return interruptFlagNibble   
    
def ReadInterruptCaptureHighNibble(registerBaseAddress): 
    interruptCaptureByte = ReadDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, InterruptCapture, PortA)    
    interruptCaptureNibble = interruptCaptureByte >> 4
    return interruptCaptureNibble  


def ReadInterruptCaptureLowNibble(registerBaseAddress): 
    interruptCaptureByte = ReadDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, InterruptCapture, PortB)    
    interruptCaptureNibble = interruptCaptureByte & 0x0f
    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 !!!
    time.sleep(2)


# * Test port toggling, poll interrupts *


def ToggleMcp23017GP(registerBaseAddress, toggleTime, toggleCount):
    SetupMcp23017BothPortAllOutput(registerBaseAddress)
    for i in range(toggleCount):
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, AllHighByte)
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortB, AllHighByte)
    time.sleep(toggleTime)
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, AllLowByte)
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortB, AllLowByte)
     time.sleep(toggleTime)


def ToggleMcp23017B1(registerBaseAddress, toggleTime, toggleCount):
    SetupMcp23017BothPortAllOutput(registerBaseAddress)


    for i in range(toggleCount):
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, AllHighByte)
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortB, AllHighByte)
    time.sleep(toggleTime)
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, AllLowByte)
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortB, AllLowByte)
     time.sleep(toggleTime)


def TestToggleMcp23017GP():
    ToggleMcp23008GP(Mcp23017BaseAddress1,  ToggleTime, ToggleCount)


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


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


# * System B test functions *


def TestToggleMcp23017SystemB1(count): 
    ToggleTime = 0.5
    ToggleCount = 4


    registerBaseAddress = Mcp23017BaseAddressSystemB1
    SetupPortAoutputPortBinputPullUpMcp23017(registerBaseAddress)


    for i in range(count):
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, AllHighByte)
    time.sleep(ToggleTime)
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, AllLowByte)
     time.sleep(ToggleTime)


def TestReadMcp23017SystemB1(count):
    registerBaseAddress = Mcp23017BaseAddressSystemB1
    SetupPortAoutputPortBinputPullUpMcp23017(registerBaseAddress)
    for i in range(count):
        dataByte = ReadDataByte(Mcp23017BaseAddressSystemB1, RegisterAddressOffsetArrayBank0, PortStatus, PortB)
        PrintEightBitPattern("Data byte read = ", dataByte)
        time.sleep(1) 


def TestBlinkMcp23017SystemB1GPIObit(bitNumber, onTime, offTime, count) :


    registerBaseAddress = Mcp23017BaseAddressSystemB1
    SetupPortAoutputPortBinputPullUpMcp23017(registerBaseAddress)


    for i in range(count):


        WriteDataBit(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, bitNumber, High)
    time.sleep(onTime)
        WriteDataBit(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, bitNumber, Low)
     time.sleep(offTime)




def TestReadMcp23017SystemB1GPIObit(bitNumber, count):


    registerBaseAddress = Mcp23017BaseAddressSystemB1
    SetupPortAoutputPortBinputPullUpMcp23017(registerBaseAddress)


    for i in range(count):
        dataBit = ReadDataBit(registerBaseAddress, RegisterAddressOffsetArrayBank0, PortStatus, PortB, bitNumber)
        if (dataBit == High):
        print "Data bit is High"
        else:
        print "Data bit is Low"
     time.sleep(1)




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


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


# * Decimal keypad using Mcp23017 v4.17 2013feb15 *


# * Configuration *
# Mcp23017 GPIO direction - Port A = output, Port B = input
# Keypad pin assignment - GPA0~A2 = Col0~2, GPB0~B3 = Row0~3


KeypadInterruptPinMcp23017 = RPiGPIOgen6


# * Local constants *


RegisterBaseAddress = 0x20
OutputLatchRegisterResetValue = 0x00
PortStatusRegisterResetValue = 0x00
InterruptCaptureRegisterResetValue = 0x00
InterruptFlagRegisterResetValue = 0x00


# * Polling modes *
NoPollJustGetKeyEverySecond = 0
PollRowStatusNibble = 1
PollMcp23017Interrupt = 2


# * Setup keypad *
def SetupKeypadMcp017(registerBaseAddress):
     SetupPortAoutputPortBinputPullUpMcp23017(registerBaseAddress)
     SetupKeypadInterruptMcp23017(registerBaseAddress) # buggy !!!!!!!!!!
     ClearKeypadInterruptMcp23017(registerBaseAddress) 
     WriteKeypadColumnsAllLowMcp23017(registerBaseAddress)


# * Setup/Clear/Check keypad interrupt *


def SetupKeypadInterruptMcp23017(registerBaseAddress):
    SetKeypadInterruptEnableMcp23017(registerBaseAddress)
    SetKeypadDefaultValueMcp23017(registerBaseAddress) 
    SetKeypadEnableCompareDefaultValueMcp23017(registerBaseAddress)  
    SetKeypadBankInterruptDriverModeMcp23017(registerBaseAddress) 


def SetKeypadInterruptEnableMcp23017(registerBaseAddress): 
    EnableInterruptLowNibble = 0x0f
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, InterruptEnable, PortB, EnableInterruptLowNibble)


def SetKeypadInterruptDisableMcp23017(registerBaseAddress): 
    DisableInterruptByte = 0x00
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, InterruptEnable, PortB, DisableInterruptByte)


def SetKeypadDefaultValueMcp23017(registerBaseAddress): 
    DefaultValueByte = 0x0f # row status nibble is all bits high if no key pressed  
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, DefaultValue, PortB, DefaultValueByte)

def SetKeypadEnableCompareDefaultValueMcp23017(registerBaseAddress): 
    InterruptOnChangeDefaultValueLowNibble = 0x0f # GPB0~3 change from default value will cause interrupt
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, CompareMode, PortB, InterruptOnChangeDefaultValueLowNibble)


def SetKeypadBankInterruptDriverModeMcp23017(registerBaseAddress): 
    # bit 7 = 0 (Bank = 0, reg with each port are in the same bank (addresses are sequential))
    # bit 6 = 0 (No mirror, INTA and INTB independent)
    # bit 5 = 1 (Sequential operation disabled)
    # bit 4 = 1 (Slew rate disabled)
    # bit 3 = 0 (Disable MCP23S17 address pins, Don't care for Mcp23017)
    # bit 2 = 0 (Interrupt driver push pull, no open drain)
    # bit 1 = 1 (Interrrupt active high)
    # bit 0 = 0 (Unimplemented, read as 0)
    # 0b00110010 = 0x32
    Bank0DriverPushPullHighActive = 0x32
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, BankInterruptPinMode, PortB, Bank0DriverPushPullHighActive)


def ClearKeypadInterruptMcp23017(registerBaseAddress):
    WriteKeypadColumnsAllLowMcp23017(registerBaseAddress)
    time.sleep(0.5)
    dummyRead = ReadInterruptCaptureLowNibble(registerBaseAddress) 


# * Write columns *


def WriteKeypadColumnsMcp23017(registerBaseAddress, columnDataByte):    
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, columnDataByte)


def WriteKeypadColumnsAllLowMcp23017(registerBaseAddress):
    WriteKeypadColumnsMcp23017(registerBaseAddress, AllLowByte)


# * Read rows *


def GetKeypadRowDataNibbleMcp23017(registerBaseAddress): 
    rowDataByte = ReadDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, PortStatus, PortB)
    rowDataNibble = rowDataByte & 0x0f
    return rowDataNibble


# * Get Row Number, Column Number, and Key Number *


def GetKeypadRowNumberMcp23017(registerBaseAddress):
    RowDataNibbleTuple = 0b1110, 0b1101, 0b1011, 0b0111, 0b1111, 0b0000 
    rowDataNibble = GetKeypadRowDataNibbleMcp23017(registerBaseAddress)


    # *** Begin debugging *****************************************************    
    PrintFourBitPattern("Row pattern = ", rowDataNibble) 
    rowNumber = RowDataNibbleTuple.index(rowDataNibble)
    print ("Row number = ", rowNumber)
    # *** End debugging *******************************************************


    rowNumber = RowDataNibbleTuple.index(rowDataNibble)
    return rowNumber


def GetKeypadColumnNumberMcp23017(registerBaseAddress):
    ColumnNibbleTuple = 0b1110, 0b1101, 0b1011
    NoKeyPressNibble = 0b1111    
    for columnNibble in (ColumnNibbleTuple):  
        WriteKeypadColumnsMcp23017(registerBaseAddress, columnNibble) 
        rowDataNibble = GetKeypadRowDataNibbleMcp23017(registerBaseAddress)
        if rowDataNibble != NoKeyPressNibble:
        break
    columnNumber = ColumnNibbleTuple.index(columnNibble)
    return columnNumber


def GetKeypadKeyNumberMcp23017(registerBaseAddress):
    rowNumber = GetKeypadRowNumberMcp23017(registerBaseAddress)
    columnNumber = GetKeypadColumnNumberMcp23017(registerBaseAddress)
    keyNumber = (rowNumber * 3) + (columnNumber + 1) 
    return keyNumber


# * Get keys *


def GetKeypadKeyMcp23017(registerBaseAddress, checkPressingKeyMode): 
    if (checkPressingKeyMode == NoPollJustGetKeyEverySecond): 
        print "Read key every second"
    time.sleep(1)
    LoopUntilOneSecondEnded()


    elif (checkPressingKeyMode == PollRowStatusNibble):   
        print "Polling Gpio"
        LoopUntilKeypadRowDataNibbleChangeMcp23017(registerBaseAddress)      


    elif (checkPressingKeyMode == PollMcp23017Interrupt):  
        print "Polling Mcp23017 interrupt INTB"
        LoopUntilKeypadInterruptPinMcp23017Active(registerBaseAddress)


    keyNumber = GetKeypadKeyNumberMcp23017(registerBaseAddress)
    return keyNumber


def GetKeypadKeySequenceMcp23017(registerBaseAddress, checkPressingKeyMode, count):     
    for i in range (count):        
        OneBeep() 
    keyNumber = GetKeypadKeyMcp23017(registerBaseAddress, checkPressingKeyMode)    
        print "Key", i + 1, " = ", keyNumber
    time.sleep(0.25) 


# * No polling, just read key at the end of each second *
def LoopUntilOneSecondEnded():
    time.sleep(1)


# * Poll row status *
def LoopUntilKeypadRowDataNibbleChangeMcp23017(registerBaseAddress): 
    WriteKeypadColumnsMcp23017(registerBaseAddress, AllLowByte)
    NoKeyPressedNibble = 0xf
    rowDataNibble = NoKeyPressedNibble 
    while (rowDataNibble == NoKeyPressedNibble):
        rowDataNibble = GetKeypadRowDataNibbleMcp23017(registerBaseAddress)        
    time.sleep(0.05) # debouncing time 50mS


# * Poll Mcp23017 interrupt pin *
def LoopUntilKeypadInterruptPinMcp23017Active(registerBaseAddress):


    # Mcp23017 INTB shifted up to 5V) logical level, then connected to Mcp23017 GPB5
    KeypadInterruptPinNumber = 5


    ClearKeypadInterruptMcp23017(registerBaseAddress)

    interruptDataBit = High  # !!! ULN2803 inverts Mcp23017 INTB, so interrupt is now Low active !!!
    while (interruptDataBit == High):
        interruptDataBit = ReadDataBit(registerBaseAddress, RegisterAddressOffsetArrayBank0, PortStatus, PortB, KeypadInterruptPinNumber)    


    time.sleep(0.1) # debouncing time


# * Test Mcp23017 Decimal keypad *


def TestKeypadMcp23017(registerBaseAddress, checkPressingKeyMode, keyCount):   
    SetupKeypadMcp017(registerBaseAddress) 
    GetKeypadKeySequenceMcp23017(registerBaseAddress, checkPressingKeyMode, keyCount)
    SetupKeypadMcp017(registerBaseAddress)


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


# * Decimal keypad using 2x Mcp23008 v4.19 2013feb17 *


RegisterBaseAddress1 = 0x21
RegisterBaseAddress1 = 0x22


# Mcp23008 #1 0x21 port = output, GP0~2 = Rows 0~2
# Mcp23008 #2 0x22 port = input, GP0~3 = Columns 0~3


KeypadInterruptPinMcp23008 = RPiGPIOgen6


# * Local constants *


OutputLatchRegisterResetValue = 0x00
PortStatusRegisterResetValue = 0x00
InterruptCaptureRegisterResetValue = 0x00
InterruptFlagRegisterResetValue = 0x00


# * Polling modes *


NoPollJustGetKeyEverySecond = 0
PollRowStatusNibble = 1
PollMcp23017Interrupt = 2


# * Setup keypad *


def SetupKeypadMcp23008(registerBaseAddress1, registerBaseAddress2):
    SetupPortAllOutputMcp23008(registerBaseAddress1)
    SetupPortAllInputMcp23008(registerBaseAddress2)
    #SetupKeypadInterruptMcp23008(registerBaseAddress) 
    #ClearKeypadInterruptMcp23008(registerBaseAddress) 
    WriteKeypadColumnsAllLowMcp23008(registerBaseAddress1)


# * Setup/Clear/Check keypad interrupt *


#def SetupKeypadInterruptMcp23008(registerBaseAddress):
#    SetKeypadInterruptEnableMcp23008(registerBaseAddress)
#    SetKeypadDefaultValueMcp23008(registerBaseAddress) 
#    SetKeypadEnableCompareDefaultValueMcp23008(registerBaseAddress)  
#    SetKeypadBankInterruptDriverModeMcp23008(registerBaseAddress) 


#def SetKeypadInterruptEnableMcp23008(registerBaseAddress): 
#    EnableInterruptLowNibble = 0x0f
#    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, InterruptEnable, PortB, EnableInterruptLowNibble)


#def SetKeypadInterruptDisableMcp23008(registerBaseAddress): 
#    DisableInterruptByte = 0x00
#    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, InterruptEnable, PortB, DisableInterruptByte)


#def SetKeypadDefaultValueMcp23008(registerBaseAddress): 
#    DefaultValueByte = 0x0f # row status nibble is all bits high if no key pressed  
#    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, DefaultValue, PortB, DefaultValueByte)

#def SetKeypadEnableCompareDefaultValueMcp23008(registerBaseAddress): 
#    InterruptOnChangeDefaultValueLowNibble = 0x0f # GPB0~3 change from default value will cause interrupt
#    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, CompareMode, PortB, InterruptOnChangeDefaultValueLowNibble)


#def SetKeypadBankInterruptDriverModeMcp23008(registerBaseAddress): 
    # bit 7 = 0 (Bank = 0, reg with each port are in the same bank (addresses are sequential))
    # bit 6 = 0 (No mirror, INTA and INTB independent)
    # bit 5 = 1 (Sequential operation disabled)
    # bit 4 = 1 (Slew rate disabled)
    # bit 3 = 0 (Disable MCP23S17 address pins, Don't care for Mcp23017)
    # bit 2 = 0 (Interrupt driver push pull, no open drain)
    # bit 1 = 1 (Interrrupt active high)
    # bit 0 = 0 (Unimplemented, read as 0)
    # 0b00110010 = 0x32
#    Bank0DriverPushPullHighActive = 0x32
#    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, BankInterruptPinMode, PortB, Bank0DriverPushPullHighActive)


#def ClearKeypadInterruptMcp23008(registerBaseAddress):
#    WriteKeypadColumnsAllLowMcp23008(registerBaseAddress)
#    time.sleep(0.5)
#    dummyRead = ReadInterruptCaptureLowNibble(registerBaseAddress) 


# * Write columns *


def WriteKeypadColumnsMcp23008(registerBaseAddress, columnDataByte):    
    WriteDataByteMcp23008(registerBaseAddress, OutputLatch, columnDataByte)


def WriteKeypadColumnsAllLowMcp23008(registerBaseAddress):
    WriteKeypadColumnsMcp23008(registerBaseAddress, AllLowByte)


# * Read rows *


def GetKeypadRowDataNibbleMcp23008(registerBaseAddress): 
    rowDataByte = ReadDataByteMcp23008(registerBaseAddress, PortStatus)
    rowDataNibble = rowDataByte & 0x0f
    return rowDataNibble


# * Get Row Number, Column Number, and Key Number *


def GetKeypadRowNumberMcp23008(registerBaseAddress):
    RowDataNibbleTuple = 0b1110, 0b1101, 0b1011, 0b0111, 0b1111, 0b0000 
    rowDataNibble = GetKeypadRowDataNibbleMcp23008(registerBaseAddress)


    # *** Begin debugging *****************************************************    
    PrintFourBitPattern("Row pattern = ", rowDataNibble) 
    rowNumber = RowDataNibbleTuple.index(rowDataNibble)
    print ("Row number = ", rowNumber)
    # *** End debugging *******************************************************


    rowNumber = RowDataNibbleTuple.index(rowDataNibble)
    return rowNumber


def GetKeypadColumnNumberMcp23008(registerBaseAddress1, registerBaseAddress2):
    ColumnNibbleTuple = 0b1110, 0b1101, 0b1011
    NoKeyPressNibble = 0b1111    
    for columnNibble in (ColumnNibbleTuple):  
        WriteKeypadColumnsMcp23008(registerBaseAddress1, columnNibble) 
        rowDataNibble = GetKeypadRowDataNibbleMcp23008(registerBaseAddress2)
        if rowDataNibble != NoKeyPressNibble:
        break
    columnNumber = ColumnNibbleTuple.index(columnNibble)
    return columnNumber


def GetKeypadKeyNumberMcp23008(registerBaseAddress1, registerBaseAddress2):
    rowNumber = GetKeypadRowNumberMcp23008(registerBaseAddress2)
    columnNumber = GetKeypadColumnNumberMcp23008(registerBaseAddress1, registerBaseAddress2)
    keyNumber = (rowNumber * 3) + (columnNumber + 1) 
    return keyNumber


# * Get keys *


def GetKeypadKeyMcp23008(registerBaseAddress1, registerBaseAddress2, checkPressingKeyMode): 
    if (checkPressingKeyMode == NoPollJustGetKeyEverySecond): 
        print "Read key every second"
    time.sleep(1)
    LoopUntilOneSecondEnded()


    elif (checkPressingKeyMode == PollRowStatusNibble):   
        print "Polling Gpio"
        LoopUntilKeypadRowDataNibbleChangeMcp23008(registerBaseAddress1, registerBaseAddress2)      


    elif (checkPressingKeyMode == PollMcp23017Interrupt):  
        print "Polling Mcp23017 interrupt INTB"
        LoopUntilKeypadInterruptPinMcp23008Active(registerBaseAddress2)


    keyNumber = GetKeypadKeyNumberMcp23008(registerBaseAddress1, registerBaseAddress2)
    return keyNumber


def GetKeypadKeySequenceMcp23008(registerBaseAddress1, registerBaseAddress2, checkPressingKeyMode, count):     
    for i in range (count):        
        OneBeep() 
    keyNumber = GetKeypadKeyMcp23008(registerBaseAddress1, registerBaseAddress2, checkPressingKeyMode)    
        print "Key", i + 1, " = ", keyNumber
    time.sleep(0.25) 


# * No polling, just read key at the end of each second *
def LoopUntilOneSecondEnded():
    time.sleep(1)


# * Poll row status *
def LoopUntilKeypadRowDataNibbleChangeMcp23008(registerBaseAddress1, registerBaseAddress2): 
    WriteKeypadColumnsMcp23008(registerBaseAddress1, AllLowByte)
    NoKeyPressedNibble = 0xf
    rowDataNibble = NoKeyPressedNibble 
    while (rowDataNibble == NoKeyPressedNibble):
        rowDataNibble = GetKeypadRowDataNibbleMcp23008(registerBaseAddress2)        
    time.sleep(0.05) # debouncing time 50mS


# * Poll interrupt pin *
def LoopUntilKeypadInterruptPinMcp23008Active(registerBaseAddress):


    # Mcp23017 INTB shifted up to 5V) logical level, then connected to Mcp23017 GPB5
    KeypadInterruptPinNumber = 5


    ClearKeypadInterrupt(registerBaseAddress)

    interruptDataBit = High  # !!! ULN2803 inverts Mcp23017 INTB, so interrupt is now Low active !!!
    while (interruptDataBit == High):
        interruptDataBit = ReadDataBit(registerBaseAddress, RegisterAddressOffsetArrayBank0, PortStatus, PortB, KeypadInterruptPinNumber)    


    time.sleep(0.1) # debouncing time


# * Test Decimal keypad Mcp23008 *


def TestKeypadMcp23008(registerBaseAddress1, registerBaseAddress2, checkPressingKeyMode, keyCount):   
    SetupKeypadMcp23008(registerBaseAddress1, registerBaseAddress2) 
    GetKeypadKeySequenceMcp23008(registerBaseAddress1, registerBaseAddress2, checkPressingKeyMode, keyCount)
    SetupKeypadMcp23008(registerBaseAddress1, registerBaseAddress2)


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


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


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


# * LCD functions Mcp23008 v4.15 2013feb15 *


def LcdGpioSetupMcp23008(registerBaseAddress):
    SetupPortAllOutputMcp23008(registerBaseAddress)
    
def LcdConfigurationMcp23008(registerBaseAddress):
    dataControlByte = AllZeroDataByte
    # PrintEightBitPattern("dataControlByte01 = ", dataControlByte)
    LcdDisableWriteMcp23008(registerBaseAddress, dataControlByte)
    time.sleep(PowerOnResetDelay)


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


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


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


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


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


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


    return dataControlByte


def LcdSelectDataRegisterMcp23008(registerBaseAddress, dataByte):
    dataByte = dataByte | SelectDataRegisterMask
    LcdWriteDataByteMcp23008(registerBaseAddress, dataByte)  
    return dataByte


def LcdSelectInstructionRegisterMcp23008(registerBaseAddress, dataByte):
    dataByte = dataByte & SelectInstructionRegisterMask
    LcdWriteDataByteMcp23008(registerBaseAddress, dataByte)
    return dataByte


def LcdEnableWriteMcp23008(registerBaseAddress, dataByte):
    dataByte = dataByte | EnableWriteMask
    LcdWriteDataByteMcp23008(registerBaseAddress, dataByte)
    return dataByte


def LcdDisableWriteMcp23008(registerBaseAddress, dataByte):
    dataByte = dataByte & DisableWriteMask
    LcdWriteDataByteMcp23008(registerBaseAddress, dataByte)
    return dataByte


def LcdWritePulseMcp23008(registerBaseAddress, dataByte):
    LcdEnableWriteMcp23008(registerBaseAddress, dataByte)
    time.sleep(WritePulseLength)
    LcdDisableWriteMcp23008(registerBaseAddress, dataByte)
    return dataByte


def LcdWriteDataByteMcp23008(registerBaseAddress, dataByte): 
    WriteDataByteMcp23008(registerBaseAddress, OutputLatch, dataByte)
    return dataByte


def LcdWriteDataNibbleMcp23008(registerBaseAddress, dataByte, dataNibble, registerType, operationDelay):
    dataNibble = dataNibble << 2
    dataByte = dataByte & 0b11000011
    dataByte = dataByte | dataNibble
    if (registerType == InstructionRegister):
       dataByte = LcdSelectInstructionRegisterMcp23008(registerBaseAddress, dataByte)
    elif (registerType == DataRegister):
       dataByte = LcdSelectDataRegisterMcp23008(registerBaseAddress, dataByte)
    LcdDisableWriteMcp23008(registerBaseAddress, dataByte)
    dataByte = LcdWriteDataByteMcp23008(registerBaseAddress, dataByte)
    LcdWritePulseMcp23008(registerBaseAddress, dataByte)
    time.sleep(operationDelay)
    return dataByte


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


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


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


def LcdWriteCharStringMcp23008(registerBaseAddress, dataControlByte, string):
    for asciiChar in (string):
        LcdWriteCharFourBitModeMcp23008(registerBaseAddress, dataControlByte, asciiChar, DataRegister)
    return dataControlByte


def LcdMoveCursorMcp23008(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
    LcdWriteDataByteFourBitModeMcp23008(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 Mcp23008 *


def TestLcdMcp23008(registerBaseAddress):
    LcdGpioSetupMcp23008(registerBaseAddress)
    dataControlByte = LcdConfigurationMcp23008(registerBaseAddress)
    dataControlByte = LcdMoveCursorMcp23008(registerBaseAddress, dataControlByte, 1, 1)
    dataControlByte = LcdWriteCharStringMcp23008(registerBaseAddress, dataControlByte, ProgramTitle1)
    dataControlByte = LcdMoveCursorMcp23008(registerBaseAddress, dataControlByte, 2, 1)
    dataControlByte = LcdWriteCharStringMcp23008(registerBaseAddress, dataControlByte, ProgramTitle2)


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


# * LCD functions Mcp23017 v4.15 2013feb15 *


def LcdGpioSetupMcp23017(registerBaseAddress):
    registerBaseAddress = Mcp23017BaseAddressSystemB1
    SetupPortAoutputPortBinputPullUpMcp23017(registerBaseAddress)


def LcdConfigurationMcp23017(registerBaseAddress):
    dataControlByte = AllZeroDataByte
    # PrintEightBitPattern("dataControlByte01 = ", dataControlByte)
    LcdDisableWriteMcp23017(registerBaseAddress, dataControlByte)
    time.sleep(PowerOnResetDelay)


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


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


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


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


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


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


    return dataControlByte


def LcdSelectDataRegisterMcp23017(registerBaseAddress, dataByte):
    dataByte = dataByte | SelectDataRegisterMask
    LcdWriteDataByteMcp23017(registerBaseAddress, dataByte)  
    return dataByte


def LcdSelectInstructionRegisterMcp23017(registerBaseAddress, dataByte):
    dataByte = dataByte & SelectInstructionRegisterMask
    LcdWriteDataByteMcp23017(registerBaseAddress, dataByte)
    return dataByte


def LcdEnableWriteMcp23017(registerBaseAddress, dataByte):
    dataByte = dataByte | EnableWriteMask
    LcdWriteDataByteMcp23017(registerBaseAddress, dataByte)
    return dataByte


def LcdDisableWriteMcp23017(registerBaseAddress, dataByte):
    dataByte = dataByte & DisableWriteMask
    LcdWriteDataByteMcp23017(registerBaseAddress, dataByte)
    return dataByte


def LcdWritePulseMcp23017(registerBaseAddress, dataByte):
    LcdEnableWriteMcp23017(registerBaseAddress, dataByte)
    time.sleep(WritePulseLength)
    LcdDisableWriteMcp23017(registerBaseAddress, dataByte)
    return dataByte


def LcdWriteDataByteMcp23017(registerBaseAddress, dataByte):
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, dataByte)
    return dataByte


def LcdWriteDataNibbleMcp23017(registerBaseAddress, dataByte, dataNibble, registerType, operationDelay):
    dataNibble = dataNibble << 2
    dataByte = dataByte & 0b11000011
    dataByte = dataByte | dataNibble
    if (registerType == InstructionRegister):
       dataByte = LcdSelectInstructionRegisterMcp23017(registerBaseAddress, dataByte)
    elif (registerType == DataRegister):
       dataByte = LcdSelectDataRegisterMcp23017(registerBaseAddress, dataByte)
    LcdDisableWriteMcp23017(registerBaseAddress, dataByte)
    dataByte = LcdWriteDataByteMcp23017(registerBaseAddress, dataByte)
    LcdWritePulseMcp23017(registerBaseAddress, dataByte)
    time.sleep(operationDelay)
    return dataByte


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


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


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


def LcdWriteCharStringMcp23017(registerBaseAddress, dataControlByte, string):
    for asciiChar in (string):
        LcdWriteCharFourBitModeMcp23017(registerBaseAddress, dataControlByte, asciiChar, DataRegister)
    return dataControlByte


def LcdMoveCursorMcp23017(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
    LcdWriteDataByteFourBitModeMcp23017(registerBaseAddress, dataControlByte, cursorByte, InstructionRegister)


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


# * Test LCD1602 functions *


def TestLcdMcp23017(registerBaseAddress):
    LcdGpioSetupMcp23017(registerBaseAddress)
    dataControlByte = LcdConfigurationMcp23017(registerBaseAddress)
    dataControlByte = LcdMoveCursorMcp23017(registerBaseAddress, dataControlByte, 1, 1)
    dataControlByte = LcdWriteCharStringMcp23017(registerBaseAddress, dataControlByte, ProgramTitle1)
    dataControlByte = LcdMoveCursorMcp23017(registerBaseAddress, dataControlByte, 2, 1)
    dataControlByte = LcdWriteCharStringMcp23017(registerBaseAddress, dataControlByte, ProgramTitle2)


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


# 8. 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        Pink     Orange   Blue    Yellow   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, RegisterAddressOffsetArrayBank0, InputOutputDirection, PortA, AllOutputByte)


    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, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, hexString1) 
       time.sleep(StepTime)
        WriteDataByte(Mcp23008BaseAddress1, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, hexString2) 
        time.sleep(StepTime)


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


    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, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, pattern) 
        time.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)


def MoveUnipolarSteppingMotor(registerBaseAddress, NibbleType, StepCount, StepTime): 
    SetupPortAoutputPortBinputPullUpMcp23017(registerBaseAddress)


    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(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, hexString1) 
       time.sleep(StepTime)
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, hexString2) 
        time.sleep(StepTime)


def TestMoveMotor():
    registerBaseAddress = Mcp23017BaseAddressSystemB1
    nibbleType = LowNibble
    stepCount = OneHundredTimes
    stepTime = OneHundredMilliSeconds
    MoveUnipolarSteppingMotor(registerBaseAddress, nibbleType, 500, 0.05)




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


DemuxRegisterBaseAddress = 0x20


def DemuxGpioSetup(registerBaseAddress):
    UpperNibbleInputLowerNibbleOutput = 0xf0 # Input (GP4~7, Row0~3), Output (GP0~3, Column0~2, 3 reserved)
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, 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, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, controlDataByte)
    controlDataByte = DemuxAddressLatch(controlDataByte, Enable)
    PrintEightBitPattern("demuxDataByte = ", controlDataByte)
    WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, 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")


    time.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")


    time.sleep(2)




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


# * SPI using bit banging *


# SpiClockPin = RPiGpioGen11
# SpiMosiPin = RPiGpioGen10
# SpiMisoPin = RPiGpioGen9
# SpiSelect0Pin = RPiGpioGen8
# SpiSelect1Pin = RPiGpioGen7


def TestRfm12b(registerBaseAddress):
    SetupGPIOpins(OutputPinList, InputPinWithNoPullUpList, InputPinWithPullUpList )


def SpiSelectDevice(deviceNumber):
    if (deviceNumber == 0):
        writeOutputPin(SpiSelect0Pin, Low)  
    else:
        writeOutputPin(SpiSelect1Pin, Low)


def SpiDisSelectDevice(deviceNumber):
    if (deviceNumber == 0):
        writeOutputPin(SpiSelect0Pin, High)  
    else:
        writeOutputPin(SpiSelect1Pin, High)


def SpiClockPulse():
    writeOutputPin(SpiClockPin, High)
    time.sleep(1)
    writeOutputPin(SpiClockPin, Low)
    time.sleep(1)


def SpiWriteBit(dataBit):
    if (dataBit == 1):
        writeOutputPin(SpiMosiPin, High)        
    else:
        writeOutputPin(SpiMosiPin, Low)


def SpiReadBit(inputPin):
    dataBit  = readInputPin(inputPin)                
    # print dataBit
    return dataBit


# * Test Spi functions *


def TestSpiSelectDevice(deviceNumber, count):
    print "Now testing SPI device select pin", deviceNumber, ",..."
    for i in range (count):
        SpiSelectDevice(deviceNumber)
        time.sleep(1)
        SpiDisSelectDevice(deviceNumber)
    time.sleep(1)


def TestSpiClockPulse(count):
    for i in range (count):
       SpiClockPulse()


def TestSpiWriteBit(count):
    for i in range (count):
        SpiWriteBit(1)
    time.sleep(1)
    SpiWriteBit(0)
    time.sleep(1)


def TestSpiReadBit(inputPin, count):
    for i in range (count):
        dataBit = SpiReadBit(inputPin)    
    if (dataBit == True):
        dataLevel = "High"
        else:
        dataLevel = "Low"
        print "dataBitRead at pin number ", inputPin, " = ", dataLevel
        time.sleep(1)


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


# * SPI using wiringPi with Python wrapping 2013feb21 *


# Question - $gpio load spi necessary?


def TestWiringPiSpi():
    print "\n" + "*** Start testing wiringPi SPI, ... ***" + "\n"


    spi = spidev.SpiDev() # create spidev object
    spi.open(0,1) # open SPI0, CE1_N
    for i in range(600):  
        TwoByteArray = spi.xfer2([0xf0, 0x0f]) # write and read 2 bytes
    time.sleep(0.01)


    print "\n" + "*** Stop testing wiringPi SPI, ... ***" + "\n"


    # PrintEightBitPattern("byte 1 read = ", TwoByteArray[0])
    # PrintEightBitPattern("byte 2 read = ", TwoByteArray[1])


# 12. Old test functions *****************************************************


# * Old tests *


# 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()


# * Rpi Mcp23017 tests *


# TestButtonEchoBuzzer()
# TestTxDpin()
# TestRxDpin() # if set as output
# TestTxDpin()
# TestTxDpinRxDpin1()
# TestTxDpinRxDpin2()
# TestButtonEchoTxD()
# TestButtonEchoRxD()
# TestRxdEchoTxD() !!! not working !!!
# TestToggleMcp23017SystemB1(count = 4)
# TestReadMcp23017SystemB1(count = 4)
# TestKeypad017(checkPressingKeyMode = NoPollJustGetKeyEverySecond, keyCount = 4) 
# TestKeypad017(checkPressingKeyMode = PollMcp23017Interrupt, keyCount = 4)
# TestKeypad017(checkPressingKeyMode = PollRowStatusNibble, keyCount = 4)


#TestLED() # blink LED 4 tmes
#TestBuzzer() # beep buzzer 4 times
#TestButtonEchoBuzzer() # echo buton with buzzer 4 times
#TestButtonEchoLED() # echo button with LED 4 times
#TestToggleMcp23017SystemB1(count = 4)
#TestBlinkMcp23017SystemB1GPIObit(bitNumber = 3, onTime = 0.1, offTime = 0.3, count = 4) 
#TestReadMcp23017SystemB1GPIObit(bitNumber = 4, count = 10)
#TestKeypad017(checkPressingKeyMode = PollRowStatusNibble, keyCount = 20)
#TestKeypad017(checkPressingKeyMode = PollMcp23017Interrupt, keyCount = 20)


#TestKeypad017(checkPressingKeyMode = PollRowStatusNibble, keyCount = 4)
#TestToggleMcp23017SystemB1(count = 4)
#TestBlinkMcp23017SystemB1GPIObit(bitNumber = 3, onTime = 0.1, offTime = 0.3, count = 4) 
#TestMoveMotor()
#TestLcd(Mcp23017BaseAddressSystemB1)
#ToggleGpMcp23008(registerBaseAddress = 0x21, toggleTime = 0.25, toggleCount = 4) 
#ReadGpMcp23008(registerBaseAddress = 0x22, count = 100)


#TestKeypadMcp23017(registerBaseAddress = 0x20, checkPressingKeyMode = NoPollJustGetKeyEverySecond, keyCount = 4) 
#TestKeypadMcp23017(registerBaseAddress = 0x20, checkPressingKeyMode = PollRowStatusNibble, keyCount = 4)
#TestKeypadMcp23017(registerBaseAddress = 0x20, checkPressingKeyMode = PollMcp23017Interrupt, keyCount = 4)
#TestKeypadMcp23008(registerBaseAddress1 = 0x21, registerBaseAddress2 = 0x22, checkPressingKeyMode =  NoPollJustGetKeyEverySecond, keyCount = 4)
#TestKeypadMcp23008(registerBaseAddress1 = 0x21, registerBaseAddress2 = 0x22, checkPressingKeyMode = PollRowStatusNibble, keyCount = 4)


#TestSpiSelectDevice(deviceNumber = 0, count = 4)
#TestSpiSelectDevice(deviceNumber = 1, count = 4)


#TestSpiClockPulse(count = 10)
#TestSpiWriteBit(count = 100)


#TestSpiReadBit(ButtonPin, count = 10) *** OK ***
#TestSpiReadBit(SpiMisoPin, count = 10) !!! Not working !!!
#TestSpiReadBit(RxDpin, count = 10) !!! Not working !!!


# 13. Main program ************************************************************


StartProgram()


TestLcdMcp23017(registerBaseAddress = 0x20)
TestLcdMcp23008(registerBaseAddress = 0x21)


TestWiringPiSpi()


StopProgram()


# *****************************************************************************
# End of Program
# *****************************************************************************
.END