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.