mattlewis
Posts: 113
Joined: Sat Jan 12, 2013 3:05 pm
Location: UK
Contact: Website

Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Tue May 31, 2016 10:02 pm

I'm attempting to access /dev/gpiomem in Java via a thin JNI wrapper that returns a DirectByteBuffer to a mmap'ed address and simply cannot get it to work.
I've uploaded my very rough source code here: https://github.com/mattjlewis/diozero/t ... mmap-tests.
Rough outline of C code:

Code: Select all

fdMem = open("/dev/gpiomem", O_RDWR | O_SYNC | O_CLOEXEC);
gpioAddr = (uint32_t *)mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fdMem, gpioBase);
return (*env)->NewDirectByteBuffer(env, gpioAddr, BLOCK_SIZE);
C output:

Code: Select all

&gpioAddr[0]=0x76f92000, gpioAddr[0]=0x21200900
&gpioAddr[1]=0x76f92004, gpioAddr[1]=0x24024
&gpioAddr[2]=0x76f92008, gpioAddr[2]=0x0
&gpioAddr[3]=0x76f9200c, gpioAddr[3]=0x1000048
&gpioAddr[4]=0x76f92010, gpioAddr[4]=0x2422400c
&gpioAddr[5]=0x76f92014, gpioAddr[5]=0x924
&gpioAddr[6]=0x76f92018, gpioAddr[6]=0x0
&gpioAddr[7]=0x76f9201c, gpioAddr[7]=0x6770696f
&gpioAddr[8]=0x76f92020, gpioAddr[8]=0x6770696f
&gpioAddr[9]=0x76f92024, gpioAddr[9]=0x6770696f
&gpioAddr[10]=0x76f92028, gpioAddr[10]=0x6770696f
&gpioAddr[11]=0x76f9202c, gpioAddr[11]=0x6770696f
&gpioAddr[12]=0x76f92030, gpioAddr[12]=0x6770696f
&gpioAddr[13]=0x76f92034, gpioAddr[13]=0xb000c1ff
&gpioAddr[14]=0x76f92038, gpioAddr[14]=0x3e5c5c
&gpioAddr[15]=0x76f9203c, gpioAddr[15]=0x0
&gpioAddr[16]=0x76f92040, gpioAddr[16]=0x0
&gpioAddr[17]=0x76f92044, gpioAddr[17]=0x0
&gpioAddr[18]=0x76f92048, gpioAddr[18]=0x0
&gpioAddr[19]=0x76f9204c, gpioAddr[19]=0x0
Java output:

Code: Select all

gpioReg[0]=0x67706900
gpioReg[1]=0x67706924
gpioReg[2]=0x67706900
gpioReg[3]=0x67706948
gpioReg[4]=0x6770690c
gpioReg[5]=0x67706924
gpioReg[6]=0x67706900
gpioReg[7]=0x6770696f
gpioReg[8]=0x6770696f
gpioReg[9]=0x6770696f
gpioReg[10]=0x6770696f
gpioReg[11]=0x6770696f
gpioReg[12]=0x6770696f
gpioReg[13]=0x677069ff
gpioReg[14]=0x6770695c
gpioReg[15]=0x67706900
gpioReg[16]=0x67706900
gpioReg[17]=0x67706900
gpioReg[18]=0x67706900
gpioReg[19]=0x67706900
I think I can correctly read and write values in C (following code snippets from pigpio and wiringPi), however, the values I see in Java are quite different (thank you JGPIO for pointing out the last byte is always correct, the first 3 bytes are always 0x677069).

Any ideas would be greatly appreciated.

Regards,
Matt
Last edited by mattlewis on Wed Jun 01, 2016 9:00 am, edited 1 time in total.
http://www.diozero.com/

User avatar
joan
Posts: 14998
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Wed Jun 01, 2016 8:48 am

In http://abyz.co.uk/rpi/pigpio/examples.h ... _tiny_gpio I do the mmap slightly differently.

Code: Select all

int gpioInitialise(void)
{
   int fd;

   piRev = gpioHardwareRevision(); /* sets piModel and piRev */

   fd = open("/dev/gpiomem", O_RDWR | O_SYNC) ;

   if (fd < 0)
   {
      fprintf(stderr, "failed to open /dev/gpiomem\n");
      return -1;
   }

   gpioReg = (uint32_t *)mmap(NULL, 0xB4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

   close(fd);

   if (gpioReg == MAP_FAILED)
   {
      fprintf(stderr, "Bad, mmap failed\n");
      return -1;
   }
   return 0;
}
At the back of my mind I seem to remember the peripherals returning an abbreviation of their name, e.g. gpio = 0x6770696f, but for the life of me I can't remember the circumstances.

JGPIO
Posts: 25
Joined: Sun Feb 28, 2016 6:29 pm

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Wed Jun 01, 2016 5:42 pm

Well done. Passing 0 to the offset parameter to mmap, allows Java to provide the full correct register value.

Many, many thanks to you.

mattlewis
Posts: 113
Joined: Sat Jan 12, 2013 3:05 pm
Location: UK
Contact: Website

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Wed Jun 01, 2016 7:55 pm

That is strange, it would indeed appear to work when accessing /dev/mem with zero offset. Next Java question - how do you then access the GPIO registers given the large offset...

Matt
http://www.diozero.com/

JGPIO
Posts: 25
Joined: Sun Feb 28, 2016 6:29 pm

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Wed Jun 01, 2016 8:01 pm

The address passed to the NewDirectByteBuffer doesn't have to be the start of the mapped memory - you can add the offset and pass that in, but it's not working right for me at the moment.

Other questions arise.

I can't yet find any documentation explaining why a zero offset to mmap works.
Last edited by JGPIO on Wed Jun 01, 2016 8:05 pm, edited 2 times in total.

User avatar
joan
Posts: 14998
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Wed Jun 01, 2016 8:02 pm

mattlewis wrote:That is strange, it would indeed appear to work when accessing /dev/mem with zero offset. Next Java question - how do you then access the GPIO registers given the large offset...

Matt
Passing 0 should only work for /dev/gpiomem. The driver is a special case and always returns a pointer to the base address of the GPIO peripheral.

For /dev/mem you need to pass the base address of the peripheral.

mattlewis
Posts: 113
Joined: Sat Jan 12, 2013 3:05 pm
Location: UK
Contact: Website

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Wed Jun 01, 2016 9:24 pm

Yes, I was hoping that /dev/gpiomem would work with a 0 offset however it gives the same strange output as when passing the offset value and using /dev/mem. Annoying
http://www.diozero.com/

JGPIO
Posts: 25
Joined: Sun Feb 28, 2016 6:29 pm

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Fri Jun 10, 2016 10:57 pm

Solved it!

Having created a test-bed in isolation, and making it so that I could configure every relevant parameter to mmap and NewDirectByteBuffer, I determined that a 0 offset to mmap does not have any effect on this problem at all.

I resorted to studying the code of the DirectByteBuffer class but this didn't produce any real possible solutions, although I did come to realise that the class itself uses a JNI call (albeit directly into the JVM presumably), and therefore this approach would unlikely be faster.

The solution to the problem is two fold:

1) Use native byte ordering: buf.order(ByteOrder.nativeOrder());
2) Access the ByteBuffer as an IntBuffer: IntBuffer intbuf = buf.asIntBuffer();

Why it's necessary to use a IntBuffer is not yet clear (the getInt() methods of ByteBuffer should suffice).

I can now reliably control the GPIO pins using a ByteBuffer returned by NewDirectByteBuffer.
Last edited by JGPIO on Fri Jun 10, 2016 11:40 pm, edited 1 time in total.

JGPIO
Posts: 25
Joined: Sun Feb 28, 2016 6:29 pm

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Fri Jun 10, 2016 11:07 pm

I was wrong!

Initial speed tests show my B+ performs a set/clr loop using a Java ByteBuffer at just short of 5Mhz!

mattlewis
Posts: 113
Joined: Sat Jan 12, 2013 3:05 pm
Location: UK
Contact: Website

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Sat Jun 11, 2016 6:19 am

I'm impressed with your perseverance, nice work!

Do you have plans for a high performance native Java device library? I'd be happy to help if so.

Matt
http://www.diozero.com/

JGPIO
Posts: 25
Joined: Sun Feb 28, 2016 6:29 pm

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Sat Jun 11, 2016 8:02 am

Thanks!

I merely need to retrofit my existing work with the ByteBuffer solution. Should take less than an hour.

I'm guessing you would like a copy?

mattlewis
Posts: 113
Joined: Sat Jan 12, 2013 3:05 pm
Location: UK
Contact: Website

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Sat Jun 11, 2016 10:32 am

That would be great thanks
http://www.diozero.com/

JGPIO
Posts: 25
Joined: Sun Feb 28, 2016 6:29 pm

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Sat Jun 11, 2016 12:01 pm

14Mhz+? Surely not?

mattlewis
Posts: 113
Joined: Sat Jan 12, 2013 3:05 pm
Location: UK
Contact: Website

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Sat Jun 11, 2016 2:03 pm

That's awesome. For my wrapper library diozero I'd like to provide both a maximum compatibility provider to support multiple devices as well as an ultra high performance Raspberry Pi specific one that has minimal dependencies. I was hoping JDK Device I/O would provide the former but seems that may not be the case. I'm currently using my thin pigpio JNI wrapper for the latter but it would be nice to implement more if it in Java to reduce other dependencies plus also be good to show that it can be done.
Matt
http://www.diozero.com/

mattlewis
Posts: 113
Joined: Sat Jan 12, 2013 3:05 pm
Location: UK
Contact: Website

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Mon Jun 13, 2016 9:56 pm

I've updated my mmap test project with basic GPIO control via /dev/gpiomem. My test app turns a GPIO on then off @ 11MHz on a Pi3, not too shabby :) Hats off to JGPIO for the IntBuffer trick.
As it uses /dev/gpiomem it can be run without sudo.
Code is here: https://github.com/mattjlewis/diozero/t ... mmap-tests.

Matt
http://www.diozero.com/

JGPIO
Posts: 25
Joined: Sun Feb 28, 2016 6:29 pm

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Tue Sep 06, 2016 7:26 pm

Sorry it's been so long.

My Java code on my B+ will run a set/clr loop at 11MHz+, and when overclocked to Medium it will run at 15MHz+. My PI3 runs the same code at 50MHz+

I need to obtain independent verification of this.

mattlewis
Posts: 113
Joined: Sat Jan 12, 2013 3:05 pm
Location: UK
Contact: Website

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Fri Sep 09, 2016 6:18 am

Are you able to share the code?
I have a cheap USB scope, happy to check what that detects.
Matt
http://www.diozero.com/

Return to “Java”