Pages

Tuesday, May 14, 2013

MCP23S17 detection program writing notes


Now I have started writing the MCP23s17 detection program.   This program does the following 2 things.

Part 1. Setup both ports's 8 pins of MCP23S17 at SPI0, CE0 as output.

Part 2. Write 0x55 to Port A, 0xaa to Port B.

So far I have written Part 1.  Next step is testing it.


# *****************************************************************************
# !/usr/bin/python2.7
#
# Project 
#   fongtoy ft2081 - tlfong01 2013may13
# License & Warranty
#   GNU GPLv3. For hobbist without any warranty.  Use at your own risk.
# Hardware & software 
#   Raspberry Pi Bv2 512MB, Av2 256 MB, Raspbian Wheezy, Python 2.7.3, 
#   RPI.GPIO 0.5.0a, PythonWiringPi 1.0.5, RPIO v0.8.4, GuzuntyPi v1.6-04
# Development style 
#   Functional/Object/StateMachine (MitOcwSc601), Prototyping, Test-driven, 
#   Iterative Incremental, Refactoring, Agile/Pair 
# References 
#   GuzuntyPi Input/Output Expander Rev 1.6-04 (CPLD)
#   Mcirochip Application Notes AN1043 (GPIO Expander)
#   Microchip Application Notes AN1081 (Matrix Keypad)
#   ShenZhen YaJingDa Electronics YJD1602A-1 20070908 (LCD1602)
#   PowerTip PC-1602F 20041011 (LCD1602)
#   Sitronix ST7066U Dot Matrix LCD Controller/Driver 20030301 
# *****************************************************************************

# *** Project Titles **********************************************************

ProgramTitle1 = "MCP23S17 Test 03"
ProgramTitle2 = "ft2082 20130514c"

# *** Imports *****************************************************************

import sys 
import time 
import smbus 
import pdb 
import spidev 
import RPIO as GPIO  
from RPIO import PWM 
from enum import Enum 
import wiringpi
import wiringpi2
from subprocess import call

GPIO.setmode(GPIO.BCM) 
smBus1 = smbus.SMBus(1) 

I2cBaseAddress20 = 0x20
I2cBaseAddress21 = 0x21
I2cBaseAddress22 = 0x22
I2cBaseAddress23 = 0x23
I2cBaseAddress24 = 0x24
I2cBaseAddress25 = 0x25
I2cBaseAddress26 = 0x26
I2cBaseAddress27 = 0x27

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

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

RPiGPIOgen2  = 27 # P1-13 Test LED
RPiGPIOgen9  = 30 # P5-05 Buzzer  
RPiGPIOgen10 = 31 # P5-06 Button  

LEDpin = RPiGPIOgen2
BuzzerPin = RPiGPIOgen9 
ButtonPin = RPiGPIOgen10

RPiTxD       = 14 # P1-08 UART TxD
RPiRxD       = 15 # P1-10 UART RxD

TxdPin = RPiTxD
RxdPin = RPiRxD 

# * PCM, Clock, Interrupt *

RPiGPIOgen1  = 18 # P1-12 PCM_CLK 
RPiGPIOGclk  = 04 # P1-07 GPIO_GCLK 
RPiGPIOgen6  = 25 # P1-22 IOx/keypad interrupt

RPiPcm       = RPiGPIOgen1  
RPiGpclk0    = RPiGPIOGclk

RpiGpioSpiSelect0  =  7 # SPI_CE1_N P1-26
RpiGpioSpiSelect1  =  8 # SPI_CE0_N P1-24
RpiGpioSpiMiso     =  9 # SPI_MISO P1-21
RpiGpioSpiMosi     = 10 # SPI_MOSI P1-10
RpiGpioSpiClk      = 11 # SPI_SCLK P1-23

SpiClockPin = RpiGpioSpiClk
SpiMosiPin = RpiGpioSpiMosi
SpiMisoPin = RpiGpioSpiMiso
SpiSelect0Pin = RpiGpioSpiSelect0
SpiSelect1Pin = RpiGpioSpiSelect1

RpiGpioGen0 = 17 # GPIO_GEN0 P1-11 Jtag TCK
RpiGpioGen3 = 22 # GPIO_GEN0 P1-15 Jtag TDO
RpiGpioGen4 = 23 # GPIO_GEN4 P1-16 Jtag TDI
RpiGpioGen5 = 24 # GPIO_GEN5 P1-18 Jtag TMS

JtagTckPin = RpiGpioGen0
JtagTdoPin = RpiGpioGen3
JtagTdiPin = RpiGpioGen4
JtagTmsPin = RpiGpioGen5

# * GPIO Input/Output/Interrupt pin list ***************************************

OutputPinList = [LEDpin, BuzzerPin, TxdPin, RPiGpclk0, RPiPcm]
InputPinWithPullUpList = [ButtonPin, RxdPin, RPiGPIOgen6]
InputPinWithNoPullUpList = []

# * Input/Output pin list for debugging only *
# OutputPinList = [LEDpin, BuzzerPin, TxdPin, SpiClockPin, SpiMosiPin, SpiSelect0Pin, SpiSelect1Pin]
# InputPinWithPullUpList = [ButtonPin, RxdPin, RPiGPIOgen6, SpiMisoPin]
# JtagOutputPinList = [JtagTckPin, JtagTdoPin, JtagTmsPin, JtagTdiPin] 
# JtagInputPinList = [] 
# JtagOutputPinList = [JtagTckPin, JtagTdoPin, JtagTmsPin]
# JtagInputPinList = [JtagTdiPin]

# *** Global constants *********************************************************

OnTime = 0.1 # On time = 0.01 second (10mS)
OffTime = 0.25 # Off time = 0.25 second (25 mS)

# *** Basic GPIO Setup/Read/Write Functions ***********************************

# * Nibble names * 

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 *

# * Individual 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
readInputPin = lambda iPin: GPIO.input(ButtonPin) # read value from input pin
setupWriteOutputPin = lambda oPin, oValue: (setupOutputPin(oPin), writeOutputPin(oPin, oValue)) # set and write

def SetupGPIO(): 
    SetupGPIOpins(OutputPinList, InputPinWithNoPullUpList, InputPinWithPullUpList )

def CleanUpGpio():      
    GPIO.cleanup()

def SetupGPIOpins(outputPinList, inputPinWithNoPullUpList, inputPinWithPullUpList): 
    for oPin in outputPinList:
       setupWriteOutputPin(oPin, Off)
    for iPin in inputPinWithNoPullUpList:
        setupInputPinWithNoPullUp(iPin)
    for iPin in inputPinWithPullUpList:
        setupInputPinWithPullUp(iPin)

# * Basic LED/Buzzer/Button 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)

# * Testing Buzzer/LED/Button/UART/JTAG/Interrupt *

def TestBuzzer(): # beep 4 times
    SetupGPIO()
    for i in range (4):
        pulsePin(BuzzerPin, OnTime, OffTime)

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

def TestGpioPin(oPin, toggleTime, toggleCount): 
    for i in range(toggleCount):
        togglePin(oPin, toggleTime)

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

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

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

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

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

def TestTxdPin(): # blink 4 times
    SetupGPIO()
    for i in range (4): 
        pulsePin(TxdPin, OnTime, OffTime)

def TestRxdPin(): # blink 4 times
    SetupGPIO()
    for i in range (4): 
        pulsePin(RxdPin, OnTime, OffTime)

def TestTxdPinRxdPin1(): # blink, read 4 times
    SetupGPIO()
    for i in range (4): 
        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 (4):
        echoPin(RxdPin, TxdPin)

def SetupJtagGpio():
    SetupGPIOpins(outputPinList = JtagOutputPinList, inputPinWithNoPullUpList = JtagInputPinList, inputPinWithPullUpList = []) 

def TestJtagPins(toggleTime, testCount): 
    SetupJtagGpio()
    Beep(1)
    for i in range(testCount):
        togglePin(JtagTckPin, toggleTime) 
    Beep(1)
    for i in range(testCount):
        togglePin(JtagTdoPin, toggleTime) 
    Beep(1)
    for i in range(testCount):
        togglePin(JtagTmsPin, toggleTime) 

    for i in range(testCount):
        writeOutputPin(JtagTdoPin, 1)
        if readInputPin(JtagTdiPin) == 1:
   print "JtagTdiPin = 1"
        else:
   print "JtagTdiPin = 0"
        time.sleep(1)
writeOutputPin(JtagTdoPin, 0)
        if readInputPin(JtagTdiPin) == 1:
   print "JtagTdiPin = 1"
        else:
   print "JtagTdiPin = 0"
        time.sleep(1)

    Beep(3)

    GPIO.cleanup()

def TestInterruptPinFallingEdgeDetection(): # !!! OUT OF DATE, not sure if still working !!!
    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"


# *** Printing and debugging functions ****************************************

# * Set/Reset one bit of a byte *

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

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

# * Print nibble/byte as 4/8 bit pattern for debugging *

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

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

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]

# * Beep/Start/Stop program functions *

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

def StopProgram():
    SetupGPIO()
    StopBeep()
    # print "\n" + "*** Resetting GPIO input, no pull up/down, no event detect. ***" + "\n"
    CleanUpGpio()
    print "\n" + "*** Stop Program ***" + "\n"    

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)


# *** Mcp23008, Mcp23017 IO Expander Functions ********************************

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

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

HalfHighHalfLow = 0xf0
HalfLowHalfHigh = 0x0f
Nibble1HighNibble2Low = 0xf0
Nibble1LowNibble2High = 0x0f
HighNibbleInputLowNibbleOutput = 0xf0

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

InputOutputDirection = 0
InputPolarity = 1
InterruptEnable = 2
DefaultValue = 3  
CompareMode = 4
BankInterruptPinMode = 5
PullUp = 6
InterruptFlag = 7
InterruptCapture = 8
PortStatus = 9
OutputLatch = 10

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

# * System A Test Functions (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 TestToggleMcp23017PortAoutput(registerBaseAddress, toggleTime, toggleCount): 
    SetupPortAoutputPortBinputPullUpMcp23017(registerBaseAddress)
    for i in range(toggleCount):
        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 TestReadMcp23017PortBinput(registerBaseAddress, readTime, readCount):
    SetupPortAoutputPortBinputPullUpMcp23017(registerBaseAddress)
    for i in range(readCount):
        dataByte = ReadDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, PortStatus, PortB)
        PrintEightBitPattern("MCP23x17 Port B 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)

# *** Decimal Keypad ********************************************************** 

# * Mcp23017 based decimal keypad *********************************************

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


# * Mcp23008 x 2 based decimal keypad functions *******************************

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)

def TestKeypadMcp23017PollingMode(i2cRegisterBaseAddress):
    TestKeypadMcp23017(i2cRegisterBaseAddress, checkPressingKeyMode = PollRowStatusNibble, keyCount = 4)


# * LCD1602/1604/2004 Functions *********************************************** 

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

PowerOnResetDelay      = 0.1    # 100mS
VeryLongOperationDelay = 0.01   # 10mS
LongOperationDelay     = 0.01   # 10mS
ShortOperationDelay    = 0.001  # 1mS
WritePulseLength       = 0.001  # 1mS

# * Mcp23008 LCD functions ****************************************************

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)


# * MCP23017 LCD Functions ****************************************************

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)
       #print "MCP23017 dataControlByte = ", hex(dataByte)  
       #pdb.set_trace()  
    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(i2cRegisterBaseAddress):
    LcdGpioSetupMcp23017(i2cRegisterBaseAddress)
    dataControlByte = LcdConfigurationMcp23017(i2cRegisterBaseAddress)
    dataControlByte = LcdMoveCursorMcp23017(i2cRegisterBaseAddress, dataControlByte, 1, 1)
    dataControlByte = LcdWriteCharStringMcp23017(i2cRegisterBaseAddress, dataControlByte, ProgramTitle1)
    dataControlByte = LcdMoveCursorMcp23017(i2cRegisterBaseAddress, dataControlByte, 2, 1)
    dataControlByte = LcdWriteCharStringMcp23017(i2cRegisterBaseAddress, dataControlByte, ProgramTitle2)


# *** 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 (from Arduino/Netduino, not tested here *************

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

# * WiringPi Python wrapping SPI test functions *******************************

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

    spi = spidev.SpiDev() # create spidev object
    spi.open(0,0) # open SPI0, CE0_N

    SendByteList = [0x55, 0xaa, 0xAA]
    ReadByteList = spi.xfer2(SendByteList)
    print "Bytes read = " + hex(ReadByteList[0]) + " " + hex(ReadByteList[1]) + " " + hex(ReadByteList[2])

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

# * RFM12b 434Mhz Wireless Transceiver Functions ******************************

# * Arduino C++ functions *
# define wkupOn   0x820E // turn on wake up timer (also xtl, lvd)
# define wkupOff  0x820C // turn off wake up timer (xtl, lvd still on)
# define wkup2ms  0XE002 // wake up every 2mS
# define config01 0x80D7
# define power01  0x8239
# define freq01   0xA640
# define drate01  0xC647
# define rxctrl01 0x94A0
# define fifors01 0xCA81
# define syncpt01 0xCED4
# define afc01    0xC483
# define txctrl01 0x9850
# define pll01    0xCC77
# define wkup01   0xE000
# define ldc01    0xC800
# define lbdclk01 0xC400

# define lvdOn    0x820C // turn on low voltage detection (also xtal, mcu clk)
# define lvd27    0xC064 // set low voltage threshold 2.7V (2.2 + 0.1 * 4) 2MHz,
# define status   0x0000 // read status to execute command and clear interrupt

#void RFM12B01::setupRfm12b()
#  {
#  putWord(0x80D7); //enable tx, rx fifo, 433MHz, 12.0pF
#  // putWord(0x8239); //enable xmitt, syn, xtal, disable clk
#  putWord(0xA640); //operation frequency ???
#  putWord(0xC647); //data rate 4.8kbps
#  putWord(0x94A0); //VDI,FAST,134kHz,0dBm,-103dBm
#  putWord(0xC2AC); //clk rec auto, digi fltr,DQD4
#  putWord(0xCA81); //fifo8,sync 1, sync, no fifo fill
#  putWord(0xCED4); //sync pattern d4
#  putWord(0xC483); //offset VDI hi, no restrict, afc, afc out
#  putWord(0x9850); //90kHz, max o/p pwr (odBm)
#  putWord(0xCC17); //10MHz, 620uA, disable pll dit, 256kbps
#  putWord(0xE000); //wake up timer not used
#  putWord(0xC800); //low duty cycle not used
#  putWord(0xC040); //1.66MHz,2.2V low battery
#  }

#void RFM12B01::testSetupClock2Mhz()
#  {
#  putWord(0xC064); 
#  }

def PrintDoubleSpaceLine(line):
    print "\n" + line + "\n"

def WaitSeconds(count):
    time.sleep(count)

# * Rfm12b 2 byte commands *

SetExternalMpuClockFrequency1Mhz  = [0xc0, 0x04] #  1 MHz, 2.7V (2.2 + 0.1 * 4)
SetExternalMpuClockFrequency2Mhz  = [0xc0, 0x64] #  2 MHz, 2.7V (2.2 + 0.1 * 4)
SetExternalMpuClockFrequency5Mhz  = [0xc0, 0xc4] #  5 MHz, 2.7V (2.2 + 0.1 * 4)
SetExternalMpuClockFrequency10Mhz = [0xc0, 0xe4] # 10 MHz, 2.7V (2.2 + 0.1 * 4)

EnableWakeupTimerEnableXtlLvd  = [0x82, 0x0e]
DisableWakeupTimerEnableXtlLvd = [0x82, 0x0c]
SetWakeUpTimer2Ms              = [0xe0, 0x02]
ClearInterrupt                 = [0x00, 0x00]

def TestRfm12bSet2Mhz():
    PrintDoubleSpaceLine("*** Start testing RFM12B 2 MHz ***")

    rfm12bSpi = spidev.SpiDev() # create spi object to entertain RFM12B
    rfm12bSpi.open(0,0) # open sci channel 0 and select slave device 0

    ExternalMpuClockFrequency2Mhz = [0xC0, 0x64]

    receiveByteList = rfm12bSpi.xfer2(ExternalMpuClockFrequency2Mhz)
    print "Bytes read = " + hex(receiveByteList[0]) + " " + hex(receiveByteList[1])

    PrintDoubleSpaceLine("*** Stop testing RFM12B ***")

def TestRfm12bSet1Mhz():
    PrintDoubleSpaceLine("*** Start testing RFM12B 1 MHz ***")

    rfm12bSpi = spidev.SpiDev() # create spi object to entertain RFM12B
    rfm12bSpi.open(0,0) # open sci channel 0 and select slave device 0

    ExternalMpuClockFrequency2Mhz = [0xc0, 0x04]

    receiveByteList = rfm12bSpi.xfer2(ExternalMpuClockFrequency2Mhz)
    print "Bytes read = " + hex(receiveByteList[0]) + " " + hex(receiveByteList[1])

    PrintDoubleSpaceLine("*** Stop testing RFM12B ***")

def OpenSpiChannel(spiObject, spiChannelNumber, spiDeviceNumber):
    spiObject.open(spiChannelNumber, spiDeviceNumber) 

def Rfm12bSendCommand1(spiChannelNumber, spiDeviceNumber, sendByteList):
    rfm12bSpi = spidev.SpiDev() 
    rfm12bSpi.open(spiChannelNumber, spiDeviceNumber)    
    receiveByteList = rfm12bSpi.xfer2(sendByteList)
    
def Rfm12bSendCommand2(rfm12bSpi, sendByteList):   
    receiveByteList = rfm12bSpi.xfer2(sendByteList)    

def Rfm12bTest1(spiChannelNumber, spiDeviceNumber):

    PrintDoubleSpaceLine("*** Start Test 1 ***")

    rfm12bSpi = spidev.SpiDev() # create spi object to entertain RFM12B
    rfm12bSpi.open(0,0) # open sci channel 0 and select slave device 0

    Rfm12bSendCommand1(spiChannelNumber, spiDeviceNumber, SetExternalMpuClockFrequency2Mhz)
    WaitSeconds(2)
    Rfm12bSendCommand1(spiChannelNumber, spiDeviceNumber, SetExternalMpuClockFrequency5Mhz)
    WaitSeconds(2)
    Rfm12bSendCommand1(spiChannelNumber, spiDeviceNumber, SetExternalMpuClockFrequency10Mhz)
    WaitSeconds(2)
    Rfm12bSendCommand1(spiChannelNumber, spiDeviceNumber, SetExternalMpuClockFrequency1Mhz)
    WaitSeconds(2)

    PrintDoubleSpaceLine("*** Stop Test 1 ***")

def Rfm12bTest2(spiChannelNumber, spiDeviceNumber):

    PrintDoubleSpaceLine("*** Start Test 2 ***")

    rfm12bSpi = spidev.SpiDev()
    OpenSpiChannel(rfm12bSpi, spiChannelNumber, spiDeviceNumber)

    Rfm12bSendCommand2(rfm12bSpi, SetExternalMpuClockFrequency2Mhz)
    WaitSeconds(2)
    Rfm12bSendCommand2(rfm12bSpi, SetExternalMpuClockFrequency5Mhz)
    WaitSeconds(2)
    Rfm12bSendCommand2(rfm12bSpi, SetExternalMpuClockFrequency10Mhz)
    WaitSeconds(2)
    Rfm12bSendCommand2(rfm12bSpi, SetExternalMpuClockFrequency1Mhz)
    WaitSeconds(2)

    PrintDoubleSpaceLine("*** Stop Test 2 ***")


def Rfm12bTest3a(spiChannelNumber, spiDeviceNumber):

    PrintDoubleSpaceLine("*** Start Test 3a ***")

    rfm12bSpi = spidev.SpiDev()
    OpenSpiChannel(rfm12bSpi, spiChannelNumber, spiDeviceNumber)

    Rfm12bSendCommand2(rfm12bSpi, SetExternalMpuClockFrequency2Mhz)
    WaitSeconds(2)
    Rfm12bSendCommand2(rfm12bSpi, SetExternalMpuClockFrequency5Mhz)
    WaitSeconds(2)
    Rfm12bSendCommand2(rfm12bSpi, SetExternalMpuClockFrequency1Mhz)
    WaitSeconds(2)
    
    Rfm12bSendCommand2(rfm12bSpi, TurnOnWakeupXtlLvd)
    Rfm12bSendCommand2(rfm12bSpi, WakeUpEvery2Ms)
    # WaitSeconds(2)
    # Rfm12bSendCommand2(rfm12bSpi, TurnOffWakeupTurnOnXtlLvd)

    PrintDoubleSpaceLine("*** Stop Test 3a ***")


