Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/devices/Tca955x/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The family contains the TCA9554 (8-bit) and the TCA9555 (16-bit) device. Both de

## Interrupt support

The `Tca955x` has one interrupt pin. The corresponding pins need to be connected to a master GPIO controller for this feature to work. You can use a GPIO controller around the MCP device to handle everything for you:
The `Tca955x` has one interrupt pin. The corresponding pins need to be connected to a master GPIO controller for this feature to work. You can use a GPIO controller around the Tca955x device to handle everything for you:

```csharp
// Gpio controller from parent device (eg. Raspberry Pi)
Expand Down
42 changes: 33 additions & 9 deletions src/devices/Tca955x/Tca955x.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ protected override void SetPinMode(int pinNumber, PinMode mode)
{
if (mode != PinMode.Input && mode != PinMode.Output && mode != PinMode.InputPullUp)
{
throw new ArgumentException("The Mcp controller supports the following pin modes: Input, Output and InputPullUp.");
throw new ArgumentException("The Tca955x controller supports the following pin modes: Input, Output and InputPullUp.");
}

byte polarityInversionRegister = GetRegisterIndex(pinNumber, Register.PolarityInversionPort);
Expand Down Expand Up @@ -432,11 +432,23 @@ protected override void AddCallbackForPinValueChangedEvent(int pinNumber, PinEve
throw new InvalidOperationException("No interrupt pin configured");
}

_interruptPins.Add(pinNumber, eventType);
_interruptLastInputValues.Add(pinNumber, Read(pinNumber));
_controller.RegisterCallbackForPinValueChangedEvent(_interrupt, PinEventTypes.Falling, InterruptHandler);
lock (_interruptHandlerLock)
{
_interruptPins.Add(pinNumber, eventType);
_interruptLastInputValues.Add(pinNumber, Read(pinNumber));

// Register the interrupt callback only once, when the first input pin is added.
// The TCA955x family of I/O expanders shares a single INT (interrupt) output pin for all input ports in input mode.
// Any rising or falling edge on any input pin configured as input will trigger this shared interrupt.
// Therefore, we only need to register the interrupt handler once, regardless of how many input pins are monitored.
// This avoids redundant registrations and ensures efficient handling of input changes.
if (_interruptPins.Count == 1)
{
_controller.RegisterCallbackForPinValueChangedEvent(_interrupt, PinEventTypes.Falling, InterruptHandler);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extra enter

_eventHandlers[pinNumber] = callback;
_eventHandlers[pinNumber] = callback;
}
}

/// <inheritdoc/>
Expand All @@ -448,11 +460,23 @@ protected override void RemoveCallbackForPinValueChangedEvent(int pinNumber, Pin
throw new InvalidOperationException("No valid GPIO controller defined. And no callbacks registered either.");
}

if (_eventHandlers.TryRemove(pinNumber, out _))
lock (_interruptHandlerLock)
{
_interruptPins.Remove(pinNumber);
_interruptLastInputValues.Remove(pinNumber);
_controller.UnregisterCallbackForPinValueChangedEvent(_interrupt, InterruptHandler);
if (_eventHandlers.TryRemove(pinNumber, out _))
{
_interruptPins.Remove(pinNumber);
_interruptLastInputValues.Remove(pinNumber);

// Unregister the interrupt callback only when no more input pins are being monitored.
// Since the TCA955x family uses a single shared interrupt pin for all input ports,
// we only need the callback while at least one input pin is active.
// Once all input pins have been removed, we can safely unregister the callback
// to avoid unnecessary interrupt handling.
if (_interruptPins.Count == 0)
{
_controller.UnregisterCallbackForPinValueChangedEvent(_interrupt, InterruptHandler);
}
}
}
}

Expand Down
Loading