Accessing the RPi GPIO pins from APL

For Raspberry Pi specific issues .. more general issues will appear in UNIX and Linux

Re: Accessing the RPi GPIO pins from APL

Postby ray on Fri Feb 03, 2017 1:39 am

Update on the PIGPIO "C" library to APL interface:

I have NOT tried yet to code those marked as "EXPERT - Not intended for general use".

I have attempted to code up all the rest (149 of them) and I have managed to get 146 defined as complied functions available in APL.

However, I know that the syntax I have supplied is incorrect for the following 5 functions:
Code: Select all
#.NAbscXfer #.NAgpioSetTimerFunc #.NAgpioSetTimerFuncEx #.NAgpioStartThread #.NAgpioStopThread
so some work is still required here.
Ray Cannon
Please excuse any smelling pisstakes.
User avatar
ray
 
Posts: 218
Joined: Wed Feb 24, 2010 12:24 am
Location: Blackwater, Camberley. UK

Re: Accessing the RPi GPIO pins from APL

Postby bwyork67 on Wed Oct 11, 2017 6:48 pm

Hi Ray

Just saw your post. Are you distributing your code?
bwyork67
 
Posts: 13
Joined: Tue Nov 12, 2013 7:01 pm

Re: Accessing the RPi GPIO pins from APL

Postby ray on Thu Oct 12, 2017 4:44 pm

Hi,

>Just saw your post. Are you distributing your code?

So far, I have just about managed to put the interface to the PIGPIO code on to GITHUB:

https://github.com/RayCannon/gpio

Currently, there is very little documentation, as I am struggling to get to grips with GITHUB, and I am NOT a fan of Dyalog Scripts as I love the old fashion workspace, and simple namespaces.

Part of the problem is I am having too much fun using the code in Robot ANT project and have no time to bother with Githib.

To give you something to get on with, here is some code for "Ant_1" my robot ant.

The ant uses a modified tripod gait.

It uses 3 servos connected to a PCA9685 (an I2C device to control up to 16 PWM devices) to drive the 6 legs.
The Right front and back legs are controlled by the first servo.
The Left front and back legs are controlled by the second servo.
The two middle legs "see-saw" the ant from side to side and use the third servo.

