I2C driver after hardware reset

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

I2C driver after hardware reset

Jump to solution
4,322 Views
Rpalenci
Contributor II

I am using MQX 3.7 with coldfire mcf52259.

I have a problem with driver implemented I2C. Sometime microcontroller is stopped waitting to bit STOP in function _mcf52xx_i2c_polled_ioctl to i2c_pol_mcf52xx.c.

It is stopped in this way:

    

while (i2c_ptr->I2SR & MCF52XX_I2C_I2SR_IBB)
{
}; // wait for completion

 

When I am debugging despite that I stop the debugger and relaunch the application it fails in the same part until I turn off power and turn on it.

So I don't have idea what's happen if I make a harware reset in the following way: 

    MCF5225_WATCHDOG_STRUCT *pla_bwt;
    pla_bwt =(MCF5225_WATCHDOG_STRUCT *) (BSP_IPSBAR + 0x140000);
    pla_bwt->WMR = 0xFFFF; /*Set count to 6.71s*/ 
    pla_bwt->WCR |= 0x01; /*Enable watchdog hardware*/

I don't know like  the driver is initialized after hardware reset it is not said in RM52259 and I know a software reset is not enough becouse I suppose it is done  by debbugger when I make STOP and launch.

Thanks in advanced.

Best Regards. 

0 Kudos
1 Solution
2,467 Views
trailman
Contributor V

I've just found the full answer in the MCF52259RM.pdf document (referance manual), chapter 29 "I2C interface" :

 

                                      NOTE
If I2SRn[IBB] is set when the I2C bus module is enabled, execute the
following pseudocode sequence before proceeding with normal
initialization code. This issues a STOP command to the slave device,
placing it in idle state as if it were power-cycled on.
     I2CRn   = 0x0
     I2CRn   = 0xA0
     dummy   read of I2DRn
     I2SRn   = 0x0
     I2CRn   = 0x0
     I2CRn = 0x80           ; re-enable

View solution in original post

0 Kudos
11 Replies
2,467 Views
trailman
Contributor V

Hi,

 

According to the satus register, your bus is found "busy" by the MCF52259 (I2SR[IBB]=1), but this is probably not caused by the driver but by the hardware connected to the I2C bus.

 

If the equipment connected to the I2C bus is powered after (or slightly after) the MCF52259 and then drives SDA and/or SCL low due to clamping on I/Os, a "bus busy" condition can be latched by the MCF52259. Then when the power is applied to the equipment, if SDA and SCL do not rise in the right order, no "stop" condition is seen by the MCF52259 and the bus is still declared "busy". That's why the only way you found to recover is to power-cycle.

 

I had the same problem than you and have found a workaround : If you are the only expected master on the bus, and it is "busy" (I2SR[IBB]=1) when you want to use it, this means that the problem occured. You can recover by setting I2CR[IEN] = 0 (I2C disable), then after a small delay I2CR[IEN] = 1 (I2C reenabled)

 

0 Kudos
2,468 Views
trailman
Contributor V

I've just found the full answer in the MCF52259RM.pdf document (referance manual), chapter 29 "I2C interface" :

 

                                      NOTE
If I2SRn[IBB] is set when the I2C bus module is enabled, execute the
following pseudocode sequence before proceeding with normal
initialization code. This issues a STOP command to the slave device,
placing it in idle state as if it were power-cycled on.
     I2CRn   = 0x0
     I2CRn   = 0xA0
     dummy   read of I2DRn
     I2SRn   = 0x0
     I2CRn   = 0x0
     I2CRn = 0x80           ; re-enable

0 Kudos
2,467 Views
trailman
Contributor V

The pseudo-code or my code above is not always able to unlock a hung I2C bus, nor my sample code.

This is the case if something goes wrong when reading some data from a slave : if for any reason some SCL clock periods are lost by the slave, it keeps on driving some data on SDA when the master terminates with a STOP. If the data driven by the slave at this time is 0, the STOP is not detected and the bus is hung.

Any attemp to disable/reenable the I2C module on MCF52259 does not help (my code fails).

Also trying to send some data, a START or a STOP fails because as the bus is found busy the MCF52259 immediately lose arbitration and does nothing on the bus (so pseudo code in doc fails)

The only workaround I found is :

- disable the I2C module (IEN bit = 0)

- switch I2C pins to GPIO mode

- set the pins at inputs (to have them high-Z)

- make 9 clock pulses on SDA by doing the following sequence 8 times :

  - set SCL GPIO to output @ 0

  - wait 1mS (example)

  - set SCL GPIO to input (high-Z)

  - wait 1mS

  this sequence makes the slave shift-out its data until the ACK bit where the master is expected to drive SDA; but as SDA is high-Z, the slave sees a NOACK and stops transmitting even if some extra clock pulses are done.

- then do a START + STOP (not sure really required) :

  - set SDA GPIO to output @ 0 (does a START)

  - wait 1mS

  - set SDA GPIO to input (high-Z, does a STOP)

  - wait 1mS

- set pins back to I2C mode (primary or secondary function depending on the port used)

- reenable the I2C module (IEN bit = 1)

The first access after that sometimes fails, but the bus is no more hung and retrying the access is OK.

.

0 Kudos
2,467 Views
rpalenc
Contributor II

I am looking this in Reference Manual MCF52259, but  I've not clear something: 

