Announcements

Help us improve the Power & Sensing Selection Guide. Share feedback

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
lgacnik97
Level 2
Level 2
10 replies posted 5 replies posted 5 sign-ins

We are having issues setting up basic operation of I2C on TC399XE microcontroller (on development board, design by our company for test purposes of TC399XE). During organization of our own libraries, we followed iLLD to the point. The following code consists of basic initialization function:

 

void I2C_init(void)
{
  /************* I2C Module 0 => MASTER **************/
  
  /* Enable access for CLC ("E" Write access mode) */
  clearWdtEndinit();
  
  /* Clock enable for I2C module */
  I2C0_CLC.B.DISR = 0;              // Enable module
  while( I2C0_CLC.B.DISS == 1 );    // Wait while disabled
  I2C0_CLC1.B.RMC = 1;              // I2C0 clock division by 1
  while( I2C0_CLC1.B.RMC != 1 );    // Wait while disabled
  I2C0_CLC1.B.DISR = 0;             // Enable module
  while( I2C0_CLC1.B.DISS == 1 );   // Wait while disabled
  
  /* Configure protocol interrupts */
  I2C0_ERRIRQSM.U = 0;
  I2C0_PIRQSM.U = 0;
  I2C0_IMSC.U = 0;
  
  /* Disable access for CLC ("E" Write access mode) */
  setWdtEndinit();
  
  /* Stop I2C0 module */
  I2C0_RUNCTRL.B.RUN = 0;     // I2C0 configuration registers now accessible
  
  /* Configure operation options */
  I2C0_ADDRCFG.U = 0;
  I2C0_ADDRCFG.B.TBAM = I2C_config.addressMode;  // Address mode select
  I2C0_ADDRCFG.B.MnS = 1;     // Module configured as master
  I2C0_ADDRCFG.B.SONA = 0;    // Don't release bus on NACK
  I2C0_ADDRCFG.B.SOPE = 0;    // After transfer, go into master restart state
  
  /* Configure FIFO settings */
  I2C0_FIFOCFG.U = 0;
  I2C0_FIFOCFG.B.RXFC = 1;     // RX FIFO as flow controller
  I2C0_FIFOCFG.B.TXFC = 1;     // TX FIFO as flow controller
  I2C0_FIFOCFG.B.RXFA = 0;     // RX FIFO Alignment - byte aligned 
  I2C0_FIFOCFG.B.TXFA = 0;     // TX FIFO Alignment - byte aligned
  I2C0_FIFOCFG.B.RXBS = 0;     // Burst size 1 word
  I2C0_FIFOCFG.B.TXBS = 0;     // Burst size 1 word
  
  /* Set Baud Rate */
  setWdtEndinit();
  clearWdtEndinit();
  
  I2C0_FDIVHIGHCFG.B.INC = 46;
  I2C0_FDIVHIGHCFG.B.DEC = 162;  // 3399 kbps
  
  I2C0_TIMCFG.B.SDA_DEL_HD_DAT = 0x3F;  // SDA delay for data hold - 66 
                                        // delay stages (stand. & fast mode)
  I2C0_TIMCFG.B.FS_SCL_LOW     = 0x01;  // Fast mode SCL low period timing, 
                                        // for INC = 1 it is 6/8 of period
  I2C0_TIMCFG.B.EN_SCL_LOW_LEN = 0x01;  // SCL low period is determined by 
                                        // the setting of SCL_LOW_LEN
  I2C0_TIMCFG.B.SCL_LOW_LEN    = 0x20;  // SCL low time is extended by the 
                                        // number of kernel_clk cycles
  
  setWdtEndinit();
  
  /* Configure PORT settings for I2C_0 SDA/SCL pins*/
  I2C0_GPCTL.B.PISEL = 0x02;  // PORT-C selected -> SDA/SCL on P015.4/P015.5 
  P15_IOCR4.B.PC4 = 0x1E;     // SCL output (Alternate output function 6)
  P15_IOCR4.B.PC5 = 0x1E;     // SDA output (Alternate output function 6)
  
  /* Configure operation options */
  I2C0_RUNCTRL.B.RUN = 1;     // I2C0 Module enabled - configuration 
                              // registers not accessible
}

 

 

Functions clearWdtEndinit() and setWdtEndinit() operate as expected so I won't include them here (they are executed in same manner as are corresponding iLLD ENDINIT functions).

In initialization function, only most necessary settings of SFR writes are executed. Unlocking certain registers is done via modifying WDT_ENDINIT bit field and enabling CLC (also CLC1 within I2C module) register.

The following function is Master write data function (currently no slave devices are defined, since our main objective is to detect any activity on I2C bus during data write function). Its content is basically the same as iLLD function, except that various ifs were excluded (not required for basic operation) and structs were replaced by variables (for simplicity sake only). But execution should be same as in case of iLLD, if certain parameters are assumed (e.g. 7-bit address mode, standard speed, etc.).

 

/** Procedure like in iLLD "write()" function **/
void I2C_masterSendData (void)
{ 
  uint8_t data[] = {0xAA};         // Send single byte for test purpose
  
  uint32_t status = 0;             // Initial status OK - bus free
  
  uint32_t size = 1;               // Size of "txdata"
  uint32_t bytesToSend = size + 1; // +1 slave device address
  uint32_t bytes;
  uint32_t addr = 0xFF;            // Slave address
  
  union data
  {
    uint32_t packet;
    uint8_t  packetbyte[4];
  } txdata;
  
  /* If bus not idle */
  if( I2C0_BUSSTAT.B.BS != 0 )
  {
    status = 0x03;  // Bus not free
  }
  
  uint32_t slAddr = (uint32_t)(addr & 0xFE);
  
  uint32_t i, j;
  if( size > 0 )
  {
    I2C0_TPSCTRL.B.TPS = size;  // Inform FIFO about size of packet to be 
                                // transmitted
    
    for (i = 0; i < (uint32_t)(size + 1); i += 4)
    {
      if (bytesToSend >= 4)
      {
        bytes        = 4;
        bytesToSend -= 4;
      }
      else
      {
        bytes       = bytesToSend;
        bytesToSend = 0;
      }
      
      txdata.packet = 0;
      
      /* Assume 7-bit addressing mode */
      for (j = 0; j < bytes; j++)
      {
        /* Put slave address at beginning of TX packet */
        if ((i == 0) && (j == 0))
        {
          txdata.packetbyte[j] = (uint8_t)slAddr;
        }
        else
        {
          txdata.packetbyte[j] = (uint8_t)data[i + j - 1];
        }
      }
      
      /* Check errors */
      
      /* NOTE:
       * "status" in iLLD is member of i2c struct of enum data type, which
       * has defined various bus states - here, those states are implemented 
       * rather as raw hex data (e.g. in ILLD IfxI2c_I2c_Status_error = 0x04) 
       * for simplicity
       */
      
      do
      {
        uint32_t ris;
        
        ris = I2C0_RIS.U;
        if (ris & (1 << 5))       // Check protocol flags
        {
          I2C0_PIRQSC.U = 0x7F;   // Clear protocol interrupt sources
          status = 0x04;
        }
        if (ris & (1 << 4))       // Error flags
        {
          I2C0_ERRIRQSC.U = 0x0F; // Clear error interrupt sources
          status = 0x04;
        }
      } while (I2C0_FFSSTAT.B.FFS == 8 && status != 0x04);    
      // Wait to prevent FIFO overflow
      
      /* If any error on the bus */
      if( status != 0x00 )
      {
        break;
      }
      
      I2C0_TXD.U = txdata.packet;   // Write whole packet to FIFO
      I2C0_ICR.U = 0x0F;            // Clear DTR interrupt sources
    }
    
    /* Wait for end of transmission */
    while( I2C0_PIRQSS.B.TX_END == 0 );
    
    I2C0_PIRQSC.U = (1 << 5); // Clear TX_END interrupt request
    
    /* Check errors */
    uint32_t ris;
    
    ris = I2C0_RIS.U;
    if (ris & (1 << 5))       // Check protocol flags
    {
      I2C0_PIRQSC.U = 0x7F;   // Clear protocol interrupt sources
      status = 0x04;
    }
    if (ris & (1 << 4))       // Error flags
    {
      I2C0_ERRIRQSC.U = 0x0F; // Clear error interrupt sources
      status = 0x04;
    }
    
    /* Set SETEND at end of transmission if bus not free */
    if (I2C0_BUSSTAT.B.BS != 0x00)
    {
      I2C0_ENDDCTRL.B.SETEND = 1;           // Wait until bus is free
      while( I2C0_PIRQSS.B.TX_END == 0 );   // Wait for end of transmission
      I2C0_PIRQSC.U = (1 << 5);             // Clear TX_END interrupt request
    }
  }
}

 

 

