Skip to content

Commit

Permalink
Fix #40, add interrupt functions
Browse files Browse the repository at this point in the history
  • Loading branch information
RobTillaart committed May 19, 2024
1 parent 594d0eb commit aac927b
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 29 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [0.5.2] - 2024-05-19
- Fix #40, add interrupt functions
- update **MCP23S17_registers.h**
- change return type of several functions
- e.g **bool enable/disableControlRegister()**
- update readme.md
- update keywords.txt


## [0.5.1] - 2024-03-02
Expand Down
146 changes: 138 additions & 8 deletions MCP23S17.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,136 @@ bool MCP23S17::getPullup16(uint16_t &mask)
}


///////////////////////////////////////////////////
//
// INTERRUPTS (experimental, see #40)
//
// TODO, catch writeReg errors
// TODO, MCP23S17_INT_MODE_ERROR?
// TODO, if register not changed no need to update?
// TODO, 8 bits optimize? more code vs speed?
//
// pin = 0..15, mode = { RISING, FALLING, CHANGE }
bool MCP23S17::enableInterrupt(uint8_t pin, uint8_t mode)
{
if (pin > 15)
{
_error = MCP23S17_PIN_ERROR;
return false;
}

// right mode
uint16_t intcon = readReg16(MCP23S17_INTCON_A);
if (mode == CHANGE)
{
// compare to previous value.
intcon &= ~(1 << pin);
}
else
{
uint16_t defval = readReg16(MCP23S17_DEFVAL_A);
if (mode == RISING)
{
intcon |= (1 << pin);
defval &= ~(1 << pin); // RISING == compare to 0
}
else if (mode == FALLING)
{
intcon |= (1 << pin);
defval |= ~(1 << pin); // FALLING == compare to 1
}
writeReg16(MCP23S17_DEFVAL_A, defval);
}
writeReg16(MCP23S17_INTCON_A, intcon);

// enable interrupt
uint16_t value = readReg16(MCP23S17_GPINTEN_A);
value |= (1 << pin);
return writeReg16(MCP23S17_GPINTEN_A, value);
}


bool MCP23S17::disableInterrupt(uint8_t pin)
{
if (pin > 15)
{
_error = MCP23S17_PIN_ERROR;
return false;
}
// disable interrupt
uint16_t value = readReg16(MCP23S17_GPINTEN_A);
value &= ~(1 << pin);
return writeReg16(MCP23S17_GPINTEN_A, value);
}


bool MCP23S17::enableInterrupt16(uint16_t mask, uint8_t mode)
{
uint16_t intcon = 0, defval = 0;
// right mode
if (mode == CHANGE)
{
// compare to previous value.
intcon = ~mask;
}
else
{
if (mode == RISING)
{
intcon = mask;
defval = ~mask; // RISING == compare to 0
}
else if (mode == FALLING)
{
intcon = mask;
defval = ~mask; // FALLING == compare to 1
}
writeReg16(MCP23S17_DEFVAL_A, defval);
}
writeReg16(MCP23S17_INTCON_A, intcon);

// enable the mask
writeReg16(MCP23S17_GPINTEN_A, mask);
return true;
}


bool MCP23S17::disableInterrupt16(uint16_t mask)
{
return writeReg16(MCP23S17_GPINTEN_A, ~mask);
}


// which pins caused the INT?
uint16_t MCP23S17::getInterruptFlagRegister()
{
return readReg(MCP23S17_INTF_A);
}


uint16_t MCP23S17::getInterruptCaptureRegister()
{
return readReg(MCP23S17_INTCAP_A);
}


bool MCP23S17::mirrorInterrupts(bool on)
{
if (on) return enableControlRegister(MCP23S17_IOCR_MIRROR);
return disableControlRegister(MCP23S17_IOCR_MIRROR);
}


bool MCP23S17::isMirroredInterrupts()
{
return (readReg(MCP23S17_IOCR) & MCP23S17_IOCR_MIRROR) > 0;
}


/////////////////////////////////////////////
//
// MISC
//
int MCP23S17::lastError()
{
int e = _error;
Expand All @@ -555,31 +685,31 @@ int MCP23S17::lastError()
}


void MCP23S17::enableControlRegister(uint8_t mask)
bool MCP23S17::enableControlRegister(uint8_t mask)
{
uint8_t reg = readReg(MCP23S17_IOCR);
reg |= mask;
writeReg(MCP23S17_IOCR, reg);
return writeReg(MCP23S17_IOCR, reg);
}


void MCP23S17::disableControlRegister(uint8_t mask)
bool MCP23S17::disableControlRegister(uint8_t mask)
{
uint8_t reg = readReg(MCP23S17_IOCR);
reg &= ~mask;
writeReg(MCP23S17_IOCR, reg);
return writeReg(MCP23S17_IOCR, reg);
}


void MCP23S17::enableHardwareAddress()
bool MCP23S17::enableHardwareAddress()
{
enableControlRegister(MCP23S17_IOCR_HAEN);
return enableControlRegister(MCP23S17_IOCR_HAEN);
}


void MCP23S17::disableHardwareAddress()
bool MCP23S17::disableHardwareAddress()
{
disableControlRegister(MCP23S17_IOCR_HAEN);
return disableControlRegister(MCP23S17_IOCR_HAEN);
}


Expand Down
27 changes: 23 additions & 4 deletions MCP23S17.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,25 @@ class MCP23S17
bool getPullup16(uint16_t &mask);


// INTERRUPTS (experimental)
// pin = 0..15, mode = { RISING, FALLING, CHANGE }
bool enableInterrupt(uint8_t pin, uint8_t mode);
bool disableInterrupt(uint8_t pin);

// mask = 0x0000..0xFFFF (overrides all earlier settings.
bool enableInterrupt16(uint16_t mask, uint8_t mode);
bool disableInterrupt16(uint16_t mask);

// which pins caused the INT?
uint16_t getInterruptFlagRegister();
uint16_t getInterruptCaptureRegister();

// merge INTA and INTB
bool mirrorInterrupts(bool on);
bool isMirroredInterrupts();


// SPI
// speed in Hz
void setSPIspeed(uint32_t speed);
uint32_t getSPIspeed() { return _SPIspeed; };
Expand All @@ -97,11 +116,11 @@ class MCP23S17
int lastError();

// set/clear IOCR bit fields (0.2.3 experimental)
void enableControlRegister(uint8_t mask);
void disableControlRegister(uint8_t mask);
bool enableControlRegister(uint8_t mask);
bool disableControlRegister(uint8_t mask);
// 0.2.5 experimental
void enableHardwareAddress();
void disableHardwareAddress();
bool enableHardwareAddress();
bool disableHardwareAddress();


private:
Expand Down
22 changes: 11 additions & 11 deletions MCP23S17_registers.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@
#define MCP23S17_DDR_B 0x01 // Data Direction Register B P18
#define MCP23S17_POL_A 0x02 // Input Polarity A P18
#define MCP23S17_POL_B 0x03 // Input Polarity B P18
#define MCP23S17_GPINTEN_A 0x04 // NOT USED interrupt enable P19
#define MCP23S17_GPINTEN_B 0x05 // NOT USED
#define MCP23S17_DEFVAL_A 0x06 // NOT USED interrupt def P19
#define MCP23S17_DEFVAL_B 0x07 // NOT USED
#define MCP23S17_INTCON_A 0x08 // NOT USED interrupt control P20
#define MCP23S17_INTCON_B 0x09 // NOT USED
#define MCP23S17_GPINTEN_A 0x04 // Interrupt enable P19
#define MCP23S17_GPINTEN_B 0x05 // Interrupt enable P19
#define MCP23S17_DEFVAL_A 0x06 // Interrupt default P19
#define MCP23S17_DEFVAL_B 0x07 // Interrupt default P19
#define MCP23S17_INTCON_A 0x08 // Interrupt control register P20
#define MCP23S17_INTCON_B 0x09 // Interrupt control register P20
#define MCP23S17_IOCR 0x0A // IO control register P20
#define MCP23S17_IOCR2 0x0B // NOT USED
#define MCP23S17_IOCR2 0x0B // NOT USED
#define MCP23S17_PUR_A 0x0C // Pull Up Resistors A P22
#define MCP23S17_PUR_B 0x0D // Pull Up Resistors A P22
#define MCP23S17_INTF_A 0x0E // NOT USED interrupt flag P22
#define MCP23S17_INTF_B 0x0F // NOT USED
#define MCP23S17_INTCAP_A 0x10 // NOT USED interrupt capture P23
#define MCP23S17_INTCAP_B 0x11 // NOT USED
#define MCP23S17_INTF_A 0x0E // Interrupt flag register P22
#define MCP23S17_INTF_B 0x0F // Interrupt flag register P22
#define MCP23S17_INTCAP_A 0x10 // Interrupt capture register P23
#define MCP23S17_INTCAP_B 0x11 // Interrupt capture register P23
#define MCP23S17_GPIO_A 0x12 // General Purpose IO A P23
#define MCP23S17_GPIO_B 0x13 // General Purpose IO B P23
#define MCP23S17_OLAT_A 0x14 // NOT USED output latch P24
Expand Down
49 changes: 43 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,48 @@ Since 0.2.6 the reading and writing to registers have been performance optimized
If there are problems please open an issue.


