Pages

Friday, February 22, 2013

Raspberry Pi postmortem notes


Now I have connected the power to the I2C and SPI hardware and tested the new SPI again, along with the old I2C things.  The buzzer beeps and the LCD display properly.  So the basic hardware and software up to the SCI are more or less OK.

Next step is zoom into SCI and see if there is any possibility of damaging the RPi hardware when testing SPI.

This time I have removed the opaque PP sheet over the glass plate, so that any white smoke coming out from the RPi under the I2C/SCI hardware can be immediately detected and power off by hand, if not too late.

During testing, I made two mistakes.

First I connected the 3V3 power with the wrong polarity.  Luckily the I2C connected with the wrong polarity 3V3 is not damaged and still works OK.  So next time I should be more careful.  I also see the good idea of splitting the power supplies, in case of any mistake in power supply, only part and not all the hardware are burnt out.

Second, I forgot to connect one of the two I2C IO expander.  It is nice that Python gives a good error message as below.


  File "fpl429.py", line 467, in WriteDataByteMcp23008
    smBus1.write_byte_data(registerBaseAddress, addressOffset, dataByte)
IOError: [Errno 5] Input/output error


.END


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

# 0. Contents ******************************************************************
#
#  1. Program title
#  2. Python imports
#  3. RPi GPIO pin assignment, MCP230xx register base assignment
#  4. Global constants
#  5. GPIO Functions
#  6. Debugging and documentation functions (beep, print bit/byte, message)
#  7. IO Expander Mcp23008, Mcp23017
#  8. Decimal keypad
#  9. LCD - LCD1602. LCD2004
# 10. Stepping motor - unlpolar steppers 28BYJ48/NPM-PF35/PX245
# 11. Demultiplexor
# 12. Old test functions
# 13. Main program

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

ProgramTitle1 = "SPI429 20130222a"
ProgramTitle2 = "FPL429 20130222a"

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

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

smBus1 = smbus.SMBus(1)

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

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

# System A I2C base address assignment *

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

# System B I2C base address assignment *

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

# * GPIO pin numbering *

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

# * RPi GPIO pin numbering *

# P1-02 5V, P1-04 5V, P1-06 Gnd
# P1-01 3V3, P1-03 I2C SDA1, P1-05 I2C SCL1
# P1-08 UART TxD (Mcp23017 Reset)
# P1-10 UART RxD (Mcp23017 INTB)
# P1-12 RPi GPIO_GEN1 (BCM18) LED (P1-12 > LED > 330R > Gnd)
# P1-14 Gnd
# P1-16 GPIO_GEN4 - Buzzer, 3V3 5mA (P1-16 > Buzzer > Gnd)
# P1-18 GPIO_GEN5 Button (3V3 > 10K > Contact 1/2 > 330R > Gnd)
# P1-20 Gnd
# P1-22 GPIO_GEN6 - Mcp23008 INT / Mcp23017 INTA  

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

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

# * SCI GPIO pins *

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

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

# * peripherals pins assignment *

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

# * GPIO input/output pins list *

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

# 4. Global constants *********************************************************

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

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

# * On/Off times *
OnTime = TenthSecond
OffTime = QuarterSecond
ButtonDebouncingTime = QuarterSecond
TestTime = FiftyMilliSeconds

OnTime = 0.1
OffTime = 0.25

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

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

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

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

# * Button states *
ButtonPressed = False
ButonReleased = True

# * Interrupt states *
Low = False
High = True

# * Setup, read/write GPIO pins *
setupOutputPin = lambda oPin: GPIO.setup(oPin, GPIO.OUT) # set GPIO pin as output

setupInputPinWithNoPullUp = lambda iPin: GPIO.setup(iPin, GPIO.IN, pull_up_down=GPIO.PUD_OFF) # set GPIO pin as input, no pull up