(See https://diyhacking.com/hexapod-walker-raspberry-pi/)

Code: Select all
 Ant_1;LeftLegs;MiddleLegs;RightLegs;centzero;dl;hand;i;leftFor;leftUp;leftup;legzero;liftmov;llegaft;llegfor;rightup;rlegaft;rlegfor;shand;stepmov;center_nums
⍝ Code to drive a robotic ant
⍝ connected via PCA9685
⍝ Channel 0=left   back and forward
⍝ Channel 1=right  back and forward
⍝ Channel 2=middle up   and down
 Close
 Init
 shand←Open_I2C'40'                   ⍝ Open I2C device PCA9685 on 0x40 (16 servo controller)
 InitServos shand                     ⍝ Initialise the 16 servos channels

 dl←0.6                               ⍝ delay for servos to complete movement
 RightLegs←0                          ⍝ right legs channel
 LeftLegs←1                           ⍝ left side front and back legs channel
 MiddleLegs←2                         ⍝ center legs channel
 centzero←54                          ⍝ MiddleServos at center position 50%
 legzero←56                           ⍝ legs at central positin
 stepmov←20                           ⍝ Step movement 25 % (initially)
 liftmov←7                            ⍝ Step lift movement 10 % (initially)
 leftup←centzero+liftmov              ⍝ move left side up
 rightup←centzero-liftmov             ⍝ move right side up
 llegfor←legzero+stepmov              ⍝ Left Leg forward
 llegaft←legzero-stepmov              ⍝ Left Leg back
 rlegfor←legzero-stepmov              ⍝ right Leg forward
 rlegaft←legzero+stepmov              ⍝ right Leg back
 center_nums←shand centzero legzero leftup rightup
 CenterServos center_nums             ⍝ Center all servos
 ⎕DL dl
 :For i :In ⍳3                        ⍝ Move 3 steps forward
     Breast shand                     ⍝ Breast-stroke movement rather than Crawl
 :End
 CenterServos center_nums             ⍝ Center all servos
 MoveServos shand ¯1 ¯1               ⍝ stop all servos
 CancelServo shand                    ⍝ Switches all PWM channels off
 Close                                ⍝ close the PIGPIO interface


Code: Select all
Close;⎕TRAP;a;b
⍝ Close down PiGpio and any open I2C devices
 ⎕TRAP←0 'E' '→⎕LC+1'
 :If 9=#.⎕NC'PiGpio'
     :If ×⍴,#.PiGpio.Handles
         a←#.PiGpio.i2cClose¨#.PiGpio.Handles
     :End
     #.PiGpio.Handles←⍬
     #.PiGpio.Devices←⍬
     b←#.PiGpio.gpioTerminate
 :End


Code: Select all
 version←Init;so;hex2int
⍝ Initialise the PIGPIO library calls

 version←'Init I2C failed'
 hex2int←{⊃16⊥('0123456789abcde'⍳⍵)-⎕IO}
⍝ Create a namespace for PIGPIO objects
 :If 0=#.⎕NC'PiGpio'
     'PiGpio'#.⎕NS''
 :End
 #.PiGpio.Devices←⍬  ⍝ Numeric Address of I2C devices
 #.PiGpio.Handles←⍬  ⍝ Handles of open I2C devices
⍝ define APL "cover" function in #.PiGpio
 so←'/home/pi/PIGPIO/libpigpio.so'    ⍝ Location of the PIGPIO DLL
 #.PiGpio NA.Init_PiGpio_via_NA so       ⍝ set up the functions in NS #.PiGpio
 ⎕NA'/home/pi/libsonarEcho.so|afun U U U4'
 ⎕NA'I /home/pi/libsonarEcho.so|mySetAlertFunc U ∇(P P P)'

⍝ Initialises the library
 #.PiGpio.(version←gpioInitialise)    ⍝ Returns the pigpio version number if OK, otherwise ¯1

 :If #.PiGpio.version<0
     'PIGPIO ERROR'⎕SIGNAL 711
 :Else
     version←'PIGPIO version #',⍕#.PiGpio.version
 :End


Code: Select all
 {id}←Open_I2C add;dev;han;bo
⍝ Open an I2C devive on hex address <add>, returning a handle to the device
 dev←#.hex2int add                    ⍝ Numeric address
 bo←#.PiGpio.Devices∊dev              ⍝ where device is in list
 han←⍬
 :If ∨/bo                             ⍝ Is the I2C device already open?
     id←bo/#.PiGpio.Handles           ⍝ handle of open device
 :Else
     han←#.PiGpio.i2cOpen 1 dev 0     ⍝ Open the device returning a handle (1←→bus number)
     :If han≥0                        ⍝ Check it opened OK
         #.PiGpio.Devices,←dev        ⍝ Add to the list
         #.PiGpio.Handles,←han
         id←han
     :Else
         'I2C OPEN ERROR'⎕SIGNAL 711
     :End
 :End


Code: Select all
 InitServos handle;mode;reg;buf;rc;val;restart;ai;sleep;allcall;och;outdrv;b2ui;shift;m1;m2
⍝ Initialise the frequency to drive the named <servos> on device with <handle>
⍝ Set the frequency to about 55 Hz

 b2ui←{⎕UCS 80 ⎕DR ⍵}                       ⍝ Convert bit string to unsigneg int
 shift←{⍵⌽¯8↑1}                             ⍝ shift by ⍵    1<<⍵
 m1←0                                       ⍝ _MODE1         = 0x00
 m2←1                                       ⍝ _MODE2         = 0x01
⍝ At first, we call for initialize() method which restarts the device by
⍝ first setting the SLEEP_BIT to ‘0’ in MODE1 register
⍝ and then setting the RESTART_BIT to ‘1’ in the same register.
⍝ Also, it sets the AI_BIT to ‘1’ which allows to perform multi byte I2C operations for faster data transfer.Auto Increment

 ⍝ Bits in mode 1
 restart←shift 7                            ⍝ _RESTART=1<<7

 ai←shift 5                                 ⍝ _AI=1<<5
    ai=Auto Increment
 sleep←shift 4                              ⍝ _SLEEP=1<<4

 allcall←shift 0                            ⍝ _ALLCALL=1<<0


⍝ Bits in Mode 2
 och←shift 3                                ⍝ _OCH=1<<3
  Outputs change on: STOP command (0) or on ACK (1)
 outdrv←shift 2                             ⍝ _OUTDRV=1<<2 The 16 LEDn outputs are configured with: an open-drain structure (0), a totem pole structure (1)

⍝ Reseting PCA9685 MODE1 (without SLEEP) and MODE2
 val←b2ui ai∨allcall                        ⍝ self._AI | self._ALLCALL
 rc←#.PiGpio.i2cWriteByteData handle m1 val ⍝ self._write_reg(self._MODE1, self._AI | self._ALLCALL)

 val←b2ui och∨outdrv                        ⍝ self._OCH | self._OUTDRV
 rc←#.PiGpio.i2cWriteByteData handle m2 val ⍝ self._write_reg(self._MODE2, self._OCH | self._OUTDRV)

 ⎕DL 0.005                                  ⍝# wait for oscillator

⍝ Read the current mode
 mode←#.PiGpio.i2cReadByteData handle m1    ⍝ mode = self._read_reg(self._MODE1)

⍝ # wake up (reset sleep)
 val←b2ui(11 ⎕DR mode)∧~sleep               ⍝ mode & ~self._SLEEP
 rc←#.PiGpio.i2cWriteByteData handle m1 val

 ⎕DL 0.005                                  ⍝# wait for oscillator
 CancelServo handle                         ⍝ Switches all PWM channels off

⍝ handle ServoFreq 200                      ⍝ self.set_frequency(200)
 handle ServoFreq 50                        ⍝ self.set_frequency(50)


Code: Select all
 CenterServos(shand centzero legzero leftup rightup)
⍝ Center all servos

⍝ Lift up left legs
 MoveServos shand MiddleLegs leftup
 ⎕DL dl
⍝ Center left legs
 MoveServos shand LeftLegs legzero

⍝  Lift up right legs
 MoveServos shand MiddleLegs rightup
 ⎕DL dl
⍝ Center right legs
 MoveServos shand RightLegs legzero
 ⎕DL dl

⍝ Center middle leg
 MoveServos shand MiddleLegs centzero
 ⎕DL dl


Code: Select all
 MoveServos(handle channel pc);reg;steps;rc;on;off;min;max;range;mid;minl;maxl;percent;hi_on;low_on;hi_off;low_off;Check
⍝ Move <servo> to position (pc%) 0%=-90 50%=0 100%=+90
⍝ Channel←→0-15 or ¯1 for all
⍝ if pc -ve STOP the servo
⍝ Channel ←→0-15    or ¯1 for all

 Check←{⍺←0                          ⍝ ⍺ ←→ count retries
     rc←⍺⍺ ⍵                         ⍝ ⍺⍺←→#.PiGpio.i2cWriteByteData
     rc=0:0                          ⍝ if it worked return 0=OK
     {}⎕DL 0.01                      ⍝ it failed, wait a mo
     count←1+⍺                       ⍝ increment count
     count=10:rc                     ⍝ exit if cont exceeded max
     rc=-82:count(⍺⍺ ∇∇)⍵            ⍝ and re-try
     rc}
 min←2.5                             ⍝ 50000÷19988.48  ⍝ minimum % for  500micro sec with pulse width 19988.48
 max←12.5                            ⍝ 250000÷19988.48 ⍝ maximum % for 2500micro sec with pulse width 19988.48
 range←max-min
⍝ Calculate the required percentage
 :If pc≤0                            ⍝ if -ve Stop the servo
     on←0
     off←0
     steps←0
 :Else
     percent←min+range×pc÷100
     steps←⌊percent×4096÷100
     :If steps>4095
         on←4096
         off←0
     :Else
         on←0
         :Select steps
         :CaseList ⍳81
             off←82
         :CaseList 81↓⍳490
             off←steps
         :CaseList 490↓4096
             off←490
         :Else
             off←0
         :End
     :End
 :End

 :If channel=¯1
     ⍝ all channels  _ALL_LED_ON_L  = 0xFA 250
     reg←hex2int'fa'
 :Else
     ⍝_LED0_ON_L     = 0x06
     reg←6+4×channel
 :End
 hi_on low_on←256 256⊤on
 hi_off low_off←256 256⊤off
 rc←#.PiGpio.i2cWriteByteData Check handle(reg+0)(low_on)
 rc←#.PiGpio.i2cWriteByteData Check handle(reg+1)(hi_on)
 rc←#.PiGpio.i2cWriteByteData Check handle(reg+2)(low_off)
 rc←#.PiGpio.i2cWriteByteData Check handle(reg+3)(hi_off)
 :If rc<0
     ('I2C WRITE BYTE ERROR ',⍕rc)⎕SIGNAL 711
 :End


Code: Select all
 Breast shand
⍝ Move forward using Breast stroke movement
 MoveServos shand MiddleLegs leftup
 ⎕DL dl
 MoveServos shand LeftLegs llegfor
 ⎕DL dl
 MoveServos shand MiddleLegs rightup
 ⎕DL dl
 MoveServos shand RightLegs rlegfor
 ⎕DL dl
 MoveServos shand MiddleLegs centzero
 ⎕DL dl
 MoveServos shand LeftLegs llegaft
 MoveServos shand RightLegs rlegaft
 ⎕DL dl


Code: Select all
 CancelServo handle;reg;buf;rc
⍝ Switches all PWM channels off   set_duty_cycle(-1, 0)
 reg←hex2int'fa'                                  ⍝ all channels  _ALL_LED_ON_L  = 0xFA 250
 buf←⎕UCS 0 0 0 0                                 ⍝ on=0 off=0
 rc←⊃PiGpio.i2cWriteI2CBlockData handle reg buf 4 ⍝ self.set_duty_cycle(-1, 0)
 :If rc<0
     ('I2C WRITE BLOCK ERROR ',⍕rc)⎕SIGNAL 711
 :End


Code: Select all
 handle ServoFreq freq;mode;val;restart;ai;sleep;allcall;och;outdrv;rc;b2ui;shift;ps;m1;m2;bits;mod;pulse_width;prescaleval
⍝ setFrequency() method is used to control the output frequency in Hz.
⍝ It calculates the prescale value based on the required frequency and writes it to the PRE_SCALE register.
⍝ freq←→ frequency in Hz Between 50 and 60 for servos
 ps←hex2int'fe'                                     ⍝ _PRESCALE      = 0xFE  254   prescale register number
 m1←0                                               ⍝ _MODE1         = 0x00
 m2←1                                               ⍝ _MODE2         = 0x01
 b2ui←{⎕UCS 80 ⎕DR ⍵}                               ⍝ Convert bit string to unsigneg int
 shift←{⍵⌽¯8↑1}                                     ⍝ shift by ⍵    1<<⍵
 ⍝ Bits in mode 1
 restart←shift 7                                    ⍝ _RESTART=1<<7
 ai←shift 5                                         ⍝ _AI=1<<5
 sleep←shift 4                                      ⍝ _SLEEP=1<<4
 allcall←shift 0                                    ⍝ _ALLCALL=1<<0
⍝ Bits in Mode 2
 och←shift 3                                        ⍝ _OCH=1<<3
  Outputs change on: STOP command (0) or on ACK (1)
 outdrv←shift 2                                     ⍝ _OUTDRV=1<<2 The 16 LEDn outputs are configured with: an open-drain structure (0), a totem pole structure (1)
⍝ "Sets the PWM frequency."

 prescaleval←25000000                               ⍝ prescaleval = 25000000.0    # 25MHz internal oscillator (requires no external components)
 prescaleval÷←4096                                  ⍝ prescaleval /= 4096.0       # 12-bit
 prescaleval÷←freq                                  ⍝ prescaleval /= float(freq)
 prescaleval-←1                                     ⍝ prescaleval -= 1.0 ⍝ Estimated pre-scale 121.0703125 @ 50hz
 prescaleval←3⌈255⌊⌊0.5+prescaleval                 ⍝ as an int between 3 and 255
⍝ Read the current mode
 mod←#.PiGpio.i2cReadByteData handle m1             ⍝ mode=self._read_reg(self._MODE1);

⍝ Go to sleep
⍝⍝// disable restart, sleep, set prescale and reset
⍝⍝ this->pBus->WriteRegisterByte(this->devAddress, MODE1, (mode & ~RESTART) | SLEEP);
⍝⍝ this->pBus->WriteRegisterByte(this->devAddress, PRESCALE, prescale);
⍝⍝ this->pBus->WriteRegisterByte(this->devAddress, MODE1, mode);
 mode←11 ⎕DR mod
⍝⍝ bits←mode∧~sleep                                   ⍝ mode & ~self._SLEEP
 bits←mode∧~restart                                   ⍝ mode & ~self._RESTART
 val←b2ui bits∨sleep                                ⍝ (mode&~self._SLEEP)|self._SLEEP
 rc←#.PiGpio.i2cWriteByteData handle m1 val         ⍝ self._write_reg(self._MODE1,(mode&~self._SLEEP)|self._SLEEP)

⍝ Set prescaleval (can only be done while asleep)
 rc←#.PiGpio.i2cWriteByteData handle ps prescaleval ⍝ self._write_reg(self._PRESCALE,prescale)

⍝ reset the mode
 rc←#.PiGpio.i2cWriteByteData handle m1 mod         ⍝ self._write_reg(self._MODE1,mode)

 ⎕DL 0.005                                          ⍝ time.sleep(0.0005)

⍝ Restart it
 val←b2ui mode∨restart                              ⍝ mode|self._RESTART
 rc←#.PiGpio.i2cWriteByteData handle m1 val         ⍝ self._write_reg(self._MODE1,mode|self._RESTART)


 freq←(25000000÷4096)÷(prescaleval+1)               ⍝ self._frequency=(25000000/4096)/(prescale+1)   freq←50.0288166 ⍕ 50
 pulse_width←(1000000÷freq)                         ⍝ self._pulse_width=(1000000/self._frequency)
     pw←19988.48


Code: Select all
 hex2int←{16⊥('0123456789abcde'⍳(819⌶)⍵)-⎕IO}
Ray Cannon
Please excuse any smelling pisstakes.
User avatar
ray
 
Posts: 218
Joined: Wed Feb 24, 2010 12:24 am
Location: Blackwater, Camberley. UK

Re: Accessing the RPi GPIO pins from APL

Postby bwyork67 on Thu Oct 12, 2017 8:57 pm

Many thanks. This is really helpful. If I get something going that is worth seeing,
you will be gratefully acknowledged.
bwyork67
 
Posts: 13
Joined: Tue Nov 12, 2013 7:01 pm

Re: Accessing the RPi GPIO pins from APL

Postby ray on Tue Apr 16, 2019 10:31 pm

An update on this topic.

With the help of Peter Cyriax and others, I have now got the C Callback process working.

A small C callback routine that is triggered when the selected pin changes level, (set up via gpioSetAlertFunc), simply writes the GPIO pin number, Level, and clock tick, to a named pipe (a FIFO file) for each interrupt.

At the APL end within a child process thread, ⎕ARBIN runs in a loop reading the named pipe, adding a token to the token pool for each interrupt read. The value of the token is set to include the GPIO pin number, Level, and clock tick.

This allows other APL threads to access the data from the interrupt via the token pool.

Using this process, I have managed (albeit under a very light load on a RPi model 3) to read distances via a HC-SR04 ultrasonic distance sensor often with an accuracy of +/- 1 tick (+/- 1.7 mm).

I hope to update GITHUB with this over the next few weeks.
Ray Cannon
Please excuse any smelling pisstakes.
User avatar
ray
 
Posts: 218
Joined: Wed Feb 24, 2010 12:24 am
Location: Blackwater, Camberley. UK

Previous

Return to Raspberry Pi

Who is online

Users browsing this forum: No registered users and 1 guest