We even made a project that consists only of iLLD libraries, which achieves operationality of I2C. With that, we compared initialization of all I2C registers (+ some out of I2C module registers, like CLC, IOCR, WDTCPUyCON0, etc.) and write content to some I2C registers during Master write function - so far everything looks the same, except project with our I2C library (edited copy of iLLD) does not respond anyhow during Master write sequence, while iLLD based project (unmodified original libraries) responds with some action.

There is only one dissimilarity between two projects - in our project, after start condition is set (RUNCTRL.RUN = 1), the bus status register BUSSTAT.BS = 1, meanwhile in iLLD project BUSSTAT.BS = 0  (after RUNCTRL.RUN = 1). Although, as Master FSM defines in reference manual, BS = 1 after RUN = 1. So, slight confusion here, otherwise everything else looks the same.

We would greatly appreciate any help or hints about what could affect non-operationality of our I2C project.

 

*P.S.: SFR defines used here are provided by Tasking compiler (which is also used in both projects).

 

0 Likes
1 Solution
lock attach
Attachments are accessible only for community members.
lgacnik97
Level 2
Level 2
10 replies posted 5 replies posted 5 sign-ins

Hello,

 

I just found out there was a problem with connections between logic analyzer and DUT. Problem is now solved and I2C (with iLLD libraries) works as a charm! Test sequence screenshoot, where address is being send from master (but no respond, since I haven't implemented slave functionality yet), is attached. Sorry for any inconvenience. 

However, I must ask few I2C related questions while we are at it:

  1. Are you able to clarify, why are asm instructions used inside "IfxI2c_initSclSdaPin()" function (check my first respond)? I haven't found a clue in reference manual about such operation/action during setting I/O for I2C module.
  2. In one of my earlier questions, I was asking about operation of "FDIVCFG" register, which controlls/generates clock cycle of appropriate duty cycle (you can check it here). I would really appreciate if you could clarify how it is meant to operate, as I'm really trying to understand TC3xx  I2C operation in detail (among other things).

Kind Regards!

View solution in original post

0 Likes
6 Replies
Nambi
Moderator
Moderator
Moderator
500 replies posted 50 likes received 5 likes given

Hi,

1. "RUNCTRL.RUN = 1" indicates that the I2C module is enabled or started. BUSSTAT.BS = 1 indicates an "I2C START" condition on the BUS. This is different from starting or enabling the I2C module.

2. The reference manual indicates that after RUN=1, the device will start monitoring the BUS. If there is an "I2C START" condition on the BUS, it will set BUSSTAT.BS = 1. If the BUS is idle, BUSSTAT.BS will remain "0" even after RUN=1.

3. Can you check the BUS levels of SDA and SCL pins when RUNCTRL.RUN is set as "1" and you observe BUSSTAT.BS change from "0" to "1"?

4. We see that there are differences between IfxI2c_initSclSdaPin() of ILLD and your configuration of port settings. Can you use the ILLD function IfxI2c_initSclSdaPin() for port setting and check the behavior?

5. Can you check if the I2C specific external configurations are met? eg: Check if the SDA and SCL lines are pulled up since the port configuration is open-drain.

6. After the comment /* Set Baud Rate */ in your code, setWdtEndinit(); seems misplaced and not necessary.

7. In order to narrow down the issue, we recommend having ILLD code as-is and replacing one function at a time with your simplified configuration and checking where the behavior deviates and the analysis can be focused on that configuration.
Since, the point of deviation occurs at "I2C0_RUNCTRL.B.RUN = 1; " within I2C_init(), you can apply this narrow down approach to I2C_init() of your code based on ILLD function IfxI2c_I2c_initModule()

Best Regards.

lgacnik97
Level 2
Level 2
10 replies posted 5 replies posted 5 sign-ins

Hello,

Sorry for late respond. Here are my observations so far.

I have found out that few "ldmst" (Load-Mask-Store) instructions has to be extecuted before setting "GPIO" register to configure on which port I2C0 is active. However, I don't know why these instructions are needed here. Also I couldn't find and source explaining such instructions had to be performed. Although, these instruction modify (what they modify, I do not know) something in "PDR" and "IOCR" registers, which are related to setting I/O functionallity for I2C.

/* Configure PORT settings for I2C_0 SDA/SCL pins*/
  P15_IOCR4.B.PC4 = 0x1E;         // SCL output (Alternate output function 6)
  P15_IOCR4.B.PC5 = 0x1E;         // SDA output (Alternate output function 6)
  /* Modify some registers related to I0CRx and Pad Driver */
  {
    clearWdtEndinit();
    volatile uint32_t *pdr = 0xF003AF14;
    volatile uint32_t *iocr = 0xF003AF40;
    __ldmst(&pdr, 0xFF << 0, 0xF0 << 0);        // SCL  IOCR.PC2 address
    __ldmst(&pdr, 0xFF << 0, 0xF0 << 0);        // SDA
    __ldmst(&iocr, 0x0F << 16, 0x08 << 16);      // SCL   PDR0.U address
    __ldmst(&iocr, 0x0F << 20, 0x08 << 20);      // SDA
    setWdtEndinit();
  }
  I2C0_GPCTL.B.PISEL = 0x02;      // PORT-C selected -> SDA/SCL on P015.4/P015.5 pins
0 Likes
lgacnik97
Level 2
Level 2
10 replies posted 5 replies posted 5 sign-ins

NOTE:

I'm currently having slight issues with project where iLLD libraries are used, as I cannot achieve I2C0 module to respond anyhow during write function, for some reason (it worked few days ago, meaning I might have messed something up in iLLD library). That is why, please consider reviewing in detail only 1. and 2. section. I will edit other part of answer (or respond with new answer) as soon as I manage to set up iLLD I2C project as it was set-up few days ago when everying was still operational.

Here is my respond to moderators statements:

  1. After "RUNCTRL.RUN" is set at the end of initialization process (function), "BUSSTAT.BS = 1". But if I understood correctly this is not how I2C module on TC3xx is meant to operate?
  2. If I understood correctly"I2C START" condition shouldn't be affected by setting/clearing "RUN" bit-field? Which action (by software) then generates "I2C START" condition?
  3. All of the following is processed inside " IfxI2c_I2c_initModule" I2C intialization function.
    SCL line is held HIGH at all times (pull-up resistors on both lines). At microcontroller reset, both lines are HIGH. After setting pin mode and pad driver mode via function " IfxI2c_initSclSdaPin", SDA goes LOW (this occurs immediately after execution of " IfxPort_setPinModeOutput" for SDA pin). 
    After initialization of SCL/SDA pins (pin mode, driver mode, port select for I2C0), function "IfxI2c_run" is executed and "RUN = 1" - immediately aftewards, "BS = 1".
  4. This is all done on project where iLLD libraries are used. I'm not using our own project to demonstrate operationability of I2C0 (for intent of this discussion).
  5. As mentioned before, both SDA/SCL lines are held HIGH via pull-up resistors (separate resistors on each line, of course).
  6. Function "setWdtEndinit()" was indeed misplaced in that section. However shouldn't affect writes/reads to/from SFRs, as it was cleared right afterwards. Here I should point out, reference manual (part 1) implies that Watchdog Endinit bit-field should be clear to access CLC register; all other registers seen here, do not require removal of R/W protection. This can be checked in Table 298 (Access Mode).However, even if SFR protection "released" for SFRs that primarly have nothing in common with such protection mechanism, such action shouldn't affect R/W to unprotected registers.
  7. As proposed, I already did so and discovered what is seen in my earlier answer. 

Otherwise we really appreciate you help with this matter.

 

Kind regards!

0 Likes
Nambi
Moderator
Moderator
Moderator
500 replies posted 50 likes received 5 likes given

Hi,

1. Setting "RUNCTRL.RUN" is not a sufficient condition to get "BUSSTAT.BS = 1".

2. No. If the RUN bit is not set, the I2C module will be disabled and will not take part in communication. The RUN bit is used to enable the I2C module. Only an enabled I2C module can generate or react to Bus levels. The START condition will be generated during communication on the bus. In the ILLD example, the START condition will be generated inside IfxI2c_I2c_write() and IfxI2c_I2c_read() APIs.

3.,5. Based on our understanding, the SDA line goes LOW while the SCL line stays HIGH after IfxPort_setPinModeOutput() and SDA remains LOW thereafter at your end. The SDA line is not expected to go LOW after IfxPort_setPinModeOutput(). Both SDA and SCL are expected to stay HIGH during the Bus Idle phase. This could be leading to the issue at your end. You can investigate further on this.

4. We understand that the base is ILLD. But as you had mentioned in the initial description, you have stripped down ILLD APIs to bare minimum register writes in your project. This was the difference we had highlighted. eg:Your configuration PORT settings for I2C_0 SDA/SCL pins consists of "I2C0_GPCTL.B.PISEL = 0x02; P15_IOCR4.B.PC4 = 0x1E; P15_IOCR4.B.PC5 = 0x1E;". The ILLD API IfxI2c_initSclSdaPin() has further configurations. Our recommendation was for you to use IfxI2c_initSclSdaPin() API as is and check at your end. Also, ensure that the parameters passed are the same as those passed in the ILLD example.

Best Regards.

0 Likes
lock attach
Attachments are accessible only for community members.
lgacnik97
Level 2
Level 2
10 replies posted 5 replies posted 5 sign-ins

Hello,

 

I just found out there was a problem with connections between logic analyzer and DUT. Problem is now solved and I2C (with iLLD libraries) works as a charm! Test sequence screenshoot, where address is being send from master (but no respond, since I haven't implemented slave functionality yet), is attached. Sorry for any inconvenience. 

However, I must ask few I2C related questions while we are at it:

  1. Are you able to clarify, why are asm instructions used inside "IfxI2c_initSclSdaPin()" function (check my first respond)? I haven't found a clue in reference manual about such operation/action during setting I/O for I2C module.
  2. In one of my earlier questions, I was asking about operation of "FDIVCFG" register, which controlls/generates clock cycle of appropriate duty cycle (you can check it here). I would really appreciate if you could clarify how it is meant to operate, as I'm really trying to understand TC3xx  I2C operation in detail (among other things).

Kind Regards!

0 Likes
Nambi
Moderator
Moderator
Moderator
500 replies posted 50 likes received 5 likes given

Hi,

1. The goal is to configure the corresponding registers as required. The asm instruction is one of the possible ways.

Best Regards.

0 Likes