|
| 1 | +/*************************************************** |
| 2 | + This is a library for the MCP23017 i2c port expander |
| 3 | +
|
| 4 | + These displays use I2C to communicate, 2 pins are required to |
| 5 | + interface |
| 6 | + Adafruit invests time and resources providing this open source code, |
| 7 | + please support Adafruit and open-source hardware by purchasing |
| 8 | + products from Adafruit! |
| 9 | +
|
| 10 | + Written by Limor Fried/Ladyada for Adafruit Industries. |
| 11 | + BSD license, all text above must be included in any redistribution |
| 12 | + ****************************************************/ |
| 13 | + |
| 14 | +#include <stdint.h> |
| 15 | +#include "Adafruit_MCP23017.h" |
| 16 | +#include <driver/gpio.h> |
| 17 | +#include <driver/i2c.h> |
| 18 | + |
| 19 | + |
| 20 | + |
| 21 | +// minihelper to keep Arduino backward compatibility |
| 22 | +/* |
| 23 | +static inline void wiresend(uint8_t x) { |
| 24 | + //Wire.write((uint8_t) x); |
| 25 | + //i2c_master_write(cmd, &x, 1, 0); |
| 26 | +} |
| 27 | +*/ |
| 28 | + |
| 29 | +/* |
| 30 | +static inline uint8_t wirerecv(void) { |
| 31 | +
|
| 32 | + //return Wire.read(); |
| 33 | + return 0; |
| 34 | +} |
| 35 | +*/ |
| 36 | + |
| 37 | +static int bitRead(uint32_t x, uint8_t n) { |
| 38 | + return ((x & 1<<n) != 0); |
| 39 | +} |
| 40 | + |
| 41 | +static uint32_t bitWrite(uint32_t x, uint8_t n, uint8_t b) { |
| 42 | + return ((x & (~(1<< n))) | (b << n)); |
| 43 | +} |
| 44 | + |
| 45 | +/** |
| 46 | + * Bit number associated to a give Pin |
| 47 | + */ |
| 48 | +uint8_t Adafruit_MCP23017::bitForPin(uint8_t pin){ |
| 49 | + return pin%8; |
| 50 | +} |
| 51 | + |
| 52 | +/** |
| 53 | + * Register address, port dependent, for a given PIN |
| 54 | + */ |
| 55 | +uint8_t Adafruit_MCP23017::regForPin(uint8_t pin, uint8_t portAaddr, uint8_t portBaddr){ |
| 56 | + return (pin<8)?portAaddr:portBaddr; |
| 57 | +} |
| 58 | + |
| 59 | +/** |
| 60 | + * Reads a given register |
| 61 | + */ |
| 62 | +uint8_t Adafruit_MCP23017::readRegister(uint8_t addr){ |
| 63 | + // read the current GPINTEN |
| 64 | + //Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); |
| 65 | + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); |
| 66 | + i2c_master_start(cmd); |
| 67 | + i2c_master_write_byte(cmd, (MCP23017_ADDRESS << 1) | I2C_MASTER_WRITE, 1 /* expect ack */); |
| 68 | + //wiresend(addr); |
| 69 | + i2c_master_write_byte(cmd, addr, 1); |
| 70 | + //Wire.endTransmission(); |
| 71 | + i2c_master_stop(cmd); |
| 72 | + i2c_master_cmd_begin(I2C_NUM_0, cmd, 0); |
| 73 | + i2c_cmd_link_delete(cmd); |
| 74 | + //Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1); |
| 75 | + cmd = i2c_cmd_link_create(); |
| 76 | + i2c_master_start(cmd); |
| 77 | + i2c_master_write_byte(cmd, (MCP23017_ADDRESS << 1) | I2C_MASTER_READ, 1 /* expect ack */); |
| 78 | + uint8_t tmpByte; |
| 79 | + i2c_master_read_byte(cmd, &tmpByte, 1); |
| 80 | + i2c_master_cmd_begin(I2C_NUM_0, cmd, 0); |
| 81 | + i2c_cmd_link_delete(cmd); |
| 82 | + return tmpByte; |
| 83 | +} |
| 84 | + |
| 85 | + |
| 86 | +/** |
| 87 | + * Writes a given register |
| 88 | + */ |
| 89 | +void Adafruit_MCP23017::writeRegister(uint8_t regAddr, uint8_t regValue){ |
| 90 | + // Write the register |
| 91 | + //Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); |
| 92 | + //wiresend(regAddr); |
| 93 | + //wiresend(regValue); |
| 94 | + //Wire.endTransmission(); |
| 95 | + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); |
| 96 | + i2c_master_start(cmd); |
| 97 | + i2c_master_write_byte(cmd, (MCP23017_ADDRESS << 1) | I2C_MASTER_WRITE, 1 /* expect ack */); |
| 98 | + i2c_master_write_byte(cmd, regAddr, 1); |
| 99 | + i2c_master_write_byte(cmd, regValue, 1); |
| 100 | + i2c_master_cmd_begin(I2C_NUM_0, cmd, 0); |
| 101 | + i2c_cmd_link_delete(cmd); |
| 102 | +} |
| 103 | + |
| 104 | + |
| 105 | +/** |
| 106 | + * Helper to update a single bit of an A/B register. |
| 107 | + * - Reads the current register value |
| 108 | + * - Writes the new register value |
| 109 | + */ |
| 110 | +void Adafruit_MCP23017::updateRegisterBit(uint8_t pin, uint8_t pValue, uint8_t portAaddr, uint8_t portBaddr) { |
| 111 | + uint8_t regValue; |
| 112 | + uint8_t regAddr=regForPin(pin,portAaddr,portBaddr); |
| 113 | + uint8_t bit=bitForPin(pin); |
| 114 | + regValue = readRegister(regAddr); |
| 115 | + |
| 116 | + // set the value for the particular bit |
| 117 | + regValue = bitWrite(regValue,bit,pValue); |
| 118 | + |
| 119 | + writeRegister(regAddr,regValue); |
| 120 | +} |
| 121 | + |
| 122 | +//////////////////////////////////////////////////////////////////////////////// |
| 123 | + |
| 124 | +/** |
| 125 | + * Initializes the MCP23017 given its HW selected address, see datasheet for Address selection. |
| 126 | + */ |
| 127 | +void Adafruit_MCP23017::begin(uint8_t addr) { |
| 128 | + if (addr > 7) { |
| 129 | + addr = 7; |
| 130 | + } |
| 131 | + i2caddr = addr; |
| 132 | + |
| 133 | + //Wire.begin(); |
| 134 | + |
| 135 | + // set defaults! |
| 136 | + // all inputs on port A and B |
| 137 | + writeRegister(MCP23017_IODIRA,0xff); |
| 138 | + writeRegister(MCP23017_IODIRB,0xff); |
| 139 | +} |
| 140 | + |
| 141 | +/** |
| 142 | + * Initializes the default MCP23017, with 000 for the configurable part of the address |
| 143 | + */ |
| 144 | +void Adafruit_MCP23017::begin(void) { |
| 145 | + begin(0); |
| 146 | +} |
| 147 | + |
| 148 | +/** |
| 149 | + * Sets the pin mode to either INPUT or OUTPUT |
| 150 | + */ |
| 151 | +void Adafruit_MCP23017::pinMode(uint8_t p, uint8_t d) { |
| 152 | + updateRegisterBit(p,(d==GPIO_MODE_INPUT),MCP23017_IODIRA,MCP23017_IODIRB); |
| 153 | +} |
| 154 | + |
| 155 | +/** |
| 156 | + * Reads all 16 pins (port A and B) into a single 16 bits variable. |
| 157 | + */ |
| 158 | +uint16_t Adafruit_MCP23017::readGPIOAB() { |
| 159 | + uint16_t ba = 0; |
| 160 | + uint8_t a; |
| 161 | + |
| 162 | + // read the current GPIO output latches |
| 163 | + /* |
| 164 | + Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); |
| 165 | + wiresend(MCP23017_GPIOA); |
| 166 | + Wire.endTransmission(); |
| 167 | + */ |
| 168 | + |
| 169 | + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); |
| 170 | + i2c_master_start(cmd); |
| 171 | + i2c_master_write_byte(cmd, (MCP23017_ADDRESS << 1) | I2C_MASTER_WRITE, 1 /* expect ack */); |
| 172 | + i2c_master_write_byte(cmd, MCP23017_GPIOA, 1); |
| 173 | + i2c_master_cmd_begin(I2C_NUM_0, cmd, 0); |
| 174 | + i2c_cmd_link_delete(cmd); |
| 175 | + |
| 176 | + /* |
| 177 | + Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 2); |
| 178 | +
|
| 179 | + a = wirerecv(); |
| 180 | + ba = wirerecv(); |
| 181 | + ba <<= 8; |
| 182 | + ba |= a; |
| 183 | + */ |
| 184 | + |
| 185 | + cmd = i2c_cmd_link_create(); |
| 186 | + i2c_master_start(cmd); |
| 187 | + i2c_master_write_byte(cmd, (MCP23017_ADDRESS << 1) | I2C_MASTER_READ, 1 /* expect ack */); |
| 188 | + i2c_master_read_byte(cmd, &a, 1); |
| 189 | + i2c_master_read_byte(cmd, (uint8_t *)&ba, 1); |
| 190 | + i2c_master_cmd_begin(I2C_NUM_0, cmd, 0); |
| 191 | + i2c_cmd_link_delete(cmd); |
| 192 | + |
| 193 | + ba <<= 8; |
| 194 | + ba |= a; |
| 195 | + |
| 196 | + return ba; |
| 197 | +} |
| 198 | + |
| 199 | +/** |
| 200 | + * Read a single port, A or B, and return its current 8 bit value. |
| 201 | + * Parameter b should be 0 for GPIOA, and 1 for GPIOB. |
| 202 | + */ |
| 203 | +uint8_t Adafruit_MCP23017::readGPIO(uint8_t b) { |
| 204 | + |
| 205 | + // read the current GPIO output latches |
| 206 | + /* |
| 207 | + Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); |
| 208 | + if (b == 0) |
| 209 | + wiresend(MCP23017_GPIOA); |
| 210 | + else { |
| 211 | + wiresend(MCP23017_GPIOB); |
| 212 | + } |
| 213 | + Wire.endTransmission(); |
| 214 | + */ |
| 215 | + |
| 216 | + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); |
| 217 | + i2c_master_start(cmd); |
| 218 | + i2c_master_write_byte(cmd, (MCP23017_ADDRESS << 1) | I2C_MASTER_WRITE, 1 /* expect ack */); |
| 219 | + if (b == 0) { |
| 220 | + i2c_master_write_byte(cmd, MCP23017_GPIOA, 1); |
| 221 | + } else { |
| 222 | + i2c_master_write_byte(cmd, MCP23017_GPIOB, 1); |
| 223 | + } |
| 224 | + i2c_master_cmd_begin(I2C_NUM_0, cmd, 0); |
| 225 | + i2c_cmd_link_delete(cmd); |
| 226 | + |
| 227 | + /* |
| 228 | + Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1); |
| 229 | + return wirerecv(); |
| 230 | + */ |
| 231 | + |
| 232 | + uint8_t byte; |
| 233 | + cmd = i2c_cmd_link_create(); |
| 234 | + i2c_master_start(cmd); |
| 235 | + i2c_master_write_byte(cmd, (MCP23017_ADDRESS << 1) | I2C_MASTER_READ, 1 /* expect ack */); |
| 236 | + i2c_master_read_byte(cmd, &byte, 1); |
| 237 | + i2c_master_cmd_begin(I2C_NUM_0, cmd, 0); |
| 238 | + i2c_cmd_link_delete(cmd); |
| 239 | + return byte; |
| 240 | +} |
| 241 | + |
| 242 | +/** |
| 243 | + * Writes all the pins in one go. This method is very useful if you are implementing a multiplexed matrix and want to get a decent refresh rate. |
| 244 | + */ |
| 245 | +void Adafruit_MCP23017::writeGPIOAB(uint16_t ba) { |
| 246 | + /* |
| 247 | + Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); |
| 248 | + wiresend(MCP23017_GPIOA); |
| 249 | + wiresend(ba & 0xFF); |
| 250 | + wiresend(ba >> 8); |
| 251 | + Wire.endTransmission(); |
| 252 | + */ |
| 253 | + |
| 254 | + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); |
| 255 | + i2c_master_start(cmd); |
| 256 | + i2c_master_write_byte(cmd, (MCP23017_ADDRESS << 1) | I2C_MASTER_WRITE, 1 /* expect ack */); |
| 257 | + i2c_master_write_byte(cmd, MCP23017_GPIOA, 1); |
| 258 | + i2c_master_write_byte(cmd, ba & 0xFF, 1); |
| 259 | + i2c_master_write_byte(cmd, ba >> 8, 1); |
| 260 | + i2c_master_cmd_begin(I2C_NUM_0, cmd, 0); |
| 261 | + i2c_cmd_link_delete(cmd); |
| 262 | +} |
| 263 | + |
| 264 | +void Adafruit_MCP23017::digitalWrite(uint8_t pin, uint8_t d) { |
| 265 | + uint8_t gpio; |
| 266 | + uint8_t bit=bitForPin(pin); |
| 267 | + |
| 268 | + |
| 269 | + // read the current GPIO output latches |
| 270 | + uint8_t regAddr=regForPin(pin,MCP23017_OLATA,MCP23017_OLATB); |
| 271 | + gpio = readRegister(regAddr); |
| 272 | + |
| 273 | + // set the pin and direction |
| 274 | + gpio = bitWrite(gpio,bit,d); |
| 275 | + |
| 276 | + // write the new GPIO |
| 277 | + regAddr=regForPin(pin,MCP23017_GPIOA,MCP23017_GPIOB); |
| 278 | + writeRegister(regAddr,gpio); |
| 279 | +} |
| 280 | + |
| 281 | +void Adafruit_MCP23017::pullUp(uint8_t p, uint8_t d) { |
| 282 | + updateRegisterBit(p,d,MCP23017_GPPUA,MCP23017_GPPUB); |
| 283 | +} |
| 284 | + |
| 285 | +uint8_t Adafruit_MCP23017::digitalRead(uint8_t pin) { |
| 286 | + uint8_t bit=bitForPin(pin); |
| 287 | + uint8_t regAddr=regForPin(pin,MCP23017_GPIOA,MCP23017_GPIOB); |
| 288 | + return (readRegister(regAddr) >> bit) & 0x1; |
| 289 | +} |
| 290 | + |
| 291 | +/** |
| 292 | + * Configures the interrupt system. both port A and B are assigned the same configuration. |
| 293 | + * Mirroring will OR both INTA and INTB pins. |
| 294 | + * Opendrain will set the INT pin to value or open drain. |
| 295 | + * polarity will set LOW or HIGH on interrupt. |
| 296 | + * Default values after Power On Reset are: (false,flase, LOW) |
| 297 | + * If you are connecting the INTA/B pin to arduino 2/3, you should configure the interupt handling as FALLING with |
| 298 | + * the default configuration. |
| 299 | + */ |
| 300 | +void Adafruit_MCP23017::setupInterrupts(uint8_t mirroring, uint8_t openDrain, uint8_t polarity){ |
| 301 | + // configure the port A |
| 302 | + uint8_t ioconfValue=readRegister(MCP23017_IOCONA); |
| 303 | + ioconfValue = bitWrite(ioconfValue,6,mirroring); |
| 304 | + ioconfValue = bitWrite(ioconfValue,2,openDrain); |
| 305 | + ioconfValue = bitWrite(ioconfValue,1,polarity); |
| 306 | + writeRegister(MCP23017_IOCONA,ioconfValue); |
| 307 | + |
| 308 | + // Configure the port B |
| 309 | + ioconfValue=readRegister(MCP23017_IOCONB); |
| 310 | + ioconfValue = bitWrite(ioconfValue,6,mirroring); |
| 311 | + ioconfValue = bitWrite(ioconfValue,2,openDrain); |
| 312 | + ioconfValue = bitWrite(ioconfValue,1,polarity); |
| 313 | + writeRegister(MCP23017_IOCONB,ioconfValue); |
| 314 | +} |
| 315 | + |
| 316 | +/** |
| 317 | + * Set's up a pin for interrupt. uses arduino MODEs: CHANGE, FALLING, RISING. |
| 318 | + * |
| 319 | + * Note that the interrupt condition finishes when you read the information about the port / value |
| 320 | + * that caused the interrupt or you read the port itself. Check the datasheet can be confusing. |
| 321 | + * |
| 322 | + */ |
| 323 | +void Adafruit_MCP23017::setupInterruptPin(uint8_t pin, uint8_t mode) { |
| 324 | + |
| 325 | + // set the pin interrupt control (0 means change, 1 means compare against given value); |
| 326 | +// updateRegisterBit(pin,(mode!=CHANGE),MCP23017_INTCONA,MCP23017_INTCONB); |
| 327 | + // if the mode is not CHANGE, we need to set up a default value, different value triggers interrupt |
| 328 | + |
| 329 | + // In a RISING interrupt the default value is 0, interrupt is triggered when the pin goes to 1. |
| 330 | + // In a FALLING interrupt the default value is 1, interrupt is triggered when pin goes to 0. |
| 331 | +// updateRegisterBit(pin,(mode==FALLING),MCP23017_DEFVALA,MCP23017_DEFVALB); |
| 332 | + |
| 333 | + // enable the pin for interrupt |
| 334 | +// updateRegisterBit(pin,HIGH,MCP23017_GPINTENA,MCP23017_GPINTENB); |
| 335 | + |
| 336 | +} |
| 337 | + |
| 338 | +uint8_t Adafruit_MCP23017::getLastInterruptPin(){ |
| 339 | + uint8_t intf; |
| 340 | + |
| 341 | + // try port A |
| 342 | + intf=readRegister(MCP23017_INTFA); |
| 343 | + for(int i=0;i<8;i++) if (bitRead(intf,i)) return i; |
| 344 | + |
| 345 | + // try port B |
| 346 | + intf=readRegister(MCP23017_INTFB); |
| 347 | + for(int i=0;i<8;i++) if (bitRead(intf,i)) return i+8; |
| 348 | + |
| 349 | + return MCP23017_INT_ERR; |
| 350 | + |
| 351 | +} |
| 352 | +uint8_t Adafruit_MCP23017::getLastInterruptPinValue(){ |
| 353 | + uint8_t intPin=getLastInterruptPin(); |
| 354 | + if(intPin!=MCP23017_INT_ERR){ |
| 355 | + uint8_t intcapreg=regForPin(intPin,MCP23017_INTCAPA,MCP23017_INTCAPB); |
| 356 | + uint8_t bit=bitForPin(intPin); |
| 357 | + return (readRegister(intcapreg)>>bit) & (0x01); |
| 358 | + } |
| 359 | + |
| 360 | + return MCP23017_INT_ERR; |
| 361 | +} |
| 362 | + |
| 363 | + |
0 commit comments