{"id":9177,"date":"2020-10-25T10:56:45","date_gmt":"2020-10-25T10:56:45","guid":{"rendered":"https:\/\/wolles-elektronikkiste.de\/port-expander-mcp23017-2"},"modified":"2026-03-08T09:46:42","modified_gmt":"2026-03-08T09:46:42","slug":"port-expander-mcp23017-2","status":"publish","type":"post","link":"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2","title":{"rendered":"Port expander MCP23017"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">About this Post<\/h2>\n\n<p>In my post about the <a href=\"https:\/\/wolles-elektronikkiste.de\/en\/esp-01-port-expansion\" target=\"_blank\" rel=\"noopener noreferrer\">port expansion for the ESP-01<\/a> I had already briefly described the MCP23017, here I would like to go into detail about its many features. In the first part of this article, I would like to show you how to use the MCP23017 (and the MCP23S17) with the help of <a href=\"https:\/\/github.com\/wollewald\/MCP23017\/tree\/master\" target=\"_blank\" rel=\"noopener noreferrer\">my library<\/a>. The second part is a deep dive for those who are interested in the internal details. I will refer to the numerous registers of the MCP23017.&nbsp;<\/p>\n\n<h2 class=\"wp-block-heading\">MCP23017 &#8211; an overview <\/h2>\n\n<p>The MCP23017 is a 16-bit I\/O port expander with convenient interrupt functions. The 16 I\/O pins are organized into two ports (A and B), which can be addressed separately (byte mode) or together (sequential mode).  The supply voltage should be between 1.8 and 5.5 volts. The maximum current at the I\/O pins is 25 mA in both directions. In total, the input current to VDD should not exceed 125 mA and not more than 150 mA should flow into VSS (GND). The communication protocoll is I\u00b2C. A data sheet for the MCP23017 can be found <a href=\"https:\/\/www.microchip.com\/en-us\/product\/mcp23017\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/p>\n<p>In terms of its flexibility, the MCP23017 is the Swiss army knife among the common port expanders, if you compare it with the 74HC595 shift register or the PCF8574, for example.&nbsp;<\/p>\n\n<h3 class=\"wp-block-heading\">Pinout of the MCP23017<\/h3>\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/pinout_mcp23017_old_new-1024x341.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"341\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/pinout_mcp23017_old_new-1024x341.png\" alt=\"\" class=\"wp-image-17847\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/pinout_mcp23017_old_new-1024x341.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/pinout_mcp23017_old_new-300x100.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/pinout_mcp23017_old_new-768x256.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/pinout_mcp23017_old_new.png 1124w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Pinout of the MCP23017 old vs. new (from 2022)<\/figcaption><\/figure>\n\n<p><strong>Important notice!!!&nbsp;<\/strong> <em>The data sheet for the MCP23017 changed in 2022. According to the new version, pins GPA7 and GPB7 have lost their input function. The pins are now pure OUTPUT pins. Microchip appears to have responded to reported problems with this change. The problems may or may not occur. You can find a statement from Microchip on this <a href=\"https:\/\/microchip.my.site.com\/s\/article\/GPA7---GPB7-Cannot-Be-Used-as-Inputs-In-MCP23017\" target=\"_blank\" rel=\"noopener\">here<\/a>.     Personally, I have not noticed any malfunctions so far. <br><\/em><\/p>\n\n<p>The 16 I\/O pins are named GPA0 to GPA7 or GPB0 to GPB7 according to the two ports. The power supply is via VDD and VSS. The connection of pins A0, A1 and A2 determines the I2C address according to the following scheme:<\/p>\n<p>1 0 0 A2 A1 A0&nbsp;<\/p>\n<p>For example, if A0 to A2 are LOW, then the address is 100000 (binary) = 32 (decimal) = 0x20 (hexadecimal). SDA and SCL are the two I\u00b2C pins. The reset pin is active-low. INTA and INTB are the interrupt pins for the two ports. You can set the polarity of the interrupt pins. You can also connect both interrupt pins together (mirror function).&nbsp;<\/p>\n\n<h2 class=\"wp-block-heading\">Controlling the MCP23017 with the library<\/h2>\n\n<p>I have developed a library that you can find and download <a href=\"https:\/\/github.com\/wollewald\/MCP23017_WE\" target=\"_blank\" rel=\"noopener noreferrer\">here<\/a> on Github. You can also install the library directly from the Arduino IDE library manager. The library is designed so that the two ports, A and B, are addressed separately in byte mode. Perhaps I will expand the library to include sequential mode at some point. &nbsp;<\/p>\n\n<h3 class=\"wp-block-heading\">Simple input\/output applications<\/h3>\n\n<p>The functionality of the I\/O pins is comparable to that of Arduino I\/O pins. The pins can be used as inputs or outputs; they are switched to HIGH or LOW, and they can act as both current sources and current sinks.  If they are set as  &nbsp;  input, the pins can serve as interrupt pins. But in the first example, we start easy, and only 16 LEDs are controlled.&nbsp;<\/p>\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/10\/Mcp23017_basic_input_output_wiring.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"792\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/10\/Mcp23017_basic_input_output_wiring-1024x792.png\" alt=\"Circuit for controlling 16 LEDs\" class=\"wp-image-3726\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/10\/Mcp23017_basic_input_output_wiring-1024x792.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/10\/Mcp23017_basic_input_output_wiring-300x232.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/10\/Mcp23017_basic_input_output_wiring-768x594.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/10\/Mcp23017_basic_input_output_wiring.png 1258w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Circuit for controlling 16 LEDs<\/figcaption><\/figure>\n\n<p>The address pins are set to LOW in my example circuit, so the I\u00b2C address is 0x20. The reset pin is connected to the Arduino pin 5. The pins of port A and B each control eight LEDs of an LED bar. The I\u00b2C lines SDA and SCL are equipped with pull-ups with 4.7 kOhm resistors. Without the pull-ups, however, it also worked well for me.<\/p>\n\n<p>In this example, the following functions are used (the MCP23017 object is called myMCP here):<\/p>\n<ul>\n<li><code>typedef MCP23017_WE MCP;<\/code> is simply for convenience. Writing MCP instead of the class name MCP23017_WE requires less typing. I renamed the class with version 2.0.0 due to problems installing libraries with classes of the same name.  <\/li>\n<li><code>MCP myMCP = MCP(MCP_ADDRESS, RESET_PIN)<\/code> creates your MCP23017 object. You must pass the I\u00b2C address. You can also pass the reset pin and\/or a wire object. With the latter, you can use both I\u00b2C buses of an ESP32, for example. You can also connect the reset pin to VDD to save a pin. However, you should then pass a dummy reset pin with a value of &gt;= 99.      This triggers a software reset instead of a hardware reset in Init().<\/li>\n<li><code>myMCP.Init();<\/code> initializes the object with some default settings.<\/li>\n<li><code>myMCP.setPinMode( pin,port,direction );<\/code> corresponds to the Arduino pinMode function, with the port added as a parameter here. \n<ul>\n<li>The port is A or B. I have defined the names in an enum. Since I have made the definition (since version 2.0.0) in the class, the class name must be prefixed (e.g. <code>MCP::A<\/code>). This is one more argument for the abbreviation &#8220;MCP&#8221;.  <\/li>\n<li>Allowed identifiers for direction are: INPUT\/OUTPUT\/INPUT_PULLUP, OFF\/ON or 0\/1<\/li>\n<li>&#8220;0&#8221; = INPUT, &#8220;1&#8221; = OUTPUT.<\/li>\n<\/ul>\n<\/li>\n<li><code>myMCP.setPortMode( value,port );<\/code> sets the pinMode for an entire port; value is best specified as a binary number.<\/li>\n<li><code>myMCP.setPortMode( value,port,INPUT_PULLUP);<\/code> with this variant, all input pins get a pull-up; no effect on output pins. <\/li>\n<li><code>myMCP.setPin( pin,port,level );<\/code> similar to the digitalWrite function;\n<ul>\n<li> allowed identifiers for level are: 0\/1, OFF\/ON, LOW\/HIGH<\/li>\n<\/ul>\n<\/li>\n<li><code>myMCP.setPort( value,port );<\/code> digitalWrite for the entire port; for me, binary numbers are more readable than decimal or hexadecimal numbers.<\/li>\n<li><code>myMCP.togglePin( pin,port );<\/code> switches a pin from LOW to HIGH or from HIGH to LOW.<\/li>\n<li><code>myMCP.setPinX( pin,port,direction,level );<\/code> &#8220;Extended version&#8221; of the setPin function or combination of setPinMode and setPin.<\/li>\n<li><code>myMCP.setPortModeX( direction,value,port );<\/code> &#8220;Extended version&#8221; of the setPort function.<\/li>\n<li><code>myMCP.setAllPins( port,level );<\/code> sets all pins of a port to LOW or HIGH.<\/li>\n<\/ul>\n<p><strong>A complete list of all library functions can be found <a href=\"https:\/\/github.com\/wollewald\/MCP23017_WE\/blob\/master\/List%20of%20public%20functions%20MCP23017_WE.pdf\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/strong><\/p>\n\n<h4 class=\"wp-block-heading\">Sample sketch for simple output applications<\/h4>\n\n<p>Here is a sketch that plays with these functions and hopefully illustrates them:<\/p>\n<\/p>\n<div class=\"scroll-paragraph-long\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"mcp23017_basic_output.ino\" data-enlighter-title=\"mcp23017_basic_output.ino\">#include &lt;Wire.h&gt;\n#include &lt;MCP23017_WE.h&gt;\n#define MCP_ADDRESS 0x20 \/\/ (A2\/A1\/A0 = LOW)\n\/* A hardware reset is performed during init(). If you want to save a pin you can define a dummy \n * reset pin &gt;= 99 and connect the reset pin to HIGH. This will trigger a software reset instead \n * of a hardware reset. \n *\/\n#define RESET_PIN 5  \n\ntypedef MCP23017_WE MCP; \/\/ just for less typing!\n\n\/* There are several ways to create your MCP23017 object:\n * MCP23017_WE myMCP = MCP23017_WE(MCP_ADDRESS) -&gt; uses Wire \/ no reset pin \n * MCP23017_WE myMCP = MCP23017_WE(MCP_ADDRESS, RESET_PIN) -&gt; uses Wire \/ RESET_PIN\n * MCP23017_WE myMCP = MCP23017_WE(&amp;Wire, MCP_ADDRESS) -&gt; passing a TwoWire object \/ no reset pin\n * MCP23017_WE myMCP = MCP23017_WE(&amp;Wire, MCP_ADDRESS, RESET_PIN) -&gt; \"all together\"\n *\/\nMCP myMCP = MCP(MCP_ADDRESS, RESET_PIN); \/\/ short version\n\/\/ MCP23017_WE myMCP = MCP23017_WE(MCP_ADDRESS, RESET_PIN); \/\/ long version\n\nint wT = 1000; \/\/ wT = waiting time\n\nvoid setup(){ \n  Serial.begin(9600);\n  Wire.begin();\n  if(!myMCP.Init()){\n    Serial.println(\"Not connected!\");\n    while(1){} \n  }\n  myMCP.setPortMode(0b11111101, MCP::A);  \/\/ Port A: all pins are OUTPUT except pin 1\n  myMCP.setPortMode(0b11111111, MCP::B);  \/\/ Port B: all pins are OUTPUT\n  delay(wT);\n  myMCP.setAllPins(MCP::A, HIGH); \/\/ alle LEDs switched on except A1\n  delay(wT);\n  myMCP.setPinX(1, MCP::A, OUTPUT, HIGH); \/\/ A1 switched on \n  delay(wT); \n  myMCP.setPort(0b11110000, MCP::B); \/\/ B4 - B7 switched on\n  delay(wT);\n  myMCP.setPort(0b01011110, MCP::A); \/\/ A0,A5,A7 switched off\n  delay(wT);\n  myMCP.setPinX(0, MCP::B, OUTPUT, HIGH); \/\/ B0 switched on\n  delay(wT);\n  myMCP.setPinX(4, MCP::B, OUTPUT, LOW); \/\/ B4 switched off\n  delay(wT);\n  myMCP.setAllPins(MCP::A, HIGH); \/\/ A0 - A7 all on\n  delay(wT);\n  myMCP.setPin(3, MCP::A, LOW); \/\/ A3 switched off\n  delay(wT);\n  myMCP.setPortX(0b11110000, 0b01101111, MCP::B); \/\/ at port B only B5,B6 are switched on\n  delay(wT);\n  myMCP.setPinMode(0, MCP::B, OUTPUT); \/\/ B0 --&gt; OUTPUT\n  for(int i=0; i&lt;5; i++){  \/\/ B0 blinking\n    myMCP.togglePin(0, MCP::B); \n    delay(200);\n    myMCP.togglePin(0, MCP::B);\n    delay(200);\n  }\n  for(int i=0; i&lt;5; i++){ \/\/ B7 blinking\n    myMCP.togglePin(7, MCP::B);\n    delay(200);\n    myMCP.togglePin(7, MCP::B);\n    delay(200);\n  }\n}\n\nvoid loop(){ \n} <\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<p>In my example, the current (technical current direction) flows from the MCP23017 through the LEDs to GND. Instead, the MCP23017 could, of course, also be used as a current sink. Then an LED would light up if the corresponding pin is OUTPUT and LOW and voltage is supplied, comparable with how the Arduino works.&nbsp;<\/p>\n\n<h4 class=\"wp-block-heading\">Read pin status<\/h4>\n\n<p>The following functions are used to read the pin status in the GPIO register:<\/p>\n<ul>\n<li><code>myMCP.getPin( pin,port );<\/code> returns the level of a pin (as a boolean).<\/li>\n<li><code>myMCP.getPort( port );<\/code> returns the status of an entire port (as uint8_t), i.e. the contents of the GPIO register.<\/li>\n<\/ul>\n<p>Here is an example circuit for you:<\/p>\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2022\/07\/mcp23s17_gpio_reading_wiring-1024x744.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"744\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2022\/07\/mcp23s17_gpio_reading_wiring-1024x744.png\" alt=\"Circuit diagram for testing the GPIO read functions\" class=\"wp-image-15274\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2022\/07\/mcp23s17_gpio_reading_wiring-1024x744.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2022\/07\/mcp23s17_gpio_reading_wiring-300x218.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2022\/07\/mcp23s17_gpio_reading_wiring-768x558.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2022\/07\/mcp23s17_gpio_reading_wiring.png 1271w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Circuit diagram for testing the GPIO read functions<\/figcaption><\/figure>\n\n<p>And here is the corresponding example sketch:<\/p>\n<\/p>\n<div class=\"scroll-paragraph-long\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"mcp23017_gpio_reading.ino\" data-enlighter-title=\"mcp23017_gpio_reading.ino\">#include &lt;Wire.h&gt;\n#include &lt;MCP23017_WE.h&gt;\n#define MCP_ADDRESS 0x20 \/\/ (A2\/A1\/A0 = LOW)\n\n\/* A hardware reset is performed during init(). If you want to save a pin you can define a dummy \n * pin &gt;= 99 and connect the reset pin to HIGH. This will trigger a software reset instead of a \n * hardware reset. \n *\/\n#define RESET_PIN 5\n\ntypedef MCP23017_WE MCP; \/\/ just for less typing!\n\n\/* There are several ways to create your MCP23017 object:\n * MCP23017_WE myMCP = MCP23017_WE(MCP_ADDRESS) -&gt; uses Wire \/ no reset pin \n * MCP23017_WE myMCP = MCP23017_WE(MCP_ADDRESS, RESET_PIN) -&gt; uses Wire \/ RESET_PIN\n * MCP23017_WE myMCP = MCP23017_WE(&amp;Wire, MCP_ADDRESS) -&gt; passing a TwoWire object \/ no reset pin\n * MCP23017_WE myMCP = MCP23017_WE(&amp;Wire, MCP_ADDRESS, RESET_PIN) -&gt; \"all together\"\n *\/\nMCP myMCP = MCP(MCP_ADDRESS, RESET_PIN); \/\/ short version\n\/\/ MCP23017_WE myMCP = MCP23017_WE(MCP_ADDRESS, RESET_PIN); \/\/ long version\n\nint wT = 1000; \/\/ wT = waiting time\nbyte portStatus;\nbool pinStatus;\n\nvoid setup(){ \n  Serial.begin(9600);\n  Wire.begin();\n  if(!myMCP.Init()){\n    Serial.println(\"Not connected!\");\n    while(1){} \n  }  \n  myMCP.setPortMode(0b00000000, MCP::A);  \/\/ Port A: all pins are INPUT\n  myMCP.setPortPullUp(0b11110000, MCP::A);  \/\/ Port A: Pin 4 - 7 are pulled up\n}\n\nvoid loop(){ \n  \n  portStatus = myMCP.getPort(MCP::A); \/\/ query the complete port status \n  Serial.print(\"Status GPIO A: \");\n  Serial.println(portStatus, BIN);\n  \n  pinStatus = myMCP.getPin(5, MCP::A); \/\/ query one pin status\n  Serial.print(\"Status Port A, Pin 5: \");\n  Serial.println(pinStatus, BIN);\n \n  Serial.println(\"-------------------------------------\");\n  delay(1000);\n} <\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<p>I\/Os configured as INPUT can get an internal pull-up:&nbsp;<\/p>\n<ul>\n<li><code>myMCP.setPinPullUp( pin,port );<\/code> sets a pull-up with a 100 kOhm resistor<\/li>\n<li><code>myMCP.setPortPullUp( value,port );<\/code> is the equivalent for an entire port<\/li>\n<\/ul>\n<p>You must add pull-down resistors externally.&nbsp;<\/p>\n<p>This is what the output of the example sketch looks like:<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2022\/07\/output_mcp23017_gpio_reading.png\"><img loading=\"lazy\" decoding=\"async\" width=\"853\" height=\"419\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2022\/07\/output_mcp23017_gpio_reading.png\" alt=\"Output of mcp23017_gpio_reading.ino\" class=\"wp-image-15276\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2022\/07\/output_mcp23017_gpio_reading.png 853w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2022\/07\/output_mcp23017_gpio_reading-300x147.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2022\/07\/output_mcp23017_gpio_reading-768x377.png 768w\" sizes=\"auto, (max-width: 853px) 100vw, 853px\" \/><\/a><figcaption class=\"wp-element-caption\">Output of mcp23017_gpio_reading.ino<\/figcaption><\/figure>\n\n<h3 class=\"wp-block-heading\">Interrupt-on-Change<\/h3>\n\n<p>All 16 I\/O pins can be configured as interrupt pins. There are two modes, the interrupt-on-change and the interrupt-on-defval-deviation. I start with the interrupt-on-change function, where every switch from LOW to HIGH or HIGH to LOW triggers an interrupt. The interrupt induces a polarity change at the respective interrupt output INTA or INTB. Alternatively, you can merge INTA and INTB. You can also set the polarity of the interrupt outputs.&nbsp;<\/p>\n<p>Only pins set as INPUT can act as interrupt pins, but this setting is done in the background by the library.<\/p>\n\n<p>I have implemented the following functions for interrupt-on-change:<\/p>\n<ul>\n<li><code>myMCP.setInterruptOnChangePin( pin,port );<\/code> sets up a single pin as an interrupt-on-change pin.<\/li>\n<li><code>myMCP.setInterruptOnChangePort( value,port );<\/code> configures several or all pins of a port as interrupt-on-change pins.&nbsp;<\/li>\n<li><code>myMCP.setInterruptPinPol( level );<\/code> sets the level of the active interrupt output\n<ul>\n<li>&nbsp;level = HIGH &#8211;&gt; active-high, level = LOW \u2192 active-low (default).&nbsp;<\/li>\n<li>&nbsp;I have only implemented one setting for both outputs, i.e. both active-high or both active-low.<\/li>\n<\/ul>\n<\/li>\n<li><code>myMCP.setIntOdr( value );<\/code> value = 1 or ON \u2192 Interrupt outputs go into open drain state, interrupt pin polarity is overwritten; value = 0 or OFF \u2192 active-low or active-high (both).<\/li>\n<li><code>myMCP.deleteAllInterruptsOnPort( port );<\/code> resets the configuration of the interrupt pins.<\/li>\n<li><code>myMCP.setIntMirror ( value );<\/code> value = 1 or ON \u2192 INTA \/ INTB are mirrored, value = 0 or OFF \u2192 INTA \/ INTB are responsible for their ports separately (default setting).<\/li>\n<li><code>myMCP.getIntFlag( port );<\/code> returns the value of the interrupt flag register as a byte (uint8_t). The bit representing the pin responsible for the last interrupt is set in the interrupt flag register. <\/li>\n<li><code>myMCPgetIntCap( port );<\/code> returns the value of the interrupt capture register. It contains the value of the GPIO register at the time of the interrupt.&nbsp;<\/li>\n<\/ul>\n\n<p>For testing, I have set up the following circuit:<\/p>\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/mcp23017_interrupt_on_change_wiring.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"775\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/mcp23017_interrupt_on_change_wiring-1024x775.png\" alt=\"Circuit for testing the Interrupt on Change function with the MCP23017\" class=\"wp-image-1734\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/mcp23017_interrupt_on_change_wiring-1024x775.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/mcp23017_interrupt_on_change_wiring-300x227.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/mcp23017_interrupt_on_change_wiring-768x582.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/mcp23017_interrupt_on_change_wiring.png 1273w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Circuit for testing the interrupt-on-pin-change function<\/figcaption><\/figure>\n\n<p>The pins of port B are set up as interrupt pins and receive HIGH signals via the buttons. The LEDs connected to port A should indicate which pin the interrupt occurred on.&nbsp;<\/p>\n\n<h4 class=\"wp-block-heading\">Example sketch for interrupt-on-change<\/h4>\n<\/p>\n<div class=\"scroll-paragraph-long\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"mcp23017_interrupt_on_change.ino\" data-enlighter-title=\"mcp23017_interrupt_on_change.ino\">#include &lt;Wire.h&gt;\n#include &lt;MCP23017_WE.h&gt;\n#define MCP_ADDRESS 0x20 \/\/ (A2\/A1\/A0 = LOW)\n\/* A hardware reset is performed during init(). If you want to save a pin you can define a dummy \n * reset pin &gt;= 99 and connect the reset pin to HIGH. This will trigger a software reset instead \n * of a hardware reset. \n *\/\n#define RESET_PIN 5  \nint interruptPin = 3;\nvolatile bool event; \n\ntypedef MCP23017_WE MCP; \/\/ just for less typing!\n\n\/* There are several ways to create your MCP23017 object:\n * MCP23017_WE myMCP = MCP23017_WE(MCP_ADDRESS) -&gt; uses Wire \/ no reset pin \n * MCP23017_WE myMCP = MCP23017_WE(MCP_ADDRESS, RESET_PIN) -&gt; uses Wire \/ RESET_PIN\n * MCP23017_WE myMCP = MCP23017_WE(&amp;Wire, MCP_ADDRESS) -&gt; passing a TwoWire object \/ no reset pin\n * MCP23017_WE myMCP = MCP23017_WE(&amp;Wire, MCP_ADDRESS, RESET_PIN) -&gt; \"all together\"\n *\/\nMCP myMCP = MCP(MCP_ADDRESS, RESET_PIN); \/\/ short version\n\/\/ MCP23017_WE myMCP = MCP23017_WE(MCP_ADDRESS, RESET_PIN); \/\/ long version\n\n\nvoid setup(){ \n  pinMode(interruptPin, INPUT);\n  attachInterrupt(digitalPinToInterrupt(interruptPin), eventHappened, RISING);\n  Serial.begin(9600);\n  Wire.begin();\n  if(!myMCP.Init()){\n    Serial.println(\"Not connected!\");\n    while(1){} \n  } \n  myMCP.setPortMode(0b11111111, MCP::A);\n  myMCP.setPort(0b11111111, MCP::A); \/\/ just an LED test\n  delay(1000); \n  myMCP.setAllPins(MCP::A, LOW);\n  delay(1000);\n  myMCP.setInterruptPinPol(HIGH); \/\/ set INTA and INTB active-high\n  delay(10);\n  myMCP.setInterruptOnChangePort(0b11111111, MCP::B); \/\/set all B pins as interrrupt Pins\n  delay(10);\n  myMCP.getIntCap(MCP::B); \/\/ ensures that existing interrupts are cleared\n  event=false;\n}  \n\nvoid loop(){ \n  if(event){\n    byte intFlagReg = myMCP.getIntFlag(MCP::B);\n    byte eventPin = log(intFlagReg)\/log(2);\n    byte intCapReg = myMCP.getIntCap(MCP::B);\n    Serial.println(\"Interrupt!\");\n    Serial.print(\"Interrupt Flag Register: \");\n    Serial.println(intFlagReg, BIN); \n    Serial.print(\"Interrupt Capture Register: \");\n    Serial.println(intCapReg, BIN); \n    Serial.print(\"Pin No.\");\n    Serial.print(eventPin);\n    Serial.print(\" went \");\n    if((intFlagReg&amp;intCapReg) == 0){  \/\/LOW-HIGH or HIGH-LOW interrupt?\n      Serial.println(\"LOW\");\n    }\n    else{\n      Serial.println(\"HIGH\");\n    }\n    myMCP.setPort(intFlagReg, MCP::A);\n    delay(200);\n    intCapReg = myMCP.getIntCap(MCP::B);\n    event = false; \n  }\n}\n\nvoid eventHappened(){\n  event = true;\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Interrupt_Output_Serial_Monitor.png\"><img loading=\"lazy\" decoding=\"async\" width=\"719\" height=\"529\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Interrupt_Output_Serial_Monitor.png\" alt=\"Output of the Interrupt-on-Pin-Change sketch\" class=\"wp-image-1618\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Interrupt_Output_Serial_Monitor.png 719w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Interrupt_Output_Serial_Monitor-300x221.png 300w\" sizes=\"auto, (max-width: 719px) 100vw, 719px\" \/><\/a><figcaption class=\"wp-element-caption\">Output of the Interrupt-on-Pin-Change sketch<\/figcaption><\/figure>\n\n<p><em>Note 1: Pressing the button briefly (&lt; 200 ms) triggers a LOW-HIGH interrupt; pressing and holding the button triggers first a LOW-HIGH interrupt and then a HIGH-LOW interrupt.&nbsp; <br><\/em><\/p>\n\n<p><em>Note 2: The interrupt remains active until a getIntCap or getPort query is performed. When things go bad because you query at the wrong time or the next interrupt comes at the wrong time, the interrupt will remain unintentional active. That is why I added an additional getIntCap query in line 67, which may seem redundant at first glance. This ensures that the MCP23017 is ready for the next interrupt. <br><\/em><\/p>\n\n<h3 class=\"wp-block-heading\">Interrupt-on-DefVal-Deviation<\/h3>\n\n<p>Here, the polarity of the interrupt pins is compared with the state defined in the so-called DEFVAL register. A deviation triggers an interrupt. If you read the interrupt capture or GPIO register, you delete the interrupt. However, if the interrupt condition is still met at this time, the next interrupt is triggered immediately.<\/p>\n<p>For this interrupt method, I have implemented the following additional functions:<\/p>\n<ul>\n<li><code>myMCP.setInterruptOnDefValDevPin( intpin,port,defvalstate );<\/code> intpin is the interruptpin, defvalstate is the default level and a deviation from it leads to the interrupt<\/li>\n<li><code>myMCP.setInterruptOnDefValDevPort( intpins,Port,defvalstate );<\/code>\n<ul>\n<li>intpins are the interruptpins, e.g. B10000001 would set the pins 0 and 7 as interrupt pins.<\/li>\n<li>defvalstate is the value of the DEFVAL register; a deviation leads to the interrupt&nbsp;<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n<p>As an example, I have set up the following circuit:<\/p>\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/mcp23017_interrupt_on_defval_def_wiring.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"778\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/mcp23017_interrupt_on_defval_def_wiring-1024x778.png\" alt=\"Circuit for testing the interrupt-on-DefVal-Deviation function of the MCP23017\" class=\"wp-image-1735\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/mcp23017_interrupt_on_defval_def_wiring-1024x778.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/mcp23017_interrupt_on_defval_def_wiring-300x228.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/mcp23017_interrupt_on_defval_def_wiring-768x583.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/06\/mcp23017_interrupt_on_defval_def_wiring.png 1272w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Circuit for testing the Interrupt-on-DefVal-Deviation function<\/figcaption><\/figure>\n\n<p>The port B pins are defined as interrupt pins. B0 to B3 are set HIGH with internal pull-ups, while B4 to B7 get pull-down resistors. The polarity on the respective pin is reversed by pushing the button. As in the last example, port A is again used to display the pin responsible for the interrupt. <\/p>\n\n<h4 class=\"wp-block-heading\">Example sketch for interrupt-on-defval-deviation<\/h4>\n<\/p>\n<div class=\"scroll-paragraph-long\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"mcp23017_interrupt_on_defval_dev.ino\" data-enlighter-title=\"mcp23017_interrupt_on_defval_dev.ino\">#include &lt;Wire.h&gt;\n#include &lt;MCP23017_WE.h&gt;\n#define MCP_ADDRESS 0x20 \/\/ (A2\/A1\/A0 = LOW)\n\/* A hardware reset is performed during init(). If you want to save a pin you can define a dummy \n * reset pin &gt;= 99 and connect the reset pin to HIGH. This will trigger a software reset instead \n * of a hardware reset. \n *\/\n#define RESET_PIN 5 \nint interruptPin = 3;\nvolatile bool event = false;\nuint8_t defValB = 0b00001111; \/\/ if port B deviates, an interrupt is triggered\n\ntypedef MCP23017_WE MCP; \/\/ just for less typing!\n\n\/* There are several ways to create your MCP23017 object:\n * MCP23017_WE myMCP = MCP23017_WE(MCP_ADDRESS) -&gt; uses Wire \/ no reset pin \n * MCP23017_WE myMCP = MCP23017_WE(MCP_ADDRESS, RESET_PIN) -&gt; uses Wire \/ RESET_PIN\n * MCP23017_WE myMCP = MCP23017_WE(&amp;Wire, MCP_ADDRESS) -&gt; passing a TwoWire object \/ no reset pin\n * MCP23017_WE myMCP = MCP23017_WE(&amp;Wire, MCP_ADDRESS, RESET_PIN) -&gt; \"all together\"\n *\/\nMCP myMCP = MCP(MCP_ADDRESS, RESET_PIN); \/\/ short version\n\/\/ MCP23017_WE myMCP = MCP23017_WE(MCP_ADDRESS, RESET_PIN); \/\/ long version\n\nvoid setup(){ \n  pinMode(interruptPin, INPUT);\n  attachInterrupt(digitalPinToInterrupt(interruptPin), eventHappened, RISING);\n  Serial.begin(9600);\n  Wire.begin();\n  if(!myMCP.Init()){\n    Serial.println(\"Not connected!\");\n    while(1){} \n  }\n  myMCP.setPortMode(0b11111111, MCP::A);\n  myMCP.setPort(0b11111111, MCP::A); \/\/ just an LED test \n  delay(1000); \n  myMCP.setAllPins(MCP::A, LOW);\n  delay(1000);\n  myMCP.setInterruptPinPol(HIGH);\n  delay(10);\n  myMCP.setInterruptOnDefValDevPort(0b11111111, MCP::B, defValB); \/\/ interrupt pins, port, DEFVALB\n  myMCP.setPortPullUp(0b00001111, MCP::B); \/\/ pull-up for B0-B3\n  delay(10);\n  myMCP.getIntCap(MCP::B); \/\/ deletes all interrupts\n  event = false;\n}  \n\nvoid loop(){ \n  if(event){\n    byte intFlagReg = myMCP.getIntFlag(MCP::B);\n    byte eventPin = log(intFlagReg)\/log(2);\n    byte intCapReg = myMCP.getIntCap(MCP::B);\n    Serial.println(\"Interrupt!\");\n    Serial.print(\"Interrupt Flag Register: \");\n    Serial.println(intFlagReg, BIN); \n    Serial.print(\"Interrupt Capture Register: \");\n    Serial.println(intCapReg, BIN); \n    Serial.print(\"Pin No.\");\n    Serial.print(eventPin);\n    Serial.print(\" went \");\n    if((intFlagReg&amp;intCapReg) == 0){ \/\/ HIGH\/LOW or LOW\/HIGH change?\n      Serial.println(\"LOW\");\n    }\n    else{\n      Serial.println(\"HIGH\");\n    }\n    myMCP.setPort(intFlagReg, MCP::A);\n    delay(400);\n    \/* if you use a pushbutton to trigger the interrupt, uncomment the \n       following line. *\/\n    \/\/ while(myMCP.getPort(MCP::B) != defValB) {} \/\/ while key is pressed\n    intCapReg = myMCP.getIntCap(MCP::B);\n    event = false;\n  }\n}\n\nvoid eventHappened(){\n  event = true;\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<p>The crucial difference to interrupt-on-change is that in this method the polarity change leads only in one direction to the interrupt. The output looks like this:<\/p>\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Interrupt_OnDefVal_Output_Serial_Monitor.png\"><img loading=\"lazy\" decoding=\"async\" width=\"764\" height=\"452\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Interrupt_OnDefVal_Output_Serial_Monitor.png\" alt=\"Output of Interrupt-on-DefVal-Dev sketch\" class=\"wp-image-1629\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Interrupt_OnDefVal_Output_Serial_Monitor.png 764w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Interrupt_OnDefVal_Output_Serial_Monitor-300x177.png 300w\" sizes=\"auto, (max-width: 764px) 100vw, 764px\" \/><\/a><figcaption class=\"wp-element-caption\">Output of Interrupt-on-DefVal-Dev sketch<\/figcaption><\/figure>\n\n<h2 class=\"wp-block-heading\">The MCP23S17<\/h2>\n\n<p>The MCP23S17 differs from the MCP23017 only in that it is controlled via SPI instead of I2C. This means that the connections are slightly different. All functions are identical. You can find a sample sketch and the corresponding wiring in my library.   &nbsp;<\/p>\n\n<h2 class=\"wp-block-heading\">MCP23017 \/ MCP23S17 internal<\/h2>\n\n<p>Here, as announced, some additional detailed information about the MCP23017 for those who still want it.&nbsp;<\/p>\n<p>The MCP23017 is only a representative of the larger MCP23XXX family, which differ from each other in the number of I\/O pins, the control (I2C vs SPI) and the external connection. The MCP23017 is probably the most popular representative. I have written a separate article about the others, which you can find <a href=\"https:\/\/wolles-elektronikkiste.de\/en\/mcp23x1y-port-expander\" target=\"_blank\" rel=\"noopener\">here<\/a>.  <\/p>\n\n<h3 class=\"wp-block-heading\">The registers of the MCP23017<\/h3>\n\n<p>First of all, you have to decide how to address the registers. For this, you can set the BANK bit in the IOCON Register. If this is set to 1, the port registers are in two separate banks. If, on the other hand, it is set to 0, the registers are located in the same bank, and the addresses are sequential. I opted for the latter and did not implement the alternative as an option.  The registers are thus defined as follows:<\/p>\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"601\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Registertabelle-1024x601.png\" alt=\"Register overview of the MCP23017 for IOCON.BANK = 0\" class=\"wp-image-1634\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Registertabelle-1024x601.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Registertabelle-300x176.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Registertabelle-768x451.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Registertabelle.png 1071w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Register overview of the MCP23017 if IOCON.BANK = 0<\/figcaption><\/figure>\n\n<h4 class=\"wp-block-heading\">IODIR &#8211; I\/O direction register<\/h4>\n\n<p>The IODIR register determines whether the pins are INPUT or OUTPUT pins. It is the only register with a start or reset value of 0b11111111. &#8220;1&#8221; means INPUT, &#8220;0&#8221; means OUTPUT, which sounds illogical to me, but maybe I am just used to the different definition in the world of Arduino. But because this irritates me, I have turned the values in my library accordingly. With<code>myMCP.setPortMode()<\/code> and <code>myMCP.setPinMode()<\/code> the IODIR register is addressed directly. For example, <code>myMCP.setPortMode(B11111111, A)<\/code> means that all pins of the port are OUTPUT pins.&nbsp;<\/p>\n\n<h4 class=\"wp-block-heading\">IPOL &#8211; input polarity register<\/h4>\n\n<p>If you set the bits in this register, the inverted level of the pins is stored in the corresponding GPIO register. <\/p>\n\n<h4 class=\"wp-block-heading\">GPINTEN &#8211; Interrupt-on-Change control register<\/h4>\n\n<p>This register controls which pins are used as interrupt pins. 0 = disable, 1 = enable. If you only want to implement interrupt-on-change, no further settings are necessary. However, in case you want to implement interrupt-on-defval-deviation, you have to make additional settings in the DEFVAL and INTCON registers. The GPINTEN register is accessed indirectly in my library via the <code>setInterruptOnChangePin()<\/code> and <code>setInterruptOnDefValDevPin()<\/code> functions or their counterparts for entire ports. <\/p>\n\n<h4 class=\"wp-block-heading\">DEFVAL &#8211; default value register<\/h4>\n\n<p>This register is required to set up Interrupts-on-Defval-Deviation. If a value in the GPIO register differs from the DEFVAL register, an interrupt is triggered if the corresponding settings have been made in the GPINTEN and INTCON registers.&nbsp;<\/p>\n\n<h4 class=\"wp-block-heading\">INTCON &#8211; interrupt control register<\/h4>\n\n<p>This register determines the conditions under which interrupts are triggered:<\/p>\n<ul>\n<li>&#8220;0&#8221;: Comparison with previous pin status (interrupt-on-change)<\/li>\n<li>&#8220;1&#8221;: Comparison with DEFVAL (Interrupt-on-DefVal-Deviation)<\/li>\n<\/ul>\n<p>However, the settings are only effective on the pins for which the corresponding bits have been set in GPINTEN.&nbsp;<\/p>\n\n<h4 class=\"wp-block-heading\">IOCON &#8211; I\/O expander configuration register<\/h4>\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/IOCON_REG.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"75\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/IOCON_REG-1024x75.png\" alt=\"\" class=\"wp-image-1632\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/IOCON_REG-1024x75.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/IOCON_REG-300x22.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/IOCON_REG-768x56.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/IOCON_REG.png 1034w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n<p>In this register, some special settings can be made for the MCP23017.<\/p>\n<ul>\n<li>BANK &#8211; Addressing mode for the registers\n<ul>\n<li>&#8220;1&#8221;: registers are in separate banks&nbsp;<\/li>\n<li>&#8220;0&#8221;: registers are in the same bank<\/li>\n<li>I did not implement an option to change that in my library<\/li>\n<\/ul>\n<\/li>\n<li>MIRROR &#8211; is adjustable in my library via setIntMirror()\n<ul>\n<li>&#8220;1&#8221;: INTA and INTB are connected (mirrored)<\/li>\n<li>&#8220;0&#8221;: INTA and INTB are separately responsible for port A and B respectively<\/li>\n<\/ul>\n<\/li>\n<li>SEQOP &#8211; sequential addressing\n<ul>\n<li>&#8220;1&#8221;: disabled &#8211; the address pointer is not incremented<\/li>\n<li>&#8220;0&#8221;: enabled &#8211; the address pointer is automatically incremented&nbsp;<\/li>\n<li>I did not implement an option to change that in my library<\/li>\n<\/ul>\n<\/li>\n<li>DISSLW &#8211; Adjustment of the slope of the SDA output\n<ul>\n<li>&#8220;1&#8221;: disabled<\/li>\n<li>&#8220;0&#8221;: enabled<\/li>\n<li>not implemented in my library<\/li>\n<\/ul>\n<\/li>\n<li>HAEN &#8211; not relevant for the MCP23017<\/li>\n<li>ODR &#8211; open drain for the interrupt pins INTA and INTB\n<ul>\n<li>&#8220;1&#8221;: open drain is active &#8211; overrides the INTPOL setting<\/li>\n<li>&#8220;0&#8221;: disabled &#8211; polarity of the interrupt signal is determined by INTPOL<\/li>\n<li>setting is done via setIntODR(); only one common setting for both pins is implemented in my library (either both 0 or both 1)<\/li>\n<\/ul>\n<\/li>\n<li>INTPOL &#8211; polarity of interrupt pins\n<ul>\n<li>&#8220;1&#8221;: active-high<\/li>\n<li>&#8220;0&#8221;: active-low<\/li>\n<li>in my library only one common setting of both pins is implemented<\/li>\n<\/ul>\n<\/li>\n<li>GPPU &#8211; GPIO pull-up register\n<ul>\n<li>&#8220;1&#8221;: pull-up with 100 kOhm resistor<\/li>\n<li>&#8220;0&#8221;: no pull-up<\/li>\n<li>implemented in my library by setPinPullUp() or setPortPullUp()<\/li>\n<li>impacts only pins configured as input<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n<h4 class=\"wp-block-heading\">INTF &#8211; interrupt flag register (read-only)<\/h4>\n\n<p>This register records which pin caused the last interrupt. The set bit reveals the &#8220;guilty&#8221; one. In my library, getIntFlag() queries the register content.&nbsp;<\/p>\n\n<h4 class=\"wp-block-heading\">INTCAP &#8211; interrupt capture value register (read-only)<\/h4>\n\n<p>This register records the contents of the GPIO register at the time of the last interrupt. I implemented the query through the getIntCap() function.&nbsp;<\/p>\n\n<h4 class=\"wp-block-heading\">GPIO &#8211; general purpose I\/O port register<\/h4>\n\n<p>Contains the pin status (HIGH\/LOW). In my library, the GPIO register is not written to directly, but via the OLAT (Output Latch) register. Read access in my library is performed via <code>getPin()<\/code> or <code>getPort()<\/code>. &nbsp;<\/p>\n\n<h4 class=\"wp-block-heading\">OLAT &#8211; output latch register<\/h4>\n\n<p>Reading gives you the state of the register, not the port&#8217;s state (this would be done through the GPIO Read). This means, for example, if a pin is set as LOW, but an external HIGH is attached, then you will read it as LOW here, whereas in the GPIO register you would read it as HIGH. <\/p>\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The 16-bit I\/O port expander MCP23017 is introduced including a library. The functions are explained by sketches and circuits.<\/p>\n","protected":false},"author":1,"featured_media":7974,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[565,566],"tags":[556,697,696,694,605,691,692,558,665,1541,689,662],"class_list":["post-9177","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-other-parts","category-port-expansion","tag-arduino-en-2","tag-circuit-en","tag-defval-en","tag-example-en","tag-example-sketch","tag-interrupt-en-2","tag-interrupt-on-change-en","tag-library-en-2","tag-mcp23017-en","tag-mcp23s17-en","tag-port-expander-2","tag-port-expansion"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Port expander MCP23017 &#8226; Wolles Elektronikkiste<\/title>\n<meta name=\"description\" content=\"The 16-bit I\/O port expander MCP23017 is introduced including a library. The functions are explained by sketches and circuits.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Port expander MCP23017 &#8226; Wolles Elektronikkiste\" \/>\n<meta property=\"og:description\" content=\"The 16-bit I\/O port expander MCP23017 is introduced including a library. The functions are explained by sketches and circuits.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2\" \/>\n<meta property=\"og:site_name\" content=\"Wolles Elektronikkiste\" \/>\n<meta property=\"article:published_time\" content=\"2020-10-25T10:56:45+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-03-08T09:46:42+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Beitragsbild_MCP23017.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1022\" \/>\n\t<meta property=\"og:image:height\" content=\"1023\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Wolfgang Ewald\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Wolfgang Ewald\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"20 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/port-expander-mcp23017-2#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/port-expander-mcp23017-2\"},\"author\":{\"name\":\"Wolfgang Ewald\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\"},\"headline\":\"Port expander MCP23017\",\"datePublished\":\"2020-10-25T10:56:45+00:00\",\"dateModified\":\"2026-03-08T09:46:42+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/port-expander-mcp23017-2\"},\"wordCount\":2655,\"commentCount\":40,\"publisher\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\"},\"image\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/port-expander-mcp23017-2#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/05\\\/Beitragsbild_MCP23017.jpg\",\"keywords\":[\"Arduino\",\"circuit\",\"Defval\",\"Example\",\"Example sketch\",\"Interrupt\",\"Interrupt-on-Change\",\"Library\",\"MCP23017\",\"MCP23S17\",\"Port expander\",\"Port expansion\"],\"articleSection\":[\"Other parts\",\"Port expansion\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/port-expander-mcp23017-2#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/port-expander-mcp23017-2\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/port-expander-mcp23017-2\",\"name\":\"Port expander MCP23017 &#8226; Wolles Elektronikkiste\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/port-expander-mcp23017-2#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/port-expander-mcp23017-2#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/05\\\/Beitragsbild_MCP23017.jpg\",\"datePublished\":\"2020-10-25T10:56:45+00:00\",\"dateModified\":\"2026-03-08T09:46:42+00:00\",\"description\":\"The 16-bit I\\\/O port expander MCP23017 is introduced including a library. The functions are explained by sketches and circuits.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/port-expander-mcp23017-2#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/port-expander-mcp23017-2\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/port-expander-mcp23017-2#primaryimage\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/05\\\/Beitragsbild_MCP23017.jpg\",\"contentUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/05\\\/Beitragsbild_MCP23017.jpg\",\"width\":1022,\"height\":1023},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/port-expander-mcp23017-2#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Startseite\",\"item\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Port expander MCP23017\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#website\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\",\"name\":\"Wolles Elektronikkiste\",\"description\":\"Die wunderbare Welt der Elektronik\",\"publisher\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\",\"name\":\"Wolfgang Ewald\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\",\"contentUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\",\"width\":512,\"height\":512,\"caption\":\"Wolfgang Ewald\"},\"logo\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Port expander MCP23017 &#8226; Wolles Elektronikkiste","description":"The 16-bit I\/O port expander MCP23017 is introduced including a library. The functions are explained by sketches and circuits.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2","og_locale":"en_US","og_type":"article","og_title":"Port expander MCP23017 &#8226; Wolles Elektronikkiste","og_description":"The 16-bit I\/O port expander MCP23017 is introduced including a library. The functions are explained by sketches and circuits.","og_url":"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2","og_site_name":"Wolles Elektronikkiste","article_published_time":"2020-10-25T10:56:45+00:00","article_modified_time":"2026-03-08T09:46:42+00:00","og_image":[{"width":1022,"height":1023,"url":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Beitragsbild_MCP23017.jpg","type":"image\/jpeg"}],"author":"Wolfgang Ewald","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Wolfgang Ewald","Est. reading time":"20 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2#article","isPartOf":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2"},"author":{"name":"Wolfgang Ewald","@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46"},"headline":"Port expander MCP23017","datePublished":"2020-10-25T10:56:45+00:00","dateModified":"2026-03-08T09:46:42+00:00","mainEntityOfPage":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2"},"wordCount":2655,"commentCount":40,"publisher":{"@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46"},"image":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2#primaryimage"},"thumbnailUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Beitragsbild_MCP23017.jpg","keywords":["Arduino","circuit","Defval","Example","Example sketch","Interrupt","Interrupt-on-Change","Library","MCP23017","MCP23S17","Port expander","Port expansion"],"articleSection":["Other parts","Port expansion"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2#respond"]}]},{"@type":"WebPage","@id":"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2","url":"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2","name":"Port expander MCP23017 &#8226; Wolles Elektronikkiste","isPartOf":{"@id":"https:\/\/wolles-elektronikkiste.de\/en#website"},"primaryImageOfPage":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2#primaryimage"},"image":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2#primaryimage"},"thumbnailUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Beitragsbild_MCP23017.jpg","datePublished":"2020-10-25T10:56:45+00:00","dateModified":"2026-03-08T09:46:42+00:00","description":"The 16-bit I\/O port expander MCP23017 is introduced including a library. The functions are explained by sketches and circuits.","breadcrumb":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2#primaryimage","url":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Beitragsbild_MCP23017.jpg","contentUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/05\/Beitragsbild_MCP23017.jpg","width":1022,"height":1023},{"@type":"BreadcrumbList","@id":"https:\/\/wolles-elektronikkiste.de\/en\/port-expander-mcp23017-2#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Startseite","item":"https:\/\/wolles-elektronikkiste.de\/en"},{"@type":"ListItem","position":2,"name":"Port expander MCP23017"}]},{"@type":"WebSite","@id":"https:\/\/wolles-elektronikkiste.de\/en#website","url":"https:\/\/wolles-elektronikkiste.de\/en","name":"Wolles Elektronikkiste","description":"Die wunderbare Welt der Elektronik","publisher":{"@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/wolles-elektronikkiste.de\/en?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46","name":"Wolfgang Ewald","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png","url":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png","contentUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png","width":512,"height":512,"caption":"Wolfgang Ewald"},"logo":{"@id":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png"}}]}},"_links":{"self":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts\/9177","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/comments?post=9177"}],"version-history":[{"count":6,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts\/9177\/revisions"}],"predecessor-version":[{"id":25752,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts\/9177\/revisions\/25752"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/media\/7974"}],"wp:attachment":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/media?parent=9177"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/categories?post=9177"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/tags?post=9177"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}