Recent

Author Topic: Help with SPI  (Read 1603 times)

MadMike

  • New Member
  • *
  • Posts: 32
Help with SPI
« on: January 14, 2021, 03:53:24 pm »
Hi,

Trying to use an A/D converter on a Raspberry.

I downloaded PascalIO and I used the SPI program to test. After a bit of head scratching, this worked fine:

Code: Pascal  [Select][+][-]
  1.  
  2. (* SPI Linux demo program
  3.  *
  4.  * This simple program demonstrates how to use the class TSPILinuxDevice to
  5.  * access a SPI bus on a Linux published in the file system by the spidev
  6.  * driver.
  7.  *
  8.  * This program assumes a imaginary SPI device on bus 0 and chipselect 1,
  9.  * which writes back $0F00 regardless of the input value.
  10.  *)
  11. program Project1;
  12. {$mode objfpc}
  13. uses
  14.   fpspi;
  15.  
  16. var
  17.   spi: TSPILinuxDevice;
  18.   rbuf, wbuf: Word;
  19. begin
  20.   // SPI Bus 0
  21.   // 2nd ChipSelect
  22.   // The numbers are directly mapped to the file name /dev/spidevB.C
  23.   // with B = Bus and C = Chipselect
  24.   // see https://www.kernel.org/doc/Documentation/spi/spidev
  25.   spi := TSPILinuxDevice.Create(0, 1);
  26.   // set a SPI mode
  27.   spi.Mode := SPI_MODE_0;
  28.   spi.MaxFrequency:= 500;
  29.   try
  30.     while true do
  31.     // data sent to device
  32.     wbuf := $01800;
  33.     // The read and write buffers don't need to be of the same size
  34.     // the larger one will determine the total bytes sent
  35.     // If length(wbuf) < length(rbuf), the remaining bytes will be sent as 0
  36.     spi.ReadAndWrite(wbuf, sizeof(wbuf), rbuf, sizeof(rbuf));
  37.     // handle result
  38.       smallrbuf:= rbuf shr 24;     //shr = shift right
  39.       highrbuf := rbuf AND $f0000;
  40.       highrbuf := highrbuf shr 8;
  41.       completerbuf:= smallrbuf + highrbuf;
  42.       WriteLn('rbuf          = '+ IntToStr(rbuf));
  43.       WriteLn('rbuf low      = '+ IntToStr(smallrbuf));
  44.       WriteLn('rbuf high     = '+ IntToStr(highrbuf));
  45.       WriteLn('rbuf complete = '+ IntToStr(completerbuf));
  46.       WriteLn('');
  47.   finally
  48.     spi.Destroy;
  49.   end;
  50. end.

I copied the code into a function in my  project and I get no joy.

Getting totally confused here, any comments gratefully received.

Needless to say, both compiled and run 'Sudo...'

Project code below (severely edited)...

Code: Pascal  [Select][+][-]
  1. unit uTesterGUI;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ComCtrls, StdCtrls,
  9.   ExtCtrls, PrintersDlgs, Types, fpspi;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.  
  17.  
  18.  
  19. ........
  20.  
  21.  
  22.  
  23.  
  24.   private
  25.     function GetADValue(ADChan: string): LongWord;
  26.  
  27.  
  28.   public
  29.  
  30.   end;
  31. Type
  32.  
  33.  
  34. ....
  35.  
  36. implementation
  37.  
  38. {$R *.lfm}
  39.  
  40. { TForm1 }
  41.  
  42. ...
  43.  
  44. procedure TForm1.bCalibrateClick(Sender: TObject);
  45. begin
  46.   lRaw.Text:= IntToStr(GetADValue('Ch0')); //AD result into LableEdit box
  47. end;
  48.  
  49. ...
  50.  
  51.  
  52. function GetADValue(ADChan: string): LongWord;
  53. var
  54.   spi: TSPILinuxDevice;
  55.   rbuf, wbuf, smallrbuf, highrbuf, completerbuf: LongWord;
  56. begin
  57.   // SPI Bus 0
  58.   // 2nd ChipSelect
  59.   // The numbers are directly mapped to the file name /dev/spidevB.C
  60.   // with B = Bus and C = Chipselect
  61.   // see https://www.kernel.org/doc/Documentation/spi/spidev
  62.   spi := TSPILinuxDevice.Create(0, 1);// bus 0, cs 1
  63.   // set a SPI mode
  64.   spi.Mode := SPI_MODE_0;
  65.   spi.MaxFrequency:= 500;
  66.   spi.LSBFirst:= false;
  67.   try
  68.  
  69.       begin
  70.  
  71.     // data sent to device
  72.     {b'0000,0001,,0000,0000,,0000,0000,,0000,0000,,0000,0000'
  73.                        $01000 A/D channel 0 & 1, Differential, ch0 = +, ch1 = -
  74.      b'0000,0001,,0010,0000,,0000,0000,,0000,0000,,0000,0000'
  75.                          $01300 A/D channel 2 & 3, Differential, ch2 =+, ch3 =-}
  76.  
  77.     if ADChan = 'Ch0' then wbuf := $01000 else wbuf:= $01200;
  78.  
  79.     // handle result
  80.       smallrbuf:= rbuf shr 24;     //shr = shift right
  81.       highrbuf := rbuf AND $f0000;
  82.       highrbuf := highrbuf shr 8;
  83.       completerbuf:= smallrbuf + highrbuf;
  84.  
  85.       GetADValue:= CompleteRbuf;
  86.       sleep(100);
  87.       end;
  88.   finally
  89.     spi.Destroy;
  90.   end;
  91. end;
  92.  
  93. end.
  94.  

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: Help with SPI
« Reply #1 on: January 14, 2021, 05:15:57 pm »
What /exactly/ happens? compilation error? runtime error?