setupInputPinWithPullUp = lambda iPin: GPIO.setup(iPin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # set GPIO pin as input, with pull up

writeOutputPin = lambda oPin, oValue: GPIO.output(oPin, oValue) # write value to output pin

setupWriteOutputPin = lambda oPin, oValue: (setupOutputPin(oPin), writeOutputPin(oPin, oValue)) # set and write

readInputPin = lambda iPin: GPIO.input(ButtonPin) # read value from input pin

def SetupGPIOpins(outputPinList, inputPinWithNoPullUpList, inputPinWithPullUpList): # set up GPIO pins in InputPinList and OutputPinList
    for oPin in outputPinList:
       setupWriteOutputPin(oPin, Off)
    for iPin in inputPinWithNoPullUpList:
        setupInputPinWithNoPullUp(iPin)
    for iPin in inputPinWithPullUpList:
        setupInputPinWithPullUp(iPin)

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

# * pulse/echo/toggle functions *

def pulsePin(oPin, onTime, offTime): # blink LED or beep buzzer
    writeOutputPin(oPin, On)
    time.sleep(onTime)
    writeOutputPin(oPin, Off)  
    time.sleep(offTime)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

# * Data bit processing and printing *

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

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

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

def ConvertIntegerToEightPattern(integer):
    EightBitPattern = [""]
    for k in range(8):
        EightBitPattern = [i+j for i in ['0','1'] for j in EightBitPattern]
    return EightBitPattern[integer]

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

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

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

def StopBeep():
    Beep(2)

def OneBeep():
    Beep(1)

def FourBeeps():
    Beep(4)

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

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

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

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

# * Local constants *

# * Port type *
PortA = 0
PortB = 1

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

# * Data constant bytes *

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

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

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

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

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

# * Mcp23008 Functions *

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

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

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

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

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

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

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

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

# * Mcp23017 Functions *

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

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

def WriteDataByte(registerBaseAddress, registerAddressArray, dataRegisterIndex, portType, dataByte):
    if (portType == PortA) | (portType == PortA):
       addressOffset = registerAddressArray[dataRegisterIndex]
    if (portType == PortB):
       addressOffset = registerAddressArray[dataRegisterIndex + 11]
    smBus1.write_byte_data(registerBaseAddress, addressOffset, dataByte)

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

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

def ReadDataBit(registerBaseAddress, registerAddressArray, dataRegisterIndex, portType, bitNumber):
    if (portType == PortA):
       addressOffset = registerAddressArray[dataRegisterIndex]
    if (portType == PortB):
       addressOffset = registerAddressArray[dataRegisterIndex + 11]
    dataByte = smBus1.read_byte_data(registerBaseAddress, addressOffset)
    bitMask = 0x01 << bitNumber
    dataByte = dataByte & bitMask
    if (dataByte == 0):
      dataBit = 0
    else:
      dataBit = 1
    return dataBit

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

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

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

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

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

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

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

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

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

def ReadInterruptCaptureLowNibble(registerBaseAddress):
    interruptCaptureByte = ReadDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, InterruptCapture, PortB)  
    interruptCaptureNibble = interruptCaptureByte & 0x0f
    return interruptCaptureNibble

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

# * Test port toggling, poll interrupts *

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

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

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

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

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

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

# * System B test functions *

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

    registerBaseAddress = Mcp23017BaseAddressSystemB1
    SetupPortAoutputPortBinputPullUpMcp23017(registerBaseAddress)

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

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

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

    registerBaseAddress = Mcp23017BaseAddressSystemB1
    SetupPortAoutputPortBinputPullUpMcp23017(registerBaseAddress)

    for i in range(count):

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


def TestReadMcp23017SystemB1GPIObit(bitNumber, count):

    registerBaseAddress = Mcp23017BaseAddressSystemB1
    SetupPortAoutputPortBinputPullUpMcp23017(registerBaseAddress)

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


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

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

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

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

KeypadInterruptPinMcp23017 = RPiGPIOgen6

# * Local constants *

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

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

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

# * Setup/Clear/Check keypad interrupt *

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

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

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

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

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

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

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

# * Write columns *

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

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

# * Read rows *

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

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

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

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

    rowNumber = RowDataNibbleTuple.index(rowDataNibble)
    return rowNumber

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

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

# * Get keys *

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

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

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

    keyNumber = GetKeypadKeyNumberMcp23017(registerBaseAddress)
    return keyNumber

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

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

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

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

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

    ClearKeypadInterruptMcp23017(registerBaseAddress)

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

    time.sleep(0.1) # debouncing time

# * Test Mcp23017 Decimal keypad *

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

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

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

RegisterBaseAddress1 = 0x21
RegisterBaseAddress1 = 0x22

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

KeypadInterruptPinMcp23008 = RPiGPIOgen6

# * Local constants *

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

# * Polling modes *

NoPollJustGetKeyEverySecond = 0
PollRowStatusNibble = 1
PollMcp23017Interrupt = 2

# * Setup keypad *

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

# * Setup/Clear/Check keypad interrupt *

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

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

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

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

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

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

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

# * Write columns *

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

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

# * Read rows *

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

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

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

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

    rowNumber = RowDataNibbleTuple.index(rowDataNibble)
    return rowNumber

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

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

# * Get keys *

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

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

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

    keyNumber = GetKeypadKeyNumberMcp23008(registerBaseAddress1, registerBaseAddress2)
    return keyNumber

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

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

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

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

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

    ClearKeypadInterrupt(registerBaseAddress)

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

    time.sleep(0.1) # debouncing time

# * Test Decimal keypad Mcp23008 *

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

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

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

LcdRegisterBaseAddress = Mcp23008BaseAddress3 # 0x26

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

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

# LCD1602 software initialization procedure

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

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

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

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

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

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

# * Constant data bytes *

AllZeroDataByte = 0x00
AllOneDataByte = 0xff

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

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

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

