Pages

Thursday, January 03, 2013

MCP23017 IO Expander - Test 02


Now I have refined the blink LED functions to be more flexible, but then I found that I could not access PortB registers.  So a lot of debugging work now.

I found the following problems.

1. It appears that the registers of the port B side could not be accessed.  One possible problem is that I am using global variables which might not be accessed inside a function, but I am not too sure.

2. Now I have three sets of register addresses, one for MCP23008, two for MCP23017, and things are getting messy.

Possible solutions.

1. Using arrays instead of single variables to represent the register addresses.

2. Using object oriented programming, on top of functional programming approaches to make the function more readable and easier to handle.


# *****************************************************************************
# *** tb1a.py ***
# Program  - Test MCP23017 
# Version  - 1.a
# Date     - 2012nov30
# Update   - 2013jan03
# Author   - tlfong01
# File     - tb1a_2013jan03.py
# Blog     - http://tlfong01.blogspot.hk/ 
# Purpose  - test basics of Raspberry Pi GPIO
# Hardware - Raspberry Pi Model B Revsion 2.0 [2012oct/nov/dec]
# Software - Raspbian Wheezy (2012sep15), Python 2.7.3 
#            GPIO 0.4.1a http://pypi.python.org/pypi/RPi.GPIO/0.4.1a
# Wiring   - RPi Board Numbering
#              P1-02 5V, P1-04 5V, P1-06 Gnd
#              P1-01 3V3, P1-03 I2C SDA1, P1-05 I2C SCL1
#              P1-08 UART TxD (MCP23017 Reset)
#              P1-10 UART RxD (MCP23017 INTB)
#              P1-12 RPi GPIO_GEN1 (BCM18) LED (P1-12 > LED > 330R > Gnd)
#              P1-14 Gnd
#              P1-16 GPIO_GEN4 - Buzzer, 3V3 5mA (P1-16 > Buzzer > Gnd) 
#              P1-18 GPIO_GEN5 Button (3V3 > 10K > Contact 1/2 > 330R > Gnd)
#              P1-20 Gnd
#              P1-22 GPIO_GEN6 - MCP23008 INT / MCP23017 INTA              

# *****************************************************************************
# *** Import Python modules ***

import smbus 
import sys
import RPi.GPIO as GPIO
from time import sleep
import select

# *****************************************************************************
# *** Configure RPi GPIO pins ***

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

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

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

# * IO device pins assignment *
LEDpin = RPiGPIOgen1
BuzzerPin = RPiGPIOgen4 
ButtonPin = RPiGPIOgen5
InterruptPin = RPiGPIOgen6
TxDpin = RPiTxD
RxDpin = RPiRxD

# * IO pins list *
OutputPinList = [LEDpin, BuzzerPin, TxDpin]
InputPinWithNoPullUpList = [ButtonPin, RxDpin]
InputPinWithPullUpList = [InterruptPin]


# *****************************************************************************
# *** Constants ***

# * General - counts, time periods, nibble types *

TWO_TIMES = 2
FOUR_TIMES = 4
EIGHT_TIMES = 8
TEN_TIMES = 10
TWENTY_TIMES = 20
FIFTY_TIMES = 50
ONE_HUNDRED_TIMES = 100
TWO_HUNDRED_TIMES = 200
FOUR_HUNDRED_TIMES = 400

TWENTY_MILLI_SECONDS = 0.02
FIFTY_MILLI_SECONDS = 0.05
TENTH_SECOND = 0.1
QUARTER_SECOND = 0.25
HALF_SECOND = 0.5
ONE_SECOND = 1
ONE_AND_HALF_SECONDS = 1.5
TWO_SECONDS = 2
ON_TIME = TENTH_SECOND
OFF_TIME = QUARTER_SECOND
BUTTON_DEBOUNCING_TIME = QUARTER_SECOND
TEST_TIME = 0.05

LOW_NIBBLE = 0
HIGH_NIBBLE = 1
BOTH_NIBBLE = 2 # full byte of 8 bits

# * Device constants *

# LED and buzzer states
OFF = False
ON = True

# Button states
PRESSED = False
RELEASED = True


# *****************************************************************************
# *** RPi GPIO functions ***

# Reference - Raspberry Pi Python GPIO Version - GPIO 0.4.1a 
# http://pypi.python.org/pypi/RPi.GPIO/0.4.1a

# RPi Model B 5V0 max current draw: 50 mA 
# RPi Model B 3V3 max current draw: 300 mA 
# GPIO maximum current draw per pin: 17mA source, 12mA sink

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

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

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

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

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

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

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

def setupGPIO(): # set up GPIO pins
     setupGPIOpins(OutputPinList, InputPinWithNoPullUpList, InputPinWithPullUpList )

# * pulse and echo functions * 
def pulsePin(oPin, onTime, offTime): # blink LED or beep buzzer
    writeOutputPin(oPin, ON)
    sleep(onTime)
    writeOutputPin(oPin, OFF)    
    sleep(offTime)

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

def togglePin(oPin, toggleTime): # toggle pin
    writeOutputPin(oPin, ON)
    sleep(toggleTime)
    writeOutputPin(oPin, OFF)    
    sleep(toggleTime)

# * Test GPIO functions *
def testBuzzer(): # beep 4 times
    setupGPIO()
    for i in range (FOUR_TIMES):
        pulsePin(BuzzerPin, ON_TIME, OFF_TIME)

def testLED(): # blink 8 times
    setupGPIO()
    for i in range (EIGHT_TIMES): 
        pulsePin(LEDpin, ON_TIME, OFF_TIME)

def testButtonEchoBuzzer(): #
    setupGPIO()
    for i in range (TEN_TIMES):
        echoPin(ButtonPin, BuzzerPin)          

def testButtonEchoLED(): #
    setupGPIO()
    for i in range (TEN_TIMES):
        echoPin(ButtonPin, LEDpin)

def testToggleTxDpin():
    while True:
        togglePin(TxDpin, TWO_SECONDS)

# *****************************************************************************
# *** Beep functions ***
def beep(count):
    for i in range(count):
        pulsePin(BuzzerPin, ON_TIME, OFF_TIME)
    
def startBeep():
    beep(TWO_TIMES)
    sleep(1)

def endBeep():
    beep(FOUR_TIMES)

def oneBeep():
    beep(1)

# *****************************************************************************
# *** IO Expander MCP23008 / MCP23017 ***

# * Bash script using i2cTools's i2cset command to toggle GPIO pins *
#!/bin/bash
# i2cset -y 1 0x20 0x00 0x00
# count=0
# while [ $count -lt 10 ];
# do
#  i2cset -y 1 0x20 0x0a 0x00
#  sleep 0.5
#  i2cset -y 1 0x20 0x0a 0xff
#  sleep 0.5
#  let count++
# done

# To run i2c-X commands in user mode: sudo chmod 666 /dev/i2c-X
# sudo chmod 666 /dev/i2c-1

# * Setup SMBus *

I2C_BUS_NUMBER = 1 # P1-03 = SDA1, P1-05 = SCL1
smBus1 = smbus.SMBus(I2C_BUS_NUMBER) # global variable, cannot be set by a function

# * MCP23008 device addresses *

MCP23008_1_REGISTER_BASE_ADDRESS = 0x20
MCP23008_2_REGISTER_BASE_ADDRESS = 0x21

# * MCP23008 register addresses *

IO_DIRECTION_REGISTER = IODIR_REG = 0x00
INTERRUPT_ON_CHANGE_REGISTER = GPINTEN = 0x02
DEFAULT_COMPARE_VALUE_REGISTER = DEFVAL = 0x03
INTERRUPT_CONTROL_REGISTER = INTCON = 0x04
IO_CONTROL_REGISTER = IOCON = 0x05
INTERRUPT_FLAG_REGISTER = INTF = 0x07
INTERRUPT_CAPTURE_REGISTER = INTCAP = 0x08
GPIO_REGISTER = GPIO_REG = 0x09
OUTPUT_LATCH_REGISTER = OLAT_REG = 0x0a

# * MCP23017 device addresses *

MCP23017_1_REGISTER_BASE_ADDRESS = 0x22
MCP23017_2_REGISTER_BASE_ADDRESS = 0x23

# * MCP23017 Bank number and Port Group *

BANK_MAP_0 = 0
BANK_MAP_1 = 1

PORT_GROUP_A = 0
PORT_GROUP_B = 1

# * MCP23017 register addresses *

IO_DIRECTION_REGISTER_A_BANK_MAP_0 = IODIRA_B0 = 0x00
IO_DIRECTION_REGISTER_B_BANK_MAP_0 = IODIRB_B0 = 0x01

INPUT_POLARITY_REGISTER_A_BANK_MAP_0 = IPOLA_B0 = 0x02
INPUT_POLARITY_REGISTER_B_BANK_MAP_0 = IPOLB_B0 = 0x03

INTERRUPT_ON_CHANGE_REGISTER_A_BANK_MAP_0 = GPINTENA_B0 = 0x04
INTERRUPT_ON_CHANGE_REGISTER_B_BANK_MAP_0 = GPINTENB_B0 = 0x05

