Re: Need help with libgpiod gpiod_line_request_both_edges_events()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 03/06/2018 02:53 PM, Bartosz Gołaszewski wrote:
2018-03-06 21:49 GMT+01:00 Timur Tabi <timur@xxxxxxxxxxxxxx>:
On 03/06/2018 01:30 PM, Timur Tabi wrote:


Never mind, I figured it out.  I was passing the wrong parameter to
pthread_create().  Everything works now.

Thanks for all your help.  libgpiod makes a lot more sense to me now.


So I found one small oddity.

When I start the interrupt test, the output pin is set to 1, and therefore
the input pin is also set to 1 since they're wired together.

It appears that gpiod_line_event_wait() is receiving a rising-edge event
when I call gpiod_line_set_value(..., 1).  That is, the input is reading 1,
and it is still set to 1, but I get a rising event anyway.


This is normal, you're generating an interrupt anyway.

How is that possible? I'm not writing to the input line directly. It should see a continuous stream of "1", so there should be nothing that triggers an interrupt. If the output is "1", and I call gpiod_line_set_value(output, 1), there should be no electrical changes on that output pin.

If I do this:

gpiod_line_set_value(output, 1);
gpiod_line_set_value(output, 1);
gpiod_line_set_value(output, 1);
gpiod_line_set_value(output, 1);
gpiod_line_set_value(output, 1);
gpiod_line_set_value(output, 1);

I should see no changes on a scope attached to the GPIO. So how could there be an interrupt?

At one point, I told it to write a 0 to output, and I still got a rising
event:


This isn't normal - do you have an strace output for that? We need to
figure out if it's something in libgpiod or in the kernel.

