[]SH vs []NA and I2C

No need to worry about going "off topic" here
Forum rules
This forum is for general chit-chat, not necessarily APL-related. However, it's not for spam or for offensive or illegal comments.

[]SH vs []NA and I2C

Postby ray on Mon Jan 02, 2017 4:41 pm

Hi,

Is ⎕NA faster than ⎕SH/⎕CMD?

I am writing code for my Raspberry Pi robot to read a HMC5883L 3 axis compass.

The HMC5883L is attached to the Pi as an I2C device.

There is a lot of Python code around to read this compass on a Pi.

Ideally, my APL code would read:

Code: Select all
Start_Turning
:While target<⎕SH 'python Get_Bearing.py'
:EndWhile
Stop_Turning


However, during the time it takes to get a reading via Python, the robot can have turned thru 5 or more degrees. This makes getting the robot to accurately turn to a desired direction (+/-0.5 degree) very difficult. So I end up with

Code: Select all
:While target<⎕SH 'python Get_Bearing.py'
   Start_Turning
   ⎕DL 0.02
   Stop_Turning
:EndWhile


This works but produces very jerky movements.
(The 0.02 delay is the smallest time period that my robot can cope with while still overcoming friction and producing an actual movement. Less than that, it stutters but does not actually turn.)


So I have cobbled together some C code (from code obtained on the internet) that is much faster. This call itself makes use of some I2C C library calls.

Currently, my compiled C code outputs the compass bearing to stdout. Thus I can make a fast compass reading via a simple ⎕SH call.

Code: Select all
Start_Turning
:While target<⎕SH 'Get_Bearing_viaC'
:EndWhile
Stop_Turning


Using this, the overshoot is limited to less than 2 degrees
(With more complex APL control code, I can now achieve +/- 0.7 degrees of my target.)

Would there be any great speed advantage in compiling the C code into a "shared object" (the Linux equivalent of a DLL) and then using ⎕NA to make the call instead?

(You might well be saying "Try it and test which is faster!".
Well I have, but my C programming skills have so far only resulted in a module that ⎕NA rejects, stating that it can't be dynamically loaded.)

If a ⎕NA call is going to be significantly faster than effectively the same C code via a ⎕SH call, then I will perceiver in producing a shared object library.

Possibly a more APL solution (with no need of any C compiling) would be to call the I2C library modules (the ones my C code already uses) directly from APL via multiple ⎕NA calls. (⎕NA calls to "Initialization" would be done outside of the "while" loop, with only the ⎕NA calls to take a reading, done inside.)

Has anyone had any experience in accessing the I2C "registers" directly from APL?
(Via ⎕ARBIN on /dev/i2c-1 possibly?)

Any suggestions gratefully received.

Happy New Year!

Ray Cannon
Ray Cannon
Please excuse any smelling pisstakes.
User avatar
ray
 
Posts: 95
Joined: Wed Feb 24, 2010 12:24 am
Location: Blackwater, Camberley. UK

Re: []SH vs []NA and I2C

Postby AndyS|Dyalog on Wed Jan 04, 2017 9:37 am

In general, I'd say that using ⎕NA-based code will be quicker than the equivalent ⎕SH-based alternative. The overhead of loading the shared library is much less than the overhead of having to generate a new process, and you should only need to load (ie call ⎕NA) once in the life of the process. I did some tests comparing calling ⎕NA'getenv' against using ⎕SH 'echo ' some time ago, and the ⎕NA call was at least 30 times quicker as far as the APL process is concerned, and most certainly lowers the load on the operating system too.

The downside is that your code may need to be altered to run on different platforms - OK, in the case of the Pi, there's (currently) only a 32-bit version of Dyalog that runs on 32-bit Raspbian, but it surely won't be too long before there will be a 64-bit Dyalog running on a 64-bit operating system on Pi 3s ..

Geoff will be in contact with you to help you write a shared library .. and once you're up and running, we'll add a sample shared library to the next version of Dyalog (and document it!). Note that as far as we are concerned, there's a big difference between a shared library that supports "A"s and "Z"s (since they need to understand the internal structure of arrays), and those that don't support them; we'll start with one of the latter.
User avatar
AndyS|Dyalog
 
Posts: 152
Joined: Tue May 12, 2009 6:06 pm

Re: []SH vs []NA and I2C

Postby ray on Sat Jan 07, 2017 1:16 am

Thanks for the feedback and support.

The bad news is that even with Geoff's help on creating a shared library, my C coding skills are not up to the task. (Adding the required "-shared" option in the gcc compile/link, caused my code to fail with a segmentation error.)

However, the good news is that I can access I2c device via ⎕NA call using the standard "PIGPIO" library without the need of my writing any new C code at all!
(See http://abyz.co.uk/rpi/pigpio/cif.html)

The "PIGPIO" library is very large and includes some simple tools to read and write to I2C devices. The documentation to install it on the Raspberry Pi, was also very easy to follow.

So now to read the 6 bytes from the I2C HCM5883L compass (that can be bought for less than £5 including postage) can be achieved from APL by defining just four ⎕NA statements
(One each to define in APL the functions: gpioInitialise, i2cOpen, i2cClose, and i2cZip.)

Initialisation is performed by "gpioInitialise", while i2cOpen returns the handle that the other I2C functions require. "i2cClose" should be performed when finished.

The most complex function to code was i2cZip:

int i2cZip(unsigned handle, char *inBuf, unsigned inLen, char *outBuf, unsigned outLen)


I converted this into APL as:

Code: Select all
⎕NA 'I /home/pi/PIGPIO/libpigpio.so|i2cZip U =C[] U =C[] U'


This function executes a sequence of I2C operations. The operations to be performed are specified by the contents of inBuf which contains the concatenated command codes and associated data.
The following command codes are supported:

Name Cmd & Data Meaning
End 0 No more commands
Escape 1 Next P is two bytes
On 2 Switch combined flag on
Off 3 Switch combined flag off
Address 4 P Set I2C address to P
Flags 5 lsb msb Set I2C flags to lsb + (msb << 8)
Read 6 P Read P bytes of data
Write 7 P ... Write P bytes of data

Example: Get a reading (6 bytes of data), at offset 3, from the HMC5883L device which is at address 0x1e:
Set address 0x1E, write 0x03, read 6 bytes, End

0x04 0x1E 0x07 0x01 0x03 0x06 0x06 0x00


This took me some experimentation to work out how to code this up.
Eventually I came up with the idea that I should use ⎕UCS to convert the hex numbers string into characters to be place in the input buffer, and after the call, convert the output character buffer back into numbers.

Code: Select all
  inBuf←⎕UCS 4 30 7 1 3 6 6 0   ⍝ 0x04 0x1E 0x07 0x01 0x03 0x06 0x06 0x00
  rc inBuf outBuf←i2cZip 0 inBuf 8 outBuf 255 ⍝ read the I2C device
  6↑⎕UCS outBuf                 ⍝ convert the char string into numbers
0 34 254 221 0 236


I still need to convert these 6 bytes into X,Y and Z axis vectors, and then into a compass direction.

But I am now happy that this will be MUCH faster than my earlier calls using ⎕SH!

Andy's 30x suggested speed-up looks about right, I can make 500 calls in less that a second on my Pi!

Thanks.
Ray Cannon
Please excuse any smelling pisstakes.
User avatar
ray
 
Posts: 95
Joined: Wed Feb 24, 2010 12:24 am
Location: Blackwater, Camberley. UK


Return to Chat

Who is online

Users browsing this forum: No registered users and 1 guest