DEFAULT_COMPARE_VALUE_REGISTER_A_BANK_MAP_0 = DEFVALA_B0 = 0x06
DEFAULT_COMPARE_VALUE_REGISTER_B_BANK_MAP_0 = DEFVALB_B0 = 0x07

INTERRUPT_CONTROL_REGISTER_A_BANK_MAP_0 = INTCONA_B0 = 0x08
INTERRUPT_CONTROL_REGISTER_B_BANK_MAP_0 = INTCONB_B0 = 0x09

IO_CONTROL_REGISTER_A_BANK_MAP_0 = IOCONA_B0 = 0x0a
IO_CONTROL_REGISTER_B_BANK_MAP_0 = IOCONB_B0 = 0x0b

PULL_UP_RESISTOR_CONFIG_REGISTER_A_BANK_MAP_0 = GPPUA_B0 = 0x0c
PULL_UP_RESISTOR_CONFIG_REGISTER_B_BANK_MAP_0 = GPPUB_B0 = 0x0d

INTERRUPT_FLAG_REGISTER_A_BANK_MAP_0 = INTFA_B0 = 0x0e
INTERRUPT_FLAG_REGISTER_B_BANK_MAP_0 = INTFB_B0 = 0x0f

INTERRUPT_CAPTURE_REGISTER_A_BANK_MAP_0 = INTCAPA_B0 = 0x10
INTERRUPT_CAPTURE_REGISTER_B_BANK_MAP_0 = INTCAPB_B0 = 0x11

GPIO_REGISTER_A_BANK_MAP_0 = GPIOA_B0 = 0x12
GPIO_REGISTER_B_BANK_MAP_0 = GPIOB_B0 = 0x13

OUTPUT_LATCH_REGISTER_A_BANK_MAP_0 = OLATA_B0 = 0x14
OUTPUT_LATCH_REGISTER_B_BANK_MAP_0 = OLATB_B0 = 0x15

IO_DIRECTION_REGISTER_A_BANK_MAP_1 = IODIRA_B1 = 0x00
INPUT_POLARITY_REGISTER_A_BANK_MAP_1 = IPOLA_B1 = 0x01
INTERRUPT_ON_CHANGE_REGISTER_A_BANK_MAP_1 = GPINTENA_B1 = 0x02
DEFAULT_COMPARE_VALUE_REGISTER_A_BANK_MAP_1 = DEFVALA_B1 = 0x03
INTERRUPT_CONTROL_REGISTER_A_BANK_MAP_1 = INTCONA_B1 = 0x04
IO_CONTROL_REGISTER_A_BANK_MAP_1 = IOCONA_B1 = 0x05
PULL_UP_RESISTOR_CONFIG_REGISTER_A_BANK_MAP_1 = GPPUA_B1 = 0x06
INTERRUPT_FLAG_REGISTER_A_BANK_MAP_1 = INTFA_B1 = 0x07
INTERRUPT_CAPTURE_REGISTER_A_BANK_MAP_1 = INTCAPA_B1 = 0x08
GPIO_REGISTER_A_BANK_MAP_1 = GPIOA_B1 = 0x09
#OUTPUT_LATCH_REGISTER_A_BANK_MAP_1 = OLATA_B1 = 0x0a
OUTPUT_LATCH_REGISTER_A_BANK_MAP_1 = 0x0a

IO_DIRECTION_REGISTER_B_BANK_MAP_1 = IODIRB_B1 = 0x10
INPUT_POLARITY_REGISTER_B_BANK_MAP_1 = IPOLB_B1 = 0x11
INTERRUPT_ON_CHANGE_REGISTER_B_BANK_MAP_1 = GPINTENB_B1 = 0x12
DEFAULT_COMPARE_VALUE_REGISTER_B_BANK_MAP_1 = DEFVALB_B1 = 0x13
INTERRUPT_CONTROL_REGISTER_B_BANK_MAP_1 = INTCONB_B1 = 0x14
IO_CONTROL_REGISTER_B_BANK_MAP_1 = IOCONB_B1 = 0x15
PULL_UP_RESISTOR_CONFIG_REGISTER_B_BANK_MAP_1 = GPPUB_B1 = 0x16
INTERRUPT_FLAG_REGISTER_B_BANK_MAP_1 = INTFB_B1 = 0x17
INTERRUPT_CAPTURE_REGISTER_B_BANK_MAP_1 = INTCAPB_B1 = 0x18
GPIO_REGISTER_B_BANK_MAP_1 = GPIOB_B1 = 0x19
#OUTPUT_LATCH_REGISTER_B_BANK_MAP_1 = OLATB_B1 = 0x1a
OUTPUT_LATCH_REGISTER_B_BANK_MAP_1 = 0x1a