# Register selection constant *

InstructionRegister = 0
DataRegister = 1

# * LCD1602A-1 control instructions *

# * Instruction codes *

EightBitInstructionMode = 0x3
FourBitInstructionMode  = 0x2

TwoLineFiveTimesEightDot1 = 0x2
TwoLineFiveTimesEightDot2 = 0x8

DisplayOnCursorOnCursorBlinkOff1 = 0x0
DisplayOnCursorOnCursorBlinkOff2 = 0xe

ClearDisplay1 = 0x0
ClearDisplay2 = 0x1

CursorIncrementDisplayNoShift1 = 0x0
CursorIncrementDisplayNoShift2 = 0x6

CursorHome1 = 0x0
CursorHome2 = 0x2

# * Instruction timing *

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

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

# * LCD functions Mcp23008 v4.15 2013feb15 *

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

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

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

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

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

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

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

    return dataControlByte

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

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

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

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

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

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

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

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

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

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

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

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

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

# * Test LCD1602 Mcp23008 *

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

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

# * LCD functions Mcp23017 v4.15 2013feb15 *

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

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

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

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

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

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

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

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

    return dataControlByte

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

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

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

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

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

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

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

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

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

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

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

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

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

# * Test LCD1602 functions *

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

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

# 8. Unipolar Stepping Motor 28BYJ48/NPM-PF35/PX245 ***************************

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

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

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

# * Move unipolar stepping motor *

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

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

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

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

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

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

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

def MoveTwoMotors(RegisterBaseAddress):
OneBeep()
WriteMotorWindingWaveSequence1324(Mcp23008BaseAddress1, LowNibble, TwentyTimes, FiftyMilliSeconds)
OneBeep()
WriteMotorWindingWaveSequence1324(Mcp23008BaseAddress1, HighNibble, TwentyTimes, FiftyMilliSeconds)
OneBeep()
WriteMotorWindingFullStepSequence13232414(Mcp23008BaseAddress1, LowNibble, TwentyTimes, OneHundredMilliSeconds)
OneBeep()
WriteMotorWindingFullStepSequence13232414(Mcp23008BaseAddress1, HighNibble, TwentyTimes, OneHundredMilliSeconds)

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

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

    if NibbleType == LowNibble:
hexString1 =  convert2PinToHex(1, 3)
  hexString2 =  convert2PinToHex(2, 4)
    else:
hexString1 = convert2PinToHighNibble(1, 3)
hexString2 = convert2PinToHighNibble(2, 4)

    for i in range(StepCount):
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, hexString1)
    time.sleep(StepTime)
        WriteDataByte(registerBaseAddress, RegisterAddressOffsetArrayBank0, OutputLatch, PortA, hexString2)
        time.sleep(StepTime)

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


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

DemuxRegisterBaseAddress = 0x20

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

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

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

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

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

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

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

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

    time.sleep(2)

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

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

    time.sleep(2)


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

# * SPI using bit banging *

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

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

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

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

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

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

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

# * Test Spi functions *

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

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

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

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

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

# * SPI using wiringPi with Python wrapping 2013feb21 *

# Question - $gpio load spi necessary?

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

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

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

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

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

# * Old tests *

# RPi System A Tests *

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

# RPi System B Tests *

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

# TestToggleMcp23017SystemB1() # toggle Mcp23017 System B1
# TestReadMcp23017SystemB1()

# * Rpi Mcp23017 tests *

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

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

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

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

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

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

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

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

StartProgram()

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

TestWiringPiSpi()

StopProgram()

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


pi@raspberrypi ~/python_programs/test_basic $ sudo python fpl429.py

*** Start Program - SPI429 20130222a ***

Traceback (most recent call last):
  File "fpl429.py", line 1836, in <module>
    TestLcdMcp23008(registerBaseAddress = 0x21)
  File "fpl429.py", line 1308, in TestLcdMcp23008
    LcdGpioSetupMcp23008(registerBaseAddress)
  File "fpl429.py", line 1180, in LcdGpioSetupMcp23008
    SetupPortAllOutputMcp23008(registerBaseAddress)
  File "fpl429.py", line 460, in SetupPortAllOutputMcp23008
    WriteDataByteMcp23008(registerBaseAddress, InputOutputDirection, AllOutputByte)
  File "fpl429.py", line 467, in WriteDataByteMcp23008
    smBus1.write_byte_data(registerBaseAddress, addressOffset, dataByte)
IOError: [Errno 5] Input/output error
pi@raspberrypi ~/python_programs/test_basic $

.END




3 comments:

Michael Horne said...

Hi.
What _does_ all this lot do? What's the eventual aim of the project? I'm intrigued!
--
Mike

tlfong01 said...

Ah, I hope to make a toy NASA Curiosity.

http://www.nasa.gov/mission_pages/msl/index.html

Michael Horne said...

Awesome :-)

Post a Comment