Skip to content
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

Extend support for Raspberry Pi 5: PWM and SPI #338

Open
FDelporte opened this issue Mar 26, 2024 · 11 comments
Open

Extend support for Raspberry Pi 5: PWM and SPI #338

FDelporte opened this issue Mar 26, 2024 · 11 comments
Assignees
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@FDelporte
Copy link
Member

FDelporte commented Mar 26, 2024

  • Should be implemented using the Linux Kernel APIs.
  • Integrate in the GpioD plugin added in 2.5.0?

https://docs.kernel.org/driver-api/pwm.html
https://www.kernel.org/doc/html/v4.14/driver-api/spi.html

@FDelporte FDelporte added enhancement New feature or request help wanted Extra attention is needed labels Mar 26, 2024
@mpilone
Copy link

mpilone commented Oct 3, 2024

I need SPI support on Pi5 to drive an SSD1306 OLED. I ended up on this ticket when I saw Pi4J doesn't support it yet. I don't have the time to fully integrate a solution, but I did come up with a quick implementation using JNA for the native bindings that demonstrates how to talk to the Linux SPI device. I don't think it would be hard for someone to do the actual integration into Pi4J.

I assume you'd want to rewrite the JNA calls into pure JNI to match the existing pattern, but JNA can save you a good bit of custom native code. The existing LinuxFS class gives you most of what you need, but you'll have to add an ioctl call that can pass a structure and all the JNI magic to read/write the structure.

Thanks for making this library available. Hopefully this helps speed the SPI implementation for Pi5.

JnaSpiPi5.zip

@eitch
Copy link
Member

eitch commented Oct 4, 2024

Hi @mpilone
Thanks, that does look easier. Maybe you could put it in a pull request? I don't know JNA, and would really appreciate that a lot! I would then check the code and try and test it with other SPI devices.

@taartspi
Copy link
Collaborator

taartspi commented Oct 5, 2024

I have not looked into that zip that was provided.  
Early summer I spent sometime researching the kernel API.  The documentation for the new 6.x kernel is terrible.   After a bit of time I decided to use ioctl.   I coded up the library portion and tested I could control a neopixel LED string using ioctl.  I am in the process of debugging the new provider code I wrote so a normal testcase can use the provider and control the neopixel.

My intent with the provider is support the same interfaces and Config as the existing pigpio-spi provider. So a user could simply change the provider used and their existing code would function. But, there are flags bits specific to pigpio provider and no longer applicable to Pi5.

I wasn't sure if the ioctl usage would be rejected as we wanted to move to the APIs.   I used JNA to connect to the 'C' library.   I created needed declarations to use JNA for the structures used by ioctl.
I have some questions to answer as far as the JNA path. Our practice of creating our own version of the kernel library code is needed as we support multiple ways for the .so to be located. This JNA usage requires more code be duplicated. For the moment to test I set the JNA path explicitly in the java code, I want to research setting it when we first determine where the library is located.

The code needs synchronization added, complete decoding flags config values and setting the ioctl based upon those, complete the functions

I will push this code so we have something else to use in this SPI conversation

@mpilone
Copy link

mpilone commented Oct 5, 2024

I created a PR for my implementation. As the PR says, it introduces a dependency on JNA and I only tested the write operations for use with an OLED. Use it how you like. If you keep the JNA approach, you could do the same with gpiod and remove the custom C JNI code. Just load libgpiod.so and let JNA handle the bindings dynamically.

@taartspi
Copy link
Collaborator

taartspi commented Oct 5, 2024

#391 PR involving ioctl/JNA Intend is, between mpilone and this we likely have much of the SPI support for Pi5

@taartspi
Copy link
Collaborator

taartspi commented Dec 3, 2024

@dariuszzbyrad @eitch @FDelporte SPI. I want to select which spi approach to complete. The PR offered by milipone uses system library. The PR i began I implemented my own library thinking it provides more isolation from changes and deprecation by the kernel. But my approach creates a much larger code base to support. Mine also has increased JNA code although comments in milipone code implies more jna is needed in his code. I can see that milipone also needs more investigation of using the config attributes. Both of these use jna and not the new APIs available in later java versions. So your thoughts. Do i do what appears to be a smaller code base to complete spi now and we refactor (rewrite) it later using new APIs or do a more elaborate implementation now and think we stay with that long term ?My thoughts is the smaller implementation so it will be an easier decision to throw it away and rewrite for the new API

@mpilone
Copy link

mpilone commented Dec 3, 2024

@taartspi Regarding JNA vs the new FFI API in the JDK, I'd recommend using JNA for now and potentially rewriting it in the future. I have a few platforms that don't have a v21 JVM available yet and the new FFI API only became final in v21+. Obviously there's a tradeoff of pulling in the JNA dependency vs the new JVM standard API, but you might not want to tie SPI support to JVM v21+ at this point.

As for the implementation approach, I'll let you all talk that out. My approach doesn't involve much code, but as you said, I'm not sure it is complete as I only needed write support on SPI 0.0. You can see in the PR thread that someone was attempting to test it and I made a couple small fixes but there hasn't been an update for a few days.

Thanks for following up on this. -mike

@dariuszzbyrad
Copy link
Member

From my perspective, consciously taking on technical debt is acceptable, especially when it aligns with the project's immediate goals. Implementing SPI support in a simplified version now seems like a practical choice, as it enables faster progress with adding support for SPI in Pi5

@FDelporte
Copy link
Member Author

As discussed in #409, we want to bump Pi4J to Java 21 so it can evolve further to the next LTS 25 and use the Foreign Memory API. This would bring Pi4J to V3.X.Y.

That being said, it would indeed be great if we still could have a good-and-small implementation in Pi4J V2.7.X.

@taartspi
Copy link
Collaborator

taartspi commented Dec 4, 2024

I will pull down Mike’s implementation to test and to see if more is required to let present users easily move to it. As his is a smaller code base it is more attractive for 2.7 as less code usually means easier maintenance.

@eitch
Copy link
Member

eitch commented Dec 5, 2024

I thought i'd ask ChatGPT what it says about SPI, and it came up with simply reading and writing to spidev. Since y'all are busy trying out things, i thought i'd post this here, and see if it works, as i didn't myself yet have time to try it out:

public class SpidevExample {

    private static final Path SPIDEV_PATH = Path.of("/dev/spidev0.0"); // Change to your SPI device

    public static void main(String[] args) {
        byte[] txBuffer = {0x01, 0x02, 0x03}; // Example data to send
        byte[] rxBuffer = new byte[txBuffer.length];

        try (var spiFile = Files.newByteChannel(SPIDEV_PATH,
                StandardOpenOption.READ, StandardOpenOption.WRITE)) {
            // Send and receive data
            spiFile.write(java.nio.ByteBuffer.wrap(txBuffer));
            java.nio.ByteBuffer rxBufferWrapper = java.nio.ByteBuffer.wrap(rxBuffer);
            spiFile.read(rxBufferWrapper);

            // Print received data
            System.out.println("Received Data:");
            for (byte b : rxBuffer) {
                System.out.printf("0x%02X ", b);
            }
            System.out.println();
        } catch (IOException e) {
            System.err.println("SPI communication failed: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

It seems to easy to be true, but who knows? =)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

6 participants