def Rfm12bTest3b(spiChannelNumber, spiDeviceNumber):

    PrintDoubleSpaceLine("*** Start Test 3b 2013feb2601 ***")

    rfm12bSpi = spidev.SpiDev()
    OpenSpiChannel(rfm12bSpi, spiChannelNumber, spiDeviceNumber)

    Rfm12bSendCommand2(rfm12bSpi, SetExternalMpuClockFrequency2Mhz)
    
    Rfm12bSendCommand2(rfm12bSpi, TurnOnWakeupXtlLvd)

    for i in range (100000):   
        Rfm12bSendCommand2(rfm12bSpi, WakeUpEvery2Ms)
        time.sleep(0.005) 
        Rfm12bSendCommand2(rfm12bSpi, ClearInterrupt)
time.sleep(0.010) 
    
    Rfm12bSendCommand2(rfm12bSpi, TurnOffWakeupTurnOnXtlLvd)

    PrintDoubleSpaceLine("*** Stop Test 3b ***")


def Rfm12bTest3c(spiChannelNumber, spiDeviceNumber0,  spiDeviceNumber1):

    PrintDoubleSpaceLine("*** Start Test 3c 2013feb2602 ***")

    rfm12bSpi00 = spidev.SpiDev()
    OpenSpiChannel(rfm12bSpi00, spiChannelNumber, spiDeviceNumber0)
    Rfm12bSendCommand2(rfm12bSpi00, SetExternalMpuClockFrequency5Mhz)
    
    rfm12bSpi01 = spidev.SpiDev()
    OpenSpiChannel(rfm12bSpi01, spiChannelNumber, spiDeviceNumber1)
    Rfm12bSendCommand2(rfm12bSpi01, SetExternalMpuClockFrequency10Mhz)

    PrintDoubleSpaceLine("*** Stop Test 3c ***")


def Rfm12bTest3d(spiChannelNumber, spiDeviceNumber0,  spiDeviceNumber1):

    PrintDoubleSpaceLine("*** Start Test 3d 2013feb2603 ***")

    rfm12bSpi00 = spidev.SpiDev()
    OpenSpiChannel(rfm12bSpi00, spiChannelNumber, spiDeviceNumber0)
    Rfm12bSendCommand2(rfm12bSpi00, SetExternalMpuClockFrequency2Mhz)
    
    rfm12bSpi01 = spidev.SpiDev()
    OpenSpiChannel(rfm12bSpi01, spiChannelNumber, spiDeviceNumber1)
    Rfm12bSendCommand2(rfm12bSpi01, SetExternalMpuClockFrequency5Mhz)

    for i in range(4):
        Beep(1)
        Rfm12bSendCommand2(rfm12bSpi00, DisableWakeupTimerEnableXtlLvd)
        Rfm12bSendCommand2(rfm12bSpi00, ClearInterrupt)
time.sleep(2)

        Beep(2)
        Rfm12bSendCommand2(rfm12bSpi00, EnableWakeupTimerEnableXtlLvd)
        Rfm12bSendCommand2(rfm12bSpi00, SetWakeUpTimer2Ms) 
        time.sleep(2)

        Beep(3)
        Rfm12bSendCommand2(rfm12bSpi01, DisableWakeupTimerEnableXtlLvd)
        Rfm12bSendCommand2(rfm12bSpi01, ClearInterrupt)
time.sleep(2)

        Beep(4)
        Rfm12bSendCommand2(rfm12bSpi01, EnableWakeupTimerEnableXtlLvd)
        Rfm12bSendCommand2(rfm12bSpi01, SetWakeUpTimer2Ms) 
        time.sleep(2)
   
    PrintDoubleSpaceLine("*** Stop Test 3d ***")

def TestRfm12bExtMpuClock(spiChannelNumber, Rfm12bSpiDeviceNumber0,  Rfm12bSpiDeviceNumber1):

    PrintDoubleSpaceLine("*** Start RFM12B Test ***")

    rfm12bSpi00 = spidev.SpiDev()
    OpenSpiChannel(rfm12bSpi00, spiChannelNumber, Rfm12bSpiDeviceNumber0)
    Rfm12bSendCommand2(rfm12bSpi00, SetExternalMpuClockFrequency5Mhz)
    
    rfm12bSpi01 = spidev.SpiDev()
    OpenSpiChannel(rfm12bSpi01, spiChannelNumber, Rfm12bSpiDeviceNumber1)
    Rfm12bSendCommand2(rfm12bSpi01, SetExternalMpuClockFrequency2Mhz)

    PrintDoubleSpaceLine("*** Stop Test ***")

def  TestDualRfm12bExtMpuClock(spiChannelNumber):
     TestRfm12bExtMpuClock(spiChannelNumber, Rfm12bSpiDeviceNumber0 = 0, Rfm12bSpiDeviceNumber1 = 1)


# * RPIO Servo PWM Functions **************************************************

# *** RPIO.PWM/Servo References ***********************************************

# RPIO.PWM, PWM via DMA for the Raspberry Pi
# http://pythonhosted.org/RPIO/pwm_py.html
# PCA9685 16-channel, 12-bit PWM Fm+ I2 C-bus LED controller 
# http://www.nxp.com/documents/data_sheet/PCA9685.pdf
# TSSOP28\SSOP28\DIP28 Adapter
# http://item.taobao.com/item.htm?spm=a230r.1.14.108.UHRZYa&id=12717853271
# Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface - PCA9685
# http://www.adafruit.com/products/815
# http://learn.adafruit.com/adafruits-raspberry-pi-lesson-8-using-a-servo-motor/servo-motors
# http://learn.adafruit.com/adafruits-raspberry-pi-lesson-8-using-a-servo-motor/the-pwm-and-servo-kernel-module

# *** Seeeduino & Towerpro SG90 Test Program (tlfong01 2009jan08) *************
# http://www.todopic.com.ar/foros/index.php?topic=24768.5;wap2

# /******************************************************************************
# * Hello Servo - Turn TowerPro SG90 servo to middle position
# * Created - 2009jan07, Last update - 2008jan07
# * Function - Turn servo to middle position
# * MPU/Programming language - Seeeduino v1.1/Arduino v12.0
# * Author - TL Fong (tlfong01, TaoBao)
# * Copyright - Creative Commons Attribution - ShareAlike 3.0 Licence
# *
# * Test procedure
# * (1) FIRST run the program, [!!! DO NOT POWER ON YET !!! tlfong01 2013mar13]
# * (2) Manually turn servo clockwise or counterclock to limit,
# * (3) Connect 5V power and MPU signal to servo,
# * (4) WARNING - Disconnect 5V power before stopping program,
# *
# * TowerPro SG90 Spec
# * CCW end 900uS, CW end 2100uS, middle 1500uS, 3.0 to 6.0V,
# * 9g, 22 * 11.5 * 27mm, 0.12sec/60 degrees (4.8V no load),
# * stall torque 17.5ozin/1.2 kgcm) (4.8V), dead band 7usec,
# * coreless motor, all nylon gear, RMB30 (TaoBao).
# *
# * Notes
# * 1. To turn counter clockwise limit, change pulse width from 1500 to 900.
# * 2. To turn clockwise limit, change pulse width from to 2100.

# *** RPi GPIO pin numbering **************************************************

# RPiGPIOgen1  = 18 # (P1-12, BCM GPIO 18) PCM_CLK 
# RPiGPIOGclk  = 04 # (P1-07, BCM GPIO 04) GPIO_GCLK 
# RPiPcm       = RPiGPIOgen1  
# RPiGpclk0    = RPiGPIOGclk

# *** Test servo functions ****************************************************

CounterClockwiseLimitPulseWidth = 900 # CCW limit = 900 uS = 0.9 mS
ClockwiseLimitPulseWidth = 2100       # CW limit = 2100 uS = 2.1 mS
MiddlePositionPulseWidth = 1500       # Middle position = 1500 uS = 1.5 mS

def TestRpioServo():

    PrintDoubleSpaceLine("*** Testing RPIO.PCM tlfong01 2013mar25 ***")

    RpioServoController = PWM.Servo() # default 20ms subcycle
    RpioServoController.set_servo(RPiPcm, CounterClockwiseLimitPulseWidth)
    RpioServoController.set_servo(RPiGpclk0, MiddlePositionPulseWidth)    
    
    time.sleep(600)
   
def SetClock(channelNumber, frequency, rpiPin, timeSeconds): # maximum frequency = 100 Hz
    cycleTime = 1000000 / frequency 
    startTime = (cycleTime / 2) / 10
    pulseWidth = ((cycleTime / 2) / 10) - 1

    PWM.setup()
    PWM.init_channel(channelNumber, cycleTime) 
    PWM.add_channel_pulse(channelNumber, rpiPin, startTime, pulseWidth) 
    time.sleep(timeSeconds)
    PWM.clear_channel_gpio(channelNumber, rpiPin)
    PWM.cleanup() 

def SetRpiGpClk0(frequencyHz, waitTime):
    SetClock(channelNumber = 0, frequency = frequencyHz, rpiPin = RPiGpclk0, timeSeconds = waitTime)   

def TestRpioClock(): # !!! works for 100/50/25Hz, other frequencies not working !!!
    PrintDoubleSpaceLine("*** Testing RPIO.PCM tlfong01 2013mar27 ***")

    PrintDoubleSpaceLine("*** Testing 100 Hz, 4 seconds ***")
    Beep(3)
    SetRpiGpClk0(100, 2)

#    PrintDoubleSpaceLine("*** Testing 50 Hz, 4 seconds ***")
#    Beep(3)
#    SetRpiGpClk0(50, 2)

#    PrintDoubleSpaceLine("*** Testing 25 Hz, 4 seconds ***")
#    Beep(3)
#    SetRpiGpClk0(25, 2)  

# *** Guzunty Pi CPLD (XC9572XL) Functions ************************************

# *** Core 16o8i ************************************************************** 

# https://raw.github.com/Guzunty/Pi/master/src/gz_16o8i/gz_16o8i.rpt

# * 16o8i Fitter Report Summary *

# * 17 Outputs **

