From 329b619757e3462ba8de32867845c44ad0c2e59c Mon Sep 17 00:00:00 2001 From: Tobias Loewen Date: Fri, 1 Aug 2025 08:20:32 +0200 Subject: [PATCH 1/3] Use only one interrup callback for one chip. Fix typo --- src/devices/Tca955x/README.md | 2 +- src/devices/Tca955x/Tca955x.cs | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/devices/Tca955x/README.md b/src/devices/Tca955x/README.md index 60c6d13d7b..b2b5b36c85 100644 --- a/src/devices/Tca955x/README.md +++ b/src/devices/Tca955x/README.md @@ -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) diff --git a/src/devices/Tca955x/Tca955x.cs b/src/devices/Tca955x/Tca955x.cs index 4334f0cf79..ebbd733c1d 100644 --- a/src/devices/Tca955x/Tca955x.cs +++ b/src/devices/Tca955x/Tca955x.cs @@ -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); @@ -434,7 +434,12 @@ protected override void AddCallbackForPinValueChangedEvent(int pinNumber, PinEve _interruptPins.Add(pinNumber, eventType); _interruptLastInputValues.Add(pinNumber, Read(pinNumber)); - _controller.RegisterCallbackForPinValueChangedEvent(_interrupt, PinEventTypes.Falling, InterruptHandler); + + // Only register the callback if this is the first add callback + if (_interruptPins.Count == 1) + { + _controller.RegisterCallbackForPinValueChangedEvent(_interrupt, PinEventTypes.Falling, InterruptHandler); + } _eventHandlers[pinNumber] = callback; } @@ -452,7 +457,12 @@ protected override void RemoveCallbackForPinValueChangedEvent(int pinNumber, Pin { _interruptPins.Remove(pinNumber); _interruptLastInputValues.Remove(pinNumber); - _controller.UnregisterCallbackForPinValueChangedEvent(_interrupt, InterruptHandler); + + // Only remove interrup callback if there are no more interrup pins active + if (_interruptPins.Count == 0) + { + _controller.UnregisterCallbackForPinValueChangedEvent(_interrupt, InterruptHandler); + } } } From b510493083757250a6d9c236a3f3e34875e8252d Mon Sep 17 00:00:00 2001 From: Tobias Loewen Date: Fri, 8 Aug 2025 07:30:20 +0200 Subject: [PATCH 2/3] Improve comment for callback of an interrupt Pin Add lock on Add and Remove Callback --- src/devices/Tca955x/Tca955x.cs | 41 +++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/devices/Tca955x/Tca955x.cs b/src/devices/Tca955x/Tca955x.cs index ebbd733c1d..9e550cd38e 100644 --- a/src/devices/Tca955x/Tca955x.cs +++ b/src/devices/Tca955x/Tca955x.cs @@ -432,13 +432,21 @@ protected override void AddCallbackForPinValueChangedEvent(int pinNumber, PinEve throw new InvalidOperationException("No interrupt pin configured"); } - _interruptPins.Add(pinNumber, eventType); - _interruptLastInputValues.Add(pinNumber, Read(pinNumber)); - - // Only register the callback if this is the first add callback - if (_interruptPins.Count == 1) + lock (_interruptHandlerLock) { - _controller.RegisterCallbackForPinValueChangedEvent(_interrupt, PinEventTypes.Falling, InterruptHandler); + _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); + } + } _eventHandlers[pinNumber] = callback; @@ -453,15 +461,22 @@ 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); - - // Only remove interrup callback if there are no more interrup pins active - if (_interruptPins.Count == 0) + if (_eventHandlers.TryRemove(pinNumber, out _)) { - _controller.UnregisterCallbackForPinValueChangedEvent(_interrupt, InterruptHandler); + _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); + } } } } From c5878083c8988e57829c79b37ad9b8d6b7ac3e2a Mon Sep 17 00:00:00 2001 From: Tobias Loewen Date: Tue, 26 Aug 2025 09:38:24 +0200 Subject: [PATCH 3/3] Fix lock routine --- src/devices/Tca955x/Tca955x.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/devices/Tca955x/Tca955x.cs b/src/devices/Tca955x/Tca955x.cs index 9e550cd38e..826040a31c 100644 --- a/src/devices/Tca955x/Tca955x.cs +++ b/src/devices/Tca955x/Tca955x.cs @@ -447,9 +447,8 @@ protected override void AddCallbackForPinValueChangedEvent(int pinNumber, PinEve _controller.RegisterCallbackForPinValueChangedEvent(_interrupt, PinEventTypes.Falling, InterruptHandler); } + _eventHandlers[pinNumber] = callback; } - - _eventHandlers[pinNumber] = callback; } ///