# * MAP23017 Register bank setting *

def setBankNumberPortGroup(bankMapNumber, portGroupType):
    if bankMapNumber == BANK_MAP_0:
        print "BANK_MAP_0"
        if portGroupType == PORT_GROUP_A:
   IO_DIRECTION_REGISTER = IO_DIRECTION_REGISTER_A_BANK_MAP_0
   OUTPUT_LATCH_REGISTER = OUTPUT_LATCH_REGISTER_A_BANK_MAP_0
   GPIO_REGISTER = GPIO_REGISTER_A_BANK_MAP_0
   INTERRUPT_ON_CHANGE_REGISTER = INTERRUPT_ON_CHANGE_REGISTER_A_BANK_MAP_0
   DEFAULT_COMPARE_VALUE_REGISTER = DEFAULT_COMPARE_VALUE_REGISTER_A_BANK_MAP_0
   INTERRUPT_CONTROL_REGISTER = INTERRUPT_CONTROL_REGISTER_A_BANK_MAP_0
   IO_CONTROL_REGISTER = IO_CONTROL_REGISTER_A_BANK_MAP_0
   INTERRUPT_FLAG_REGISTER = INTERRUPT_FLAG_REGISTER_A_BANK_MAP_0
   INTERRUPT_CAPTURE_REGISTER = INTERRUPT_CAPTURE_REGISTER_A_BANK_MAP_0 
        else: # portGroupType == PORT_GROUP_B:
   
   IO_DIRECTION_REGISTER = IO_DIRECTION_REGISTER_B_BANK_MAP_0
   OUTPUT_LATCH_REGISTER = OUTPUT_LATCH_REGISTER_B_BANK_MAP_0
   GPIO_REGISTER = GPIO_REGISTER_B_BANK_MAP_0
   INTERRUPT_ON_CHANGE_REGISTER = INTERRUPT_ON_CHANGE_REGISTER_B_BANK_MAP_0
   DEFAULT_COMPARE_VALUE_REGISTER = DEFAULT_COMPARE_VALUE_REGISTER_B_BANK_MAP_0
   INTERRUPT_CONTROL_REGISTER = INTERRUPT_CONTROL_REGISTER_B_BANK_MAP_0
   IO_CONTROL_REGISTER = IO_CONTROL_REGISTER_B_BANK_MAP_0
   INTERRUPT_FLAG_REGISTER = INTERRUPT_FLAG_REGISTER_B_BANK_MAP_0
   INTERRUPT_CAPTURE_REGISTER = INTERRUPT_CAPTURE_REGISTER_B_BANK_MAP_0 

    else: # bankMapNumber == BANK_MAP_1:
        print "BANK_MAP_1"
        if portGroupType == PORT_GROUP_A:
            print "PORT_GROUP_A"
   IO_DIRECTION_REGISTER = IO_DIRECTION_REGISTER_A_BANK_MAP_1
   OUTPUT_LATCH_REGISTER = OUTPUT_LATCH_REGISTER_A_BANK_MAP_1
   GPIO_REGISTER = GPIO_REGISTER_A_BANK_MAP_1
   INTERRUPT_ON_CHANGE_REGISTER = INTERRUPT_ON_CHANGE_REGISTER_A_BANK_MAP_1
   DEFAULT_COMPARE_VALUE_REGISTER = DEFAULT_COMPARE_VALUE_REGISTER_A_BANK_MAP_1
   INTERRUPT_CONTROL_REGISTER = INTERRUPT_CONTROL_REGISTER_A_BANK_MAP_1
   IO_CONTROL_REGISTER = IO_CONTROL_REGISTER_A_BANK_MAP_1
   INTERRUPT_FLAG_REGISTER = INTERRUPT_FLAG_REGISTER_A_BANK_MAP_1
   INTERRUPT_CAPTURE_REGISTER = INTERRUPT_CAPTURE_REGISTER_A_BANK_MAP_1 
        else: # portGroupType == PORT_GROUP_B:
            print "PORT_GROUP_B"
   IO_DIRECTION_REGISTER = IO_DIRECTION_REGISTER_B_BANK_MAP_1
   OUTPUT_LATCH_REGISTER = OUTPUT_LATCH_REGISTER_B_BANK_MAP_1
   GPIO_REGISTER = GPIO_REGISTER_B_BANK_MAP_1
   INTERRUPT_ON_CHANGE_REGISTER = INTERRUPT_ON_CHANGE_REGISTER_B_BANK_MAP_1
   DEFAULT_COMPARE_VALUE_REGISTER = DEFAULT_COMPARE_VALUE_REGISTER_B_BANK_MAP_1
   INTERRUPT_CONTROL_REGISTER = INTERRUPT_CONTROL_REGISTER_B_BANK_MAP_1
   IO_CONTROL_REGISTER = IO_CONTROL_REGISTER_B_BANK_MAP_1
   INTERRUPT_FLAG_REGISTER = INTERRUPT_FLAG_REGISTER_B_BANK_MAP_1
   INTERRUPT_CAPTURE_REGISTER = INTERRUPT_CAPTURE_REGISTER_B_BANK_MAP_1 

