IIC Transmission Failing, Single SCL Pulse Just a "Blip"?

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

IIC Transmission Failing, Single SCL Pulse Just a "Blip"?

603 Views
PLacerenza
Contributor II

Good day everyone,

 

This has been an issue coworkers and I have been contending with with a couple weeks during development, along with other issues, so bare with me in the description.

 

On one board, we have a MC9S12XA256CAL, set up as IIC master, an IIC line driver-receiver (I don't have the p/n at the moment), a few sets of H-bridges driven by PWM (right now, each one is at 10kHz frequency), and other circuitry currently unused at this point in development testing.

On another board, we have an IIC slave IO Expander (MCP23017), an IIC line driver-receiver (ditto to p/n), and connectors to various motors controlled by the other board's H-bridges.

Connecting the two boards is a single, 25-wire cable, included in that cable being PWMed output of the H-bridges for the motors, the line driver-receiver's version of SDA and SCL, and digital return.

 

When no motors are running, IIC seems to work perfectly fine with no issues.  Turn on one of the motors, and this appears to change.  Here's the thing, though, the scope capture doesn't look like noise from the motor PWM is the problem.  One such scope capture is attached in this post, "Scope Cap SCL1 SDA2 ClkBlip.png."

Channel 1, the yellow channel, is the SCL of the IIC right before the IO Expander.  The first 2 pulses on the left are from the previous transmission (writing the internal register address to the IO expander), followed by a "repeated start" condition (the longer pulse on both SCL and SDA).

The next intended transmission is 0x45, for sending the IO expander address and telling it we'll be reading.

However the SCL has 3 clock pulses, a "blip" (for lack of a better word), then 5 more clock pulses.

It looks to me like the IO expander IIC slave is holding the clock low when the micro controller lets go of the SCL to let it go high (from the passive pull up).

I'll be also seeing if I can get any information from the IO Expander manufacturers, but any help would be appreciated

 

At this point in time in testing, there are no other IIC devices, just the micro controller master and the IO expander slave.

 

I run off of an 80MHz Xtal, so my clock frequency is 40MHz.

 

Below is my IIC0 register initialization code:

(note, we have experimented with several different IBFD values)

 

IIC0_IBCR = 0; //clear entire IBCR for settingIIC0_IBFD = 0x9B;//0x9B:// div=512 -> 78.125kHz// SDAhold=68sysclks// HoldStart=248sysclks// HoldStop=260sysclks//for 100kHz, IIC specs say max SDAhold is 3.45usec, or <137sysclks@40MHz//IOExpand0 minimum HoldStart and HoldStop is 4usec, or >160sysclks@40MHzIIC0_IBAD = 0xA0; //(just in case)IIC0_IBCR_IBSWAI = 1; //1 = dont wait while in halt modeIIC0_IBCR_RSTA = 0; //1 = send restart signal *now,* 0=nothingIIC0_IBCR_TXAK = 0; //1 = disable acknowledgement of received dataIIC0_IBCR_TX_RX = 1; //1 = set to transmitIIC0_IBCR_MS_SL = 0; //0->1 = generate start signal ; 1->0 = generate stopIIC0_IBCR_IBIE = 0; //1 = enable iic interrupt, we're pollingIIC0_IBCR_IBEN = 1; //1 = enable the module

 

If one looks at the IO Expander, they'll see there are multiple registers to set.  This initialization is acknowledged completely, and the only register that may make any difference to anyone reading this post is "IOCON" which is set to 0x00 (DISSLW bit = 0, not disabled, setting to 1 changed nothing).

 

Finally, every 10ms, I run the following function.  It works well while no motors are running, but as soon as we set a motor running, it fails in a way similar to the scope capture attached and doesn't receive an acknowledgement.

 

IIC0_IBCR_TXAK = 0;         // TXAK = 0;IIC0_IBCR_TX_RX = 1;IIC0_IBCR_MS_SL = 1;     // generate START condition;IIC0_IBDR = HARDWARE_ADDRESS_IOEXP0 | IIC_WRITE;while (!IIC0_IBSR_IBIF);    // wait until IBIF;IIC0_IBSR_IBIF=1;           // clear the interrupt event flag;while(IIC0_IBSR_RXAK);      // check for RXAK;    //-----Slave ACK occurred------------     IIC0_IBDR = IOEXP0_REGADDR_GPIOA;while (!IIC0_IBSR_IBIF);    // wait until IBIF;IIC0_IBSR_IBIF=1;           // clear the interrupt event flag;while(IIC0_IBSR_RXAK);      // check for RXAK;    //-----Slave ACK occurred------------IIC0_IBCR_RSTA = 1; //send restart ("repeated start") signalIIC0_IBDR = HARDWARE_ADDRESS_IOEXP0 | IIC_READ;while (!IIC0_IBSR_IBIF);    // wait until IBIF;IIC0_IBSR_IBIF=1;           // clear the interrupt event flag;while(IIC0_IBSR_RXAK);      // check for RXAK; <----FAILED HERE IN SCOPE CAPTURE    //-----Slave ACK occurred------------IIC0_IBCR_TXAK  = 0;        // acknowledge enable, 2 bytes to readIIC0_IBCR_TX_RX = 0; //set for receive mode(void)IIC0_IBDR;    // dummy read to get read cycle startedwhile (!IIC0_IBSR_IBIF);    // wait until IBIF;IIC0_IBSR_IBIF=1;           // clear the interrupt event flag;    //-----Read Complete-----------------IIC0_IBCR_TXAK = 1; //ack disable, next read will be lastIOExpanders[EXPANDER_0A].CurrentLiveValue = IIC0_IBDR; //read GPIOA value, put in struct, start read cycle for Bwhile (!IIC0_IBSR_IBIF);    // wait until IBIF;IIC0_IBSR_IBIF=1;           // clear the interrupt event flag;    //-----Read Complete-----------------IIC0_IBCR_MS_SL = 0; //generate stop signalIIC0_IBCR_TX_RX = 1; //set to transmit mode (technically not needed)IOExpanders[EXPANDER_0B].CurrentLiveValue = IIC0_IBDR; //read GPIOB value, put in struct

 

As stated before, any advice or help would be appreciated.

 

As a note: we've also experimented with delaying with multiple "asm NOP;" lines before setting IBDR, just to experiment, though I feel the code above will work if timeouts are added and the like after getting this hardware to work.

Labels (1)
0 Kudos
2 Replies

332 Views
kef
Specialist I

So it is hardware problem. IIC is not something very immune to the noise. Certainly you need to dig into details how does this spike come to IIC lines or IC's. We can't solve this remotely with so few details.

But you software is bad, because it doesn't provide any recovery mechanisms. All loops are suspicious. In case you wait for some external condition to happen, you should always expect that bad things happen and your event may never happen, turning while or for loop into wait forever loop. You need to incorporate some timeouts or provide otheer recovery mechanisms.

 

You code looks like copy paste from bad code example.

     

    while(IIC0_IBSR_RXAK);      // check for RXAK; <----FAILED HERE IN SCOPE CAPTURE

 

RXAK bit is updated when IBIF flag becomes set. It doesn't make sense to wait for RXAK, since it is not something that will change. RXAK is constant until another byte is send and IBIF becomes set again. Instead of waiting here, you need to signal some error, generate stop condition and exit.

 

    IIC0_IBSR_IBIF=1;           // clear the interrupt event flag;

 

Comment is wrong. It should be "clear IBIF and IBAL flags".  To clear just IBIF you need to

 

    IIC0_IBSR = IIC0_IBSR_IBIF_MASK;

  

BTW you need to monitor IBAL flag (loss of arbitration). Noise may make IIC module detecting loss of arbitration. You need to provide recovery mechanism also for IBAL=1.

0 Kudos

332 Views
PLacerenza
Contributor II

kef wrote:

So it is hardware problem. IIC is not something very immune to the noise. Certainly you need to dig into details how does this spike come to IIC lines or IC's. We can't solve this remotely with so few details.

But you software is bad, because it doesn't provide any recovery mechanisms. All loops are suspicious. In case you wait for some external condition to happen, you should always expect that bad things happen and your event may never happen, turning while or for loop into wait forever loop. You need to incorporate some timeouts or provide otheer recovery mechanisms.

 

You code looks like copy paste from bad code example.

     

    while(IIC0_IBSR_RXAK);      // check for RXAK; <----FAILED HERE IN SCOPE CAPTURE

 

RXAK bit is updated when IBIF flag becomes set. It doesn't make sense to wait for RXAK, since it is not something that will change. RXAK is constant until another byte is send and IBIF becomes set again. Instead of waiting here, you need to signal some error, generate stop condition and exit.

 

    IIC0_IBSR_IBIF=1;           // clear the interrupt event flag;

 

Comment is wrong. It should be "clear IBIF and IBAL flags".  To clear just IBIF you need to

 

    IIC0_IBSR = IIC0_IBSR_IBIF_MASK;

  

BTW you need to monitor IBAL flag (loss of arbitration). Noise may make IIC module detecting loss of arbitration. You need to provide recovery mechanism also for IBAL=1.


On the software:

1.) Agreed on the while loops for real use, but for testing this works fine.