### Interrupts (experimental 0.5.2)

Read the datasheet for the details, page 24,25.
Note: Error handling is limited.

pin = 0..15
mode = { RISING, FALLING, CHANGE }
- **bool enableInterrupt(uint8_t pin, uint8_t mode)**
Returns true if successful.
Returns MCP23S17_PIN_ERROR if pin > 15.
- **bool disableInterrupt(uint8_t pin)**
Returns true if successful.
Returns MCP23S17_PIN_ERROR if pin > 15.


16 pin interface, overrides all earlier settings.
Sets all pins to the same interrupt mode { RISING, FALLING, CHANGE }.
- **bool enableInterrupt16(uint16_t mask, uint8_t mode)** mask = 0x0000..0xFFFF.
- **bool disableInterrupt16(uint16_t mask)**


Determine which pins caused the Interrupt. (datasheet).
- **uint16_t getInterruptFlagRegister()** Reads all 16 pins.
- **uint16_t getInterruptCaptureRegister()** Reads all 16 pins.
Is used to detect if multiple pins triggered an interrupt.


Merge INTA and INTB into one signal so only one line handles all interrupts.
This reduces the number of interrupt lines to handle, however one has
to read more registers to find the changed ones.

- **bool mirrorInterrupts(bool on)** enables / disables mirror mode.
- **bool isMirroredInterrupts()** returns set option.


### IO Control Register

Since 0.2.3 the library supports setting bit fields in the IO control register.
Read the datasheet carefully!

- **void enableControlRegister(uint8_t mask)**
- **void disableControlRegister(uint8_t mask)**
- **bool enableControlRegister(uint8_t mask)**
- **bool disableControlRegister(uint8_t mask)**


| constant | mask | description |
Expand All @@ -198,8 +233,8 @@ Read the datasheet carefully!

Two dedicated functions are added since 0.2.5.

- **void enableHardwareAddress()** set IOCR_HAEN bit.
- **void disableHardwareAddress()** clear IOCR_HAEN bit.
- **bool enableHardwareAddress()** set IOCR_HAEN bit.
- **bool disableHardwareAddress()** clear IOCR_HAEN bit.


### Error codes
Expand Down Expand Up @@ -241,15 +276,17 @@ See examples.
- IOCON.HAEN, Hardware Address ENable.
- should this be enabled in **begin()** by default? 0.3.0
- check address range in constructor.
- enhance INTERRUPTS, IOCON.ODR and IOCON.INTPOL to set polarity.
- **bool setInterruptPolarity(polarity)** + getter
HIGH LOW Open Drain?
- fix TODO's in code

#### Could

- check need for writing in all functions (Polarity / Pull-up)
- check if bit mask changes.
- what is performance gain vs footprint?
- investigate and reimplement the INPUT_PULLUP for pinMode() ?
- RP2040 support for SPI, setGPIOpins() etc
- See MCP_DAC
- AVR software SPI optimize
- dao and clock - see fastShiftOut.

Expand Down
10 changes: 10 additions & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ getPolarity16 KEYWORD2
setPullup16 KEYWORD2
getPullup16 KEYWORD2

enableInterrupt KEYWORD2
disableInterrupt KEYWORD2
enableInterrupt16 KEYWORD2
disableInterrupt16 KEYWORD2
getInterruptFlagRegister KEYWORD2
getInterruptCaptureRegister KEYWORD2

mirrorInterrupts KEYWORD2
isMirroredInterrupts KEYWORD2

setSPIspeed KEYWORD2
getSPIspeed KEYWORD2
usesHWSPI KEYWORD2
Expand Down

0 comments on commit aac927b

Please sign in to comment.