# * Direction setting *

DIRECTION_BYTE_ALL_OUTPUT = 0x00
DIRECTION_BYTE_LOW_NIBBLE_OUTPUT_HIGH_NIBBLE_INPUT = 0xf0

# * Data pattern *

DATA_BYTE_ALL_ZERO = 0x00
DATA_BYTE_ALL_ONE = 0xff
DATA_BYTE_HIGH_NIBBLE_ONE_LOW_NIBBLE_ZERO = 0xf0
DATA_BYTE_HIGH_NIBBLE_ZERO_LOW_NIBBLE_ONE = 0x0f

# * Interrupt setting *

ENABLE_INTERRUPT_HIGH_NIBBLE = 0xf0
DEFAULT_COMPARE_VALUE_HIGH_NIBBLE = 0xf0
INTERRUPT_CONTROL_COMPARE_DEFAULT_HIGH_NIBBLE = 0xf0
INTERRUPT_PIN_PUSH_PULL_DRIVER_HIGH_ACTIVE = 0b00111010 # 0x3a, no auto add incre, no slew rate
INTERRUPT_PIN_OPEN_DRAIN = 0b00111000 # 0x38, no auto add incre, no slew rate

# * Setup IO direction *

def setIOxPinsAllOutput(registerBaseAddress): # set up all 8 IOx pins as output
    smBus1.write_byte_data(registerBaseAddress, IO_DIRECTION_REGISTER, DIRECTION_BYTE_ALL_OUTPUT)

def setIOxPinsLowNibbleOutputHighNibbleInput(registerBaseAddress): # set low nibble output, high nibble input
    smBus1.write_byte_data(registerBaseAddress, IO_DIRECTION_REGISTER, DIRECTION_BYTE_LOW_NIBBLE_OUTPUT_HIGH_NIBBLE_INPUT)

# * Writing data *

def writeIOxPinsAllLow(registerBaseAddress): # write zeros to all 8 IOx output pins
    smBus1.write_byte_data(registerBaseAddress, OUTPUT_LATCH_REGISTER, DATA_BYTE_ALL_ZERO)

def writeIOxPinsAllHigh(registerBaseAddress): #write ones to all 8 IOx output pins
    smBus1.write_byte_data(registerBaseAddress, OUTPUT_LATCH_REGISTER, DATA_BYTE_ALL_ONE)   

def writeIOxPins(registerBaseAddress, dataHexString): # write 8 bit hex string to IOx output pins
    smBus1.write_byte_data(registerBaseAddress, OUTPUT_LATCH_REGISTER, dataHexString) 

# * Reading data *

def readIOxPinsByte(registerBaseAddress): # read 8 bit hex data from GPIO register (= 8 output pin)
    hexByte = smBus1.read_byte_data(registerBaseAddress, GPIO_REGISTER)
    return hexByte

def readIOxPinsHighNibble(registerBaseAddress): # read high nibble from GPIO resister (= upper nible of output pins)
    hexByte = readIOxPinsByte(registerBaseAddress)
    hexNibble = hexByte >> 4
    return hexNibble

# * Interrupt setting *

def enableInterruptOnChangeHighNibble(registerBaseAddress): # enable high nibble interrupt on change
    smBus1.write_byte_data(registerBaseAddress, INTERRUPT_ON_CHANGE_REGISTER, ENABLE_INTERRUPT_HIGH_NIBBLE) 

def setInterruptOnChangeDefaultHighNibble(registerBaseAddress): # set high nibble default compare values
    smBus1.write_byte_data(registerBaseAddress, DEFAULT_COMPARE_VALUE_REGISTER, DEFAULT_COMPARE_VALUE_HIGH_NIBBLE) 

def setInterruptOnChangeCompareDefaultHighNibble(registerBaseAddress): # enable high nibble compare default
    smBus1.write_byte_data(registerBaseAddress, INTERRUPT_CONTROL_REGISTER, INTERRUPT_CONTROL_COMPARE_DEFAULT_HIGH_NIBBLE)

def setHighNibbleCompareDefault(registerBaseAddress): # set high nibble compare default
    enableInterruptOnChangeHighNibble(registerBaseAddress)
    setInterruptOnChangeDefaultHighNibble(registerBaseAddress)
    setInterruptOnChangeCompareDefaultHighNibble(registerBaseAddress)

def setInterruptPinPushPullHighActive(registerBaseAddress):  # interrupt pin push pull high active, no auto add inc, no slew rate
    smBus1.write_byte_data(registerBaseAddress, IO_CONTROL_REGISTER, INTERRUPT_PIN_PUSH_PULL_DRIVER_HIGH_ACTIVE) 

def setInterruptPinOpenDrain(registerBaseAddress): # interrupt pin open drain, no auto add inc, no slew rate
    smBus1.write_byte_data(registerBaseAddress, IO_CONTROL_REGISTER, INTERRUPT_PIN_OPEN_DRAIN) 


# * Interrupt reading *

def readInterruptFlagPinsHighNibble(registerBaseAddress): # read high nibble interrupt flag register
    hexByte = smBus1.read_byte_data(registerBaseAddress, INTERRUPT_FLAG_REGISTER)    
    hexNibble = hexByte >> 4
    return hexNibble   
    
def readInterruptCapturePinsHighNibble(registerBaseAddress): # read high nibble interrupt capture register
    hexByte = smBus1.read_byte_data(registerBaseAddress, INTERRUPT_CAPTURE_REGISTER)    
    hexNibble = hexByte >> 4
    return hexNibble  

# * Blink LED *

def switchOffAll8LEDs(RegisterBaseAddress): 
    writeIOxPinsAllLow(RegisterBaseAddress)

def switchOnfAll8LEDs(RegisterBaseAddress): 
    writeIOxPinsAllHigh(RegisterBaseAddress)

def blink8LEDs(RegisterBaseAddress, OnTime, OffTime, Count): 
    setIOxPinsAllOutput(RegisterBaseAddress)
    for i in range(Count):
        writeIOxPinsAllHigh(RegisterBaseAddress)
        sleep(OnTime)
        writeIOxPinsAllLow(RegisterBaseAddress)
        sleep(OffTime)

def blink4LEDs(RegisterBaseAddress, OnTime, OffTime, Count): 
    setIOxPinsLowNibbleOutputHighNibbleInput(RegisterBaseAddress)
    for i in range(Count):
        writeIOxPinsAllHigh(RegisterBaseAddress)
        sleep(OnTime)
        writeIOxPinsAllLow(RegisterBaseAddress)
        sleep(OffTime)

# * Testing *

def blink8LEDsMCP230080x20(): 
    blink8LEDs(MCP23008_1_REGISTER_BASE_ADDRESS, HALF_SECOND, ONE_SECOND, FOUR_TIMES) 

def blink4LEDsMCP230080x21(): 
    blink4LEDs(MCP23008_2_REGISTER_BASE_ADDRESS, HALF_SECOND, ONE_SECOND, FOUR_TIMES) 

def blink8LEDsMCP23017(deviceAddress, bankNumber, portGroup): 
    setBankNumberPortGroup(bankNumber, portGroup)
    blink8LEDs(deviceAddress, HALF_SECOND, HALF_SECOND, TEN_TIMES) 

def testInterruptPinFallingEdgeDetection(): 
    GPIO.cleanup() # set all input pins no pull up, disable all interutp detection setting
    setupGPIO()   
    GPIO.set_low_event(InterruptPin) # set up low level detection 

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

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

# *****************************************************************************
# *** Unipolar Stepping Motor 28BYJ48 etc ***

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

# * Move unipolar stepping motor *
def writeSequence_13_24(RegisterBaseAddress, NibbleType, StepCount, StepTime): # move motor using 13-24 sequence  
    setIOxPinsAllOutput(RegisterBaseAddress)
    if NibbleType == LOW_NIBBLE:
hexString1 =  convert2PinToHex(1, 3)
  hexString2 =  convert2PinToHex(2, 4)
    else:
hexString1 = convert2PinToHighNibble(1, 3)
hexString2 = convert2PinToHighNibble(2, 4)
    for i in range(StepCount):
        writeIOxPins(RegisterBaseAddress, hexString1)
    sleep(StepTime)
    writeIOxPins(RegisterBaseAddress, hexString2)
        sleep(StepTime)
def writeSequence_13_23_24_14(RegisterBaseAddress, NibbleType, StepCount, StepTime): #move motor using 13-23-24-14 sequence 
    setIOxPinsAllOutput(RegisterBaseAddress)
    if NibbleType == LOW_NIBBLE:
    motorWindingActivationPatternArray = (0x05, 0x06, 0x0a, 0x09)
    else:
motorWindingActivationPatternArray = (0x50, 0x60, 0xa0, 0x90)
    for i in range(StepCount):
for pattern in motorWindingActivationPatternArray:
   writeIOxPins(RegisterBaseAddress, pattern)
   sleep(StepTime)
def move2MotorsUsingMCP23008_1(): # move 2 motors one after another 
oneBeep()
writeSequence_13_24(MCP23008_1_REGISTER_BASE_ADDRESS, LOW_NIBBLE, TWENTY_TIMES, FIFTY_MILLI_SECONDS)
oneBeep()
writeSequence_13_24(MCP23008_1_REGISTER_BASE_ADDRESS, HIGH_NIBBLE, ONE_HUNDRED_TIMES, TWENTY_MILLI_SECONDS)
oneBeep()
writeSequence_13_23_24_14(MCP23008_1_REGISTER_BASE_ADDRESS, LOW_NIBBLE, TWENTY_TIMES, FIFTY_MILLI_SECONDS)
oneBeep()
writeSequence_13_23_24_14(MCP23008_1_REGISTER_BASE_ADDRESS, HIGH_NIBBLE, ONE_HUNDRED_TIMES, TWENTY_MILLI_SECONDS)
def move1MotorUsingMCP23008_2(RegisterBaseAddress, OnTime, OffTime, BlinkCount, CycleCount, StepTime ): # move motor on MCP23008 #2
    setIOxPinsLowNibbleOutputHighNibbleInput(RegisterBaseAddress)
    oneBeep
    for i in range(BlinkCount):
        writeIOxPinsAllHigh(RegisterBaseAddress)
        sleep(OnTime)
        writeIOxPinsAllLow(RegisterBaseAddress)
        sleep(OffTime)
    oneBeep()
    writeSequence_13_24(RegisterBaseAddress, LOW_NIBBLE, CycleCount, StepTime)


# *****************************************************************************
# *** Keypad *** 

# Keypad scanning procedure version 1.0
# 1. Set 3 column ports GP0, GP1, GP2 as output (GP3 don't care)
# 2. Set 4 row ports GP4, GP5, GP6, GP7 as input
# 3. Write LOW to all row ports
# 4. Wait for interrupt 
# 5. Pressing any key would cause an interrupt
# 6. When interrupt occurs check which row reads LOW
# 7. Write LOW only to column GP0, if row port still LOW, then this column 0
#    has a key pressed, if not, check if column 1, if not column 3.
# 8. Calculate the key using the column and row data.

# Read MCP23008 GP4 ~ GP7
def testReadGP4567(): # read keypad input rows 0~3
    RegisterBaseAddress = MCP23008_2_REGISTER_BASE_ADDRESS
    setIOxPinsLowNibbleOutputHighNibbleInput(RegisterBaseAddress)
    writeIOxPinsAllLow(RegisterBaseAddress)
    nibble = 0xf0
    while True: 
        oneBeep()
        sleep(1)
        nibble = smBus1.read_byte_data(RegisterBaseAddress, GPIO_REGISTER)
print "GP4 to GP7 = ", hex(nibble)   

## * keypad base address and smBus setting *

keypadRegisterBaseAddress = MCP23008_2_REGISTER_BASE_ADDRESS
keypadSmBus = smBus1

# * Keypad columns numbering *
ColumnOutputPin0 = 0
ColumnOutputPin1 = 1
ColumnOutputPin2 = 2
ColumnOutputPinTuple = ColumnOutputPin0, ColumnOutputPin1, ColumnOutputPin2
# * Keypad row numbering *
RowInputPin0 = 0
RowInputPin1 = 1
RowInputPin2 = 2
RowInputPin3 = 3
RowInputPinTuple = RowInputPin0, RowInputPin1, RowInputPin2, RowInputPin2
# * Keypad matrix numbering *
KeyPadMatrixTuple = ColumnOutputPinTuple, RowInputPinTuple
# * Write keypad column data patterns *
ColumnAllLow = 0b000
Column0Low = 0b110
Column1Low = 0b101
Column2Low = 0b011
ColumnLowTuple = Column0Low, Column1Low, Column2Low

# * Read keypad data patterns *
RowAllHigh = 0b1111
Row0Low = 0b1110
Row1Low = 0b1101
Row2Low = 0b1011
Row3Low = 0b0111
RowLowTuple = Row0Low, Row1Low, Row2Low, Row3Low