I initially had a "for(timeoutDly = 200; IIC0_IBSR_RXAK && timeoutDly; timeoutDly--);" with a "goto" statement to a failure point if timeoutDly was 0.  The while loop is for easier telling *where* it happened during experimentation.

 

2.) On the comment about IBIF... while it's not purely semantics, we are getting to that territory.  Let's leave it at "I know that" and move on to the next point.

 

3.) On the point of needing IBAL recovery, this is a very fair point: any advice on this?  In my mind, it seems like every, single, solitary case would have to be handled individually, not exactly something easy to work about in software.  If this is true, then I can only receive general advice.

I'll add an IF after checking IBIF to see if it's caused by IBAL before clearing IBIF/IBAL before my next tests.

This 3rd point can derail the initial intention of this topic fairly easily, so on to my next question:

 

On the hardware:

Normally I'd agree with you: just noise.  BUT, and this is a big but here, the blip/spike I refer to is only on the SCL line.  It's not in the SDA line.  They travel together for their entire path.  If it were noise, any noise would be shared, common.

It appears to me as though the micro controller lets go of the SCL line, to let it be pulled high, then something (master or slave) pulls it low again, making the micro controller think it put out a clock pulse (reaches proper levels for logic 1), but messes up the data it's putting out.

 

I've also checked for noise on the 5V line, to make sure none of the components are resetting or similar, and checked at the IO expander's reset pin and VDD/VSS pins.  Straight 5V the whole time.

The measurements I took with the attached scope capture in the original post was right at the IO expander's SCL and SDA pins.

There are line driver/receivers right before/after (depending on your point of view) the cable between the two boards I describe.  The pull up on the master end and the slave end is 4.7k on each line.  There's a single pull up on each line between the driver/receivers of 235 Ohms.  At this point, it is only the one master and one slave device.

The line driver/receiver detail may be useless information, however, as I was seeing something similar happen prior to our addition of the line drivers/receivers, when we had approximately 2k pull ups and an additional slave device that was not being addressed yet (everything else the same).

 

I know that without sufficient information, no one, experts or otherwise, can give decent technical support, advice, or suggestions.  However, saying "we don't have enough information" is not enough for me to know what information you don't have that would be needed.

If you know what kind of information is missing that could be helpful, ask and I'll hopefully be able to provide it.

Hopefully, it isn't a case of "Information seems to be missing, but I don't know what it is to ask for it," but I know that's possible.

 

Thanks for attempting assistance, it is appreciated.

0 Kudos