what's mean? ' dummy   read of I2DRn':

- I have to make a read one of I2DRn or 

- After I2CRn   = 0xA0 it is done by I2C driver.

 

And how you solve this, you produce a soft reset and then you write this pseudocode or you look time waiting to BIT STOP and then write this pseudocode whitout a soft reset?

0 Kudos
2,467 Views
trailman
Contributor V

Hi,

 

Here is the function I wrote to check and unlock I2C buses if needed.

I call it once at startup, before doing any access to the I2C buses, for each bus (physbusnum = 0 for I2C0, then physbusnum = 1 for I2C1) :

 

void libi2c_reset_bus_if_busy(uint_8 physbusnum) {
  VMCF52XX_I2C_STRUCT_PTR i2c_ptr = _bsp_get_i2c_base_address (physbusnum);
  if (i2c_ptr == NULL)
    return;
  if (
      ((i2c_ptr->I2SR & MCF52XX_I2C_I2SR_ICF) == 0) ||
      (i2c_ptr->I2SR & MCF52XX_I2C_I2SR_IBB)
      ) {
    printf("WARNING: I2C bus %d was busy and has been reset.\n", physbusnum); fflush(stdout);
    i2c_ptr->I2CR &= (~MCF52XX_I2C_I2CR_IEN);
    _time_delay(1);
    i2c_ptr->I2CR |= MCF52XX_I2C_I2CR_IEN;
    _time_delay(1);
  }
}

A dummy read of I2DRn means you read the I2DRn register but without using the returned value. This read is just to start a read cycle of one byte on the bus; this toggles the CLK signal and help the devices to exit from any bad state.

According to the doc, the code would be :

 

void libi2c_reset_bus_if_busy(uint_8 physbusnum) {
  VMCF52XX_I2C_STRUCT_PTR i2c_ptr = _bsp_get_i2c_base_address (physbusnum);
  volatile uint_8 tmp;
  if (i2c_ptr == NULL)
    return;
  if (
      ((i2c_ptr->I2SR & MCF52XX_I2C_I2SR_ICF) == 0) ||
      (i2c_ptr->I2SR & MCF52XX_I2C_I2SR_IBB)
      ) {
    printf("WARNING: I2C bus %d was busy and has been reset.\n", physbusnum); fflush(stdout);
    i2c_ptr->I2CR = 0x0;
    i2c_ptr->I2CR = 0xA0;
    tmp = i2c_ptr->I2DR;
    i2c_ptr->I2SR = 0x0;
    i2c_ptr->I2CR = 0x0;
    i2c_ptr->I2CR = 0x80;
  }
}

 

If this does not work, try to add

_time_delay(1); 

between each access to registers.

 

0 Kudos
2,467 Views
Rpalenci
Contributor II

Hi,

 

I am reading again reference manual and it said "If I2SRn[IBB] is set when the I2C bus module is enabled", so, I think pseudocode:

I2CRn = 0x0
I2CRn = 0xA0
dummy read of I2DRn
I2SRn = 0x0
I2CRn = 0x0
I2CRn = 0x80

 

have to be executed after initialize I2C channel, and not on startup of my application.

I am not sure about that....

 

Do you first disable and enable I2c and then initialize I2C? or First, do you initialize I2C and then disable and enable the dirver if it is busy?

Do you understand me?

Thanks in advanced.

0 Kudos
2,467 Views
trailman
Contributor V

Hi,

 

I run this code once the I2C driver is installed (should be done by BSP) and the device is open by my application.

 

0 Kudos
2,467 Views
Rpalenci
Contributor II

Yes, driver is installed by BSP and the device is open and initialized in your applicattion with fopen(("i2cn:", NULL); and after that you run this code (disable, enable... if busy)

 

And you only run it once after that because  you only open I2C device one time and don't close it.

 

My problem is my application each time write in I2C bus do fopen -- write/read -- close.

 

And so my "pseudocode (disable, enable... if busy)" have to verify if the bus is busy  always it do fopen().

 

Is it right?

 

It is possible to produce error? Because is very difficult for me know if it solve my problem.

 

Thanks again and best regards. 

0 Kudos
2,467 Views
trailman
Contributor V

Yes, in your case you should check if bus is busy and run pseudocode after each open().

 

However, as you do open() + close() each time you do an access, this should already solve the problem as according to BSP source code :

- each open()  does a disable + enable

- each close() does a disable

So no pseudocode should be required : even if th first open()+close() fails, another one should be OK.

 

Perhaps something is wrong in your code. Make sure you have a 

   fflush(fd)

or

   ioctl (fd, IO_IOCTL_FLUSH_OUTPUT, NULL)

after each read or write

 

Also check the value returned by close() : It should return MQX_OK (0) if OK, but if you have something else this is abnormal.

If you get I2C_ERROR_DEVICE_BUSY, this means you call close() while the bus is still busy

 

Also check the value of the IBB bit just after open() and after the close(), it should not be set.

 

If everything seems OK in code, the last debug is to put a scope on the SDA and SCL signals to find-out what's going on.

0 Kudos
2,467 Views
Rpalenci
Contributor II

Hi,

 

I will try it.

Thanks a lot for your reply. :smileyhappy:

0 Kudos
2,467 Views
rpalenc
Contributor II

Thanks a lot for your help.

I try solve it as you tell me.

0 Kudos