# * Set, write, read keypad pins *
def assignKeypadReadWritePins(keypadRegisterBaseAddress):
    setIOxPinsLowNibbleOutputHighNibbleInput(keypadRegisterBaseAddress)

def writeColumnPins(keypadRegisterBaseAddress, columnPattern):    
    hexString = 0xff & columnPattern
    writeIOxPins(keypadRegisterBaseAddress, hexString)

def readRowPins():    
    hexNibble = readIOxPinsHighNibble(keypadRegisterBaseAddress)
    return hexNibble

# * test polling/interrupt keypad *
def scanKeypad(registerBaseAddress):   
    assignKeypadReadWritePins(registerBaseAddress)
    setInterruptPinOpenDrain(registerBaseAddress) # *** interrupt pin open drain !!!
    
    writeColumnPins(registerBaseAddress, ColumnAllLow)

    setHighNibbleCompareDefault(registerBaseAddress) # *** testing interrupt ***

    # Loop until one key pressed
    keyPressed = False
    while True:
        nibble = readRowPins()
        if nibble == Row0Low:
   row = 0
            keyPressed = True  
            print "Row 0 key pressed"
        elif  nibble == Row1Low:
   row = 1
            keyPressed = True 
            print "Row 1 key pressed"
        elif  nibble == Row2Low:
   row = 2
            keyPressed = True 
            print "Row 2 key pressed"     
        elif  nibble == Row3Low:
   row = 3
            keyPressed = True 
            print "Row 3 key pressed"
        else:
   row = 9
            #print "No key or more than one key pressed"                

        if keyPressed == True:
   # Check which column the key is pressed
     column = 9
   writeColumnPins(registerBaseAddress, Column0Low)
   nibble = readRowPins()     
   if nibble != RowAllHigh:
       column = 0
                print "Column 0 key pressed"
            else:
                writeColumnPins(registerBaseAddress, Column1Low)
                nibble = readRowPins()     
                if nibble != RowAllHigh:
                    column = 1
                    print "Column 1 key pressed"
                else:
                    writeColumnPins(registerBaseAddress, Column2Low)
                    nibble = readRowPins()     
                    if nibble != RowAllHigh:
                        column = 2
                        print "Column 2 key pressed"
                    else:  
                        print "Error"

            # Calculate which key pressed

            keyNumber = (row * 3) + (column + 1) 

            print "Key pressed              = ", '{0:8}'.format(str(keyNumber).zfill(2).rjust(10))

            # *******************************************************************************
   # checking interrupt
            # *******************************************************************************
   # Check which column interrupt 

            interruptFlagNibble = readInterruptFlagPinsHighNibble(registerBaseAddress)
            interruptCaptureNibble = readInterruptCapturePinsHighNibble(registerBaseAddress)

            print "Interrupt flag nibble    = ", '{0:8}'.format((str(bin(interruptFlagNibble)).zfill(4)).rjust(10))
            print "Interrupt capture nibble = ", '{0:8}'.format((str(bin(interruptCaptureNibble)).zfill(4)).rjust(10))

   keyPressed = False
   sleep(1)
        else:
            pass

def testScanKeypad():
    scanKeypad(MCP23008_2_REGISTER_BASE_ADDRESS)

# *****************************************************************************
# * Old test functions *
# testLEDandBuzzer()
# testButton() !!! not working !!!
# blink8LEDsIOx1()
# blink4LEDsIOx2()
# testConvert1PinToHex()
# blink4LEDsIOx2()
# move1MotorUsingMCP23008_2(MCP23008_2_REGISTER_BASE_ADDRESS, HALF_SECOND, 
#                           ONE_SECOND, FOUR_TIMES, TWENTY_TIMES, ONE_SECOND)

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

# *** Old test functions ***

#setupGPIO()
#startBeep()
#blink4LEDsIOx2() # OK 2012dec14
#testReadGP4567() # OK 2012dec14
#testScanKeypad()
#endBeep()

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

# *** Current test functions ***

#testBuzzer() # beep buzzer 4 times
#testLED() # blink LED 8 tmes
#testButtonEchoBuzzer() # echo buton with buzzer 10 times
#testButtonEchoLED() # echo buton with LED 10 times
#testToggleTxDpin() # toggle TxD pin every 2 seconds

setupGPIO()
startBeep()

#blink8LEDsMCP23017(MCP23017_1_REGISTER_BASE_ADDRESS, BANK_MAP_1, PORT_GROUP_A)
blink8LEDsMCP23017(MCP23017_1_REGISTER_BASE_ADDRESS, BANK_MAP_1, PORT_GROUP_B)

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

.END

No comments:

Post a Comment