Somebody was asking a similar question about SPI a few days ago, and I think it got as far as him going away to look carefully at the order of the bytes that were coming back from the device.

https://forum.lazarus.freepascal.org/index.php/topic,52759.msg389647.html#msg389647

MarkMLl
« Last Edit: January 14, 2021, 05:52:54 pm by MarkMLl »
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

MadMike

  • New Member
  • *
  • Posts: 32
Re: Help with SPI
« Reply #2 on: January 15, 2021, 12:14:28 pm »
What /exactly/ happens? compilation error? runtime error?

Somebody was asking a similar question about SPI a few days ago, and I think it got as far as him going away to look carefully at the order of the bytes that were coming back from the device.

https://forum.lazarus.freepascal.org/index.php/topic,52759.msg389647.html#msg389647

MarkMLl

Hey that was me!

I realised in the end I had to re-arrange the received bytes.

Now I had the program working, I thought I could transfer the code into an 'app' (one with a form).

The program ('app') compiles and runs, no errors.

When the function is called (button is pressed) I get no output.

If I keep frantically pressing the button, after 16 presses the program hangs.

As I said, I run the program stand alone with Sudo ... , so there's no IDE supervision but even under the IDE, with no expectation of output, I get no errors.

I obviously have a lot to learn, but where to start! 

Mike

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: Help with SPI
« Reply #3 on: January 15, 2021, 12:27:45 pm »
I realised in the end I had to re-arrange the received bytes.

You didn't /realise/, you were /told/ by both Jamie and myself. But thanks for updating the thread so that anybody who gets there via Google etc. can learn from it.

Now, apropos the current problem. I'm not sure whether anybody here has the same device and a working RPi, and since you haven't posted a complete project we're all of us griping in the dark.

What I suggest is that you change your button handler so that the first thing it does is disable the button so that you /can't/ press it multiple times, and then on exit (i.e. in a finally block) re-enable the button.

That will probably get you focussing on the fact that your code to read the SPI is never returning, at which point you "simply" need to look at what you're doing that's different.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

MadMike

  • New Member
  • *
  • Posts: 32
Re: Help with SPI
« Reply #4 on: January 15, 2021, 01:40:57 pm »
Apologies. What I should have said was I realised, after I read the helpful advice, that I had to re-arrange the received bytes.

I'll try your suggestion re disabling the button and report back.

I'll also post the complete code.

I was 'frantically pressing' while monitoring the various spi lines hoping to see some activity!

Mike

MadMike

  • New Member
  • *
  • Posts: 32
Re: Help with SPI
« Reply #5 on: January 15, 2021, 01:55:40 pm »
Hi Mark,

Well, I changed

Code: Pascal  [Select][+][-]
  1. procedure TForm1.bCalibrateClick(Sender: TObject);
  2. begin
  3.   lRaw.Text:= IntToStr(GetADValue('Ch0')); //AD result into LableEdit box
  4. end;

to