write(1, "Running interrupt test", 22)  = 22
write(1, "\n", 1)                       = 1
mmap(NULL, 8388608, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0xffff99fa0000
mprotect(0xffff99fa0000, 65536, PROT_NONE) = 0
clone(child_stack=0xffff9a79eb20, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0xffff9a79f2e0, tls=0xffff9a79f900, child_tidptr=0xffff9a79f2e0) = 24048
close(5)                                = 0
ioctl(3, _IOC(_IOC_READ|_IOC_WRITE, 0xb4, 0x04, 0x30), 0xffffec347160) = 0
ioctl(3, GPIO_GET_LINEINFO_IOCTL, 0xffffec3470b8) = 0
ioctl(5, _IOC(_IOC_READ|_IOC_WRITE, 0xb4, 0x08, 0x40), 0xffffec347170) = 0
write(1, "Initial input value: 1\n", 23) = 23
futex(0xffffec347470, FUTEX_WAKE_PRIVATE, 2147483647) = 1
ppoll([{fd=5, events=POLLIN|POLLPRI}], 1, {1, 0}, NULL, 8) = 1 ([{fd=5, revents=POLLIN}], left {0, 999996600})
read(5, "\20i\272\226Tp\31\25\1\0\0\0\0\0\0\0", 16) = 16
ioctl(5, _IOC(_IOC_READ|_IOC_WRITE, 0xb4, 0x08, 0x40), 0xffffec347170) = 0
write(1, "Received rising edge event, valu"..., 56) = 56
ppoll([{fd=5, events=POLLIN|POLLPRI}], 1, {1, 0}, NULL, 8) = 1 ([{fd=5, revents=POLLIN}], left {0, 999998200})
read(5, "\17J\300\226Tp\31\25\2\0\0\0\0\0\0\0", 16) = 16
ioctl(5, _IOC(_IOC_READ|_IOC_WRITE, 0xb4, 0x08, 0x40), 0xffffec347170) = 0
write(1, "Received falling edge event, val"..., 56) = 56
futex(0xffff9a79f2e0, FUTEX_WAIT, 24048, NULL) = 0
write(1, "All tests passed", 16)        = 16

I've attached the latest version of my code.

Oscillate thread started
Initial input value: 1
Setting output to 0
Received rising edge event, value=0, elapsed=-0.000204s

And it apparently went backwards in time.

What happens in this scenario:

1. gpiod_line_request_both_edges_events() is called
2. GPIO line transitions from 0 to 1
3. gpiod_line_event_wait() is called


Is it possible that this event was queued in the kernel buffer and
that you actually read one of the previous events?

Yes, it's very possible.  How do I flush that queue?

--
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm
Technologies, Inc.  Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.
#include <stdio.h>
#include <linux/gpio.h>
#include <gpiod.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>

int loopback(struct gpiod_chip *chip, struct gpiod_line *output, struct gpiod_line *input, int value)
{
	int ret;

	ret = gpiod_line_set_value(output, value);
	if (ret < 0)
		return ret;

	if (value != gpiod_line_get_value(input))
		return -1;

	return 0;
}

struct toggle_data {
	struct gpiod_line *output;
	unsigned int frequency;
	volatile bool finished;
	pthread_barrier_t barrier;
};

void *oscillate(void *p)
{
	struct toggle_data *data = p;
	unsigned int toggle = 1 - gpiod_line_get_value(data->output);
	int ret;

	printf("Oscillate thread started\n");
	pthread_barrier_wait(&data->barrier);

	while (!data->finished) {
		printf("Setting output to %i\n", toggle);
		ret = gpiod_line_set_value(data->output, toggle);
		if (ret < 0) {
			printf("Error: could not set value (errno=%i)\n", errno);
			break;
		}

		sleep(data->frequency);
		toggle = 1 - toggle;
	}

exit:
	printf("Oscillate thread exits\n");

	return NULL;
}

int interrupt(struct gpiod_chip *chip, struct gpiod_line *output, struct gpiod_line *input)
{
	struct toggle_data data;
	pthread_t thread;
	struct timespec timeout = { 0 };
	struct timespec previous = { 0 };
	double elapsed;
	struct gpiod_line_event event;
	unsigned int count = 3;
	int ret;

	data.output = output;
	data.frequency = 1;
	data.finished = false;
	timeout.tv_sec = data.frequency;

	pthread_barrier_init(&data.barrier, NULL, 2);
	ret = pthread_create(&thread, NULL, oscillate, &data);
	if (ret < 0) {
		printf("Error: Could not create thread (ret=%i)\n", ret);
		return ret;
	}

	/* We need to release the line so that it can be requested */
	gpiod_line_release(input);

	ret = gpiod_line_request_both_edges_events(input, NULL);
	if (ret < 0) {
		printf("Error: Could not create event (errno=%i)\n", errno);
		return ret;
	}

	printf("Initial input value: %i\n", gpiod_line_get_value(input));

	pthread_barrier_wait(&data.barrier);
	clock_gettime(CLOCK_REALTIME, &previous);

	while (--count) {
		ret = gpiod_line_event_wait(input, &timeout);
		if (ret < 0) {
			printf("Error: Could not wait for event (errno=%i)\n", errno);
			break;
		}
		if (ret == 0) {
			printf("Error: Timeout waiting for event (errno=%i)\n", errno);
			ret = -1;
			break;
		}
		ret = gpiod_line_event_read(input, &event);
		elapsed = (event.ts.tv_sec + event.ts.tv_nsec / 1000000000.0) -
			(previous.tv_sec + previous.tv_nsec / 1000000000.0);
		printf("Received %s edge event, value=%i, elapsed=%fs\n",
		       event.event_type == GPIOD_LINE_EVENT_RISING_EDGE ? "rising" : "falling",
		       gpiod_line_get_value(input), elapsed);
		previous = event.ts;
	}

	data.finished = true;
	pthread_join(thread, NULL);
	return ret;
}


int test(struct gpiod_chip *chip, int _output, int _input)
{
	struct gpiod_line *output;
	struct gpiod_line *input;
	int ret, i;

	printf("Chip Name: %s\n", gpiod_chip_name(chip));
	printf("Output pin: %i\n", _output);
	printf("Input pin: %i\n", _input);

	output = gpiod_chip_get_line(chip, _output);
	if (!output) {
		printf("Error: Could not obtain gpioline for pin %i\n", _output);
		return 2;
	}

	input = gpiod_chip_get_line(chip, _input);
	if (!input) {
		printf("Error: Could not obtain gpioline for pin %i\n", _input);
		return 2;
	}

	printf("Configuring pins\n");

	ret = gpiod_line_request_output(output, NULL, 0);
	if (ret) {
		printf("Error: Could not configure pin %i to output\n", _output);
		return 2;
	}
	if (gpiod_line_direction(output) != GPIOD_LINE_DIRECTION_OUTPUT) {
		printf("Error: Pin %i refuses to be configured for output\n", _output);
		return 2;
	}

	ret = gpiod_line_request_input(input, NULL);
	if (ret) {
		printf("Error: Could not configure pin $i to input\n", _output);
		return 2;
	}
	if (gpiod_line_direction(input) != GPIOD_LINE_DIRECTION_INPUT) {
		printf("Error: Pin %i refuses to be configured for input\n", _input);
		return 2;
	}

	printf("Running loopback test\n");

	for (i = 0; i < 10; i++) {
		ret = loopback(chip, output, input, 0);
		if (ret) {
			printf("Error: Loopback test #%iA failed\n", i);
			return 2;
		}

		ret = loopback(chip, output, input, 1);
		if (ret) {
			printf("Error: Loopback test #%iB failed\n", i);
			return 2;
		}
	}

	printf("Running interrupt test\n");

	ret = interrupt(chip, output, input);
	if (ret < 0)
		return 2;

	printf("All tests passed\n");

	return 0;
}

int main(int argc, char *argv[])
{
	struct gpiod_chip *chip;
	int ret, fd;

	if (argc != 4) {
		printf("Usage: %s chip-device output-pin input-pin\n", argv[0]);
		return 1;
	}

	/* Test to see if the gpiochip device exists and is readable */
	fd = open(argv[1], O_RDWR);
	if (fd < 0) {
		printf("Error: Device %s is not accessible (error=%i)\n", fd);
		return 1;
	} else
		close(fd);

	chip = gpiod_chip_open(argv[1]);
	if (!chip) {
		printf("Error: Device %s is not a gpiochip device\n");
		return 1;
	}

	setbuf(stdout, NULL);

	ret = test(chip, atoi(argv[2]), atoi(argv[3]));
	gpiod_chip_close(chip);

	return ret;
}

[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux