Daniel Wyatt / MCP23008

Files at this revision

API Documentation at this revision

Comitter:
dewyatt
Date:
Fri Jun 15 03:31:48 2012 +0000
Commit message:

Changed in this revision

MCP23008.cpp Show annotated file Show diff for this revision Revisions of this file
MCP23008.hpp Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 4f712bd739d0 MCP23008.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MCP23008.cpp	Fri Jun 15 03:31:48 2012 +0000
@@ -0,0 +1,120 @@
+#include "MCP23008.hpp"
+
+namespace {
+const uint8_t MCP23008_ADDRESS  = 0x40;
+
+/* MCP23008 registers */
+const uint8_t IODIR             = 0x00;
+const uint8_t IPOL              = 0x01;
+const uint8_t GPINTEN           = 0x02;
+const uint8_t DEFVAL            = 0x03;
+const uint8_t INTCON            = 0x04;
+const uint8_t IOCON             = 0x05;
+const uint8_t GPPU              = 0x06;
+const uint8_t INTF              = 0x07;
+const uint8_t INTCAP            = 0x08;
+const uint8_t GPIO              = 0x09;
+const uint8_t OLAT              = 0x0A;
+};
+
+MCP23008::MCP23008 ( PinName sda, PinName scl, uint8_t address, Frequency freq )
+        : i2c ( sda, scl ),
+        i2c_address ( MCP23008_ADDRESS | address ) {
+    if ( address > 7 )
+        error ( "MCP23008::MCP23008: address is out of range, must be <= 7\n" );
+
+    i2c.frequency ( ( int ) freq );
+    reset ();
+}
+
+void MCP23008::set_input_pins ( uint8_t pins ) {
+    uint8_t value = read_register ( IODIR );
+    write_register ( IODIR, value | pins );
+}
+
+void MCP23008::set_output_pins ( uint8_t pins ) {
+    uint8_t value = read_register ( IODIR );
+    write_register ( IODIR, value & ~pins );
+}
+
+void MCP23008::write_outputs ( uint8_t values ) {
+    write_register ( GPIO, values );
+}
+
+uint8_t MCP23008::read_outputs () {
+    return read_register ( OLAT );
+}
+
+uint8_t MCP23008::read_inputs () {
+    return read_register ( GPIO );
+}
+
+void MCP23008::set_input_polarity ( uint8_t values ) {
+    write_register ( IPOL, values );
+}
+
+uint8_t MCP23008::get_input_polarity () {
+    return read_register ( IPOL );
+}
+
+void MCP23008::set_pullups ( uint8_t values ) {
+    write_register ( GPPU, values );
+}
+
+uint8_t MCP23008::get_pullups () {
+    return read_register ( GPPU );
+}
+
+void MCP23008::interrupt_on_changes ( uint8_t pins ) {
+    uint8_t value = read_register ( INTCON );
+    value &= ~pins;
+    write_register ( INTCON, value );
+    value = read_register ( GPINTEN );
+    value |= pins;
+    write_register ( GPINTEN, value );
+}
+
+void MCP23008::disable_interrupts ( uint8_t pins ) {
+    uint8_t value = read_register ( GPINTEN );
+    value &= ~pins;
+    write_register ( GPINTEN, value );
+}
+
+void MCP23008::acknowledge_interrupt ( uint8_t &pin, uint8_t &values ) {
+    pin = read_register ( INTF );
+    values = read_register ( INTCAP );
+}
+
+uint8_t MCP23008::read_register ( uint8_t reg ) {
+    char data[] = {reg};
+    if ( 0 != i2c.write ( i2c_address, data, 1 ) )
+        error ( "MCP23008::read_register: Missing ACK for write\n" );
+
+    if ( 0 != i2c.read ( i2c_address, data, 1 ) )
+        error ( "MCP23008:read_register: Missing ACK for read\n" );
+
+    return data[0];
+}
+
+void MCP23008::write_register ( uint8_t reg, uint8_t value ) {
+    char data[] = {reg, value};
+    if ( 0 != i2c.write ( i2c_address, data, 2 ) )
+        error ( "MCP23008::write_register: Missing ACK for write\n" );
+}
+
+void MCP23008::write_mask ( uint8_t reg, uint8_t mask, bool value ) {
+    uint8_t val;
+    val = read_register ( reg );
+    if ( value )
+        val |= mask;
+    else
+        val &= ~mask;
+
+    write_register ( reg, val );
+}
+
+void MCP23008::reset ( ) {
+    write_register ( IODIR, 0xFF );
+    for ( uint8_t reg = IPOL; reg <= OLAT; reg++ )
+        write_register ( reg, 0 );
+}
diff -r 000000000000 -r 4f712bd739d0 MCP23008.hpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MCP23008.hpp	Fri Jun 15 03:31:48 2012 +0000
@@ -0,0 +1,162 @@
+#include "mbed.h"
+
+/** MCP23008 class
+ *
+ * Allow access to an I2C connected MCP23008 8-bit I/O extender chip
+ *
+ */
+class MCP23008 {
+public:
+    enum Frequency {
+        Frequency_100KHz = 100000,
+        Frequency_400KHz = 400000,
+        /* Note: 1.7MHz probably won't work for mbed */
+        Frequency_1700KHz = 1700000
+    };
+    enum Pin {
+        Pin_GP0 = 0x01,
+        Pin_GP1 = 0x02,
+        Pin_GP2 = 0x04,
+        Pin_GP3 = 0x08,
+        Pin_GP4 = 0x10,
+        Pin_GP5 = 0x20,
+        Pin_GP6 = 0x40,
+        Pin_GP7 = 0x80,
+        Pin_All = 0xFF
+    };
+    /** Constructor
+     *
+     * @param sda I2C sda pin
+     * @param scl I2C scl pin
+     * @param address The hardware address of the MCP23008. This is the 3-bit
+     * value that is physically set via A0, A1, and A2.
+     * @param freq The I2C frequency. Should probably be 100KHz or 400KHz.
+     */
+    MCP23008 ( PinName sda, PinName scl, uint8_t address, Frequency freq = Frequency_100KHz );
+
+    /** Set pins to input mode
+     *
+     * This function is used to set which pins are inputs (if any). Example:
+     * set_inputs ( Pin_GP0 | Pin_GP1 | Pin_GP2 );
+     * Note that these are set to input in addition to the previously set.
+     * In other words, the following:
+     * set_inputs ( Pin_GP1 );
+     * set_inputs ( Pin_GP2 );
+     * Results in at least two pins set to input.
+     *
+     * @param pins A bitmask of pins to set to input mode.
+     */
+    void set_input_pins ( uint8_t pins );
+    /** Set pins to output mode
+     *
+     * This function is used to set which pins are outputs (if any). Example:
+     * set_outputs ( Pin_GP0 | Pin_GP1 | Pin_GP2 );
+     * Note that these are set to output in addition to the previously set.
+     * In other words, the following:
+     * set_outputs ( Pin_GP1 );
+     * set_outputs ( Pin_GP2 );
+     * Results in at least two pins set to output.
+     *
+     * @param pins A bitmask of pins to set to output mode.
+     */
+    void set_output_pins ( uint8_t pins );
+
+    /** Write to the output pins.
+     *
+     * This function is used to set output pins on or off.
+     *
+     * @param values A bitmask indicating whether a pin should be on or off.
+     */
+    void write_outputs ( uint8_t values );
+    /** Read back the outputs.
+     *
+     * This function is used to read the last values written to the output pins.
+     *
+     * @returns The value from the OLAT register.
+     */
+    uint8_t read_outputs ();
+
+    /** Read from the input pins.
+     *
+     * This function is used to read the values from the input pins.
+     *
+     * @returns A bitmask of the current state of the input pins.
+     */
+    uint8_t read_inputs ();
+
+    /** Set the input pin polarity.
+     *
+     * This function sets the polarity of the input pins.
+     * A 1 bit is inverted polarity, a 0 is normal.
+     *
+     * @param values A bitmask of the input polarity.
+     */
+    void set_input_polarity ( uint8_t values );
+    /** Read back the current input pin polarity.
+     *
+     * This function reads the current state of the input pin polarity.
+     *
+     * @returns The value from the IPOL register.
+     */
+    uint8_t get_input_polarity ();
+
+    /** Enable and disable the internal pull-up resistors for input pins.
+     *
+     * This function enables the internal 100 kΩ pull-up resistors.
+     * A 1 bit enables the pull-up resistor for the corresponding input pin.
+     *
+     * @param values A bitmask indicating which pull-up resistors should be enabled/disabled.
+     */
+    void set_pullups ( uint8_t values );
+    /** Get the current state of the internal pull-up resistors.
+     *
+     * @returns The current state of the pull-up resistors.
+     */
+    uint8_t get_pullups ();
+
+    /** Generate an interrupt when a pin changes.
+     *
+     * This function enables interrupt generation for the specified pins.
+     * The interrupt is active-low by default.
+     * The function acknowledge_interrupt must be called before another
+     * interrupt will be generated.
+     * Example:
+     * @code
+     * InterruptIn in ( p16 );
+     * MCP23008 mcp ( p9, p10, 0 );
+     * in.fall ( &interrupt );
+     * mcp.interrupt_on_changes ( MCP23008::Pin_GP0 );
+     * while ( 1 ) {
+     *      wait ( 1 );
+     * }
+     * @endcode
+     * 
+     * @param pins A bitmask of the pins that may generate an interrupt.
+     */
+    void interrupt_on_changes ( uint8_t pins );
+    /** Disables interrupts for the specified pins.
+     *
+     * @param values A bitmask indicating which interrupts should be disabled.
+     */
+    void disable_interrupts ( uint8_t pins );
+
+    /** Acknowledge a generated interrupt.
+     *
+     * This function must be called when an interrupt is generated to discover
+     * which pin caused the interrupt and to enable future interrupts.
+     *
+     * @param pin An output paramter that specifies which pin generated the interrupt.
+     * @param values The current state of the input pins.
+     */
+    void acknowledge_interrupt ( uint8_t &pin, uint8_t &values );
+
+private:
+    uint8_t read_register ( uint8_t reg );
+    void write_register ( uint8_t reg, uint8_t value );
+    void write_mask ( uint8_t reg, uint8_t mask, bool value );
+
+    void reset ();
+
+    I2C i2c;
+    uint8_t i2c_address;
+};