Pages

Thursday, January 03, 2013

MCP23017 IO Expander - Test 01

Now I am writing a Raspberry Pi Python function to blink the 8 LEDs connected to the IO port GPA0 to GPA7 of the IO Expander MCP23017 at device address 0x22.  

I will be using the register addresses with IOCON.BANK = 0.


I am using the following constants/variables/functions for the register addresses.


# * MCP23017 register addresses *

IO_DIRECTION_REGISTER_A_BANK_0 = IODIRA_B0 = 0x00
IO_DIRECTION_REGISTER_B_BANK_0 = IODIRB_B0 = 0x01

INPUT_POLARITY_REGISTER_A_BANK_0 = IPOLA_B0 = 0x02
INPUT_POLARITY_REGISTER_B_BANK_0 = IPOLB_B0 = 0x03

INTERRUPT_ON_CHANGE_REGISTER_A_BANK_0 = GPINTENA_B0 = 0x04
INTERRUPT_ON_CHANGE_REGISTER_B_BANK_0 = GPINTENB_B0 = 0x05

DEFAULT_COMPARE_VALUE_REGISTER_A_BANK_0 = DEFVALA_B0 = 0x06
DEFAULT_COMPARE_VALUE_REGISTER_B_BANK_0 = DEFVALB_B0 = 0x07

INTERRUPT_CONTROL_REGISTER_A_BANK_0 = INTCONA_B0 = 0x08
INTERRUPT_CONTROL_REGISTER_B_BANK_0 = INTCONB_B0 = 0x09

IO_CONTROL_REGISTER_A_BANK_0 = IOCONA_B0 = 0x0a
IO_CONTROL_REGISTER_B_BANK_0 = IOCONB_B0 = 0x0b

PULL_UP_RESISTOR_CONFIG_REGISTER_A_BANK_0 = GPPUA_B0 = 0x0c
PULL_UP_RESISTOR_CONFIG_REGISTER_B_BANK_0 = GPPUB_B0 = 0x0d

INTERRUPT_FLAG_REGISTER_A_BANK_0 = INTFA_B0 = 0x0e
INTERRUPT_FLAG_REGISTER_B_BANK_0 = INTFB_B0 = 0x0f

INTERRUPT_CAPTURE_REGISTER_A_BANK_0 = INTCAPA_B0 = 0x10
INTERRUPT_CAPTURE_REGISTER_B_BANK_0 = INTCAPB_B0 = 0x11

GPIO_REGISTER_A_BANK_0 = GPIOA_B0 = 0x12
GPIO_REGISTER_B_BANK_0 = GPIOB_B0 = 0x13

OUTPUT_LATCH_REGISTER_A_BANK_0 = OLATA_B0 = 0x14
OUTPUT_LATCH_REGISTER_B_BANK_0 = OLATB_B0 = 0x15

IO_DIRECTION_REGISTER_A_BANK_1 = IODIRA_B1 = 0x00
INPUT_POLARITY_REGISTER_A_BANK_1 = IPOLA_B1 = 0x01
INTERRUPT_ON_CHANGE_REGISTER_A_BANK_1 = GPINTENA_B1 = 0x02
DEFAULT_COMPARE_VALUE_REGISTER_A_BANK_1 = DEFVALA_B1 = 0x03
INTERRUPT_CONTROL_REGISTER_A_BANK_1 = INTCONA_B1 = 0x04
IO_CONTROL_REGISTER_A_BANK_1 = IOCONA_B1 = 0x05
PULL_UP_RESISTOR_CONFIG_REGISTER_A_BANK_1 = GPPUA_B1 = 0x06
INTERRUPT_FLAG_REGISTER_A_BANK_1 = INTFA_B1 = 0x07
INTERRUPT_CAPTURE_REGISTER_A_BANK_1 = INTCAPA_B1 = 0x08
GPIO_REGISTER_A_BANK_1 = GPIOA_B1 = 0x09
OUTPUT_LATCH_REGISTER_A_BANK_1 = OLATA_B1 = 0x0a

IO_DIRECTION_REGISTER_B_BANK_1 = IODIRB_B1 = 0x10
INPUT_POLARITY_REGISTER_B_BANK_1 = IPOLB_B1 = 0x11
INTERRUPT_ON_CHANGE_REGISTER_B_BANK_1 = GPINTENB_B1 = 0x12
DEFAULT_COMPARE_VALUE_REGISTER_B_BANK_1 = DEFVALB_B1 = 0x13
INTERRUPT_CONTROL_REGISTER_B_BANK_1 = INTCONB_B1 = 0x14
IO_CONTROL_REGISTER_B_BANK_1 = IOCONB_B1 = 0x15
PULL_UP_RESISTOR_CONFIG_REGISTER_B_BANK_1 = GPPUB_B1 = 0x16
INTERRUPT_FLAG_REGISTER_B_BANK_1 = INTFB_B1 = 0x17
INTERRUPT_CAPTURE_REGISTER_B_BANK_1 = INTCAPB_B1 = 0x18
GPIO_REGISTER_B_BANK_1 = GPIOB_B1 = 0x19
OUTPUT_LATCH_REGISTER_B_BANK_1 = OLATB_B1 = 0x1a

# * MAP23017 Register bank setting *

def setRegisterAddresses(bank):
    if bank == 0:
        IO_DIRECTION_REGISTER = IO_DIRECTION_REGISTER_A_BANK_0
OUTPUT_LATCH_REGISTER = OUTPUT_LATCH_REGISTER_A_BANK_0
GPIO_REGISTER = GPIO_REGISTER_A_BANK_0
INTERRUPT_ON_CHANGE_REGISTER = INTERRUPT_ON_CHANGE_REGISTER_A_BANK_0
DEFAULT_COMPARE_VALUE_REGISTER = DEFAULT_COMPARE_VALUE_REGISTER_A_BANK_0
INTERRUPT_CONTROL_REGISTER = INTERRUPT_CONTROL_REGISTER_A_BANK_0
IO_CONTROL_REGISTER = IO_CONTROL_REGISTER_A_BANK_0
INTERRUPT_FLAG_REGISTER = INTERRUPT_FLAG_REGISTER_A_BANK_0
INTERRUPT_CAPTURE_REGISTER = INTERRUPT_CAPTURE_REGISTER_A_BANK_0 
    else:
        IO_DIRECTION_REGISTER = IO_DIRECTION_REGISTER_A_BANK_1
OUTPUT_LATCH_REGISTER = OUTPUT_LATCH_REGISTER_A_BANK_1
GPIO_REGISTER = GPIO_REGISTER_A_BANK_1
INTERRUPT_ON_CHANGE_REGISTER = INTERRUPT_ON_CHANGE_REGISTER_A_BANK_1
DEFAULT_COMPARE_VALUE_REGISTER = DEFAULT_COMPARE_VALUE_REGISTER_A_BANK_1
INTERRUPT_CONTROL_REGISTER = INTERRUPT_CONTROL_REGISTER_A_BANK_1
IO_CONTROL_REGISTER = IO_CONTROL_REGISTER_A_BANK_1
INTERRUPT_FLAG_REGISTER = INTERRUPT_FLAG_REGISTER_A_BANK_1
INTERRUPT_CAPTURE_REGISTER = INTERRUPT_CAPTURE_REGISTER_A_BANK_1 

I am using the following function to blink 8 LEDs


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)

And the following 3 functions blinks the 8 LEDs connected to MCP23017 GPA0 to GPA7 OK


setupGPIO()
startBeep()

blink8LEDs0x22MCP23017()


So far so good.  Next step is to update the project documentation.





.END



# *****************************************************************************
# *** tb18.py ***
# Program  - Test MCP23017 
# Version  - 1.9
# Date     - 2012nov30
# Update   - 2013jan03
# Author   - tlfong01(at)yahoo.cn 
# File     - tb19_2013jan03.py
# Blog     - http://blog.yahoo.com/_ZGD2MIDSBMSKHSUJW23LXRS2EQ/
# 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 register addresses *

IO_DIRECTION_REGISTER_A_BANK_0 = IODIRA_B0 = 0x00
IO_DIRECTION_REGISTER_B_BANK_0 = IODIRB_B0 = 0x01

INPUT_POLARITY_REGISTER_A_BANK_0 = IPOLA_B0 = 0x02
INPUT_POLARITY_REGISTER_B_BANK_0 = IPOLB_B0 = 0x03

INTERRUPT_ON_CHANGE_REGISTER_A_BANK_0 = GPINTENA_B0 = 0x04
INTERRUPT_ON_CHANGE_REGISTER_B_BANK_0 = GPINTENB_B0 = 0x05

DEFAULT_COMPARE_VALUE_REGISTER_A_BANK_0 = DEFVALA_B0 = 0x06
DEFAULT_COMPARE_VALUE_REGISTER_B_BANK_0 = DEFVALB_B0 = 0x07

INTERRUPT_CONTROL_REGISTER_A_BANK_0 = INTCONA_B0 = 0x08
INTERRUPT_CONTROL_REGISTER_B_BANK_0 = INTCONB_B0 = 0x09

IO_CONTROL_REGISTER_A_BANK_0 = IOCONA_B0 = 0x0a
IO_CONTROL_REGISTER_B_BANK_0 = IOCONB_B0 = 0x0b

PULL_UP_RESISTOR_CONFIG_REGISTER_A_BANK_0 = GPPUA_B0 = 0x0c
PULL_UP_RESISTOR_CONFIG_REGISTER_B_BANK_0 = GPPUB_B0 = 0x0d

INTERRUPT_FLAG_REGISTER_A_BANK_0 = INTFA_B0 = 0x0e
INTERRUPT_FLAG_REGISTER_B_BANK_0 = INTFB_B0 = 0x0f

INTERRUPT_CAPTURE_REGISTER_A_BANK_0 = INTCAPA_B0 = 0x10
INTERRUPT_CAPTURE_REGISTER_B_BANK_0 = INTCAPB_B0 = 0x11

GPIO_REGISTER_A_BANK_0 = GPIOA_B0 = 0x12
GPIO_REGISTER_B_BANK_0 = GPIOB_B0 = 0x13

OUTPUT_LATCH_REGISTER_A_BANK_0 = OLATA_B0 = 0x14
OUTPUT_LATCH_REGISTER_B_BANK_0 = OLATB_B0 = 0x15

IO_DIRECTION_REGISTER_A_BANK_1 = IODIRA_B1 = 0x00
INPUT_POLARITY_REGISTER_A_BANK_1 = IPOLA_B1 = 0x01
INTERRUPT_ON_CHANGE_REGISTER_A_BANK_1 = GPINTENA_B1 = 0x02
DEFAULT_COMPARE_VALUE_REGISTER_A_BANK_1 = DEFVALA_B1 = 0x03
INTERRUPT_CONTROL_REGISTER_A_BANK_1 = INTCONA_B1 = 0x04
IO_CONTROL_REGISTER_A_BANK_1 = IOCONA_B1 = 0x05
PULL_UP_RESISTOR_CONFIG_REGISTER_A_BANK_1 = GPPUA_B1 = 0x06
INTERRUPT_FLAG_REGISTER_A_BANK_1 = INTFA_B1 = 0x07
INTERRUPT_CAPTURE_REGISTER_A_BANK_1 = INTCAPA_B1 = 0x08
GPIO_REGISTER_A_BANK_1 = GPIOA_B1 = 0x09
OUTPUT_LATCH_REGISTER_A_BANK_1 = OLATA_B1 = 0x0a

IO_DIRECTION_REGISTER_B_BANK_1 = IODIRB_B1 = 0x10
INPUT_POLARITY_REGISTER_B_BANK_1 = IPOLB_B1 = 0x11
INTERRUPT_ON_CHANGE_REGISTER_B_BANK_1 = GPINTENB_B1 = 0x12
DEFAULT_COMPARE_VALUE_REGISTER_B_BANK_1 = DEFVALB_B1 = 0x13
INTERRUPT_CONTROL_REGISTER_B_BANK_1 = INTCONB_B1 = 0x14
IO_CONTROL_REGISTER_B_BANK_1 = IOCONB_B1 = 0x15
PULL_UP_RESISTOR_CONFIG_REGISTER_B_BANK_1 = GPPUB_B1 = 0x16
INTERRUPT_FLAG_REGISTER_B_BANK_1 = INTFB_B1 = 0x17
INTERRUPT_CAPTURE_REGISTER_B_BANK_1 = INTCAPB_B1 = 0x18
GPIO_REGISTER_B_BANK_1 = GPIOB_B1 = 0x19
OUTPUT_LATCH_REGISTER_B_BANK_1 = OLATB_B1 = 0x1a

# * MAP23017 Register bank setting *

def setRegisterAddresses(bank):
    if bank == 0:
        IO_DIRECTION_REGISTER = IO_DIRECTION_REGISTER_A_BANK_0
OUTPUT_LATCH_REGISTER = OUTPUT_LATCH_REGISTER_A_BANK_0
GPIO_REGISTER = GPIO_REGISTER_A_BANK_0
INTERRUPT_ON_CHANGE_REGISTER = INTERRUPT_ON_CHANGE_REGISTER_A_BANK_0
DEFAULT_COMPARE_VALUE_REGISTER = DEFAULT_COMPARE_VALUE_REGISTER_A_BANK_0
INTERRUPT_CONTROL_REGISTER = INTERRUPT_CONTROL_REGISTER_A_BANK_0
IO_CONTROL_REGISTER = IO_CONTROL_REGISTER_A_BANK_0
INTERRUPT_FLAG_REGISTER = INTERRUPT_FLAG_REGISTER_A_BANK_0
INTERRUPT_CAPTURE_REGISTER = INTERRUPT_CAPTURE_REGISTER_A_BANK_0 
    else:
        IO_DIRECTION_REGISTER = IO_DIRECTION_REGISTER_A_BANK_1
OUTPUT_LATCH_REGISTER = OUTPUT_LATCH_REGISTER_A_BANK_1
GPIO_REGISTER = GPIO_REGISTER_A_BANK_1
INTERRUPT_ON_CHANGE_REGISTER = INTERRUPT_ON_CHANGE_REGISTER_A_BANK_1
DEFAULT_COMPARE_VALUE_REGISTER = DEFAULT_COMPARE_VALUE_REGISTER_A_BANK_1
INTERRUPT_CONTROL_REGISTER = INTERRUPT_CONTROL_REGISTER_A_BANK_1
IO_CONTROL_REGISTER = IO_CONTROL_REGISTER_A_BANK_1
INTERRUPT_FLAG_REGISTER = INTERRUPT_FLAG_REGISTER_A_BANK_1
INTERRUPT_CAPTURE_REGISTER = INTERRUPT_CAPTURE_REGISTER_A_BANK_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 blink8LEDs0x22MCP23017(): 
    blink8LEDs(MCP23017_1_REGISTER_BASE_ADDRESS, TWO_SECONDS, TWO_SECONDS, ONE_HUNDRED_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()

blink8LEDs0x22MCP23017()

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

.END

No comments:

Post a Comment