Jump to content
  • 0

I2C SCL line remains low on Cora Z7 board


solonfw

Question

We're trying to get I2C communication working on our Cora Z7 boards.  I tried sending data using the XIicPs_MasterSendPolled() function, but the data never gets clocked out.

I found that after programming the FPGA, the SCL line goes low and stays low, which seems relevant. Any tips on what we might be doing wrong?  Or how we can proceed?

The I2C peripheral is in the ARM microcontroller itself, not in the FPGA fabric.  The functions for setting up the peripheral and for transmitting data are shown below. 



bool I2CInit(void)
{
    printf("  I2C Init: ");
    XIicPs_Config *Config = XIicPs_LookupConfig(XPAR_XIICPS_0_DEVICE_ID);
    if (NULL == Config)
    {
        printf("I2C Driver failed to get config\r\n");
        return false;
    }
    if (XIicPs_CfgInitialize(&Iic, Config, Config->BaseAddress) != XST_SUCCESS)
    {
        printf("I2c Driver initialization failed\r\n");
        return false;
    }
    if (XIicPs_SelfTest(&Iic) != XST_SUCCESS)
    {
        printf("I2C Driver self test failed\r\n");
        return false;
    }
    // Set the I2C serial clock rate.
    XIicPs_SetSClk(&Iic, IIC_SCLK_RATE);
    printf("OK\r\n");
    return true;
}


bool I2CSendData(uint8_t *msg, int32_t num_bytes, uint16_t slave_addr)
{
    while (XIicPs_BusIsBusy(&Iic))
        ;
    return (XIicPs_MasterSendPolled(&Iic, msg, num_bytes, slave_addr)
            == XST_SUCCESS);
}

Link to comment
Share on other sites

6 answers to this question

Recommended Posts

D@n, I am also working on this problem.  Thanks for your input.  I instantiated an ILA in the FPGA fabric and looked at the signals coming out of the Zynq-Arm core, see the picture included.  When we try to access the Ps7-I2C peripheral, I see toggling on the I2C0_SCL_T signal. Interesting that it does not stay low, it goes back active high, implying to me that the SCL line is to be tri-stated, as if the Zynq-Arm was expecting to be a slave I2C device.  I see no toggling on the SCL line, even when trigger only on it (in the FPGA fabric) after multiple accesses to the ps7-I2C peripheral.  I checked through the XDC file and the pinout for the Zynq on the Cora board seems correct.

I route the signals coming out of the Zynq-Arm to the block design wrapper (VHDL).  I then just drive the SCL straight out of the Zynq, without any tri-state buffer, but I do tri-state the SDA line as required.   Any more thoughts?  Thanks again.

 


--Tri state buffer for I2C interface
I2C0_SCL <= I2C0_SCL_O;
I2C0_SDA <= I2C0_SDA_O when (I2C0_SDA_T = '0') else 'Z';
I2C0_SDA_I <= I2C0_SDA;
    

I2C problem on Cora1.png

Link to comment
Share on other sites

@PaulW,

You might want to dig into the I2C protocol a bit more--tri-states are required on both SCL and SDA lines.  The protocol is supposed to be a shared protocol between both multiple slaves and multiple masters.  You should only ever drive those lines either tri-state or  low, never-high--much like you are doing for SDA above.  They're also supposed to be pulled high by the circuit board itself if nothing is using them.  It is valid and possible for the slave to pull the SCL line low, in what's called "clock stretching."

If it helps, you can find my own I2C controller(s) here.  You'll find both a master and a slave controller there, both with WB interfaces.  (You can find an AXI-lite to WB converter here, if you need it.)  Using them, though, requires placing two tri-state assignments in your top-level file, like these.

Finally, I ran into some trouble myself working with I2C and a monitor some time back.  One problem I experienced was that reloading my design did not (necessarily) reset a broken I2C connection with another device.  I still needed to send a STOP condition--particularly if I hadn't stopped it with a prior (broken) design.  While the bus probably should have timeouts in its definition, implementing them is (IIUC) a violation of the protocol.

Dan

Link to comment
Share on other sites

This has helped my problem:  https://forums.xilinx.com/t5/UltraScale-Architecture/I2C-through-EMIO/td-p/984165

particularly:  " 

As would be expected this turned out to be something very simple. For I2C not only is the SDA bi-directional but SCL is also, but I had not set up the return path for SCL. So the controller was always waiting to see the SCL line high before starting anything and that's why both SDA and SCL showed no activity even on the ILA.

In the vhdl wrapper for the block design just the second line below was the fix:

<SCL I/O pin name> <= I2C1_scl_out when (I2C1_scl_tristate = '0') else 'Z';

I2C1_scl_in <= <SCL I/O pin name>; -- <- This was the missing vhdl line that I needed to add in the wrapper vhdl file

<SDA I/O pin name> <= I2C1_sda_out when (I2C1_sda_tristate = '0') else 'Z';

I2C1_sda_in <= <SDA I/O pin name>;

 

Thanks for the help on this.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...