PYNQ: PYTHON PRODUCTIVITY

Using pmod_grove_imu code available

Hello,

I have seen that some code to read an imu is provided (seems to be from @rock) (https://github.com/Xilinx/PYNQ/blob/master/pynq/lib/pmod/pmod_grove_imu.py). I have a similar board from WaveShare which has the same MPU9250 and the BMP180. However, i do not have a Grove pmod adapter so I would like to directly use 2 pins of the PMOD (A or B, doesn’t matter) or the any of the arduino shield headers. So, where should I define the PINS I want to use in the code?

Moreover, I see that it also uses the .bin file. What is this for?

I am running everything directly on the pynq (via ssh) and I would like to run the python script but I haven’t succeed so far:

xilinx@pynq:~$ sudo python3.6
Python 2.7.15rc1 (default, Apr 15 2018, 21:51:34)
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from pynq.lib.pmod import pmod_grove_imu

And then? How should I call the functions? imu = pmod_grove_imu.Grove_IMU() ?
Thank you for your help.

That WaveShare board looks odd to me. By convention, Pmods have VCC, then Gnd aligned to the left, so it looks like you would need to insert this board upside down. Other than that, it seems it is pin compatible.

I2C needs pins with pull-up resistors. You can use the “G3” or “G4” pins in the base overlay as these are already set to have pull-up resistors. See here for more info:
https://pynq.readthedocs.io/en/v2.6.1/pynq_libraries/grove.html
There is a software check to make sure you use these pins.

On the same link above, you will see examples of using other Grove devices.
More info on IMU here:
https://pynq.readthedocs.io/en/v2.6.1/pynq_package/pynq.lib/pynq.lib.pmod.html#module-pynq.lib.pmod.pmod_grove_imu

Also, try have a look at similar examples: (you will need to use Jupyter, rather than just ssh)
https://github.com/Xilinx/PYNQ/blob/master/boards/Pynq-Z1/base/notebooks/pmod/pmod_grove_light.ipynb

The Pmod is connected to a Microblaze. The real driver for the Pmod is running on the MicroBlaze. Think of the Python file as a wrapper. The bin is the MicroBlaze executable (pre-compiled).

I see the WaveShare has extra int and fsync pins though. I didn’t check what these are.

Cathal

That is correct. However, I will use some cables so it will not be an issue.

That was actually my question. How do I set which pin will be SCL and which one will be SDA? Or which PMOD should I use?

The following code works, but of course is not reading anything from the sensors as I am not sure which PMOD pins to use for SDA and SCL:

from pynq.overlays.base import BaseOverlay
from pynq.lib.pmod import pmod_grove_imu
from pynq.lib.pmod import PMOD_GROVE_G4

base = BaseOverlay("base.bit")
imu = pmod_grove_imu.Grove_IMU(base.PMODA,PMOD_GROVE_G4)
accl = imu.get_accl()
print("Accelerator: ", accl)
altitude = imu.get_altitude()
print("Altitude: ", altitude)

Thanks @cathalmccabe for your help!

I modified the coded a bit and I am able to read it:

import time
from pynq.overlays.base import BaseOverlay
from pynq.lib.pmod import Grove_IMU
from pynq.lib.pmod import PMOD_GROVE_G3

base = BaseOverlay("base.bit")
mb_info = {'ip_name':'iop_pmoda/mb_bram_ctrl' ,'rst_name':'mb_iop_pmoda_reset'}
gr_pin = PMOD_GROVE_G3	# SDA on 3, SCL on 7 (PMOD A): https://pynq.readthedocs.io/en/v2.0/pynq_libraries/grove.html
imu = Grove_IMU(mb_info, gr_pin)
imu.reset()

SampleFreq=30
delay=1/SampleFreq

while True:
    accl = imu.get_accl()
    print("Accelerator: ", accl)
    mag = imu.get_compass()
    print("Magnetometer: ", mag)
    gyro = imu.get_gyro()
    print("Gyroscope: ", gyro, "\n")
    time.sleep(delay)
1 Like

Similar to this topic I only get 2 Hz if I want to read accelerometer, gyroscope and magnetometer. I am measuring as follows:

import time
from pynq.overlays.base import BaseOverlay
from pynq.lib.pmod import Grove_IMU
from pynq.lib.pmod import PMOD_GROVE_G3

base = BaseOverlay("base.bit")
mb_info = {'ip_name':'iop_pmoda/mb_bram_ctrl' ,'rst_name':'mb_iop_pmoda_reset'}
gr_pin = PMOD_GROVE_G3  # SDA on 3, SCL on 7 (PMOD A): https://pynq.readthedocs.io/en/v2.0/pynq_libr$
imu = Grove_IMU(mb_info, gr_pin)
imu.reset()

SampleFreq=30
delay=1/SampleFreq

while True:
    start_time = time.time()
    accl = imu.get_accl()
    print("Accelerator: ", accl)
    mag = imu.get_compass()
    print("Magnetometer: ", mag)
    gyro = imu.get_gyro()
    print("Gyroscope: ", gyro)
    elapsed_time = time.time() - start_time
    print("Total time: ", elapsed_time, "\n")
    time.sleep(delay)

And obtaining always:

Accelerator:  [0.02, 0.02, 1.03]
Magnetometer:  [0.88, 0.59, 5.86]
Gyroscope:  [-0.43, 1.19, 0.66]
Total time:  0.43114256858825684 

It would be nice to read at at least 30 Hz. @cathalmccabe @rock any idea?

Thank you.

You included print into your measured time period, which will be slow. Also, reading it multiple times may not be the right thing to do; you are counting in the communication overhead each time. Logging might be the right thing to do, i.e., reading multiple values one time.

@rock As I am also doing this with ROS, there are NO prints and I still get the same sample rate. Even if I remove the print in the code I share I get the same number…Even if I read only one sensor, it takes ~ 140ms.

Then if reading multiple times may not be the right thing to do, how should I read only once all three at once still at 30 Hz? What do you mean by logging?

The reason why you have a long delay is that each read will incur a communication roundtrip overhead between Python, PS, and Microblaze. Logging is something we provide to deal with multiple values- basically just pushing back multiple values into the circular buffer and read them back all at once. e.g.


If you want to do that you need to either recompile microblaze binary if logging is not a valid command yet, or use ipython microblaze to directly do this in Jupyter notebook. Check https://github.com/Xilinx/PYNQ/tree/image_v2.6.0/boards/Pynq-Z1/base/notebooks/microblaze for example of ipython microblaze.

That seems quite cumbersome. Wouldn’t it be more efficient to re-compile the kernel to use the I2C from the arm?

But the pmod pins are not connected to PS directly. Then you will have to rebuild a customized bitstream, which uses EMIO to connect your pmod pins to PS i2c pins - takes even longer time.

So, if I understood correctly, I should do those multiple I2C reads directly on the microblaze, store them in a circular buffer and directly read that buffer rather than the I2C?
How can I check if logging is a valid command? I should modify then this file (arduino_grove_imu.c), right?

Yes, you will need to add more cases to support logging. To recompile the arduino_grove_imu.bin you will need the microblaze BSP and Pynq-Z2 base.xsa file, and that is tedious. What I recommend is to use ipython microblaze, which allows you to program C code directly in jupyter cell.

The issue is that I cannot use jupyter, that is why I am directly ssh’ing into it. I already have a compiled image with the PS i2c pins routed to the pmods so the question would be, can I copy the pynq folder to use partial bitstream like in the dma example?

Thank @rock for your help.

If you are just using PS I2C, you don’t need to touch pynq folder IMO. You can use linux i2c driver to directly control the pmod device.

But I still think you need to fix the jupyter access, if that is an issue.

Yes, that is how I am doing it. The question was because I would still like to have the possibility to use partial bitstream for new hw components without having to re-compile everything. That is why I cited the DMA example.

There are examples:

This is needed if you want to add your kernel driver. But I think you should start with user-space first.

Suppose you connect it to PS I2C0, first I would check if PS I2C0 is already enabled in your PS configuration (in FSBL). If not you will need to enable that first - recompile the boot partition at least. Then the second step would be just reload the bitstream and do i2cdetect -y -r 0 to see if the new address is responsive.

@Rock the i2c is clear, I can see it with i2cdetect -y -r 0, can read and write the register. I am able to read at 30 Hz accl, gyro and mag. The question is if I can still use the pynq functionalities when I create a new bitstream like that dma example by just copying the pynq folder to my new image.

The pynq folder does not have to be touched. Sure you can replace that with some of your functionalities but I would just put all customized things outside so you have a separate package. When specifying the bitstream, just give the absolute path, e.g.:

from pynq import Overlay
ol = Overlay('/home/xilinx/i2c.bit')

And also, all pynq functionalities will be there if you want to use.

If you want to reuse the IMU driver code, maybe just copying out those methods out to your own driver.