# Signal Name         Loc     Pin 
# outputs<0>          FB1_2   1    
# outputs<1>          FB1_5   2    
# outputs<2>          FB1_6   3    
# outputs<3>          FB1_8   4    
# outputs<4>          FB1_15  8    
# outputs<5>          FB1_17  9    
# outputs<6>          FB2_2   35   
# outputs<7>          FB2_5   36   
# outputs<8>          FB2_6   37   
# outputs<9>          FB2_8   38   
# outputs<10>         FB2_15  43   
# outputs<11>         FB2_17  44   
# outputs<12>         FB3_2   11   
# outputs<13>         FB3_5   12   
# outputs<14>         FB3_8   13   
# outputs<15>         FB3_9   14   
# miso                FB4_17  34    

# ** 11 Inputs **

# Signal Name         Loc     Pin  Type       
# inputs<6>           FB1_11  6~   GCK/I/O [GCK2]
# inputs<7>           FB1_14  7~   GCK/I/O [GCK3]
# inputs<0>           FB3_14  19   I/O     
# inputs<1>           FB3_15  20   I/O     
# inputs<2>           FB3_16  24   I/O     
# inputs<3>           FB3_17  22   I/O     
# inputs<4>           FB4_2   25   I/O     
# inputs<5>           FB4_5   26   I/O     
# sclk                FB4_11  28   I/O     
# mosi                FB4_14  29   I/O     
# sel                 FB4_15  33   I/O     

# * Guzunty Pi 16o8i Pin numbering ********************************************

# Pin O0  = Winding 1 (Unipolar stepping motor A- winding)
# Pin O1  = Winding 2 (Unipolar stepping motor A+ winding)
# Pin O2  = Winding 3 (Unipolar stepping motor B- winding)
# Pin O3  = Winding 4 (Unipolar stepping motor B+ winding)

# Pin O4  = Column 0 (Decimal keypad Column 0)
# Pin O5  = Column 1 (Decimal keypad Column 1)
# Pin O5  = Column 2 (Decimal keypad Column 2)
# Pin O7  = Reserved

# Pin O8  = RegisterSelect (LCD1602A)
# Pin O9  = WriteEnable (LCD1602A) 
# Pin O10 = DataBit4 (LCD1602A) 
# Pin O11 = DataBit5 (LCD1602A)

# Pin O12 = DataBit6 (LCD1602A)
# Pin O13 = DataBit7 (LCD1602A)
# Pin O14 = Reserved
# Pin O15 = Reserved

# Pin I0  = Row 0 (Decimal keypad Row 0)
# Pin I1  = Row 1 (Decimal keypad Row 1)
# Pin I2  = Row 2 (Decimal keypad Row 2)
# Pin I3  = Row 2 (Decimal keypad Row 3)

# Pin I4  = Reserved 
# Pin I5  = Reserved
# Pin I6  = Reserved
# Pin I7  = Reserved

# *** Guzunty Pi 28BYJ48 Unipolar Stepping Motor Functions ********************

def WindingHex(windingTuple):
    windingHex0 = 0x01 << (windingTuple[0] - 1)
    windingHex1 = 0x01 << (windingTuple[1] - 1)
    windingHex = windingHex0 | windingHex1
    return windingHex

def TestGuzuntyPiSteppingMotor(windingTuple0, windingTuple1, stepCount, stepTime):
    PrintDoubleSpaceLine("*** Start testing Guzunty Pi Stepping Motor ***")    
    windingHex0 = WindingHex(windingTuple0)
    windingHex1 = WindingHex(windingTuple1)
    guzuntypiSpi = spidev.SpiDev() 
    guzuntypiSpi.open(0, 0) 
    Beep(4)
    for i in range(stepCount):
        guzuntypiSpi.xfer2([windingHex0, 0x00])
        time.sleep(stepTime)
        guzuntypiSpi.xfer2([windingHex1, 0x00])
        time.sleep(stepTime)  
    guzuntypiSpi.close() 
    PrintDoubleSpaceLine("*** Stop testing GuzuntyPi ***")

# *** Guzunty Pi Decimal Keypad Fuctions **************************************

AllColumnLowTuple   = (0b01110000, 0b00000000) 
Column0LowTuple     = (0b00010000, 0b00000000)
Column1LowTuple     = (0b00100000, 0b00000000)
Column2LowTuple     = (0b01000000, 0b00000000)
ColumnLowTupleTuple = (Column0LowTuple, Column1LowTuple, Column2LowTuple)

def LoopUntilKeypadRowDataNibbleChangeGuzuntyPi(spi, allColumnLowTuple): 
    NoKeyPressedNibble = 0x0f      
    rowDataNibble = NoKeyPressedNibble 
    while (rowDataNibble == NoKeyPressedNibble):
        rowDataNibble = GetKeypadRowDataNibbleGuzuntyPi(spi, allColumnLowTuple)             
    time.sleep(0.05) # debouncing time 50mS

def GetKeypadRowDataNibbleGuzuntyPi(spi, twoByteTuple): 
    twoByteList = [twoByteTuple[0], twoByteTuple[1]]
    rowDataByteList = spi.xfer2(twoByteList)
    rowDataNibble = 0x0f & (rowDataByteList[0])
    return rowDataNibble

def GetKeypadColumnNumberGuzuntyPi(spi):
    NoKeyPressRowDataNibble = 0b1111    
    for i in range(3):  
        rowDataNibble = GetKeypadRowDataNibbleGuzuntyPi(spi, ColumnLowTupleTuple[i])
        if rowDataNibble != NoKeyPressRowDataNibble:
   break
    columnNumber = i
    print "Column number = ", columnNumber
    return columnNumber

def GetKeypadRowNumberGuzuntyPi(spi): 
    rowDataNibble = GetKeypadRowDataNibbleGuzuntyPi(spi, AllColumnLowTuple)
    DataNibbleTuple = (0b00001110, 0b00001101, 0b00001011, 0b00000111)
    for dataNibble in (DataNibbleTuple):
        if (dataNibble == rowDataNibble):
            break
    rowNumber = DataNibbleTuple.index(dataNibble)
    # PrintFourBitPattern("Row data nibble = ", rowDataNibble)
    print "Row number = ", rowNumber
    return rowNumber

def GetKeyNumberGuzuntyPi(rowNumber, columnNumber): 
    keyNumber = (rowNumber * 3) + (columnNumber + 1) 
    print "Key number = ", keyNumber
    return keyNumber

def TestKeypadGuzuntyPi(): 

    # *** setup GuzuntyPi ***
    spiGuzuntyPi = spidev.SpiDev() 
    spiGuzuntyPi.open(0, 0) 

    keyNumber = 99
    while (keyNumber != 12):
        print "\nPress any key between 0 and 9.  Press # to exit, ...\n"
        LoopUntilKeypadRowDataNibbleChangeGuzuntyPi(spiGuzuntyPi, AllColumnLowTuple)
        columnNumber = GetKeypadColumnNumberGuzuntyPi(spiGuzuntyPi)
        rowNumber = GetKeypadRowNumberGuzuntyPi(spiGuzuntyPi) 
        keyNumber = GetKeyNumberGuzuntyPi(rowNumber, columnNumber)
time.sleep(0.5)
   
    # *** End of keypad test ***
    spiGuzuntyPi.close() 


# LCD1602 software initialization procedure

# Part A - 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 B - 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)

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

# AllZeroDataByte = 0x00
# AllOneDataByte = 0xff

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

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

# InstructionRegister = 0
# DataRegister = 1

# EightBitInstructionMode = 0x3
# FourBitInstructionMode  = 0x2

# TwoLineFiveTimesEightDot1 = 0x2
# TwoLineFiveTimesEightDot2 = 0x8

# DisplayOnCursorOnCursorBlinkOff1 = 0x0
# DisplayOnCursorOnCursorBlinkOff2 = 0xe

# ClearDisplay1 = 0x0
# ClearDisplay2 = 0x1

# CursorIncrementDisplayNoShift1 = 0x0
# CursorIncrementDisplayNoShift2 = 0x6

# CursorHome1 = 0x0
# CursorHome2 = 0x2

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

# * Guzunty Pi LCD1602 Functions **********************************************

def LcdConfigurationGuzuntyPi(spi):

    dataControlByte = 0x00
    LcdDisableWriteGuzuntyPi(spi, dataControlByte)
    time.sleep(PowerOnResetDelay)

    LcdWriteDataNibbleGuzuntyPi(spi, EightBitInstructionMode, InstructionRegister, VeryLongOperationDelay) 
    LcdWriteDataNibbleGuzuntyPi(spi, EightBitInstructionMode, InstructionRegister, LongOperationDelay) 
    LcdWriteDataNibbleGuzuntyPi(spi, EightBitInstructionMode, InstructionRegister, LongOperationDelay) 
    LcdWriteDataNibbleGuzuntyPi(spi, FourBitInstructionMode, InstructionRegister, LongOperationDelay) 

    LcdWriteDataNibbleGuzuntyPi(spi, TwoLineFiveTimesEightDot1, InstructionRegister, LongOperationDelay) 
    LcdWriteDataNibbleGuzuntyPi(spi, TwoLineFiveTimesEightDot2, InstructionRegister, LongOperationDelay)
    
    LcdWriteDataNibbleGuzuntyPi(spi, DisplayOnCursorOnCursorBlinkOff1, InstructionRegister, LongOperationDelay) 
    LcdWriteDataNibbleGuzuntyPi(spi, DisplayOnCursorOnCursorBlinkOff2, InstructionRegister, LongOperationDelay)
    
    LcdWriteDataNibbleGuzuntyPi(spi, ClearDisplay1, InstructionRegister, LongOperationDelay) 
    LcdWriteDataNibbleGuzuntyPi(spi, ClearDisplay2, InstructionRegister, LongOperationDelay)
    
    LcdWriteDataNibbleGuzuntyPi(spi, CursorIncrementDisplayNoShift1, InstructionRegister, LongOperationDelay) 
    LcdWriteDataNibbleGuzuntyPi(spi, CursorIncrementDisplayNoShift2, InstructionRegister, LongOperationDelay)
    
    LcdWriteDataNibbleGuzuntyPi(spi, CursorHome1, InstructionRegister, LongOperationDelay) 
    LcdWriteDataNibbleGuzuntyPi(spi, CursorHome2, InstructionRegister, LongOperationDelay)