Code: Pascal  [Select][+][-]
  1. procedure TForm1.bCalibrateClick(Sender: TObject);
  2. begin
  3.   try
  4.     begin
  5.       bCalibrate.Enabled:=false;
  6.       lRaw.Text:= IntToStr(GetADValue('Ch0')); //AD result into LableEdit box
  7.     end
  8.   finally
  9.     bCalibrate.Enabled:=true;
  10.   end;
  11. end;

And it worked.

I have absolutely no idea why.

Can you shed any light?

Thanks for your help, again.

Now I just have to figure out why the ttyS0 port doesn't work. Not even using CUPS.

Such fun.

Mike

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: Help with SPI
« Reply #6 on: January 15, 2021, 02:35:21 pm »
> Can you shed any light?

No, but it's a button so I'd speculate contact bounce :-) Specifically, check that nothing else is invoking that handler unexpectedly, or that something in a timer isn't invoking the press event.

> Now I just have to figure out why the ttyS0 port doesn't work. Not even using CUPS.

Usually because the user isn't in the dialout group. Look at the ownership of /dev/ttyS0, and check the content of /etc/group. Tends to be discussed by users who ask "how do I make myself route", often in capitals :-)

HOLD IT: check the RPi docs. I've not got one to hand but some of the devices were mutually-exclusive. If you stay stuck I might be able to find some notes somewhere. I'm not even sure about the device name... shouldn't it be something like /dev/ttyAMA0?

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

MadMike

  • New Member
  • *
  • Posts: 32
Re: Help with SPI
« Reply #7 on: January 15, 2021, 05:12:38 pm »
At the moment there's not a lot else in the program, just some print, open & save dialogs.

The default user (pi) is in the 'dialout' group.

Within '/etc/group' there is a line 'dialout:x:21:pi', amongst others.

I have a Pi 3 B. From what I've read, (and it may be wrong!) ttyAMA0 now talks to the blue tooth and now I need to use 'ttyS0' or 'serial0'. (or was it 'serial1'?)

Any way, that's for another day. Thanks to you I have a chance to write some useful 'spi' code today.

The project by the way is a cable tester. the GPIO pins dial in the addresses of different connector pins, the a/d checks the connection against a wiring list and hopefully there will be a printout of the results to a local printer.

Thanks again.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: Help with SPI
« Reply #8 on: January 15, 2021, 06:05:23 pm »
I think RPi device names might vary depending on exactly what mix of peripherals is enabled (config.txt?). Found this in my stuff earlier apropos enumerating serial devices:

(* Run through all devices named like /dev/tty[[:alpha:]]+[[:digit:]]+ checking *)
(* that they're character-mode and not blacklisted and saving the major device  *)
(* number; repeat for /dev/rfcomm0 etc. to accommodate the default naming       *)
(* convention of Bluetooth-connected serial devices. The final result is a list *)
(* of devices without appended digits, plus the associated major device number. *)
...
(* The cached result should first have traditional serial devices /dev/ttySx,   *)
(* ISDN devices /dev/ttyIx, USB devices /dev/ttyUSBx with additional support    *)
(* for devices implemented by multiport cards etc. inserted as appropriate      *)
(* based on the major device numbers which were allocated approximately         *)
(* chronologically. It should also have any "Low-density serial ports" found    *)
(* to be present, hopefully at the end of the list, where those are e.g. on-    *)
(* chip console ports and are distinguished by minor rather than major device   *)
(* number:                                                                      *)
(*                                                                              *)
(*   4 /dev/ttySx                                                               *)
(*  43 /dev/ttyIx                                                               *)
(* 188 /dev/ttyUSBx                                                             *)
(* 204 /dev/ttyAMAx etc.                                                        *)
(*                                                                              *)
(* The overall result will hopefully be "correct" both from the system and user *)
(* POV. See Documentation/devices.txt or Documentation/admin-guide/devices.txt  *)
(* in the Linux source tree.                                                    *)

There's more in there, but the bottom line is that enumerating serial devices can be a pain on Linux.

I've definitely seen an rfcomm device on a PC, but haven't investigated Bluetooth on an RPi to that extent. Most of my experience with them has been with Debian, and everybody doing that appears to have found their BT and WiFi dead a few months ago. I'm holding off a reinstall until I really have to, got more pressing jobs.

Cable testers are fun, one of the trickiest bits often being getting the right connectors :-) OTOH I go back to when peripheral cables were >1" diameter and had 25 screened conductors.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

 

TinyPortal © 2005-2018