├── library.properties ├── .gitattributes ├── examples ├── basic │ └── basic.ino ├── out_example │ └── out_example.ino └── manyButtonsWithInterrupt │ └── manyButtonsWithInterrupt.ino ├── _includes └── MCP23S17_registers.h ├── .gitignore ├── keywords.txt ├── README.md ├── gpio_MCP23S17.cpp └── gpio_MCP23S17.h /library.properties: -------------------------------------------------------------------------------- 1 | name=gpio_MCP23S17 2 | version=0.9 3 | author=sumotoy 4 | maintainer=sumotoy 5 | sentence=A complete library for Microchip GPIO expander MCP23S17. 6 | paragraph=The SPI driver it's fully SPI Transaction compatible, can be easily included in other libraries, it supports Microchip HAEN technology 7 | category=Device Control 8 | url=https://github.com/sumotoy/gpio_MCP23S17 9 | architectures=* 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /examples/basic/basic.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include // import library 3 | 4 | //define CS pin and using HAEN or not 5 | //to use HAEN, address should be 0x20 to 0x27 6 | gpio_MCP23S17 mcp(10,0x27);//instance (address A0,A1,A2 tied to +) 7 | 8 | void setup(){ 9 | mcp.begin();//x.begin(1) will override automatic SPI initialization 10 | mcp.gpioPinMode(OUTPUT); 11 | } 12 | 13 | void loop(){ 14 | for (int i = 0; i < 15; i++) { 15 | mcp.gpioDigitalWrite(i, HIGH); 16 | delay(10); 17 | } 18 | 19 | for (int i = 0; i < 15; i++) { 20 | mcp.gpioDigitalWrite(i, LOW); 21 | delay(10); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /_includes/MCP23S17_registers.h: -------------------------------------------------------------------------------- 1 | #ifndef __MCP23S17REG_H 2 | #define __MCP23S17REG_H 3 | 4 | #include 5 | //------------------------- REGISTERS 6 | 7 | const static byte MCP23S17_IODIR = 0x00; 8 | const static byte MCP23S17_IPOL = 0x02; 9 | const static byte MCP23S17_GPINTEN = 0x04; 10 | const static byte MCP23S17_DEFVAL = 0x06; 11 | const static byte MCP23S17_INTCON = 0x08; 12 | const static byte MCP23S17_IOCON = 0x0A; 13 | const static byte MCP23S17_GPPU = 0x0C; 14 | const static byte MCP23S17_INTF = 0x0E; 15 | const static byte MCP23S17_INTCAP = 0x10; 16 | const static byte MCP23S17_GPIO = 0x12; 17 | const static byte MCP23S17_OLAT = 0x14; 18 | #endif 19 | -------------------------------------------------------------------------------- /examples/out_example/out_example.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include // import library 3 | 4 | //define CS pin and using HAEN or not 5 | //to use HAEN, address should be 0x20 to 0x27 6 | gpio_MCP23S17 mcp(10,0x20);//instance 7 | 8 | void setup(){ 9 | mcp.begin();//x.begin(1) will override automatic SPI initialization 10 | // Set all pins to be outputs (by default they are all inputs) 11 | mcp.gpioPinMode(OUTPUT); 12 | // Change all pins at once, 16-bit value 13 | mcp.gpioPort(0xFFFF); 14 | } 15 | 16 | void loop(){ 17 | for (int i=0;i<16;i++){ 18 | mcp.gpioDigitalWrite(i,LOW); 19 | delay(150); 20 | } 21 | for (int i=0;i<16;i++){ 22 | mcp.gpioDigitalWrite(i,HIGH); 23 | delay(150); 24 | } 25 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For gpio_MCP23S17 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | gpio_MCP23S17 KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | begin KEYWORD2 16 | postSetup KEYWORD2 17 | writeByte KEYWORD2 18 | writeWord KEYWORD2 19 | readAddress KEYWORD2 20 | gpioPinMode KEYWORD2 21 | gpioPort KEYWORD2 22 | readGpioPort KEYWORD2 23 | readGpioPortFast KEYWORD2 24 | gpioDigitalWrite KEYWORD2 25 | gpioDigitalWriteFast KEYWORD2 26 | gpioDigitalRead KEYWORD2 27 | gpioRegisterReadByte KEYWORD2 28 | gpioRegisterReadWord KEYWORD2 29 | gpioDigitalReadFast KEYWORD2 30 | gpioRegisterWriteByte KEYWORD2 31 | gpioRegisterWriteWord KEYWORD2 32 | portPullup KEYWORD2 33 | gpioPortUpdate KEYWORD2 34 | getInterruptNumber KEYWORD2 35 | 36 | ####################################### 37 | # Constants (LITERAL1) 38 | ####################################### 39 | 40 | MCP23S17_IOCON LITERAL1 41 | MCP23S17_IODIR LITERAL1 42 | MCP23S17_GPPU LITERAL1 43 | MCP23S17_GPIO LITERAL1 44 | MCP23S17_GPINTEN LITERAL1 45 | MCP23S17_IPOL LITERAL1 46 | MCP23S17_DEFVAL LITERAL1 47 | MCP23S17_INTF LITERAL1 48 | MCP23S17_INTCAP LITERAL1 49 | MCP23S17_OLAT LITERAL1 50 | MCP23S17_INTCON LITERAL1 51 | 52 | 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gpio_MCP23S17 2 | A complete library for Microchip MCP23S17 http://ww1.microchip.com/downloads/en/DeviceDoc/21952b.pdf, a 16 port I/O GPIO with Interrupt and *HAEN* that let you use 8 chip (16 * 8 ports!) by using just 3 (or 4 if you need input) CPU pins!
3 | The library it's fully *SPI transaction compatible* and can be easily included in other libraries, has methods that let's you change ports values fastly and you can easily access chip register directly.
4 | This library has been extracted from my universal gpio_library and I will maintain separately.
5 | I have included several examples but a complete wiki and detailed images will be published soon.
6 | Features:
7 | - Full access to all MCP features. 8 | - Scalable complexity, easy to use as GPIO but advanced use always possible. 9 | - Mature and stable library, I've used in many projects. 10 | - Fully SPI transaction compatible, it doesn't interfere with other SPI devices. 11 | - Compatible with all 8bit arduino, DUE, Teensy(all), ESP8266. 12 | - 13 | 14 | ------------------------------ MCP23S17 WIRING ------------------------------------
15 | This chip has a very useful feature called HAEN that allow you to share the same CS pin trough
16 | 8 different addresses. Of course chip has to be Microchip and should be assigned to different addresses!
17 |
18 | Basic Address: 00100 A2 A1 A0 (from 0x20 to 0x27)
19 | A2,A1,A0 tied to ground = 0x20
20 |
21 | IOB-0.[|...U...|] IOA-7
22 | IOB-1.[|.........|] IOA-6
23 | IOB-2.[|.........|] IOA-5
24 | IOB-3.[|.........|] IOA-4
25 | IOB-4.[|.........|] IOA-3
26 | IOB-5.[|.........|] IOA-2
27 | IOB-6.[|.........|] IOA-1
28 | IOB-7.[|.........|] IOA-0
29 | VCC...[|.........|] INT-A
30 | GND..[|.........|] INT-B
31 | CS.....[|.........|] RST (connect to +)
32 | SCK...[|.........|] A2
33 | MOSI.[|.........|] A1
34 | MISO.[|____|] A0
35 |
36 | -------------------------------------------------------------------------------- /examples/manyButtonsWithInterrupt/manyButtonsWithInterrupt.ino: -------------------------------------------------------------------------------- 1 | /* 2 | In this example we will use a MCP23S17 with 16 push buttons. 3 | The example show how to deal with registers to set-up the GPIO chip in different mode than default. 4 | It use a processor interrupt to trig the key decode. 5 | */ 6 | #include 7 | #include // import library 8 | 9 | #define GPIO_ADRS 0x20//SPI 10 | #define GPIO_CS 10 11 | #define GPIO_INTPIN 2 12 | 13 | volatile boolean keyPressed = false; 14 | 15 | gpio_MCP23S17 mcp(GPIO_CS, GPIO_ADRS); 16 | 17 | 18 | void setup() { 19 | pinMode(GPIO_INTPIN, INPUT); 20 | Serial.begin(38400); 21 | long unsigned debug_start = millis (); 22 | while (!Serial && ((millis () - debug_start) <= 5000)); 23 | Serial.println("started"); 24 | mcp.begin();//it will initialize SPI as well 25 | //setup the gpio! 26 | /* 7 6 5 4 3 2 1 0 27 | IOCON = BANK MIRROR SEQOP DISSLW HAEN ODR INTPOL -NC- */ 28 | mcp.gpioRegisterWriteByte(MCP23S17_IOCON, 0b01101100);//HAEN,SEQOP,MIRROR,OPENDRAIN(add a 10K res on INT) 0b01100000 29 | mcp.gpioRegisterWriteByte(MCP23S17_GPPU, 0xFF, true);//pull-up every port (A&B) 30 | mcp.gpioRegisterWriteByte(MCP23S17_IPOL, 0xFF, true);//invert polarity on every port (A&B) 31 | mcp.gpioRegisterWriteByte(MCP23S17_GPINTEN, 0xFF, true);//enable interrups on every port (A&B) 32 | mcp.gpioRegisterReadByte(MCP23S17_INTCAP); //read interrupt capture port A (it clear port) 33 | mcp.gpioRegisterReadByte(MCP23S17_INTCAP + 1);//read interrupt capture port B (it clear port) 34 | 35 | int intNumber = mcp.getInterruptNumber(GPIO_INTPIN); 36 | if (intNumber < 255) { 37 | attachInterrupt(intNumber, keypress, FALLING);//attack interrupt 38 | } else { 39 | Serial.println("sorry, pin has no INT capabilities!"); 40 | } 41 | 42 | } 43 | 44 | int onKeypress() { 45 | uint16_t keyValue = 0; 46 | delay(10);//debounce 47 | int result = -1;//default, no button pressed 48 | uint8_t i; 49 | keyPressed = false;//time to reset keypress 50 | 51 | if (mcp.gpioRegisterReadByte(MCP23S17_INTF)) { 52 | keyValue &= 0x00FF; 53 | keyValue |= mcp.gpioRegisterReadByte(MCP23S17_INTCAP); // read value at time of interrupt 54 | } 55 | if (mcp.gpioRegisterReadByte(MCP23S17_INTF + 1)) { 56 | keyValue &= 0xFF00; 57 | keyValue |= mcp.gpioRegisterReadByte(MCP23S17_INTCAP + 1) << 8; // port B is in low-order byte 58 | } 59 | for (i = 0; i < 16; i++) { 60 | if (keyValue & (1 << i)) { 61 | result = i; 62 | break; 63 | } 64 | } 65 | return result; 66 | } 67 | 68 | void loop() { 69 | if (keyPressed) { 70 | int key = onKeypress(); 71 | if (key >= 0) Serial.println(key); 72 | } 73 | } 74 | 75 | static void keypress() { 76 | keyPressed = true; 77 | } 78 | -------------------------------------------------------------------------------- /gpio_MCP23S17.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "gpio_MCP23S17.h" 8 | #include //this chip needs SPI 9 | 10 | 11 | 12 | gpio_MCP23S17::gpio_MCP23S17(){ 13 | 14 | } 15 | 16 | 17 | 18 | //return 255 if the choosed pin has no INT, otherwise return INT number 19 | //if there's support for SPI transactions it will use SPI.usingInterrupt(intNum); 20 | //to prevent problems from interrupt 21 | /*USE: 22 | int intNumber = mcp.getInterruptNumber(gpio_int_pin); 23 | if (intNumber < 255){ 24 | attachInterrupt(intNumber, keypress, FALLING);//attack interrupt 25 | } else { 26 | Serial.println("sorry, pin has no INT capabilities!"); 27 | } 28 | */ 29 | 30 | int gpio_MCP23S17::getInterruptNumber(byte pin) { 31 | int intNum = digitalPinToInterrupt(pin); 32 | if (intNum != NOT_AN_INTERRUPT) { 33 | #if defined (SPI_HAS_TRANSACTION) && !defined(ESP8266) 34 | SPI.usingInterrupt(intNum); 35 | #endif 36 | return intNum; 37 | } 38 | return 255; 39 | } 40 | 41 | 42 | 43 | 44 | #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) 45 | gpio_MCP23S17::gpio_MCP23S17(const uint8_t csPin,const uint8_t haenAdrs,const uint8_t mosi_pin,const uint8_t sclk_pin,const uint8_t miso_pin){ 46 | _mosi = mosi_pin; 47 | _miso = miso_pin; 48 | _sclk = sclk_pin; 49 | if (mosi_pin != 11 || sclk_pin != 13 || miso_pin != 12){ 50 | _useAltSPI = true; 51 | } else { 52 | _useAltSPI = false; 53 | } 54 | postSetup(csPin,haenAdrs); 55 | } 56 | 57 | void gpio_MCP23S17::postSetup(const uint8_t csPin,const uint8_t haenAdrs,const uint8_t mosi_pin,const uint8_t sclk_pin,const uint8_t miso_pin){ 58 | _cs = csPin; 59 | _mosi = mosi_pin; 60 | _miso = miso_pin; 61 | _sclk = sclk_pin; 62 | if (!_useAltSPI){ 63 | if (mosi_pin != 11 || sclk_pin != 13 || miso_pin != 12){ 64 | _useAltSPI = true; 65 | } else { 66 | _useAltSPI = false; 67 | } 68 | } 69 | if (haenAdrs > 0x19 && haenAdrs < 0x28){//HAEN works between 0x20...0x27 70 | _adrs = haenAdrs; 71 | _useHaen = 1; 72 | } else { 73 | _adrs = 0; 74 | _useHaen = 0; 75 | } 76 | _readCmd = (_adrs << 1) | 1; 77 | _writeCmd = _adrs << 1; 78 | } 79 | #else 80 | gpio_MCP23S17::gpio_MCP23S17(const uint8_t csPin,const uint8_t haenAdrs){ 81 | postSetup(csPin,haenAdrs); 82 | } 83 | 84 | void gpio_MCP23S17::postSetup(const uint8_t csPin,const uint8_t haenAdrs){ 85 | _cs = csPin; 86 | if (haenAdrs > 0x19 && haenAdrs < 0x28){//HAEN works between 0x20...0x27 87 | _adrs = haenAdrs; 88 | _useHaen = 1; 89 | } else { 90 | _adrs = 0; 91 | _useHaen = 0; 92 | } 93 | #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) 94 | _useAltSPI = false; 95 | #endif 96 | _readCmd = (_adrs << 1) | 1; 97 | _writeCmd = _adrs << 1; 98 | } 99 | #endif 100 | 101 | 102 | 103 | void gpio_MCP23S17::begin(bool protocolInitOverride) { 104 | if (!protocolInitOverride){ 105 | #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) 106 | if (_useAltSPI){ 107 | if ((_mosi == 11 || _mosi == 7) && (_miso == 12 || _miso == 8) && (_sclk == 13 || _sclk == 14)) {//valid SPI pins? 108 | SPI.setMOSI(_mosi); 109 | SPI.setMISO(_miso); 110 | SPI.setSCK(_sclk); 111 | } else { 112 | return; 113 | } 114 | } 115 | if (!SPI.pinIsChipSelect(_cs)) return; 116 | #endif 117 | SPI.begin(); 118 | #if !defined (SPI_HAS_TRANSACTION) 119 | SPI.setClockDivider(SPI_CLOCK_DIV4); // 4 MHz (half speed) 120 | SPI.setBitOrder(MSBFIRST); 121 | SPI.setDataMode(SPI_MODE0); 122 | #endif 123 | } 124 | pinMode(_cs, OUTPUT); 125 | #if defined(ESP8266) 126 | GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, _pinRegister(_cs));//H 127 | #else 128 | digitalWrite(_cs, HIGH); 129 | #endif 130 | delay(100); 131 | 132 | _useHaen == 1 ? _GPIOwriteByte(MCP23S17_IOCON,0b00101000) : _GPIOwriteByte(MCP23S17_IOCON,0b00100000); 133 | _gpioDirection = 0xFFFF;//all in 134 | _gpioState = 0xFFFF;//all low 135 | } 136 | 137 | 138 | 139 | uint16_t gpio_MCP23S17::gpioReadAddress(byte addr){ 140 | _GPIOstartSend(1); 141 | SPI.transfer(addr); 142 | #if !defined(__SAM3X8E__) && ((ARDUINO >= 160) || (TEENSYDUINO > 121)) 143 | uint16_t temp = SPI.transfer16(0x0); 144 | _GPIOendSend(); 145 | return temp; 146 | #else 147 | byte low_byte = SPI.transfer(0x0); 148 | byte high_byte = SPI.transfer(0x0); 149 | _GPIOendSend(); 150 | uint16_t temp = low_byte | (high_byte << 8); 151 | return temp; 152 | #endif 153 | } 154 | 155 | 156 | void gpio_MCP23S17::gpioPinMode(uint16_t mode){ 157 | if (mode == INPUT){ 158 | _gpioDirection = 0xFFFF; 159 | } else if (mode == OUTPUT){ 160 | _gpioDirection = 0x0000; 161 | _gpioState = 0x0000; 162 | } else { 163 | _gpioDirection = mode; 164 | } 165 | _GPIOwriteWord(MCP23S17_IODIR,_gpioDirection); 166 | } 167 | 168 | void gpio_MCP23S17::gpioPinMode(uint8_t pin, bool mode){ 169 | if (pin < 16){//0...15 170 | mode == INPUT ? _gpioDirection |= (1 << pin) :_gpioDirection &= ~(1 << pin); 171 | _GPIOwriteWord(MCP23S17_IODIR,_gpioDirection); 172 | } 173 | } 174 | 175 | 176 | void gpio_MCP23S17::gpioPort(uint16_t value){ 177 | if (value == HIGH){ 178 | _gpioState = 0xFFFF; 179 | } else if (value == LOW){ 180 | _gpioState = 0x0000; 181 | } else { 182 | _gpioState = value; 183 | } 184 | _GPIOwriteWord(MCP23S17_GPIO,_gpioState); 185 | } 186 | 187 | void gpio_MCP23S17::gpioPort(byte lowByte, byte highByte){ 188 | _gpioState = highByte | (lowByte << 8); 189 | _GPIOwriteWord(MCP23S17_GPIO,_gpioState); 190 | } 191 | 192 | 193 | uint16_t gpio_MCP23S17::readGpioPort(){ 194 | return gpioReadAddress(MCP23S17_GPIO); 195 | } 196 | 197 | uint16_t gpio_MCP23S17::readGpioPortFast(){ 198 | return _gpioState; 199 | } 200 | 201 | void gpio_MCP23S17::portPullup(uint16_t data) { 202 | if (data == HIGH){ 203 | _gpioState = 0xFFFF; 204 | } else if (data == LOW){ 205 | _gpioState = 0x0000; 206 | } else { 207 | _gpioState = data; 208 | } 209 | _GPIOwriteWord(MCP23S17_GPPU, _gpioState); 210 | } 211 | 212 | 213 | void gpio_MCP23S17::gpioDigitalWrite(uint8_t pin, bool value){ 214 | if (pin < 16){//0...15 215 | value == HIGH ? _gpioState |= (1 << pin) : _gpioState &= ~(1 << pin); 216 | _GPIOwriteWord(MCP23S17_GPIO,_gpioState); 217 | } 218 | } 219 | 220 | void gpio_MCP23S17::gpioDigitalWriteFast(uint8_t pin, bool value){ 221 | if (pin < 16){//0...15 222 | value == HIGH ? _gpioState |= (1 << pin) : _gpioState &= ~(1 << pin); 223 | } 224 | } 225 | 226 | void gpio_MCP23S17::gpioPortUpdate(){ 227 | _GPIOwriteWord(MCP23S17_GPIO,_gpioState); 228 | } 229 | 230 | int gpio_MCP23S17::gpioDigitalRead(uint8_t pin){ 231 | if (pin < 16) return (int)(gpioReadAddress(MCP23S17_GPIO) & 1 << pin); 232 | return 0; 233 | } 234 | 235 | 236 | int gpio_MCP23S17::gpioDigitalReadFast(uint8_t pin){ 237 | int temp = 0; 238 | if (pin < 16) temp = bitRead(_gpioState,pin); 239 | return temp; 240 | } 241 | 242 | uint8_t gpio_MCP23S17::gpioRegisterReadByte(byte reg){ 243 | uint8_t data = 0; 244 | _GPIOstartSend(1); 245 | SPI.transfer(reg); 246 | data = SPI.transfer(0); 247 | _GPIOendSend(); 248 | return data; 249 | } 250 | 251 | uint16_t gpio_MCP23S17::gpioRegisterReadWord(byte reg){ 252 | uint16_t data = 0; 253 | _GPIOstartSend(1); 254 | SPI.transfer(reg); 255 | #if !defined(__SAM3X8E__) && ((ARDUINO >= 160) || (TEENSYDUINO > 121)) 256 | data = SPI.transfer16(0); 257 | #else 258 | data = SPI.transfer(0); 259 | data = SPI.transfer(0) << 8; 260 | #endif 261 | _GPIOendSend(); 262 | return data; 263 | } 264 | 265 | void gpio_MCP23S17::gpioRegisterWriteByte(byte reg,byte data,bool both){ 266 | if (!both){ 267 | _GPIOwriteByte(reg,(byte)data); 268 | } else { 269 | _GPIOstartSend(0); 270 | SPI.transfer(reg); 271 | SPI.transfer(data); 272 | SPI.transfer(data); 273 | _GPIOendSend(); 274 | } 275 | } 276 | 277 | 278 | 279 | void gpio_MCP23S17::gpioRegisterWriteWord(byte reg,word data){ 280 | _GPIOwriteWord(reg,(word)data); 281 | } 282 | 283 | 284 | 285 | 286 | -------------------------------------------------------------------------------- /gpio_MCP23S17.h: -------------------------------------------------------------------------------- 1 | /* 2 | ___ _ _ _ __ ___ ___ | |_ ___ _ _ 3 | / __|| | | || '_ ` _ \ / _ \ | __|/ _ \ | | | | 4 | \__ \| |_| || | | | | || (_) || |_| (_) || |_| | 5 | |___/ \__,_||_| |_| |_| \___/ \__|\___/ \__, | 6 | |___/ 7 | 8 | gpio_MCP23S17 - A complete library for Microchip MCP23S17 for many MCU's. 9 | 10 | model: company: pins: protocol: Special Features: 11 | --------------------------------------------------------------------------------------------------------------------- 12 | mcp23s17 Microchip 16 SPI INT/HAEN 13 | --------------------------------------------------------------------------------------------------------------------- 14 | Version history: 15 | 0.9 Fixed an example, added getInterruptNumber function. 16 | 0.95 Added compatibility with ESP8266 17 | --------------------------------------------------------------------------------------------------------------------- 18 | Copyright (c) 2013-2014, s.u.m.o.t.o.y [sumotoy(at)gmail.com] 19 | --------------------------------------------------------------------------------------------------------------------- 20 | 21 | gpio_MCP23S17 Library is free software: you can redistribute it and/or modify 22 | it under the terms of the GNU General Public License as published by 23 | the Free Software Foundation, either version 3 of the License, or 24 | (at your option) any later version. 25 | 26 | gpio_MCP23S17 Library is distributed in the hope that it will be useful, 27 | but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 | GNU General Public License for more details. 30 | 31 | You should have received a copy of the GNU General Public License 32 | along with Foobar. If not, see . 33 | 34 | */ 35 | 36 | /* ------------------------------ MCP23S17 WIRING ------------------------------------ 37 | This chip has a very useful feature called HAEN that allow you to share the same CS pin trough 38 | 8 different addresses. Of course chip has to be Microchip and should be assigned to different addresses! 39 | 40 | Basic Address: 00100 A2 A1 A0 (from 0x20 to 0x27) 41 | A2,A1,A0 tied to ground = 0x20 42 | __ __ 43 | IOB-0 [| U |] IOA-7 44 | IOB-1 [| |] IOA-6 45 | IOB-2 [| |] IOA-5 46 | IOB-3 [| |] IOA-4 47 | IOB-4 [| |] IOA-3 48 | IOB-5 [| |] IOA-2 49 | IOB-6 [| |] IOA-1 50 | IOB-7 [| |] IOA-0 51 | ++++ [| |] INT-A 52 | GND [| |] INT-B 53 | CS [| |] RST (connect to +) 54 | SCK [| |] A2 55 | MOSI [| |] A1 56 | MISO [|_____|] A0 57 | */ 58 | #ifndef _GPIO_MCP23S17_H_ 59 | #define _GPIO_MCP23S17_H_ 60 | 61 | #include 62 | 63 | #include //this chip needs SPI 64 | 65 | #if defined (SPI_HAS_TRANSACTION) 66 | #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) 67 | const static uint32_t _MCPMaxSpeed = 30000000UL; 68 | #elif defined(ESP8266) 69 | const static uint32_t _MCPMaxSpeed = 80000000UL; 70 | #elif defined(__MKL26Z64__) //Teensy LC 71 | const static uint32_t _MCPMaxSpeed = 12000000UL; 72 | #elif defined(ARDUINO) && defined(__arm__) && !defined(CORE_TEENSY) //DUE 73 | const static uint32_t _MCPMaxSpeed = 24000000UL; 74 | #elif defined(__32MX320F128H__) || defined(__32MX795F512L__) //uno and max chipkit 75 | const static uint32_t _MCPMaxSpeed = 8000000UL; 76 | #elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) 77 | const static uint32_t _MCPMaxSpeed = 2000000UL; 78 | #elif defined (__AVR_ATmega328P__) || defined (__AVR_ATmega168P__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644PA__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) 79 | const static uint32_t _MCPMaxSpeed = 8000000UL; 80 | #else 81 | const static uint32_t _MCPMaxSpeed = 2000000UL; 82 | #endif 83 | #endif 84 | 85 | 86 | /* The IOCON register! 87 | 7 6 5 4 3 2 1 0 88 | IOCON = BANK MIRROR SEQOP DISSLW HAEN ODR INTPOL -NC- 89 | ----------------------------------------------------------------------- 90 | 0b01101100 91 | BANK: (Controls how the registers are addressed) 92 | 1 The registers associated with each port are separated into different banks 93 | 0 The registers are in the same bank (addresses are sequential) 94 | MIRROR: (INT Pins Mirror bit) 95 | 1 The INT pins are internally connected 96 | 0 The INT pins are not connected. INTA is associated with PortA and INTB is associated with PortB 97 | SEQOP: (Sequential Operation mode bit) 98 | 1 Sequential operation disabled, address pointer does not increment 99 | 0 Sequential operation enabled, address pointer increments. 100 | DISSLW: (Slew Rate control bit for SDA output, only I2C) 101 | HAEN: (Hardware Address Enable bit, SPI only) 102 | 1 Enables the MCP23S17 address pins 103 | 0 Disables the MCP23S17 address pins 104 | ODR: (This bit configures the INT pin as an open-drain output) 105 | 1 Open-drain output (overrides the INTPOL bit). 106 | 0 Active driver output (INTPOL bit sets the polarity). 107 | INTPOL: (This bit sets the polarity of the INT output pin) 108 | 1 Active high 109 | 0 Active low 110 | */ 111 | 112 | #include "_includes/MCP23S17_registers.h" 113 | 114 | #if defined(ESP8266) 115 | #include 116 | #endif 117 | 118 | class gpio_MCP23S17 { 119 | 120 | public: 121 | 122 | #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) 123 | gpio_MCP23S17(const uint8_t csPin,const uint8_t haenAdrs,const uint8_t mosi_pin=11,const uint8_t sclk_pin=13,const uint8_t miso_pin=12);//any pin,0x20....0x27 124 | void postSetup(const uint8_t csPin,const uint8_t haenAdrs,const uint8_t mosi_pin=11,const uint8_t sclk_pin=13,const uint8_t miso_pin=12);//used with other libraries only 125 | #else 126 | gpio_MCP23S17(const uint8_t csPin,const uint8_t haenAdrs);//any pin,0x20....0x27 127 | void postSetup(const uint8_t csPin,const uint8_t haenAdrs);//used with other libraries only 128 | #endif 129 | gpio_MCP23S17();//For include inside other libraries 130 | 131 | void begin(bool protocolInitOverride=false); //protocolInitOverride=true will not init the SPI 132 | 133 | void gpioPinMode(uint16_t mode); //OUTPUT=all out,INPUT=all in,0xxxx=you choose 134 | void gpioPinMode(uint8_t pin, bool mode); //set a unique pin as IN(1) or OUT (0) 135 | void gpioPort(uint16_t value); //HIGH=all Hi, LOW=all Low,0xxxx=you choose witch low or hi 136 | void gpioPort(byte lowByte, byte highByte); //same as abowe but uses 2 separate bytes 137 | uint16_t readGpioPort(); //read the state of the pins (all) 138 | uint16_t readGpioPortFast(); 139 | 140 | void gpioDigitalWrite(uint8_t pin, bool value); //write data to one pin 141 | void gpioDigitalWriteFast(uint8_t pin, bool value); 142 | int gpioDigitalRead(uint8_t pin); //read data from one pin 143 | uint8_t gpioRegisterReadByte(byte reg); //read a byte from chip register 144 | uint16_t gpioRegisterReadWord(byte reg); //read a word from chip register 145 | int gpioDigitalReadFast(uint8_t pin); 146 | void gpioRegisterWriteByte(byte reg,byte data,bool both=false);//write a byte in a chip register, optional for both ports 147 | //if both=true it will write the same register in bank A & B 148 | void gpioRegisterWriteWord(byte reg,word data); //write a word in a chip register 149 | void portPullup(uint16_t data); // HIGH=all pullup, LOW=all pulldown,0xxxx=you choose witch 150 | void gpioPortUpdate(); 151 | int getInterruptNumber(byte pin); 152 | // direct access commands 153 | uint16_t gpioReadAddress(byte addr); 154 | 155 | 156 | 157 | protected: 158 | 159 | 160 | inline __attribute__((always_inline)) 161 | void _GPIOstartSend(bool mode) { 162 | #if defined (SPI_HAS_TRANSACTION) 163 | SPI.beginTransaction(SPISettings(_MCPMaxSpeed, MSBFIRST, SPI_MODE0)); 164 | #endif 165 | #if defined(ESP8266) 166 | GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, _pinRegister(_cs));//L 167 | #elif defined(__FASTWRITE) 168 | digitalWriteFast(_cs, LOW); 169 | #else 170 | digitalWrite(_cs, LOW); 171 | #endif 172 | mode == 1 ? SPI.transfer(_readCmd) : SPI.transfer(_writeCmd); 173 | } 174 | 175 | 176 | inline __attribute__((always_inline)) 177 | void _GPIOendSend(void){ 178 | #if defined(ESP8266) 179 | GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, _pinRegister(_cs));//H 180 | #elif defined(__FASTWRITE) 181 | digitalWriteFast(_cs, HIGH); 182 | #else 183 | digitalWrite(_cs, HIGH); 184 | #endif 185 | 186 | #if defined (SPI_HAS_TRANSACTION) 187 | SPI.endTransaction(); 188 | #endif 189 | } 190 | 191 | inline __attribute__((always_inline)) 192 | void _GPIOwriteByte(byte addr, byte data){ 193 | _GPIOstartSend(0); 194 | SPI.transfer(addr); 195 | SPI.transfer(data); 196 | _GPIOendSend(); 197 | } 198 | 199 | inline __attribute__((always_inline)) 200 | void _GPIOwriteWord(byte addr, uint16_t data){ 201 | _GPIOstartSend(0); 202 | SPI.transfer(addr); 203 | #if !defined(__SAM3X8E__) && ((ARDUINO >= 160) || (TEENSYDUINO > 121)) 204 | SPI.transfer16(data); 205 | #else 206 | //SPI.transfer(word2lowByte(data)); 207 | //SPI.transfer(word2highByte(data)); 208 | SPI.transfer(data >> 8); 209 | SPI.transfer(data & 0xFF); 210 | #endif 211 | _GPIOendSend(); 212 | } 213 | 214 | #if defined(ESP8266) 215 | uint32_t _pinRegister(uint8_t pin) 216 | __attribute__((always_inline)) { 217 | return _BV(pin); 218 | } 219 | #endif 220 | 221 | 222 | 223 | private: 224 | uint8_t _cs; 225 | uint8_t _adrs; 226 | #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) 227 | uint8_t _miso, _mosi, _sclk; 228 | boolean _useAltSPI; 229 | #endif 230 | 231 | 232 | uint8_t _useHaen; 233 | uint8_t _readCmd; 234 | uint8_t _writeCmd; 235 | uint16_t _gpioDirection; 236 | uint16_t _gpioState; 237 | }; 238 | #endif --------------------------------------------------------------------------------