def LcdWriteDataControlByteGuzuntyPi(spi, dataControlByte):
    twoByteList = [0x00, dataControlByte]
    rowDataByteList = spi.xfer2(twoByteList)

def LcdDisableWriteGuzuntyPi(spi, dataControlByte):
    DisableWriteAndMask = 0xfd # 0b 1111 1101
    dataControlByte = dataControlByte & DisableWriteAndMask
    LcdWriteDataControlByteGuzuntyPi(spi, dataControlByte)

def LcdEnableWriteGuzuntyPi(spi, dataControlByte):
    EnableWriteOrMask = 0x02 # 0b 0000 0010
    dataControlByte = dataControlByte | EnableWriteOrMask
    LcdWriteDataControlByteGuzuntyPi(spi, dataControlByte)

def LcdWritePulseGuzuntyPi(spi, dataControlByte):
    LcdEnableWriteGuzuntyPi(spi, dataControlByte)
    time.sleep(WritePulseLength)
    LcdDisableWriteGuzuntyPi(spi, dataControlByte)

def LcdSelectInstructionRegisterGuzuntyPi(spi, dataControlByte):
    SelectInstructionRegisterAndMask = 0xfe # 0b 1111 1110
    dataControlByte = dataControlByte & SelectInstructionRegisterAndMask
    LcdWriteDataControlByteGuzuntyPi(spi, dataControlByte)

def LcdSelectDataRegisterGuzuntyPi(spi, dataControlByte):
    SelectDataRegisterOrMask = 0x01 # 0b 0000 0001
    dataControlByte = dataControlByte | SelectDataRegisterOrMask
    LcdWriteDataControlByteGuzuntyPi(spi, dataControlByte)

def LcdWriteDataNibbleGuzuntyPi(spi, dataNibbleByte, registerType, operationDelay):
    dataControlByte = dataNibbleByte << 2
    if (registerType == InstructionRegister):
       LcdSelectInstructionRegisterGuzuntyPi(spi, dataControlByte)
    #elif (registerType == DataRegister):
    else:
       LcdSelectDataRegisterGuzuntyPi(spi, dataControlByte) 
       #print "dataControlByte = ", hex(dataControlByte)  
       #pdb.set_trace()  

    LcdDisableWriteGuzuntyPi(spi, dataControlByte)
    LcdWriteDataControlByteGuzuntyPi(spi, dataControlByte)
    LcdWritePulseGuzuntyPi(spi, dataControlByte) # perhaps include it in LcdWriteDataByteGuzuntyPi()
    time.sleep(operationDelay)

#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 LcdMoveCursorGuzuntyPi(spi, 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
    LcdWriteDataByteFourBitModeGuzuntyPi(spi, cursorByte, InstructionRegister)

def LcdWriteDataByteFourBitModeGuzuntyPi(spi, dataByte, registerType):
    upperNibble = dataByte >> 4
    lowerNibble = dataByte & 0x0f
    LcdWriteCharTwoNibblesGuzuntyPi(spi, upperNibble, lowerNibble, registerType)

def LcdWriteCharTwoNibblesGuzuntyPi(spi, upperNibble, lowerNibble, registerType):
    LcdWriteDataNibbleGuzuntyPi(spi, upperNibble, registerType, ShortOperationDelay) 
    LcdWriteDataNibbleGuzuntyPi(spi, lowerNibble, registerType, ShortOperationDelay)

def LcdWriteCharStringGuzuntyPi(spi, string):
    for asciiChar in (string):
        LcdWriteCharFourBitModeGuzuntyPi(spi, asciiChar)

def LcdWriteCharFourBitModeGuzuntyPi(spi, asciiChar):
    charUpperNibble = ord(asciiChar) >> 4
    charLowerNibble = ord(asciiChar) & 0x0f
    LcdWriteCharTwoNibblesGuzuntyPi(spi, charUpperNibble, charLowerNibble, DataRegister)

def TestLcdGuzuntyPi():

    spiGuzuntyPi = spidev.SpiDev() 
    spiGuzuntyPi.open(0, 0)     

    LcdConfigurationGuzuntyPi(spiGuzuntyPi)
    
    LcdMoveCursorGuzuntyPi(spiGuzuntyPi, 1, 1)
    #pdb.set_trace()

    #LcdMoveCursorGuzuntyPi(spiGuzuntyPi, 2, 1)
    #pdb.set_trace()

    #LcdWriteCharFourBitModeGuzuntyPi(spiGuzuntyPi, "A")
    #pdb.set_trace()

    #LcdWriteCharFourBitModeGuzuntyPi(spiGuzuntyPi, "A")
    #pdb.set_trace()

    #LcdWriteCharFourBitModeGuzuntyPi(spiGuzuntyPi, "A")
    #pdb.set_trace()

    LcdWriteCharStringGuzuntyPi(spiGuzuntyPi, ProgramTitle1)

    spiGuzuntyPi.close() 

# * Guzunty Pi 8p8i core PWM functions ****************************************

# * Robot Arm Functions *

# * Set 1 Robot Arm pwm duty cycle
def SetPwmDutyCycle0(spiChannel, stateVector, index):
     spiChannel.xfer2([index, stateVector[index]])
     
# * Set all Robot Arm pwm duty cycles
def SetRobotArmPwmDutyCycle(spiChannel, robotArmStateVector):
    for i in range(len(robotArmStateVector)):
SetPwmDutyCycle0(spiChannel, robotArmStateVector, i)

# * Robot Neck Functions *

# * Set 1 Robot Neck pwm duty cycle
def SetPwmDutyCycle4(spiChannel, stateVector, index):
     spiChannel.xfer2([index + 4, stateVector[index]]) # *** Robot Neck PWM start at 4
     
# * Set all Robot Neck pwm duty cycles
def SetRobotNeckPwmDutyCycle(spiChannel, robotNeckStateVector):
    for i in range(len(robotNeckStateVector)):
SetPwmDutyCycle4(spiChannel, robotNeckStateVector, i)

# * Move servo *
def MoveServo(spiChannel, PwmNumber, PwmDutyCycle):
    spiChannel.xfer2([PwmNumber, PwmDutyCycle])

    
# *****************************************************************************
# *****************************************************************************
# *****************************************************************************
# *****************************************************************************
# *****************************************************************************
# * Test reset, all left most, right most, middle, shoulder only *

def TestGuzuntyPiRobotArm():

    PrintDoubleSpaceLine("*** Start testing Guzunty Pi Robot Arm ***")   

    # Open SPI Channel 0,0 for Guzunty Pi
    spiGuzuntyPi = spidev.SpiDev() 
    # spiGuzuntyPi.open(0, 0)  
    spiGuzuntyPi.open(0, 1)

    # * PWM duty cycle calibration *
    # CwMax = 13 # for 14.4 KHz clock
    # CcwMax = 30

    CwMax = 6 # for 7.2 KHz clock
    CcwMax = 16
    RightMost = CwMax
    LeftMost = CcwMax
    Middle = (RightMost + LeftMost) / 2

    OpenFully = RightMost
    CloseFully = LeftMost
    OpenHalf = Middle

    UpMost = RightMost
    DownMost = LeftMost

    # * Robot arm parts numbering *
    Shoulder = 0
    Elbow = 1
    Wrist = 2
    Hand = 3

    # * Robot neck parts numbering *
    NeckLeftRight = 4
    NeckUpDown = 5

    # * Bobot head parts numbering *
    HeadEye = 6
    HeadEar = 7

    # * Robot arm state vector constants *
    RobotArmStateVectorReset        = [RightMost, Middle, RightMost, LeftMost]
    RobotArmStateVectorAllMiddle    = [Middle, Middle, Middle, Middle, Middle]
    RobotArmStateVectorAllRightMost = [RightMost, RightMost, RightMost, RightMost]
    RobotArmStateVectorAllLeftMost  = [LeftMost, LeftMost, LeftMost, LeftMost]

    # * Robot neck state vector constants *
    RobotNeckStateVectorReset              = [Middle, Middle]
    RobotNeckStateVectorAllMiddle          = [Middle, Middle]
    RobotNeckStateVectorAllRightMostUpMost = [RightMost, UpMost]
    RobotNeckStateVectorAllLeftMostDownMost = [LeftMost, DownMost]

    # *** Robot arm functions *************************************************

    # Robot arm state vector variable *
    robotArmStateVector = [] 

    # * Robot arm reset *
    robotArmStateVector = RobotArmStateVectorReset[:]
    SetRobotArmPwmDutyCycle(spiGuzuntyPi, robotArmStateVector)
    time.sleep(1)

    # * All servos left most *
    robotArmStateVector = RobotArmStateVectorAllLeftMost[:]
    SetRobotArmPwmDutyCycle(spiGuzuntyPi, robotArmStateVector)
    time.sleep(1)

    # * All servos right most *
    robotArmStateVector = RobotArmStateVectorAllRightMost[:]
    SetRobotArmPwmDutyCycle(spiGuzuntyPi, robotArmStateVector)
    time.sleep(1)

    # * All servos middle *
    robotArmStateVector = RobotArmStateVectorAllMiddle[:]
    SetRobotArmPwmDutyCycle(spiGuzuntyPi, robotArmStateVector)
    time.sleep(1)

    # * Robot arm reset *
    robotArmStateVector = RobotArmStateVectorReset[:]
    SetRobotArmPwmDutyCycle(spiGuzuntyPi, robotArmStateVector)
    time.sleep(1)

    # * Shoulder middle *    
    robotArmStateVector[Shoulder] = Middle
    SetPwmDutyCycle0(spiGuzuntyPi, robotArmStateVector, Shoulder)
    time.sleep(1)

    # * Shoulder right most *    
    robotArmStateVector[Shoulder] = RightMost
    SetPwmDutyCycle0(spiGuzuntyPi, robotArmStateVector, Shoulder)
    time.sleep(1)

    # * Shoulder left most *    
    robotArmStateVector[Shoulder] = LeftMost
    SetPwmDutyCycle0(spiGuzuntyPi, robotArmStateVector, Shoulder)
    time.sleep(1)

    # * Hand fully open *    
    robotArmStateVector[Hand] = OpenFully
    SetPwmDutyCycle0(spiGuzuntyPi, robotArmStateVector, Hand)
    time.sleep(1)

    # * Hand fully close *    
    robotArmStateVector[Hand] = CloseFully
    SetPwmDutyCycle0(spiGuzuntyPi, robotArmStateVector, Hand)
    time.sleep(1)

    # * Hand half open *    
    robotArmStateVector[Hand] = OpenHalf
    SetPwmDutyCycle0(spiGuzuntyPi, robotArmStateVector, Hand)
    time.sleep(1)

    # * Robot arm reset *
    robotArmStateVector = RobotArmStateVectorReset[:]
    SetRobotArmPwmDutyCycle(spiGuzuntyPi, robotArmStateVector)
    time.sleep(1)

    # *** Robot Neck Functions ************************************************

    # Robot neck state vector variable *
    robotNeckStateVector = [] 

    # * Robot neck reset *
    robotNeckStateVector = RobotNeckStateVectorReset[:]
    SetRobotNeckPwmDutyCycle(spiGuzuntyPi, robotNeckStateVector)
    time.sleep(1)

    # * All servos right most up most *
    robotNeckStateVector = RobotNeckStateVectorAllRightMostUpMost[:]
    SetRobotNeckPwmDutyCycle(spiGuzuntyPi, robotNeckStateVector)
    time.sleep(1)

    # * All servos left most down most *
    robotNeckStateVector = RobotNeckStateVectorAllLeftMostDownMost[:]
    SetRobotNeckPwmDutyCycle(spiGuzuntyPi, robotNeckStateVector)
    time.sleep(1)

    spiGuzuntyPi.close() 

    PrintDoubleSpaceLine("*** Stop testing Guzunty Pi ***") 

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

# * Test one servo *

def TestGuzuntyPiServo(testTime):

    PrintDoubleSpaceLine("*** Start testing Guzunty Pi Servo ***")   

    spiGuzuntyPi = spidev.SpiDev() 
    spiGuzuntyPi.open(0, 1)

    for i in range(2, 32):
       print "Duty cycle = ", i 
       MoveServo(spiGuzuntyPi, PwmNumber = 6, PwmDutyCycle = i)
       time.sleep(testTime)
  
    spiGuzuntyPi.close() 

    PrintDoubleSpaceLine("*** Stop testing Guzunty Pi ***") 

# *** 7-segment LED functions *********************************************

def TestSetGpClk0WiringPi1(frequency):
    io = wiringpi.GPIO(wiringpi.GPIO.WPI_MODE_PINS)

    # io.pinMode(7, io.OUTPUT)
    # io.digitalWrite(7, io.HIGH)

    io.pinMode(7, io.GPIO_CLOCK)  # not working !!!
    io.gpioClockSet (7, frequency) # not working !!!

def TestSetGpClk0WiringPi2(frequency):
    wiringpi2.wiringPiSetupSys
    wiringpi2.pinMode(7, 1)       # OK
    wiringpi2.digitalWrite(7, 1)  # OK

    # wiringpi2.pinMode(7, GPIO_CLOCK)     # not working !!!
    # wiringpi2.digitalWrite(7, frequency) # not working !!!

def TestSetGpClk0Subprocess(frequency):

    returnCode = call(["gpio mode 7 clock", "gpio clock 7 960000"], shell = True)
    time.sleep(4)
    returnCode = call("gpio clock 7 4800000", shell=True)
    time.sleep(4)    
    returnCode = call("gpio clock 7 2400000", shell=True)
    time.sleep(4)
    returnCode = call("gpio clock 7 15000", shell=True)
    time.sleep(4)

# *** Backup 2013apr17 ***
#def TestLedDriverCore():
#
#    # *** Set GPCLK0 pin to 4.68 KHz ***
#    returnCode = call(["gpio mode 7 clock"], shell = True)
#    returnCode = call("gpio clock 7 4688", shell=True)

#    # *** Setup SPI channel ***
#    spiGuzuntyPi = spidev.SpiDev() 
#    spiGuzuntyPi.open(0, 0)  

#    # *** Load 4 7-segment patterns to digit pattern list ***
#    SevenSegments0 = 0x3f
#    SevenSegments1 = 0x06
#    SevenSegments2 = 0x5b
#    SevenSegments3 = 0x4f
#    sevenSegmentsList = [SevenSegments0, SevenSegments1, SevenSegments2, SevenSegments3]

#    sevenSegmentsListAllHigh = [0xff, 0xff, 0xff, 0xff]
#    sevenSegmentsListAllLow  = [0x00, 0x00, 0x00, 0x00]

#    # *** Write digit pattern list to Guzunty Pi ***
#    for i in range(10):
#        spiGuzuntyPi.xfer2(sevenSegmentsListAllHigh)
#        time.sleep(1)
#        spiGuzuntyPi.xfer2(sevenSegmentsListAllLow)
#        time.sleep(1)

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

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

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

#def LcdWriteCharFourBitModeMcp23017(registerBaseAddress, dataControlByte, asciiChar, registerType):
#    charUpperNibble = ord(asciiChar) >> 4
#    charLowerNibble = ord(asciiChar) & 0x0f
#    LcdWriteCharTwoNibblesMcp23017(registerBaseAddress, dataControlByte, charUpperNibble, charLowerNibble, 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 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

# *** Old test functions ******************************************************

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

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

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

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

#TestSpiReadBit(ButtonPin, count = 4) # *** OK ***
#TestSpiReadBit(SpiMisoPin, count = 4) # !!! Not working !!!
#TestSpiReadBit(RxdPin, count = 4) # !!! Not working !!

#PrintDoubleSpaceLine("*** Start Test x ***")
#TestRfm12bSet2Mhz()
#WaitSeconds(2)
#TestRfm12bSet1Mhz()
#WaitSeconds(2)
#PrintDoubleSpaceLine("*** Stop Test x ***")

# Rfm12bTest2(spiChannelNumber = 0, spiDeviceNumber = 0)
# Rfm12bTest3b(spiChannelNumber = 0, spiDeviceNumber = 0)
# Rfm12bTest3d(spiChannelNumber = 0, spiDeviceNumber0 = 0, spiDeviceNumber1 = 1)

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

# TestToggleMcp23017PortAoutput(registerBaseAddress = 0x20, toggleTime = 0.5, toggleCount = 100)
# TestReadMcp23017PortBinput(registerBaseAddress = 0x20, readTime = 2, readCount = 100)
# TestKeypadMcp23017(registerBaseAddress = 0x20, checkPressingKeyMode = PollRowStatusNibble, keyCount = 4)
# TestLcdMcp23017(registerBaseAddress = 0x20)

# TestBuzzer() # beep system buzzer 4 times
# TestLED() # blink system LED 4 tmes
# TestButtonEchoBuzzer() # echo system buton with system buzzer 4 times
# TestButtonEchoLED() # echo system button with system LED 4 times
# TestGpioPin(oPin = TxdPin, toggleTime = 0.5, toggleCount = 10)

# TestLcdMcp23017(registerBaseAddress = 0x21)  
# TestKeypadMcp23017(registerBaseAddress = 0x21, checkPressingKeyMode = PollRowStatusNibble, keyCount = 4)
# TestRfm12bExtMpuClock(spiChannelNumber = 0, Rfm12bSpiDeviceNumber0 = 0, Rfm12bSpiDeviceNumber1 = 1)

# TestJtagPins(toggleTime = 1, testCount = 2)

# TestLcdMcp23017(i2cRegisterBaseAddress = 0x21) #I2C1
# TestKeypadMcp23017PollingMode(i2cRegisterBaseAddress = 0x21) #I2C1
# TestDualRfm12bExtMpuClock(spiChannelNumber = 0) # SPI0

# TestRpioServo()
# SetClock(channelNumber = 0, frequency = 100, rpiPin = RPiGpclk00, timeSeconds = 4)
# SetClock(channelNumber = 0, frequency = 100, rpiPin = RPiPcm)
# SetRpiGpClk0Frequency64Hz()
# SetRpiGpClk0(frequencyHz = 100, waitTime = 60)

# TestLcdMcp23017(i2cRegisterBaseAddress = 0x21) #I2C1
# TestKeypadMcp23017PollingMode(i2cRegisterBaseAddress = 0x21) #I2C1
# TestDualRfm12bExtMpuClock(spiChannelNumber = 0) # SPI0
# TestRpioServo()

# TestKeypadMcp23017PollingMode(i2cRegisterBaseAddress = 0x21) 
# TestGuzuntyPiSteppingMotor(windingTuple0 = (1,3), windingTuple1 = (2, 4), stepCount = 50, stepTime = 0.05)
# TestKeypadGuzuntyPi()
# TestLcdGuzuntyPi()
# TestRpioServo()
# TestRpioClock()
# ResetGuzuntyPiRobotArm()
# TestGuzuntyPiSteppingMotor(windingTuple0 = (1,3), windingTuple1 = (2, 4), stepCount = 500, stepTime = 0.05)
# TestMoveGuzuntyPiRobotArm()

#TestLcdMcp23017(i2cRegisterBaseAddress = 0x21)
#TestGuzuntyPiSteppingMotor(windingTuple0 = (1,3), windingTuple1 = (2, 4), stepCount = 50, stepTime = 0.05)
#TestKeypadGuzuntyPi()
#TestGuzuntyPiServo(2)
#TestGuzuntyPiRobotArm()

# TestSetGpClk0WiringPi1(480000) # not working
# TestSetGpClk0WiringPi2(480000) # not working

# TestSetGpClk0Subprocess(frequency = 9600000)

# TestFourDigitSevenSegmentLedModule()

# *** 2013apr16 ***
#TestBuzzer()
#TestLED()
#TestButtonEchoBuzzer()
#TestButtonEchoLED()
#TestDualRfm12bExtMpuClock(spiChannelNumber = 0) # SPI0

#TestRxdEchoTxD()
#TestKeypadMcp23017PollingMode(i2cRegisterBaseAddress = 0x21)
#TestLcdMcp23017(i2cRegisterBaseAddress = 0x21)

# *** Current test function ***************************************************

def SetRpiPin7GpClk0Frequency4688Hz():
    returnCode = call(["gpio mode 7 clock"], shell = True)
    returnCode = call("gpio clock 7 4688", shell=True)

def TestGuzuntyPiLedDriver(chipEnableNumber):

    SetRpiPin7GpClk0Frequency4688Hz()

    # *** Setup SPI channel ***
    spiGuzuntyPi = spidev.SpiDev() 

    if (chipEnableNumber == 0):
        spiGuzuntyPi.open(0, 0)  

    if (chipEnableNumber == 1):
        spiGuzuntyPi.open(0, 1) 

    # *** 7 segment patterns ***
    #PatternDigit0     = ~0x3f
    #PatternDigit1     = ~0x06
    #PatternDigit2     = ~0x5b
    #PatternDigit3     = ~0x4f
    #PatternSpace      = 0xff
    #PatternAllOn      = 0x00

    PatternBigG       = 0x3d
    PatternSmallG     = 0x6f
    PatternSmallO     = 0x5c
    PatternSmallD     = 0x5e

    PatternDigit0     = 0x3f
    PatternDigit1     = 0x06
    PatternDigit2     = 0x5b
    PatternDigit3     = 0x4f
    PatternSpace      = ~0xff
    PatternAllOn      = ~0x00

    PatternBigG       = 0x3d
    PatternSmallG     = 0x6f
    PatternSmallO     = 0x5c
    PatternSmallD     = 0x5e

    PatternListAllOff = [PatternSpace, PatternSpace, PatternSpace, PatternSpace]
    PatternListAllOn  = [PatternAllOn, PatternAllOn, PatternAllOn, PatternAllOn]
    PatternList0123   = [PatternDigit0, PatternDigit1, PatternDigit2, PatternDigit3]
    PatternListGood   = [PatternBigG, PatternSmallO, PatternSmallO, PatternSmallD]     
  
    # *** time digits ***
    timeString =  time.asctime(time.localtime(time.time()))
    print timeString

    print "Hour   digit 1 = ", timeString[11:12]
    print "Hour   digit 2 = ", timeString[12:13]
    print "Minute digit 1 = ", timeString[14:15]
    print "Minute digit 2 = ", timeString[15:16]
    print "Second digit 1 = ", timeString[17:18]
    print "Second digit 2 = ", timeString[18:19]

    hourDoubleDigit = [timeString[11:12], timeString[12:13]]
    print "hourDoubleDigit 0 = ", hourDoubleDigit[0]
    print "hourDoubleDigit 1 = ", hourDoubleDigit[1]

    minuteDoubleDigit = [timeString[14:15], timeString[15:16]]
    print "minuteDoubleDigit 0 = ", minuteDoubleDigit[0]
    print "minuteDoubleDigit 1 = ", minuteDoubleDigit[1]

    secondDoubleDigit = [timeString[17:18], timeString[18:19]]
    print "secondDoubleDigit 0 = ", secondDoubleDigit[0]
    print "secondDoubleDigit 1 = ", secondDoubleDigit[1]

    timeHexDigit = [hourDoubleDigit, minuteDoubleDigit, secondDoubleDigit]
    print "hour = ", (timeHexDigit[0])[0], (timeHexDigit[0])[1]

    timeHexDigit = [minuteDoubleDigit, minuteDoubleDigit, secondDoubleDigit]
    print "minute = ", (timeHexDigit[0])[0], (timeHexDigit[0])[1]

    timeHexDigit = [secondDoubleDigit, minuteDoubleDigit, secondDoubleDigit]
    print "second = ", (timeHexDigit[0])[0], (timeHexDigit[0])[1]

    # *** 4 digit 7-segment LED displays ***    

    spiGuzuntyPi.xfer2(PatternListAllOff)    
    time.sleep(1)

    spiGuzuntyPi.xfer2(PatternListAllOn)    
    time.sleep(1)

    spiGuzuntyPi.xfer2(PatternList0123)    
    time.sleep(2)

    spiGuzuntyPi.xfer2(PatternListGood)    
    time.sleep(2)

    # SevenSegmentPatternNumbers = [~0x3f, ~0x06, ~0x5b, ~0x4f, ~0x66, ~0x6d, ~0x7d, ~0x07, ~0x7f, ~0x6f]
    SevenSegmentPatternNumbers = [0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f]
    
    decimalPoint = 0

    for i in range(2):
        timeString =  time.asctime(time.localtime(time.time()))

        hourDoubleDigit = [timeString[11:12], timeString[12:13]]
        minuteDoubleDigit = [timeString[14:15], timeString[15:16]]
secondDoubleDigit = [timeString[17:18], timeString[18:19]]

        minuteDigit0 = SevenSegmentPatternNumbers[int(minuteDoubleDigit[0])]
        minuteDigit1 = SevenSegmentPatternNumbers[int(minuteDoubleDigit[1])]
        secondDigit0 = SevenSegmentPatternNumbers[int(secondDoubleDigit[0])]
        secondDigit1 = SevenSegmentPatternNumbers[int(secondDoubleDigit[1])]

        if (decimalPoint == 0):
   #minuteDigit1 &= ~0x80
   minuteDigit1 |= 0x80
   decimalPoint = 1
        else:
            decimalPoint = 0

        minuteSecondQuadDigitList = [minuteDigit0, minuteDigit1, secondDigit0, secondDigit1]
        
spiGuzuntyPi.xfer2(minuteSecondQuadDigitList)    
        
time.sleep(1)


def TestMcp3201():

    PrintDoubleSpaceLine("*** Start testing MCP3201 ADC ***")   

    spiGuzuntyPi = spidev.SpiDev() 
    spiGuzuntyPi.open(0, 0)  

    DummyDoubleByteList = [0x00, 0x00]
    adcOutputDoubleByteList = [0x55, 0x55]
    adcOutputDoubleByteList = spiGuzuntyPi.xfer2(DummyDoubleByteList) 

    PrintEightBitPattern("ADC output byte 1 = ", adcOutputDoubleByteList[0])
    PrintEightBitPattern("ADC output byte 2 = ", adcOutputDoubleByteList[1])

    adcDecimalValue = (adcOutputDoubleByteList[1] >> 1) + (adcOutputDoubleByteList[0] * (2 ** 7))

    # adcAnalogVoltage = (float(adcDecimalValue) / 4096) * 4.10 # without half voltage divider
    adcAnalogVoltage = ((float(adcDecimalValue) / 4096) * 4.10) * 2 # with half voltage divider
    
    print "Analog voltage = ", adcAnalogVoltage

    spiGuzuntyPi.close() 

    PrintDoubleSpaceLine("*** Stop testing MCP3201 ***") 
    
def Test25Lc256():

    PrintDoubleSpaceLine("*** Start testing 25LC256 ***")   

    # *** Set up SPI channel ***  
    # import spidev # WiringPi Python wrapper
    # spidev.max_speed_hz = 10000 # seems no effect, clock always 2 MHz !!!
    spiEeprom = spidev.SpiDev() 
    spiEeprom.open(0, 0)  

    # *** 25LC256 instructions ***
    EepromCommandWriteStatusRegister = 0x01
    EepromcCommandWrite              = 0x02 
    EePromCommandRead                = 0x03 
    EepromCommandWriteLatchDisable   = 0x04
    EepromCommandReadStatusRegister  = 0x05
    EePromCommandWriteLatchEnable    = 0x06 

    # *** 25LC256 addresses ***
    #EepromStartAddress0= 0x0000
    #EepromStartAddressUpper0 = 0x00
    #EepromStartAddressLower0 = 0x00

    EepromStartAddress1= 0x0300
    EepromStartAddressUpper1 = 0x03
    EepromStartAddressLower1 = 0x00

    # *** SPI variables and constants ***

    writeSpiDataList = [0x00]
    readSpiDataList  = [0x00, 0x00, 0x00, 0x00, 0x00]

    DummyDataByte = 0x00
    TestDataByte55  = 0x55
    TestDataByteAa  = 0xaa

    WriteProtectNone        = 0x00 # -
    WriteProtectUpperFourth = 0x04 # 0x6000 to 0x7fff
    WriteProtectUpperHalf   = 0x08 # 0x4000 to 0x7fff
    WriteProtectAll         = 0x0c # 0x0000 to 0x7fff

    FiveMilliSeconds = 0.005
  
    # *** Enable Write Enable Latch ***
    # while True:

    # ********************************************************
    # spiEeprom.xfer2([EePromCommandWriteLatchEnable])     

    #SpiWrite(spiEeprom.xfer2, [EePromCommandWriteLatchEnable])
    SpiWrite(spiEeprom, [EePromCommandWriteLatchEnable])

    time.sleep(FiveMilliSeconds)

    # *** Read Status Register ***
    readSpiDataList = spiEeprom.xfer2([EepromCommandReadStatusRegister, DummyDataByte]) 
    PrintDoubleSpaceLine("*** Checking WEL bit after Write Enable ***")  
    PrintEightBitPattern("Read back byte 0 = ", readSpiDataList[0])
    PrintEightBitPattern("Read back byte 1 = ", readSpiDataList[1])
    time.sleep(FiveMilliSeconds)

    # *** Disable Write Enable Latch ***
    spiEeprom.xfer2([EepromCommandWriteLatchDisable]) 
    time.sleep(FiveMilliSeconds)

    # *** Check WEL (Write Enable Latch) bit (Bit 1) ***
    readInByteList = spiEeprom.xfer2([EepromCommandReadStatusRegister, DummyDataByte]) 
    PrintDoubleSpaceLine("*** Checking WEL bit after Write Disable ***")  
    PrintEightBitPattern("Read back byte 0 = ", readInByteList[0])
    PrintEightBitPattern("Read back byte 1 = ", readInByteList[1])
    time.sleep(0.005)

    # *** Write Enable ***
    spiEeprom.xfer2([EePromCommandWriteLatchEnable]) 
    time.sleep(0.005)

    # *** Check WEL (Write Enable Latch) bit (Bit 1) ***
    readInByteList = spiEeprom.xfer2([EepromCommandReadStatusRegister, DummyDataByte]) 
    PrintDoubleSpaceLine("*** Checking WEL bit after Write Enable ***")  
    PrintEightBitPattern("Read back byte 0 = ", readInByteList[0])
    PrintEightBitPattern("Read back byte 1 = ", readInByteList[1])
    time.sleep(0.005)

    # *** Write protect upper fourth ****
    spiEeprom.xfer2([EepromCommandWriteStatusRegister, WriteProtectUpperFourth])
    time.sleep(0.005)

    # *** Check Status Register ***
    readInByteList = spiEeprom.xfer2([EepromCommandReadStatusRegister, DummyDataByte]) 
    PrintDoubleSpaceLine("*** Checking Write Protect Bits ***")  
    PrintEightBitPattern("Read back byte 0 = ", readInByteList[0])
    PrintEightBitPattern("Read back byte 1 = ", readInByteList[1])
    time.sleep(0.005)

    # *** Write Enable ***
    spiEeprom.xfer2([EePromCommandWriteLatchEnable]) 
    time.sleep(0.005)

    # *** Write protect none ****
    spiEeprom.xfer2([EepromCommandWriteStatusRegister, WriteProtectNone])
    time.sleep(0.005)

    # *** Check Status Register ***
    readInByteList = spiEeprom.xfer2([EepromCommandReadStatusRegister, DummyDataByte]) 
    PrintDoubleSpaceLine("*** Checking Write Protect Bits ***")  
    PrintEightBitPattern("Read back byte 0 = ", readInByteList[0])
    PrintEightBitPattern("Read back byte 1 = ", readInByteList[1])
    time.sleep(0.005)

    # *** Write Enable ***
    spiEeprom.xfer2([EePromCommandWriteLatchEnable]) 
    time.sleep(0.005)

    # *** Write 2 data bytes at address 0x0300 ****

    # while True:
    spiEeprom.xfer2([EepromcCommandWrite, EepromStartAddressUpper1, EepromStartAddressLower1, TestDataByte55, TestDataByte55]) 

    # *** Check WIP (Write In Progress) bit (Bit 0) ***
    readInByteList = spiEeprom.xfer2([EepromCommandReadStatusRegister, DummyDataByte]) 
    PrintDoubleSpaceLine("*** Checking WIP bit immediately after writing 1 byte without waiting 5 mS ***")  
    PrintEightBitPattern("Read back byte 0 = ", readInByteList[0])
    PrintEightBitPattern("Read back byte 1 = ", readInByteList[1]) 
    time.sleep(0.005)

    # *** Check WIP (Write In Progress) bit (Bit 0) ***
    readInByteList = spiEeprom.xfer2([EepromCommandReadStatusRegister, DummyDataByte]) 
    PrintDoubleSpaceLine("*** Checking WIP bit again, after waiting 5 mS ***")  
    PrintEightBitPattern("Read back byte 0 = ", readInByteList[0])
    PrintEightBitPattern("Read back byte 1 = ", readInByteList[1])

    # *** Enable Write Enable Latch ***
    spiEeprom.xfer2([EePromCommandWriteLatchEnable]) 
    time.sleep(FiveMilliSeconds)

    # *** Read back data at address 0x0000 ***
    
    time.sleep(1)

    #while True:
    readSpiDataList = spiEeprom.xfer2([EePromCommandRead, EepromStartAddressUpper1, EepromStartAddressLower1, DummyDataByte, DummyDataByte])   
    
    PrintDoubleSpaceLine("*** Read back data byte written ***")  
    PrintEightBitPattern("Read back byte 0 = ", readSpiDataList[0])
    PrintEightBitPattern("Read back byte 1 = ", readSpiDataList[1])
    PrintEightBitPattern("Read back byte 2 = ", readSpiDataList[2])
    PrintEightBitPattern("Read back byte 3 = ", readSpiDataList[3]) 
    PrintEightBitPattern("Read back byte 4 = ", readSpiDataList[4]) 

    # *** Write Enable ***
    spiEeprom.xfer2([EePromCommandWriteLatchEnable]) 
    time.sleep(0.005)

    # *** Write 2 data bytes at address 0x0000 ****
    spiEeprom.xfer2([EepromcCommandWrite, EepromStartAddressUpper1, EepromStartAddressLower1, TestDataByteAa, TestDataByteAa]) 

    # *** Check WIP (Write In Progress) bit (Bit 0) ***
    readInByteList = spiEeprom.xfer2([EepromCommandReadStatusRegister, DummyDataByte]) 
    PrintDoubleSpaceLine("*** Checking WIP bit immediately after writing 1 byte without waiting 5 mS ***")  
    PrintEightBitPattern("Read back byte 0 = ", readInByteList[0])
    PrintEightBitPattern("Read back byte 1 = ", readInByteList[1]) 
    time.sleep(0.005)

    # *** Check WIP (Write In Progress) bit (Bit 0) ***
    readInByteList = spiEeprom.xfer2([EepromCommandReadStatusRegister, DummyDataByte]) 
    PrintDoubleSpaceLine("*** Checking WIP bit again, after waiting 5 mS ***")  
    PrintEightBitPattern("Read back byte 0 = ", readInByteList[0])
    PrintEightBitPattern("Read back byte 1 = ", readInByteList[1])

    # *** Enable Write Enable Latch ***
    spiEeprom.xfer2([EePromCommandWriteLatchEnable]) 
    time.sleep(FiveMilliSeconds)

    # *** Read back data at address 0x0000 ***
    
    readSpiDataList = spiEeprom.xfer2([EePromCommandRead, EepromStartAddressUpper1, EepromStartAddressLower1, DummyDataByte, DummyDataByte])   
    
    PrintDoubleSpaceLine("*** Read back data byte written ***")  
    PrintEightBitPattern("Read back byte 0 = ", readSpiDataList[0])
    PrintEightBitPattern("Read back byte 1 = ", readSpiDataList[1])
    PrintEightBitPattern("Read back byte 2 = ", readSpiDataList[2])
    PrintEightBitPattern("Read back byte 3 = ", readSpiDataList[3]) 
    PrintEightBitPattern("Read back byte 4 = ", readSpiDataList[4])    
    
    # *** Close SPI channel ***
    spiEeprom.close() 

    #while True:
    #    pass

    PrintDoubleSpaceLine("*** Stop testing write/read 25LC256 ***") 


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

# *** Mcp23x17 Functions v1.0 tlfong01 2013may14 ***

# *** Constants and variables ***

# * Port type *

PortA = 0
PortB = 1

# * Register address array index *

InputOutputDirectionIndex = 0
InputPolarityIndex = 1
InterruptEnableIndex = 2
DefaultValueIndex = 3  
CompareModeIndex = 4
BankInterruptPinModeIndex = 5
PullUpIndex = 6
InterruptFlagIndex = 7
InterruptCaptureIndex = 8
PortStatusIndex = 9
OutputLatchIndex = 10

# * Data constant bytes *

All8pinOutput = 0x00
All8pinInput = 0xff
All8bitOne = 0xff
All8bitZero = 0x00
All8bitPullUp = 0xff

# * Direction setting bytes *

Upper8bitOneLower8bitZero  = 0xff00
Upper8bitZeroLower8bitOne  = 0x00ff
Upper4bitOneLower4bitZero  = 0xf0
Upper4bitZeroLower4bitOne  = 0x0f

Upper8pinInputLower8pinOutput  = 0xff00
Upper8pinOutputLower8pinInput  = 0x00ff
Upper4pinInputLower4pinOutput  = 0xf0
Upper4pinOutputLower4pinInput  = 0x0f

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

Mcp23s17WriteCommand = 0b01000000

Mcp23s17ChipSubAddress0 = 0b000


# *** SPI write/read functions ***

def SpiWrite(spiChannel, spiWriteList):
    spiChannel.xfer2(spiWriteList)

def SpiRead(spiChannelWriteFunction, writeList):
    readList = spiChannelWriteFunction(writeList)

# *** Mcp23s17 Functions ******************************************************

def GetRegisterAddress(registerAddressArray, registerIndex, portType):
    if (portType == PortA):
       registerAddress = registerAddressArray[registerIndex]
    if (portType == PortB):
       registerAddress = registerAddressArray[registerIndex + 11]
    return registerAddress

def WriteDataByteMcp23s17(spiChannel, spiChipSubAddress3bit, registerAddressArray, registerIndex, portType, dataByte):
    mcp23s17RegisterAddress = GetRegisterAddress(registerAddressArray, registerIndex, portType)
    mcp23s17WriteCommand = Mcp23s17WriteCommand | (mcp23s17RegisterAddress << 1)
    mcp23s17WriteDataByte = dataByte
    spiWriteList = [mcp23s17WriteCommand, mcp23s17RegisterAddress, mcp23s17WriteDataByte]
    SpiWrite(spiChannel, spiWriteList)

def SetupMcp23s17BothPortAll8PinOutput(spiChannel, spiChipSubAddress3bit):
    WriteDataByteMcp23s17(spiChannel, spiChipSubAddress3bit, RegisterAddressArrayMcp23s17, InputOutputDirectionIndex, PortA, All8pinOutput)
    WriteDataByteMcp23s17(spiChannel, spiChipSubAddress3bit, RegisterAddressArrayMcp23s17, InputOutputDirectionIndex, PortB, All8pinOutput)

def TestMcp23s1701(spiChannelNumber, spiChipEnableNumber1bit, spiChipSubAddress3bit):
    # *** Set up SPI channel ***
    spiChannelMcp23s17 = spidev.SpiDev() 
    spiChannelMcp23s17.open(spiChannelNumber, spiChipEnableNumber1bit)  

    # *** Set up MCP23s17 both ports all 8 pins as output ***
    SetupMcp23s17BothPortAll8PinOutput(spiChannelMcp23s17, spiChipSubAddress3bit)

    # *** Write 0x55 to Port A and 0xaa to Port B ***
    # To be written, ...

    # *** Description ***
    # Open SPI channel for MCP23S17 at sub address 0b000
    # Assign MCP23s17 Port A and Port B all 8 pins as output
    # Write 0x55 to Port A and 0xaa to Port B

    # *** Sample call to test MCP23S17 with SPI channel 0, CE0, chip address 0
    # TestMcp23s17(spiChannelNumber = 0, spiChipEnableNumber1bit = 0, spiChipSubAddress3bit = 0)

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

# *** Main program ***********************************************************

StartProgram()

#TestBuzzer()
#TestLED()
#TestButtonEchoBuzzer()
#TestButtonEchoLED()

# TestGuzuntyPiLedDriver(chipEnableNumber = 0)
# TestGuzuntyPiLedDriver(chipEnableNumber = 1)
# TestMcp3201()

TestGuzuntyPiLedDriver(chipEnableNumber = 1) # CE1
# Test25Lc256() # CE0

TestMcp23s1701(spiChannelNumber = 0, spiChipEnableNumber1bit = 0, spiChipSubAddress3bit = 0)

StopProgram()

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

# .END

No comments:

Post a Comment