I2c_Rd or I2c_Wr blocking when interrupted.

General discussion on mikroPascal.
Author
Message
Dany
Posts: 3854
Joined: 18 Jun 2008 11:43
Location: Nieuwpoort, Belgium
Contact:

I2c_Rd or I2c_Wr blocking when interrupted.

#1 Post by Dany » 15 Aug 2008 16:14

Hi all,
It seems to be, experienced by myself and a number of other people on the forum, that the I2c routines (not the "soft" ones, the "hard" ones) can be blocked (they never "return").
This happens when interrupts are used that are not related at all to I2c (e.g. Timer0 or Usart). In my case it is the I2c_Rd that is blocking after a while with the SCL line on "low". The SCL line is not blocked by the I2c peripheral since removal of the perpheral does not release SCL to high.

Gentlemen of mikroElektronica, can you please give advice? Or is disabling interrupts the only solution for this problem :?:

Thanks in advance. :D

p.s. I do use mikroPascal, the others are using mikroBasic I think:
http://www.mikroe.com/forum/viewtopic.p ... highlight=
http://www.mikroe.com/forum/viewtopic.p ... highlight=
http://www.mikroe.com/forum/viewtopic.p ... highlight=
Kind regards, Dany.
Forget your perfect offering. There is a crack in everything, that's how the light gets in... (L. Cohen)
Remember when we were young? We shone like the sun. (David Gilmour)

yo2lio
Posts: 1878
Joined: 19 Sep 2006 12:57
Location: Romania, Arad City
Contact:

#2 Post by yo2lio » 15 Aug 2008 20:21

Hi,

I don't know ....

I have few industrial applications :

1. Ethernet, EEprom 24LC256 (for storing events) + Interrupt with TMR2 at every 1 ms, tested in real world, more than one year, hard conditions (extreme temperatures -20 C to + 60 C) and I don't have any problems !!!

2. Ethernet, EEprom 24LC256, PCF8583, Interrupt with TMR2 at every 1 ms, also tested in real world, no problems.

3. CAN, EEprom 24LC256, PCF8583, one master and 6 slaves, Interrupt with TMR2 at every 1 ms, industrial applications, in functions from March 2007, no problems.

PS. I use MikroPascal 7.0
Best regards, Florin Andrei Medrea.

http://www.microelemente.ro/
http://www.microelemente.ro/produse-si-servicii/
http://www.microelemente.ro/custom-software/

mail : florin@microelemente.ro

janni
Posts: 5373
Joined: 18 Feb 2006 13:17
Contact:

#3 Post by janni » 16 Aug 2008 02:16

I'm affraid that Danny is right - appropriately (or rather 'inappropriately' :wink: ) timed interrupts may lock some of the I2C library routines. That's caused by an elementary programming error, i.e. clearing a flag after starting a hardware process that ends with setting this flag and then waiting for it. This is better described by:

Code: Select all

  1. Start process
      - here comes an interrupt which takes longer than the process
  2. Clear flag
      - hardware process did set the flag, but it's been cleared 
  3. Wait for flag being set
      - infinite loop locks here
Errors like the above may be found in both I2C_read and I2C_write routines. I've pointed it out in 2006, but the bug apparently survived (though another one was fixed).

One should also take into account that the library is very basic. Moreover, the PIC18s' I2C state machine is not perfect and in some processors it's not easy to make certain it won't block itself in specific circumstances. Not to mention silicon errors...

You had some luck, Florin, that others are missing :( . Or your programs' structure is resistant to this bug :) .

yo2lio
Posts: 1878
Joined: 19 Sep 2006 12:57
Location: Romania, Arad City
Contact:

#4 Post by yo2lio » 16 Aug 2008 06:18

Maybe it's luck ...

or this problem was resolved at PIC18F97J60 family ?!
Best regards, Florin Andrei Medrea.

http://www.microelemente.ro/
http://www.microelemente.ro/produse-si-servicii/
http://www.microelemente.ro/custom-software/

mail : florin@microelemente.ro

Dany
Posts: 3854
Joined: 18 Jun 2008 11:43
Location: Nieuwpoort, Belgium
Contact:

#5 Post by Dany » 16 Aug 2008 12:03

yo2lio wrote:or this problem was resolved at PIC18F97J60 family ?!
Maybe this is the case. I am still using the 16F877! :cry:
Kind regards, Dany.
Forget your perfect offering. There is a crack in everything, that's how the light gets in... (L. Cohen)
Remember when we were young? We shone like the sun. (David Gilmour)

Dany
Posts: 3854
Joined: 18 Jun 2008 11:43
Location: Nieuwpoort, Belgium
Contact:

#6 Post by Dany » 16 Aug 2008 12:08

janni wrote:

Code: Select all

  1. Start process
      - here comes an interrupt which takes longer than the process
  2. Clear flag
      - hardware process did set the flag, but it's been cleared 
  3. Wait for flag being set
      - infinite loop locks here
Errors like the above may be found in both I2C_read and I2C_write routines. I've pointed it out in 2006, but the bug apparently survived (though another one was fixed).
Thanks very much Janni. Do you think there is a possibility to solve this (by using other -- self made -- I2c_Rd and I2C_Wr routines)? If there are no fundamental problems than I will try to write my own routines. However, if you think this can not be solved due to fundamental problems then I will continue to disable interrupts while reading or writing I2c.
Kind regards, Dany.
Forget your perfect offering. There is a crack in everything, that's how the light gets in... (L. Cohen)
Remember when we were young? We shone like the sun. (David Gilmour)

Dany
Posts: 3854
Joined: 18 Jun 2008 11:43
Location: Nieuwpoort, Belgium
Contact:

#7 Post by Dany » 16 Aug 2008 19:06

janni wrote:

Code: Select all

  1. Start process
      - here comes an interrupt which takes longer than the process
  2. Clear flag
      - hardware process did set the flag, but it's been cleared 
  3. Wait for flag being set
      - infinite loop locks here
Hi, looking at the above investigation results of Janni, I did some experiments.
It seems (as far as I can test anyway) that this code does not suffer from the "interrupt" problem:

Code: Select all

function New_I2C_Rd(ack : byte) : byte;
begin
  SSPCON2.RCEN := 1;                              // enable reception
  
  repeat until SSPSTAT.BF = 1;                    // wait for full buffer
  Result := SSPBUF;                               // read buffer
  
  if ack > 0
  then SSPCON2.ACKDT := 0                         // ack (inverted!)
  else SSPCON2.ACKDT := 1;                        // nack
  SSPCON2.ACKEN := 1;                             // and send ack/nack
  
  repeat until SSPCON2.ACKEN = 0;                 // ack/nack has been sent, all done
end;
Anyway, in the above code the flag indicating "full buffer" is not reset after the reception was enabled. Also the "BF" flag (Buffer Full) is used instead of the interrupt flag (SSPIF) in the original I2c_Rd routine. Using BF has the advantage that it is cleared by the hardware.
The routine can simply be used in stead of I2c_Rd (I hope). :D
Kind regards, Dany.
Forget your perfect offering. There is a crack in everything, that's how the light gets in... (L. Cohen)
Remember when we were young? We shone like the sun. (David Gilmour)

janni
Posts: 5373
Joined: 18 Feb 2006 13:17
Contact:

#8 Post by janni » 16 Aug 2008 23:24

yo2lio wrote:Maybe it's luck ...

or this problem was resolved at PIC18F97J60 family ?!
I don't think so - library is the same and the processor doesn't differ that much from the rest of the PIC18 family. More probable is that your ISR is short enough (clock fast enough) or your program structure disfavors the collision (for example, interrupts are more or less synchronised with main loop).
Dany wrote:Do you think there is a possibility to solve this (by using other -- self made -- I2c_Rd and I2C_Wr routines)?
Sure. You could use a copy of official lib routines with the bug fixed:

Code: Select all

function cI2C_rd(ack:byte): byte;
 begin
  PIR1.SSPIF:=0;
  SSPCON2.RCEN:=1;
  while PIR1.SSPIF=0 do begin end;
  result:=SSPBUF;
  SSPCON2.ACKDT:=0;
  if ack<>0 then SSPCON2.ACKDT:=1;
  PIR1.SSPIF:=0;
  SSPCON2.ACKEN:=1;
  while PIR1.SSPIF=0 do begin end;
 End;{cI2C_rd}
 

function cI2C_wr(data:byte): byte;
 begin
  PIR1.SSPIF:=0;
  SSPBUF:=data;
  while PIR1.SSPIF=0 do begin end;
  if SSPCON2.ACKSTAT=0 then result:=0
   else
    begin
     SSPCON2.PEN:=1;
     result:=2;
    end;
 End;{cI2C_wr}
 
procedure cI2C_Stop;
 begin
  SSPCON2.PEN:=1;
 End;{cI2C_Stop}
The I2C_Stop routine was added only to show how rudimentary the library is.

I'm sure you'll find the above working better with interrupts than the official version but take into account that there are still infinite loops there and the routines are far from ideal. Writing your own routines is certainly a good idea.

Dany
Posts: 3854
Joined: 18 Jun 2008 11:43
Location: Nieuwpoort, Belgium
Contact:

#9 Post by Dany » 17 Aug 2008 10:01

Thanks Janni.
I studied the datasheet,did some experiments myself and came up with some Rd and Rw routines that seem to work with repect to inturrupts. I did publish them on my small PIC related website:
http://users.edpnet.be/rosseel01/DRO/PI ... #PIC_Units, see "NewI2c.ppas". Perhaps you can have a look and see if troubles can be expected with them?
Thanks for all the support! :D
Last edited by Dany on 19 Aug 2008 12:20, edited 6 times in total.
Kind regards, Dany.
Forget your perfect offering. There is a crack in everything, that's how the light gets in... (L. Cohen)
Remember when we were young? We shone like the sun. (David Gilmour)

yo2lio
Posts: 1878
Joined: 19 Sep 2006 12:57
Location: Romania, Arad City
Contact:

#10 Post by yo2lio » 17 Aug 2008 12:05

Very interesting subject !

I made few tests , real hardware, PIC18F67J60, EEprom 24LC16 (my HWZA board) and I can't reproduce this problem.

I use 1 ms interrupt with TMR2 and in main loop I write, read and verify , continuously from EEprom.

I use Watchdog and if device hang I can see this ....

This is the program :

Code: Select all

program Test_I2C;

uses aditional_string_util,lib1_18F97J60_V3_4,eth_lib_user,lib2_18F97J60_V3_4; // Don't touch this line !!!!!

var  user_ip_addr : IpHeader;
     count_led : byte;
     data_user : string[100];
     add_eep : word;
     txt : string[6];
     eol : string[2];
     cycle : word;
     
procedure interrupt; // with TMR2 timer
begin
  if TestBit(PIR1,TMR2IF) = 1 then      // 1 ms TMR2 Q=41.6667 MHz
    begin
      PIR1.TMR2IF:=0;
      CounterTask;  // must called at 1ms
      inc(count_led);
      if count_led > 99 then
        begin
          count_led := 0;
          asm
            BTG PORTB,0
          end;
       end;
      delay_us(500); // Use here 50 % from processor
    end;
end;

procedure SI2C_Write(ee_adr : word; ee_data : byte);
begin
  I2C_Start();                              // Issue I2C start signal
  I2C_Wr($A0 + (Hi(ee_adr) shl 1));         // Send byte via I2C(command to 24c16)
  I2C_Wr(Lo(ee_adr));                       // Send byte(address for EEPROM)
  I2C_Wr(ee_data);                          // Send data(data that will be written)
  I2C_Stop();
  while true do
    begin
      I2C_Start;                            // Issue I2C start signal
      I2C_Wr($A0);                          // Send byte via I2C
      if (I2C_Is_Idle) then break;
    end;
end;

function SI2C_Read(ee_adr : word) : byte;
begin
  I2C_Start();                              // Issue I2C start signal
  I2C_Wr($A0 + (Hi(ee_adr) shl 1));         // Send byte via I2C(command to 24c16)
  I2C_Wr(Lo(ee_adr));                       // Send byte(address for EEPROM)
  I2C_Repeated_Start;                       // Issue I2C signal repeated start
  I2C_Wr($A1 + (Hi(ee_adr) shl 1));         // Send byte(request data from EEPROM)
  result := I2C_Rd(0);                      // Read the data
  I2C_Stop();                               // Issue I2C_stop signal
end;

Procedure Init; // with TMR2 timer interrupt
Begin
  OSCTUNE := %01000000;
  delayms(100);
  ClrWdt;
  TRISB := %11111110;
  PORTB := 0;
  count_led := 0;
  WDTCON := 1;
  CMCON := $07;
  ADCON1 := %00001111;         // All digital
  ADCON2 := %10000111;         // RC Clock
  INTCON2 := %01111111;        // PORTB Pull Up
  I2C_Init(100000);
  PIE1 := %00000010;
  PR2 := 216;
  T2CON := %00010010;          // prescaler 16, poscaler 3 ; 16*3*217*4 / 41.666667 = 999.935
  TMR2 := 0;
  T2CON.TMR2ON := 1;           // start TMR2
  INTCON := %11000000;         // intrerupere TMR2 la 1 ms
  eol[0] := $0D;
  eol[1] := $0A;
  eol[2] := 0;
End;

Procedure Eth_SetParameters;   // set your parameters here
Begin
  Str2Ip('192.168.1.253',eth_ip_addr);
  Str2Ip('192.168.1.1',eth_gateway);
  Str2Ip('255.255.255.0',eth_mask);
  Str2Mac('0004A3008080',eth_mac);
  Str2Ip('192.168.1.2',user_ip_addr);
  eth_port := 10001;
  dest_port := 10001;
end;

begin
  Init;
  Eth_SetParameters;
  Eth_Init;
  Wait_for_LAN;
  data_user := 'Device Reseted ...';
  Str_Cat(data_user,eol);
  Send_UDP(user_ip_addr, dest_port, eth_port, Str_Len(data_user), data_user);
  cycle := 0;
  while PORTB.6 = 0 do
    begin
      data_user := 'Cycle no. : ';
      Word2Str(cycle,txt);
      Str_Cat(data_user,txt);
      Str_Cat(data_user,eol);
      Send_UDP(user_ip_addr, dest_port, eth_port, Str_Len(data_user), data_user);
      add_eep := 0;
        while add_eep < 2048 do
          begin
            Eth_DoPacket; // process incoming packets
            SI2C_Write(add_eep,255);  // Write 255 first
            SI2C_Write(add_eep,Lo(add_eep));
            if SI2C_Read(add_eep) <> Lo(add_eep) then
              begin
                data_user := 'Error at address : ';
                Word2Str(add_eep,txt);
                Str_Cat(data_user,txt);
                Str_Cat(data_user,eol);
                Send_UDP(user_ip_addr, dest_port, eth_port, Str_Len(data_user), data_user);
              end;
            inc(add_eep);
          end;
        inc(cycle);
    end;
  reset;
end.
Best regards, Florin Andrei Medrea.

http://www.microelemente.ro/
http://www.microelemente.ro/produse-si-servicii/
http://www.microelemente.ro/custom-software/

mail : florin@microelemente.ro

Dany
Posts: 3854
Joined: 18 Jun 2008 11:43
Location: Nieuwpoort, Belgium
Contact:

#11 Post by Dany » 17 Aug 2008 12:18

Thanks Florin,

Did you also do a test wherein more than 1 byte is read from I2c? In the application where I detected the problem, every read I did (on the mikroElektronika Real Time Clock) was a consecutive read of the first 8 registers of the I2c device.
Perhaps the problem only shows with consecutive I2c_Rd's?

Thanks in advance and keep up the good work! :D
Kind regards, Dany.
Forget your perfect offering. There is a crack in everything, that's how the light gets in... (L. Cohen)
Remember when we were young? We shone like the sun. (David Gilmour)

yo2lio
Posts: 1878
Joined: 19 Sep 2006 12:57
Location: Romania, Arad City
Contact:

#12 Post by yo2lio » 17 Aug 2008 12:30

I change SI2C_Read with this :

Code: Select all

function SI2C_Read_8(ee_adr : word) : byte;
begin
  I2C_Start();                              // Issue I2C start signal
  I2C_Wr($A0 + (Hi(ee_adr) shl 1));         // Send byte via I2C(command to 24c16)
  I2C_Wr(Lo(ee_adr));                       // Send byte(address for EEPROM)
  I2C_Repeated_Start;                       // Issue I2C signal repeated start
  I2C_Wr($A1 + (Hi(ee_adr) shl 1));         // Send byte(request data from EEPROM)
  result := I2C_Rd(1);                      // Read the data
  I2C_Rd(1);
  I2C_Rd(1);
  I2C_Rd(1);
  I2C_Rd(1);
  I2C_Rd(1);
  I2C_Rd(1);
  I2C_Rd(0);
  I2C_Stop();                               // Issue I2C_stop signal
end;
and no hang ....
Best regards, Florin Andrei Medrea.

http://www.microelemente.ro/
http://www.microelemente.ro/produse-si-servicii/
http://www.microelemente.ro/custom-software/

mail : florin@microelemente.ro

Dany
Posts: 3854
Joined: 18 Jun 2008 11:43
Location: Nieuwpoort, Belgium
Contact:

#13 Post by Dany » 17 Aug 2008 15:27

Hi Florin, is it possible for you to test with the e.g. 16F877A?
Perhaps those I2c routines are not of the same structure as those of the PIC18F67J60.
As far as I understand it the problem is the order of the first statements in I2c_Rd: first enabling reception and after that clearing the interrupt bit gives the problem. Is that order indeed the same in the PIC18F67J60 I2c library or are they not comparable (I do not know the PIC18F67J60).

Thanks in advance. :D
Kind regards, Dany.
Forget your perfect offering. There is a crack in everything, that's how the light gets in... (L. Cohen)
Remember when we were young? We shone like the sun. (David Gilmour)

yo2lio
Posts: 1878
Joined: 19 Sep 2006 12:57
Location: Romania, Arad City
Contact:

#14 Post by yo2lio » 17 Aug 2008 19:18

Sorry Dany, I don't have 16F877A !

For JANNI : I put one post few months ago :

http://www.mikroe.com/forum/viewtopic.php?t=14464

I have a bad experience with I2C_Is_Idle at PIC18F452 ...
Best regards, Florin Andrei Medrea.

http://www.microelemente.ro/
http://www.microelemente.ro/produse-si-servicii/
http://www.microelemente.ro/custom-software/

mail : florin@microelemente.ro

janni
Posts: 5373
Joined: 18 Feb 2006 13:17
Contact:

#15 Post by janni » 17 Aug 2008 20:49

yo2lio wrote:I made few tests , real hardware, PIC18F67J60, EEprom 24LC16 (my HWZA board) and I can't reproduce this problem.

I use 1 ms interrupt with TMR2 and in main loop I write, read and verify , continuously from EEprom.
Well, the interrupt comes every several thousand instructions, your main loop also takes thousands of them, the interrupt is probably synchronised with the main loop (unless Send_UDP introduces random delays) - how frequently, do you think, the interrupt will happen exactly between the two critical instructions in I2C routines?

To prove that there is no problem with the library, one would have to make sure that the interrupt would, once in a while, happen after every assembly instruction. To show that there is a problem, one would have to do almost the same :) , but it should be a bit easier. I'll try to find some time for it, but Dany's post already indicates that the locking problem vanishes with another way of reading I2C device.

About your other post - there is no need to confirm that Microchip's datasheets are far from ideal :wink: .

Post Reply

Return to “mikroPascal General”