Reading the level of a pin in an IST after an interrupt has been triggered sometimes results an incorrect value being returned

What I am attempting to do is to detect both the rising and falling edges of a particurlar gpio. To do this I want to configure the interrupt to trigger on ‘anyedge’. When this occurs, in my IST I can then call Imx6Gpio_GetLevel(…) to determine the level of the pin.

I’ve added a modified version of the sample program provided with your API for initialising and detecting interrupts to demonstrate my problem. For the purpose of this query I’ve configured the sample program to detect the rising edge only.

When the IST is released after the interrupt is triggered I am reading the pin state. As I have configured the interrupt to be triggered on the rising edge only, I would expect the pin level returned from the call to Imx6Gpio_GetLevel(…) to always return a value of ioHigh. On occasion however it returns ioLow.

My initial thought was that there was a latency issue, and that the pin state had changed from high back to low before I had read it. To test this I connected a signal generator outputting a square wave, to allow me to vary the length of time the pin remained high for (The duty cycle was always 50%). I set the period to 8 seconds. At this there is 4 second window for the IST to read the pin level, more than enough to cover any latency issues. However, when tracking the total amount of interrupts that are triggered and the pin level when they occur, I am still reading incorrect values when the app is left running, and the signal generator outputting the 8 second square wave.
At this point it did not appear to be a latency issue. I then tried putting the thread to sleep for 5ms after detecting the interrupt, but before reading the pin. This seems to fix the issue.

Is my approach for detecting the pin state correct? I need to be able to detect both rising and falling edges, so reading the pin level is required. Is there some caching taking place at a lower level and I am attempting to read the pin state before it has been updated?

Sample program:

//  ToradexIntLibTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h> 
#include "gpio_imx6.h"
#include "int.h"

HANDLE hEvent;
HANDLE hIrqThread;
HANDLE hMonitorThread;
HANDLE hIntr = NULL;
BOOL threadStatus = 1;

HANDLE hGpio = NULL;

uIo io = COLIBRI_PIN(23);

int risingCount = 0;
int fallingCount = 0;
int interruptCount = 0;

void GpioIrqThread(DWORD *sysirq);

void MonitorThread(DWORD *sysirq);

int wmain(int argc, wchar_t *argv[])
{
	printf("Beginning... \r\n");

	DWORD irqNum = 0;
	DWORD sysIrq;

	// Set the IO used for interrupt pin as GPIO input
	hGpio = Gpio_Init(NULL);
	Gpio_Open(hGpio);
	Gpio_ConfigureAsGpio(hGpio, io);  // Configure io as GPIO
	Gpio_SetDir(hGpio, io, ioInput);  // Set GPIO Configured io as input
	io = Gpio_NormalizeIo(hGpio, io); // Convert IO (ioColibriPin, ioApalis or ioGpio) to ioGpio

    // Initialize interrupt Library
	hIntr = Int_Init();
	if (hIntr == NULL)
		printf("ERROR:: interrupt init function");

	// Set Edge to trigger
	Gpio_SetConfigString(hGpio, io, NULL, L"irqtrig=rising", StoreVolatile);
	// Get Irq number from GPIO number
	Gpio_GetConfigInt(hGpio, io, L"irq", &irqNum);

	// Create an Event
	hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (hEvent == NULL)
	{
		printf("ERROR:: Create Event failed number %d\r\n", GetLastError());
		return -1;
	}
	// Get the system interrupt for the corresponding Irq number
	sysIrq = Int_RequestSysInterrupt(hIntr, irqNum);
	// Initialize interrupt
	Int_InterruptInitialize(hIntr, sysIrq, hEvent, NULL, 0);

	// Create thread to handle Interrupt event
	hIrqThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)GpioIrqThread, &sysIrq, 0, NULL);

	hMonitorThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MonitorThread, NULL, 0, NULL);

	printf("Enter key to Exit Irq test\r\n");
	getchar();

	threadStatus = 0;
	SetEvent(hEvent);
	Sleep(100);

	// It is very important to deinitalise the interrupt, otherwise it will not work the next time you start the program
	Int_InterruptDisable(hIntr, sysIrq);
	Int_ReleaseSysIntr(hIntr, sysIrq);
	Int_Deinit(hIntr);

	// Close and DeInitialize GPIO handle
	Gpio_Close(hGpio);
	Gpio_Deinit(hGpio);

	return 0;
}

void MonitorThread(DWORD *sysirq)
{
	while (threadStatus != false)
	{
		printf("Rising count: %d    Falling count: %d   Interrupt count:   %d\r\n", risingCount, fallingCount, interruptCount);

		Sleep(5000);
	}
}

void GpioIrqThread(DWORD *sysirq)
{
	CeSetThreadPriority(GetCurrentThread(), CE_THREAD_PRIO_256_TIME_CRITICAL);

	tIoLevel pinLevel;

	int count = 1;
	DWORD lpdwExitCode;
	while (threadStatus)
	{
		if (WAIT_OBJECT_0 == WaitForSingleObject(hEvent, INFINITE) && threadStatus)
		{
			Sleep(5);

			interruptCount++;

			pinLevel = Imx6Gpio_GetLevel(hGpio, io);

			if (pinLevel == ioLow)
			{
				fallingCount++;
			}
			else if (pinLevel == ioHigh)
			{
				risingCount++;
			}

			Int_InterruptDone(hIntr, *sysirq);
		}
		else
		{
			printf("Close Thread \r\n");
			break;
		}
	}
	if (hIrqThread != NULL)
	{
		GetExitCodeThread(hIrqThread, &lpdwExitCode);
		// Exit Thread
		ExitThread(lpdwExitCode);
		hIrqThread = NULL;
	}
}

The pin is connected directly to the input signal or you have some components in between that may generate spikes or other changes to the signal? This is a UART1 signal, did you detach the jumpers connecting it to the RS232 converter and to the USB to serial converter?

Yes, the pin from the function generator is going directly into the breakout area. There are no components in between. The original test had jumpers in place routing the UART pins to the X25 connector, but I have now removed jumpers 17,19,20 & 21, rerun the tests, and the results are still the same. When reading the pin state I am frequently getting readings of ioLow when the app is run with the call to Sleep(…) commented out.

After running more tests on a different Colibri evaluation board (V3.1A) the issue was not present. I’m assuming that there was an issue with the original carrier board I was testing on.