Recent

Author Topic: Ultibo SPI with MCP3008 on Raspberry 4  (Read 444 times)

Koodie83

  • Newbie
  • Posts: 2
Ultibo SPI with MCP3008 on Raspberry 4
« on: August 17, 2023, 04:36:20 pm »
Hi,
im coming from rather Script languages and I'm quite new to Freepascal, im trying to build a function that can read a specific channel of the MCP3008 via SPI.
I have not found to much documentation on the SPI read, but looking at the datasheet i know i have to send over "what" i want to read and then parse the answer.
Issue is that i am really new to pointers and i have been struggling around for 2,3 days and been reading alot, i just cant get it to work.

Here is my code, if anyone could give me feedback on where i am going wrong. currently i get "FileHandling.lpr(35,14) Error: Incompatible types: got "Pointer" expected "AnsiString"
Which makes sense, i figured i can set a pointer to a variable using x = @Y but how do i get the data of the pointer back into a variable? Do i manually shift trough it for the 16 bit of answer?

Any help is appreciated, please be gentle, i just started programming shortly. Also, why does my compiler say "FileHandling.lpr(34,67) Warning: Local variable "DataBuffer" does not seem to be initialized" But OutBuffer is working fine?

Best regards,
Marcus
Code: Pascal  [Select][+][-]
  1. program DrawBMP;
  2.  
  3. {$mode delphi}{$H+}
  4.  
  5. uses
  6.   RaspberryPi4,    {Include the RaspberryPi4 unit to give us network, filesystem etc}
  7.   GlobalConst,
  8.   Devices,
  9.   SPI,
  10.   GlobalTypes,
  11.   Threads,
  12.   Console,
  13.   GraphicsConsole, {Include the GraphicsConsole unit so we can create a graphics window}
  14.   Classes,         {Include the Classes unit for the TFileStream class}
  15.   SysUtils;
  16.  
  17. var
  18.  SPIDevice:PSPIDevice;
  19.  Count:LongWord;
  20.  Value:Byte;
  21.  WindowHandle:TWindowHandle;
  22.  
  23. function ReadMCP3008(channel:int):AnsiString;
  24. var
  25.  DataBuffer:Pointer;
  26.  OutBuffer:Pointer;
  27.  
  28. begin
  29.  
  30.   channel       := channel or $0011;        //  # start bit for channel + single-ended bit
  31.   channel       := $00000000 or channel;    //  # Fill with 0 because function expects 16 BIT
  32.   OutBuffer     := @channel;
  33.  
  34.   IF SPIDeviceWriteRead(SPIDevice, SPI_CS_0, OutBuffer, DataBuffer, 16, SPI_TRANSFER_NONE, Count) = ERROR_SUCCESS then begin
  35.      result:=DataBuffer;
  36.   end;
  37. end;
  38.  
  39.  
  40. begin
  41.   SPIDevice:=PSPIDevice(DeviceFindByDescription('BCM2711 SPI0 Master')); {Device on Raspberry 4 is called BMC2711}
  42.   if SPIDeviceStart(SPIDevice, SPI_MODE_4WIRE, 100000, SPI_CLOCK_PHASE_LOW, SPI_CLOCK_POLARITY_LOW) = ERROR_SUCCESS then begin
  43.   end;
  44.  
  45.   Sleep(3000);
  46.   WindowHandle:=ConsoleWindowCreate(ConsoleDeviceGetDefault,CONSOLE_POSITION_FULL,True);
  47.   ConsoleWindowWriteLn(WindowHandle,ReadMCP3008(0));
  48.   ThreadHalt(0);
  49. end.
« Last Edit: August 17, 2023, 05:12:23 pm by Koodie83 »

ccrause

  • Hero Member
  • *****
  • Posts: 793
Re: Ultibo SPI with MCP3008 on Raspberry 4
« Reply #1 on: August 18, 2023, 08:34:13 am »
Hi,
im coming from rather Script languages and I'm quite new to Freepascal, im trying to build a function that can read a specific channel of the MCP3008 via SPI.
I have not found to much documentation on the SPI read, but looking at the datasheet i know i have to send over "what" i want to read and then parse the answer.
Issue is that i am really new to pointers and i have been struggling around for 2,3 days and been reading alot, i just cant get it to work.

Here is my code, if anyone could give me feedback on where i am going wrong. currently i get "FileHandling.lpr(35,14) Error: Incompatible types: got "Pointer" expected "AnsiString"
Which makes sense, i figured i can set a pointer to a variable using x = @Y but how do i get the data of the pointer back into a variable? Do i manually shift trough it for the 16 bit of answer?

Pascal is quite strict when it comes to type conversions, so often the programmer needs to explicitly convert between types.  The compiler error is (obviously) telling you that a pointer is incompatible with an ansistring.  The reason for this error is that a pointer is untyped, i.e. the compiler does not know what data type is located at the pointer address.  If for example the data at the pointer address is a string, it can simply be copied to Result, but if it is an integer, the value needs to be converted to a string before it can be assigned to a string variable.  Another problem in your code is that you declare DataBuffer as a pointer, but the pointer isn't initialized to point to valid memory.  Below is my take on how to implement your ReadMCP3008 function:

Code: Pascal  [Select][+][-]
  1. function ReadMCP3008(channel: uint16):AnsiString;
  2. var
  3.   DataBuffer: uint16;  // 16 bit unsigned integer
  4. begin
  5.   channel := 8 or (channel and 7);        //  # start bit for channel + single-ended bit
  6.   DataBuffer := 0; // Local variables are not initialized, manually initialize to 0.  May not be necessary but now it is at a known value.
  7.  
  8.   IF SPIDeviceWriteRead(SPIDevice, SPI_CS_0, @channel, @DataBuffer, 16, SPI_TRANSFER_NONE, Count) = ERROR_SUCCESS then begin
  9.      result := IntToStr(DataBuffer)
  10.   else
  11.     result := 'Error';
  12.   end;
  13. end;

Further notes:
  • channel := $00000000 or channel; doesn't do anything so can be omitted.
  • The address operator (@) returns a pointer to the variable, in this case DataBuffer is declared as a 16 bit integer, so the compiler will automatically allocate memory (on the stack) for this.
  • Similarly you can simply pass the address of channel to the SPI function, no need to store the address in a separate variable.
  • A number starting with $ indicates a hexadecimal value, I assume you wanted to indicate the bit pattern of the channel mask, for that use % to indicate a binary value, but this is not available in Delphi mode.
  • The channel bits may need to be shifted depending on exactly how the SPI driver shifts out the data, see e.g. Figure 6.2 for alignment if the SPI driver only operates on whole bytes. Also the return value from the SPI read may have to be shifted
  • Variable count is not initialized in your example.

Koodie83

  • Newbie
  • Posts: 2
Re: Ultibo SPI with MCP3008 on Raspberry 4
« Reply #2 on: August 18, 2023, 02:14:43 pm »
I got it working with the help and some other things.
What was wrong was the 8 or (channel and 7). The first bit 16 is the start bit and 8 is the single mode config bit.

Here is the working code, hope it helps someone some day:

Code: Pascal  [Select][+][-]
  1. program DrawBMP;
  2.  
  3. {$mode delphi}{$H+}
  4.  
  5. uses
  6.   RaspberryPi4,    {Include the RaspberryPi4 unit to give us network, filesystem etc}
  7.   GlobalConst,
  8.   Devices,
  9.   Platform,
  10.   SPI,
  11.   GlobalTypes,
  12.   Threads,
  13.   Console,
  14.   GraphicsConsole, {Include the GraphicsConsole unit so we can create a graphics window}
  15.   Classes,         {Include the Classes unit for the TFileStream class}
  16.   SysUtils;
  17.  
  18. var
  19.  SPIDevice:PSPIDevice;
  20.  Count:LongWord;
  21.  
  22.  WindowHandle:TWindowHandle;
  23.  
  24. function ReadMCP3008(channel:uint16):uint16;
  25. var
  26.   Data:uint16;
  27.  
  28. begin
  29.   Data    := 0;
  30.   channel := 24 or channel;   //  # start bit + single-ended bit
  31.  
  32.   IF SPIDeviceWriteRead(SPIDevice, SPI_CS_0, @channel, @Data, 16, SPI_TRANSFER_NONE, Count) = ERROR_SUCCESS then
  33.   begin
  34.      Result:=Data shr 4;
  35.   end
  36.   else begin
  37.     result := 0;
  38.   end
  39. end;
  40.  
  41.  
  42. begin
  43.   SPIDevice:=SPIDeviceFindByDescription(SPIGetDescription(0)); {Device on Raspberry 4 is called BCM2838}
  44.   if SPIDeviceStart(SPIDevice, SPI_MODE_4WIRE, 100000, SPI_CLOCK_PHASE_LOW, SPI_CLOCK_POLARITY_LOW) = ERROR_SUCCESS then begin
  45.   end; { if SPIDeviceStart }
  46.  
  47.   WindowHandle:=ConsoleWindowCreate(ConsoleDeviceGetDefault,CONSOLE_POSITION_FULL,True);
  48.  
  49.   while 1=1 do
  50.   begin
  51.        Sleep(3000);
  52.  
  53.        ConsoleWindowWrite(WindowHandle,IntToStr(ReadMCP3008(0)));
  54.        ConsoleWindowWrite(WindowHandle,'/');
  55.        ConsoleWindowWrite(WindowHandle,IntToStr(ReadMCP3008(1)));
  56.        ConsoleWindowWrite(WindowHandle,'/');
  57.        ConsoleWindowWrite(WindowHandle,IntToStr(ReadMCP3008(2)));
  58.        ConsoleWindowWrite(WindowHandle,'/');
  59.        ConsoleWindowWrite(WindowHandle,IntToStr(ReadMCP3008(3)));
  60.        ConsoleWindowWrite(WindowHandle,'/');
  61.        ConsoleWindowWrite(WindowHandle,IntToStr(ReadMCP3008(4)));
  62.        ConsoleWindowWrite(WindowHandle,'/');
  63.        ConsoleWindowWrite(WindowHandle,IntToStr(ReadMCP3008(5)));
  64.        ConsoleWindowWrite(WindowHandle,'/');
  65.        ConsoleWindowWrite(WindowHandle,IntToStr(ReadMCP3008(6)));
  66.        ConsoleWindowWrite(WindowHandle,'/');
  67.        ConsoleWindowWriteln(WindowHandle,IntToStr(ReadMCP3008(7)));
  68.  
  69.   end;
  70.   ThreadHalt(0);
  71. end.

 

TinyPortal © 2005-2018