-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use Linux kernel's character device API to implement IRQ capability #943
Comments
FYI just tested the interrupt example and pigpio doesn't seem to work on the RPi5 yet.
Now you have me playing with pthreads again... this is going to take some time. I have a simple example triggering an IRQ using pins 22 or others that are toggled manually, but can't get it to trigger on the radio IRQ pin for some reason. Wondering if it needs to be set to an input or something first? The guide doesn't seem to do that... Also wondering if I should continue, or if this is something you wanted to take on? |
I could take a stab at it, but I'll need you to verify RPi5 compatibility. |
Still researching pthread... Looks like the function passed to Clearly, I've never done multi-threaded processing in C++. Some of my input here might be obvious to those with experience using pthread. |
Ok, I'm booted into Ubuntu and inspecting /usr/include/linux/gpio.h. And I just found out that there is a v2 API in gpio.h. Some of the structs used in #942 are actually deprecated already 😡. For example: /**
* struct gpiohandle_request - Information about a GPIO handle request
* @lineoffsets: an array of desired lines, specified by offset index for the
* associated GPIO device
* @flags: desired flags for the desired GPIO lines, such as
* %GPIOHANDLE_REQUEST_OUTPUT, %GPIOHANDLE_REQUEST_ACTIVE_LOW etc, added
* together. Note that even if multiple lines are requested, the same flags
* must be applicable to all of them, if you want lines with individual
* flags set, request them one by one. It is possible to select
* a batch of input or output lines, but they must all have the same
* characteristics, i.e. all inputs or all outputs, all active low etc
* @default_values: if the %GPIOHANDLE_REQUEST_OUTPUT is set for a requested
* line, this specifies the default output value, should be 0 (low) or
* 1 (high), anything else than 0 or 1 will be interpreted as 1 (high)
* @consumer_label: a desired consumer label for the selected GPIO line(s)
* such as "my-bitbanged-relay"
* @lines: number of lines requested in this request, i.e. the number of
* valid fields in the above arrays, set to 1 to request a single line
* @fd: if successful this field will contain a valid anonymous file handle
* after a %GPIO_GET_LINEHANDLE_IOCTL operation, zero or negative value
* means error
*
* Note: This struct is part of ABI v1 and is deprecated.
* Use &struct gpio_v2_line_request instead.
*/
struct gpiohandle_request {
__u32 lineoffsets[GPIOHANDLES_MAX];
__u32 flags;
__u8 default_values[GPIOHANDLES_MAX];
char consumer_label[GPIO_MAX_NAME_SIZE];
__u32 lines;
int fd;
}; and /**
* struct gpio_v2_line_request - Information about a request for GPIO lines
* @offsets: an array of desired lines, specified by offset index for the
* associated GPIO chip
* @consumer: a desired consumer label for the selected GPIO lines such as
* "my-bitbanged-relay"
* @config: requested configuration for the lines.
* @num_lines: number of lines requested in this request, i.e. the number
* of valid fields in the %GPIO_V2_LINES_MAX sized arrays, set to 1 to
* request a single line
* @event_buffer_size: a suggested minimum number of line events that the
* kernel should buffer. This is only relevant if edge detection is
* enabled in the configuration. Note that this is only a suggested value
* and the kernel may allocate a larger buffer or cap the size of the
* buffer. If this field is zero then the buffer size defaults to a minimum
* of @num_lines * 16.
* @padding: reserved for future use and must be zero filled
* @fd: if successful this field will contain a valid anonymous file handle
* after a %GPIO_GET_LINE_IOCTL operation, zero or negative value means
* error
*/
struct gpio_v2_line_request {
__u32 offsets[GPIO_V2_LINES_MAX];
char consumer[GPIO_MAX_NAME_SIZE];
struct gpio_v2_line_config config;
__u32 num_lines;
__u32 event_buffer_size;
/* Pad to fill implicit padding and reserve space for future use. */
__u32 padding[5];
__s32 fd;
}; |
LOL. Well I don't know about you, but I'm taking a break. |
Yeah, I have plans for later today. But I'll keep at it throughout the coming weeks. I think I'll finish the clang-format updates first. |
I've also been looking at the libgpiod code, and they cache everything, possibly because nVidia Jetson/TX2/etc (& probably some RPi clones) can have multiple character devices ( |
Apparently, MRAA can detect RPi5 hardware, but I don't know if that means MRAA will completely work on RPi5. They have been supporting character device API for some time... Pigpio (joan2937/pigpio#586) and BCM2835 (google groups discussion) libs are still assessing how to implement compatibility. I doubt BCM2835 will go on supporting the new hardware paradigm (which ultimately requires interfacing with PCI express). Pigpio might move forward since it already uses the Linux kernel for some stuff. Remember, WiringPi is dead and littleWire has been broken since before I joined nRF24 org. It is starting to look like our SPIDEV driver might be the only way to support Linux in the future. I'm not saddened by this from a maintenance point of view, but user projects will suffer this new RPi hardware paradigm. |
libgpiod's asynch_watch_line_value.c example seems like a big clue about using pthread with char-dev API. |
Yeah, I had a feeling the GPIO changes would be slower than previous implementations, but never did any performance testing. Now that we have some working code for v2, caching everything shouldn't be a big challenge. |
I took a break. From a design perspective, I think the gpio caches and interrupt implementation should be separate to reduce the created thread's resources. As far as caching, I think we can just have 1 I might be entirely overthinking again. |
Hey if it works it works. We should be able to get the same or better performance out of it than prior versions I would hope. I agree with keeping the GPIO and IRQ stuff separate. The caching I don't know about, I'd have to look at GPIO code more in depth to form a relevant opinion. |
So I think I'm caching the FDs properly (in char-dev-irq branch).
It compiles and executes without errors, but running the scanner example shows no signal gets detected. 😞 On a side note, I think our old sysfs approach does not properly free the pins upon app exit. I have to reboot my RPi to allow the char-dev API access to the GPIO22. |
Nice work! Mostly tests fine for me, but I am seeing differences in the scanner example on RPi5, RPi4 and RPi3. Teh RPI4 comes up with long lines like this sometimes tho: Also, on a side note, since the FDs are cached and remain open, the driver now gives a nice error report if you try to run two instances of RF24 at the same time, using the same pins. Sweet deal, this is something I did regularly when running through tests etc. so its a nice behavior to have. |
Hmm, taking a look at RF24.cpp, I found something unusual that I haven't thought about in a while, but see the lines here Essentially, on faster devices, I put in a delay of 5us when toggling the CS pin. If I modify the code to the following, it seems to work much better on RPi with the scanner example.
Maybe we could do something like the following for the CS pin?:
The whole point of the delay was double: The higher layers seem to perform better too with this change. |
I was going to play with char-dev debouncing too. I think with caching, we've hit the too fast problem. |
I also set the consumer string in the |
Yup, there needs to be some sort of GPIO/CSN delay for Linux now along with the faster MCUs. Will you include this in your branch then? |
Yeah, coding the debouncing now. There's a limit of 10 attrs that we can configure for each |
I think only 1 type of attr (input flag, output flag, debounce period) can be associated with a pin. Meaning, setting an attr for output and another attr for debouncing on 1 pin doesn't take. /**
* struct gpio_v2_line_config - Configuration for GPIO lines
* @flags: flags for the GPIO lines, with values from &enum
* gpio_v2_line_flag, such as %GPIO_V2_LINE_FLAG_ACTIVE_LOW,
* %GPIO_V2_LINE_FLAG_OUTPUT etc, added together. This is the default for
* all requested lines but may be overridden for particular lines using
* @attrs.
* @num_attrs: the number of attributes in @attrs
* @padding: reserved for future use and must be zero filled
* @attrs: the configuration attributes associated with the requested
* lines. Any attribute should only be associated with a particular line
* once. If an attribute is associated with a line multiple times then the
* first occurrence (i.e. lowest index) has precedence.
*/
struct gpio_v2_line_config {
__aligned_u64 flags;
__u32 num_attrs;
/* Pad to fill implicit padding and reserve space for future use. */
__u32 padding[5];
struct gpio_v2_line_config_attribute attrs[GPIO_V2_LINE_NUM_ATTRS_MAX];
}; My initial (local) test has not shown any difference. I pushed what I have in case you can test with it. I also tried using 1 attr to define both output flag and debouncing period (for all output pins), alas I still don't see a difference. I might have to switch to a |
Yeah, I just tested it and I don't think debounce introduces a delay of 5us after toggling, it would be to prevent togging more often than 5us, so I don't think it will work in this application. |
My I think the debounce period is more for input pins. https://www.kernel.org/doc/html/v4.17/driver-api/gpio/driver.html#gpios-with-debounce-support I added the |
Seems to be working now. I'm going to take a break before tackling the IRQ implementation... |
I'm still getting the same erroneous results with the scanner examples with your current code. The CE pin is only toggled on transmit, and delays are added in the lib already if needed. re: startWrite() |
But the gpio stuff in SPIDEV isn't managing the CS. The asserted CE used for entering RX is how I perceived the problem with the scanner example. Works fine on my RPi3. |
In theory, using Also, according to the Linux kernel docs:
|
well at least we know the debounce attr was having an effect, but I don't think that is the desired behavior we're looking for.
Maybe if we decrease the debounce time? I only ever tried increasing the debounce time and it didn't seem to have an effect while using |
I also tried using a |
Hmm, the data_fail test is passing now, not sure what was going on before, but I have the interrupt example running with RF24Gateway now with the two changes mentioned above and it works great! |
With only using |
Yeah, I expect it to work much faster than any of our previous IRQ support/attempts. The char-dev API is definitively better than the old sysfs API. |
Can you push these changes? I like this idea. |
You want me to push all the changes or just to the example? |
just the example changes. I'm reworking your read() only suggestion to account for events' sequential number. |
Yeah, the debounce config was mucking it up somehow. Sad that we can't get that to work (for now). I pushed your |
I would be more excited if I didn't have to work so hard toward this achievement. Now I just feel like I've learned all new ways in which I am a dummy. 😆 |
Haha, welcome to the club. |
Now I want to subsidize the other Linux drivers to use this IRQ implementation where the driver doesn't already have IRQ support... for both CMake and the old ./configure script. |
Pretty nice not having to run as root and still have interrupts! |
I'll probably have to expose this in pyRF24 as well. Currently, that lib's IRQ example uses RPi.GPIO for IRQ support. But RPi.GPIO (& pigpio) won't work on RPi5... BTW, installing the python binding for libgpiod (via pip) requires users build it from source. I'm not sure if pyRF24 will compile properly for binary distribution since it requires the system have linux/gpio.h header, and the CI uses CentOS (or the CentOS replacement Rocky Linux) to compile the pyRF24 wrappers. I see piwheels can build gpiod lib with linux/gpio.h present (that gpiod pkg requires python v3.9+), so 32-bit RPi OS will still be able to use binary dists from piwheels (as is done currently). |
Just pushed some major code clean-up (specifically for Linux drivers)
I test-compiled these changes in WSL Ubuntu, but I haven't actually tested these changes on hardware yet. |
Have this running via SPIDEV on my RPi4 & 5 and others soon! |
This thread just became the 2nd-most commented issue in the repo. I'm going to sleep now. I'll start HW testing tomorrow. |
When ur up and at er, plz take a look at RF24/utility/SPIDEV/interrupt.cpp Line 79 in a066d0c
I was having some problems on my RPi3 with heavy traffic running the Gateway Interrupt Ncurses example, and added some code to check for out-of-sync interrupts. There were some interrupts being dropped so I increased this to 48U. The return value of read is also 48, so I think there may have been a problem with overflowing the buffer but I'm not sure based on the documentation. |
According to the docs & header comments), its actually the default value when not specified (16 * |
We could try printing a warning in if (lastEventSeqNo + 1 != irqEventInfo.line_seqno) {
// I haven't had much luck using printf() from a non-main thread
std::cout << "missed event on IRQ pin" << std::endl;
} just to see if/when this occurs during testing. |
I think if we were to add something I would prefer a counter or something that could be accessed outside RF24, because I do a lot of my testing at the Gateway/Ethernet layers using the ncurses examples. |
We could pass the event number into the ISR callback. For that matter, we could just pass the entire |
We also can't use an ISR signature with a default value to avoid breaking behavior void interruptHandler(void* eventInfo = nullptr);
attachInterrupt(24 FALLING, &interruptHandler); because |
I think we're getting a bit complicated, maybe just stick to the |
pushed increase to |
I just tested the IRQ implementation with the RPi driver on my RPi3 & RPi4. On my RPi4, I got garbled text printed from the RX FIFO:
I lowered the SPI speed on my RPi4 to 6MHz and it worked out fine. Weird that I didn't get that problem with the SPIDEV driver using default SPI speed. |
Using wiringPi on RPi4 with interruptConfigure.cpp, I had to change the pin numbers according to
|
This guide does mention how IRQ could be done using some
poll()
function (which is unknown to me), but it uses a blocking example.The main obstacle (for me) is using a separate processor thread to poll the GPIO for IRQ edge detection.
Originally posted by @2bndy5 in #942 (comment)
Useful resources
poll()
docsThe text was updated successfully, but these errors were encountered: