Recent

Author Topic: i2c on Raspberrypi  (Read 16799 times)

woodengineer

  • New Member
  • *
  • Posts: 20
i2c on Raspberrypi
« on: July 10, 2012, 05:36:54 pm »
Hello,
I am trying to use the I2c bus on the Raspberrypi SBC.
I ported this code from a little C pogram I did.
Both fpioctl and fpwrite return -1.
The C version uses the constant I2C_SLAVE which seems to hold 1795, but fp does not have this constant defined.
Can someone point me in the right direction on what I am doing wrong.
When I run this code, everything runs but the device does not get
updated.  8)

Thanks!
Alex

The function below just sents one byte of data (number 9) to Register 8 on the DS1307.
Code: [Select]
program project1;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
   BaseUnix,Classes, sysutils;
{$R *.res}

Procedure InitI2cDevice();
const
  p : PChar = '89';     //Characters to sent 8=RAM Register 9=Value
  I2C_SLAVE = 1795;     //?? Got 1795 from C-Funtcion

Var
  iwf,iio : integer;
  fd, address : Cint;
  paddress : ^integer;

begin
 address := $68;                //Device Address DS1307 RTC
 paddress := @address;    //Pointer to Device Address

 fd := fpopen('/dev/i2c-0',O_WrOnly);     //Open the I2C bus
 iio:= fpioctl(fd, I2C_SLAVE, paddress);

 iwf := fpwrite(fd, p[0], 2);             //write to the Device

 //fpioctl and fpwrite both return -1
 writeln('After fpwrite: ' + #13 + #10 + 'FileRet#: ' + Inttostr(fd) + ' fpwriteRet#: ' + IntToStr(iwf) + ' fpioctl#: ' + IntToStr(iio));
 fpclose(fd);
end;

begin
  InitI2cDevice;
  readln;
end.

!! UPDATED !!!

Here is some working code for the DS1307 RTC. This is just an example and needs some better error handling etc. But I tested it and it works with my DS1307. On newer raspberrypi's (Made in the UK), the device may change to /dev/i2c-1.  :)
Have fun!

Code: [Select]
program project1;

{$mode objfpc}{$H+}
{$R *.res}

uses
  cthreads, BaseUnix,Classes, sysutils;

Const
  I2C_SLAVE = 1795;

Var
  buf     : Array[0..20] of Byte;
  aDate   : Array[0..4] of Byte;    //Array to hold date components 0=Start address
  aTime   : Array[0..3] of Byte;    //Array to hold Time components 0=Start address
  aValue  : Array[0..3] of Byte;    //Array to hold Time components 0=Start address
  ds1307  : Integer;                //Variable to hold file handle. Used Device Name for clarity. Use multiple variables to talk to multiple devices


// Helper function to convert Bcd To Byte
Function bcdtoByte(bcd: Byte): Byte;
 begin
  bcdtoByte := (((bcd shr 4) * 10 + (bcd and $0f)));
 end;

// Helper function to convert Byte to Bcd
Function btobcd(b: Byte): Byte;
 begin
  btobcd := (((b div 10) shl 4) + (b mod 10));
 end;

//Function to initialize device. iDevAddr = Address of the i2c device
Function InitI2cDevice(devpath: String; iDevAddr: Cint; var iInst: Integer):Integer;
Var
 iio : integer;
begin
 Try
  iInst := fpopen(devpath,O_RDWR);                       //Open the I2C bus in Read/Write mode
  iio:= FpIOCtl(iInst, I2C_SLAVE, pointer(iDevAddr));    //Set options
  If (iio = 0) and (iInst > 0) Then InitI2cDevice := iInst Else InitI2cDevice := -1;
 Except
  InitI2cDevice := -1;
 End;
end;


//can be used for writing any one register with or without BCD conversion
Function setRegister(startregno: Integer; val: Byte; cbcd: boolean; var iInst: Integer):Integer;
begin
 Try
  buf[0] := startregno;
  If cbcd = true then
   Begin
     buf[1] := btobcd(val);
     fpwrite(iInst, buf[0], 2);
   end
  Else
   Begin
     buf[1] := val;
     fpwrite(iInst, buf[0], 2);
   End;
 Except
   setRegister := -1;
 End;
   setRegister := 1;
end;


//can be used for reading any one register with or without BCD conversion
Function getRegister(val: Byte; cbcd: boolean; var iInst: Integer):Byte;
Var
 iRet : Byte;
begin
 Try
  buf[0] := val;
  If cbcd = true then
   Begin
     fpwrite(iInst, buf[0], 1);
     fpread(iInst, iRet, 1);
     iRet := btobcd(iRet);
   end
  Else
   Begin
     fpwrite(iInst, buf[0], 1);
     fpread(iInst, iRet, 1);
   End;
 Except
   getRegister := -1;
 End;
   getRegister := iRet;
end;


//can be used for writing multiple register with or without BCD conversion
//regs[0] = first Register, regs is an array  to hold values
//cbcd = True or False convert regs to BCD or not
//Values in the array have to be consecutive on the device
//BCD conversion will not affect regs[0].
Function setNRegister(regs: Array of Byte; cbcd: boolean; var iInst: Integer):Integer;
Var
  i : integer;
  ifpw : Integer;
begin
 Try
  If cbcd = true then
   Begin
     buf[0] :=  regs[0];
     For i := 1 to High(regs) do
      Begin
        buf[i] := btobcd(regs[i]);
        //Writeln('Buf: ' + IntTostr(i+1) + '  Val: ' + intToStr(regs[i]) + ' Write: ' + IntToStr(High(regs)+1));
      end;
   end
  Else
   Begin
     For i := 0 to High(regs) do
      Begin
        buf[i] := regs[i];
        //Writeln('Buf: ' + IntTostr(i) + '  Val: ' + intToStr(regs[i]) + ' Write: ' + IntToStr(High(regs)+1));
      end;
   End;
  ifpw := fpwrite(iInst, buf[0], High(regs)+1);
  //Writeln('ifpw: ' + IntToStr(ifpw));
 Except
   setNRegister := 1;
 End;
   setNRegister := 0;
end;

//can be used for getting multiple register with or without BCD conversion
//regs[0] = first Register, regs is an array  to hold values
//cbcd = True or False convert regs to BCD or not
//Values in the array have to be onsecutive on the device
Procedure getNRegister(var regs: Array of Byte; cbcd: boolean; var iInst: Integer);
Var
  ifpw, ifpr, i : Integer;
Begin
 Try
  ifpw := fpwrite(iInst, regs[0], 1);
  ifpr := fpread(iInst, regs, 3);
  If cbcd = True then
   Begin
    For i := 0 to High(regs) do regs[i] := bcdtoByte(regs[i])
   End;
 Except
  Writeln('Error, Fpwrite: ' + IntToStr(ifpw) + ' fpread: ' + IntToStr(ifpr));
 End;
end;

//Main Program
Begin

  InitI2cDevice('/dev/i2c-0',$68,ds1307);   //Init must always be called first for any device


  aDate[0] := 3;    //0 = Start Register, 3 for Date on DS1307
  aDate[1] := 7;    //1 = Day 1-7
  aDate[2] := 14;   //2 = Date 1-31
  aDate[3] := 7;    //3 = Month 1-12
  aDate[4] := 12;   //4 = Year 0-99

  aTime[0] := 0;    //0 = Start Register 0 for Time on DS1307
  aTime[1] := 0;    //Seconds 0-59
  aTime[2] := 3;    //Minutes 0-59
  aTime[3] := 5;    //Hours 0-23 Normal can be changed to 1-12

  aValue[0] := 8;   //8 = Start Register, 8 for first GP reg on DS1307
  aValue[1] := 99;
  aValue[2] := 2;
  aValue[3] := 3;

  setNRegister(aDate,True,ds1307);  //Set Multiple Register Starting at Address 3 (aDate[0])
  setNRegister(aTime,True,ds1307);  //Set Multiple Register Starting at Address 0 (aTime[0])

  setRegister(8,177,False,ds1307);                                    //Set Register 8 first GP Ram Register no BCD
  Writeln('Reg 8 Value: ' + IntToStr(getRegister(8,False,ds1307)));   //Get First GP RAM Register no BCD
  Writeln('Day of Month: ' + IntToStr(getRegister(4,True,ds1307)));   //Get Day of Month on DS1307 = Reg 04h with BCD

  setNRegister(aValue,False,ds1307);  //Set Multiple GP Register Starting at Address 8 (first GP Register)
  getNRegister(aValue,false,ds1307);  //Reads multiple registers from device
  writeln('Read multiple Regs: 1: ' + IntToStr((aValue[0])) + ' 2: ' + IntToStr((aValue[1]))  + ' 3: ' + IntToStr((aValue[2])));
  fpclose(ds1307); //close device handle
end.

« Last Edit: September 19, 2012, 04:44:51 am by woodengineer »

BigChimp

  • Hero Member
  • *****
  • Posts: 5740
  • Add to the wiki - it's free ;)
    • FPCUp, PaperTiger scanning and other open source projects
Re: i2c on Raspberrypi
« Reply #1 on: July 10, 2012, 05:53:48 pm »
No idea, but I'd definitely try this on the FPC mailing list as well as you might reach people who might be knowledgeable about this...
Want quicker answers to your questions? Read http://wiki.lazarus.freepascal.org/Lazarus_Faq#What_is_the_correct_way_to_ask_questions_in_the_forum.3F

Open source including papertiger OCR/PDF scanning:
https://bitbucket.org/reiniero

Lazarus trunk+FPC trunk x86, Windows x64 unless otherwise specified

Laksen

  • Hero Member
  • *****
  • Posts: 724
    • J-Software
Re: i2c on Raspberrypi
« Reply #2 on: July 10, 2012, 07:40:26 pm »
Do you run it with the correct privileges?

Also you should be sending bytes not characters. You can emulate this by doing:
 p : PChar = #8#9;

Which is basically char(8)+char(9)

woodengineer

  • New Member
  • *
  • Posts: 20
Re: i2c on Raspberrypi
« Reply #3 on: July 10, 2012, 08:55:46 pm »
Thanks for the Reply!

I run as root.
Made the changes you suggested  (p : PChar = #8#9;), but no difference.

I am trying to create my own wrapper to ioctl etc. by linking directly to libc, but
so far no luck!

Any ideas?
Thanks!

eny

  • Hero Member
  • *****
  • Posts: 1634
Re: i2c on Raspberrypi
« Reply #4 on: July 10, 2012, 09:36:40 pm »
How did you check that the DS1307 was not updated?
Apparently you need to make some kernel modifications to get I2C working.
Did you do that?

Also as Laksen said:
Made the changes you suggested  (p : PChar = #8#9;), but no difference.
This change is necessary whatever else is going wrong.

Maybe this links contains some info: http://binerry.de/post/26685647322/raspberry-pi-and-i2c
All posts based on: Win10 (Win64); Lazarus 2.0.10 'stable' (x64) unless specified otherwise...

Laksen

  • Hero Member
  • *****
  • Posts: 724
    • J-Software
Re: i2c on Raspberrypi
« Reply #5 on: July 10, 2012, 10:06:39 pm »
Try this:
Code: [Select]
   iio:= fpioctl(fd, I2C_SLAVE, pointer($68));

The POSIX documentation writes that the arg must be either a pointer or an integer depending on the request, and while C api's use varargs you should probably be passing an integer. The I2C documentation should probably be stating that but it seems like a big mess.

woodengineer

  • New Member
  • *
  • Posts: 20
Re: i2c on Raspberrypi
« Reply #6 on: July 10, 2012, 11:22:17 pm »
Hello,

BIG thanks to Laksen and Eny for your help!!

Using
Code: [Select]
p : PChar = #8#150;
iio:= fpioctl(fd, I2C_SLAVE, pointer($68));
did the trick.

However I used
Code: [Select]
  p : Array[0..20] of shortint and then assigned them like p[0] := 8 etc.
This will make receiving bytes from the device easier.

Also, to the earlier question from eny, I am running Archlinux and I compiled Chris Boots kernel on my pi.   
I did throw some C code together earlier to test the device (the pascal code is basically a conversion of the C code). 

Thanks
Alex

woodengineer

  • New Member
  • *
  • Posts: 20
Re: i2c on Raspberrypi
« Reply #7 on: July 10, 2012, 11:37:57 pm »
One more thing,

the line:
Code: [Select]
p : Array[0..20] of shortint
//should be
p : Array[0..20] of Byte

Byte has the proper range. :D

eny

  • Hero Member
  • *****
  • Posts: 1634
Re: i2c on Raspberrypi
« Reply #8 on: July 11, 2012, 01:05:58 pm »
Also, to the earlier question from eny, I am running Archlinux and I compiled Chris Boots kernel on my pi.   
I did throw some C code together earlier to test the device (the pascal code is basically a conversion of the C code). 
Good stuff.
Maybe I should get me one too (I'm kind of a PIC enthusiast :D).
All posts based on: Win10 (Win64); Lazarus 2.0.10 'stable' (x64) unless specified otherwise...

 

TinyPortal © 2005-2018