All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.pi4j.gpio.extension.mcp.MCP23008GpioProvider Maven / Gradle / Ivy

The newest version!
package com.pi4j.gpio.extension.mcp;

import com.pi4j.io.gpio.*;
import com.pi4j.io.gpio.event.PinDigitalStateChangeEvent;
import com.pi4j.io.gpio.event.PinListener;
import com.pi4j.io.gpio.exception.InvalidPinException;
import com.pi4j.io.gpio.exception.UnsupportedPinPullResistanceException;
import com.pi4j.io.i2c.I2CBus;
import com.pi4j.io.i2c.I2CDevice;
import com.pi4j.io.i2c.I2CFactory;
import com.pi4j.io.i2c.I2CFactory.UnsupportedBusNumberException;

import java.io.IOException;

/*
 * #%L
 * **********************************************************************
 * ORGANIZATION  :  Pi4J
 * PROJECT       :  Pi4J :: GPIO Extension
 * FILENAME      :  MCP23008GpioProvider.java
 *
 * This file is part of the Pi4J project. More information about
 * this project can be found here:  https://www.pi4j.com/
 * **********************************************************************
 * %%
 * Copyright (C) 2012 - 2021 Pi4J
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

/**
 * 

* This GPIO provider implements the MCP23008 I2C GPIO expansion board as native Pi4J GPIO pins. * More information about the board can be found here: * * http://ww1.microchip.com/downloads/en/DeviceDoc/21919e.pdf * http://learn.adafruit.com/mcp230xx-gpio-expander-on-the-raspberry-pi/overview *

* *

* The MCP23008 is connected via I2C connection to the Raspberry Pi and provides * 8 GPIO pins that can be used for either digital input or digital output pins. *

* * @author Robert Savage * */ public class MCP23008GpioProvider extends GpioProviderBase implements GpioProvider { public static final String NAME = "com.pi4j.gpio.extension.mcp.MCP23008GpioProvider"; public static final String DESCRIPTION = "MCP23008 GPIO Provider"; public static final int REGISTER_IODIR = 0x00; private static final int REGISTER_GPINTEN = 0x02; private static final int REGISTER_DEFVAL = 0x03; private static final int REGISTER_INTCON = 0x04; private static final int REGISTER_GPPU = 0x06; private static final int REGISTER_INTF = 0x07; // private static final int REGISTER_INTCAP = 0x08; public static final int REGISTER_GPIO = 0x09; private int currentStates = 0; private int currentDirection = 0; private int currentPullup = 0; private boolean i2cBusOwner = false; private I2CBus bus; private I2CDevice device; private GpioStateMonitor monitor = null; public MCP23008GpioProvider(int busNumber, int address) throws UnsupportedBusNumberException, IOException { // create I2C communications bus instance this(I2CFactory.getInstance(busNumber), address); i2cBusOwner = true; } public MCP23008GpioProvider(I2CBus bus, int address) throws IOException { // set reference to I2C communications bus instance this.bus = bus; // create I2C device instance device = bus.getDevice(address); // read initial GPIO pin states currentStates = device.read(REGISTER_GPIO); // set all default pins directions device.write(REGISTER_IODIR, (byte) currentDirection); // set all default pin interrupts device.write(REGISTER_GPINTEN, (byte) currentDirection); // set all default pin interrupt default values device.write(REGISTER_DEFVAL, (byte) 0x00); // set all default pin interrupt comparison behaviors device.write(REGISTER_INTCON, (byte) 0x00); // set all default pin states device.write(REGISTER_GPIO, (byte) currentStates); // set all default pin pull up resistors device.write(REGISTER_GPPU, (byte) currentPullup); } @Override public String getName() { return NAME; } @Override public void export(Pin pin, PinMode mode) { // make sure to set the pin mode super.export(pin, mode); setMode(pin, mode); } @Override public void unexport(Pin pin) { super.unexport(pin); setMode(pin, PinMode.DIGITAL_OUTPUT); } @Override public void setMode(Pin pin, PinMode mode) { super.setMode(pin, mode); try { // determine register and pin address int pinAddress = pin.getAddress(); // determine update direction value based on mode if (mode == PinMode.DIGITAL_INPUT) { currentDirection |= pinAddress; } else if (mode == PinMode.DIGITAL_OUTPUT) { currentDirection &= ~pinAddress; } // next update direction value device.write(REGISTER_IODIR, (byte) currentDirection); // enable interrupts; interrupt on any change from previous state device.write(REGISTER_GPINTEN, (byte) currentDirection); } catch (IOException ex) { throw new RuntimeException(ex); } // if any pins are configured as input pins, then we need to start the interrupt monitoring // thread if (currentDirection > 0) { // if the monitor has not been started, then start it now if (monitor == null) { // start monitoring thread monitor = new GpioStateMonitor(device); monitor.start(); } } else { // shutdown and destroy monitoring thread since there are no input pins configured if (monitor != null) { monitor.shutdown(); monitor = null; } } } @Override public PinMode getMode(Pin pin) { return super.getMode(pin); } @Override public void setState(Pin pin, PinState state) { super.setState(pin, state); try { // determine pin address int pinAddress = pin.getAddress(); // determine state value for pin bit if (state.isHigh()) { currentStates |= pinAddress; } else { currentStates &= ~pinAddress; } // update state value device.write(REGISTER_GPIO, (byte) currentStates); } catch (IOException ex) { throw new RuntimeException(ex); } } @Override public PinState getState(Pin pin) { // call super method to perform validation on pin PinState result = super.getState(pin); // determine pin address int pinAddress = pin.getAddress(); // determine pin state result = (currentStates & pinAddress) == pinAddress ? PinState.HIGH : PinState.LOW; // cache state getPinCache(pin).setState(result); return result; } @Override public void setPullResistance(Pin pin, PinPullResistance resistance) { // validate if (hasPin(pin) == false) { throw new InvalidPinException(pin); } // validate if (!pin.getSupportedPinPullResistance().contains(resistance)) { throw new UnsupportedPinPullResistanceException(pin, resistance); } try { // determine pin address int pinAddress = pin.getAddress(); // determine pull up value for pin bit if (resistance == PinPullResistance.PULL_UP) { currentPullup |= pinAddress; } else { currentPullup &= ~pinAddress; } // next update pull up resistor value device.write(REGISTER_GPPU, (byte) currentPullup); } catch (IOException ex) { throw new RuntimeException(ex); } // cache resistance getPinCache(pin).setResistance(resistance); } @Override public PinPullResistance getPullResistance(Pin pin) { return super.getPullResistance(pin); } @Override public void shutdown() { // prevent reentrant invocation if(isShutdown()) return; // perform shutdown login in base super.shutdown(); try { // if a monitor is running, then shut it down now if (monitor != null) { // shutdown monitoring thread monitor.shutdown(); monitor = null; } // if we are the owner of the I2C bus, then close it if(i2cBusOwner) { // close the I2C bus communication bus.close(); } } catch (IOException e) { throw new RuntimeException(e); } } /** * This class/thread is used to to actively monitor for GPIO interrupts * * @author Robert Savage * */ private class GpioStateMonitor extends Thread { private I2CDevice device; private boolean shuttingDown = false; public GpioStateMonitor(I2CDevice device) { this.device = device; } public void shutdown() { shuttingDown = true; } public void run() { while (!shuttingDown) { try { // only process for interrupts if a pin is configured as an input pin if (currentDirection > 0) { // process interrupts int pinInterrupt = device.read(REGISTER_INTF); // validate that there is at least one interrupt active if (pinInterrupt > 0) { // read the current pin states int pinInterruptState = device.read(REGISTER_GPIO); // loop over the available pins for (Pin pin : MCP23008Pin.ALL) { // is there an interrupt flag on this pin? //if ((pinInterrupt & pin.getAddress()) > 0) { // System.out.println("INTERRUPT ON PIN [" + pin.getName() + "]"); evaluatePinForChange(pin, pinInterruptState); //} } } } // ... lets take a short breather ... Thread.currentThread(); Thread.sleep(50); } catch (Exception ex) { ex.printStackTrace(); } } } private void evaluatePinForChange(Pin pin, int state) { if (getPinCache(pin).isExported()) { // determine pin address int pinAddress = pin.getAddress(); if ((state & pinAddress) != (currentStates & pinAddress)) { PinState newState = (state & pinAddress) == pinAddress ? PinState.HIGH : PinState.LOW; // cache state getPinCache(pin).setState(newState); // determine and cache state value for pin bit if (newState.isHigh()) { currentStates |= pinAddress; } else { currentStates &= ~pinAddress; } // change detected for INPUT PIN // System.out.println("<<< CHANGE >>> " + pin.getName() + " : " + state); dispatchPinChangeEvent(pin.getAddress(), newState); } } } private void dispatchPinChangeEvent(int pinAddress, PinState state) { // iterate over the pin listeners map for (Pin pin : listeners.keySet()) { // System.out.println("<<< DISPATCH >>> " + pin.getName() + " : " + // state.getName()); // dispatch this event to the listener // if a matching pin address is found if (pin.getAddress() == pinAddress) { // dispatch this event to all listener handlers for (PinListener listener : listeners.get(pin)) { listener.handlePinEvent(new PinDigitalStateChangeEvent(this, pin, state)); } } } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy