This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

RTOS/LAUNCHXL-CC2640R2: I2C_transfer function is not working in timer callback function

Part Number: LAUNCHXL-CC2640R2

Tool/software: TI-RTOS

Hi,

I have a project that was customized by blestack/simple_pheripheral project. Then I need to add Off_Chip OAD Property to that project. To do that, I want to customize blestack/oad_off_chip and I ported my code to that example. In my original code, I used additional thread so I tried to add a thread to that example but somehow I cannot do that (I created another issue about that and its process in progress).

Because of that, I changed my code structure to remove the additional thread and I created a clock. In that clock callback, I tried to read my I2C sensors periodically but I could not.

When I try to read the sensors in "SimplePeripheral_processCharValueChangeEvt" function for example (I used to start my additional thread via BLE command in my old code) for just one time, it works. But, when I tried to read sensors in timer callback function it does not. But timer works because I toggled a led in the callback function.

Here is a code example that explains what I mean;

static void DD_SystemGeneralTimerHandle(UArg arg)
{
#ifdef OLD_STYLE_CODE
    GPIO_toggle(Board_GPIO_LED0);
#else
    GPIO_toggle(Board_GPIO_LED1);
    uint8_t txBuffer[1];
	uint8_t rxBuffer[6];
    I2C_Transaction i2cTransaction;

    txBuffer[0] = WHO_AM_I_REG;
    i2cTransaction.slaveAddress = SENSOR_ADDRESS;
    i2cTransaction.writeBuf = txBuffer;
    i2cTransaction.writeCount = 1;
    i2cTransaction.readBuf = rxBuffer;
    i2cTransaction.readCount = 6;

    I2C_transfer((*i2c), &i2cTransaction);	//there is no stucking, it just cannot read, it return false
#endif
}


static void SimplePeripheral_processCharValueChangeEvt(uint8_t paramID)
{
    switch(paramID)
    {
      case CUSTOM_PROFILE_CHAR1:  //Working mode characteristic
        SimpleProfile_GetParameter(CUSTOM_PROFILE_CHAR1, Characteristic_1_Run_Time_Read_Value);
        if(Characteristic_1_Run_Time_Read_Value[0] == 0xBA)	//start sensor reading here for example
        {
			#ifdef OLD_STYLE_CODE //I used to start additional thread here before
				myThread_create();
			#else	//there is no error in here
				GPIO_init();
				I2C_init();
				SPI_init();

				/* Create I2C for usage */
				I2C_Params_init(&glb_i2cParams);	//glb_i2cParams this is global static variable
				glb_i2cParams.bitRate = I2C_400kHz;
				glb_i2c = I2C_open(Board_I2C_TMP, &glb_i2cParams);	//glb_i2c this is global static variable
				if (glb_i2c == NULL)
				{
					vErrorStateUpdte((uint32_t) __LINE__, (char *)__func__);
					while(1){}
				}
				
				/////////////////This part is used for I2C sensor search in I2C bus
				int ii = 0;
				uint8_t rxBuffer[0];
				uint8_t txBuffer[0];
				I2C_Transaction i2cTransaction;
				for(ii=0; ii<256; ii++)
				{
					rxBuffer[0] = 0;
					uint8_t AG_ADDRESS  = ii;
					txBuffer[0] = WHO_AM_I_XG;
					i2cTransaction.slaveAddress = AG_ADDRESS;
					i2cTransaction.writeBuf = txBuffer;
					i2cTransaction.writeCount = 1;
					i2cTransaction.readBuf = rxBuffer;
					i2cTransaction.readCount = 1;

					if (I2C_transfer(glb_i2c, &i2cTransaction))
					{
						if((ii == LSM9DS1_AG_ADDR_FIRST) || (ii == LSM9DS1_M_ADDR_FIRST))
						{
							glb_sensorExist = true;
							break;
						}
					}

					usleep(50000);
				}
				//////////////////////end of I2C sensor search process
				//set up timer here
			    //clock init, reference link is: coecsl.ece.illinois.edu/.../SYSBIOS_usersguide.pdf
			    Clock_Params clockParams;
			    Clock_Handle myClock;
			    Error_Block eb;
			    Error_init(&eb);
			    Clock_Params_init(&clockParams);
			    clockParams.period = DD_SYSTEM_GENERAL_TIMER_PERIOD;
			    clockParams.startFlag = TRUE;
			    clockParams.arg = (UArg)DD_TIMER_ARG;
			    myClock = Clock_create(DD_SystemGeneralTimerHandle, DD_CLOCK_TIMEOUT, &clockParams, &eb);	//one sensor reading sequence takes 15ms approximately. I arranged my timer for bigger than 200ms
			    if (myClock == NULL)
			    {
				  vErrorStateUpdte((uint32_t) __LINE__, (char *)__func__);
				  while(1){}
			    }
			#endif
		}
}

My SDK ver: 3.10.0.15

My ccs ver: 8.3.0.00009 

Thanks,

Dogus

  • Can you provide some more information about how the I2C handle and global parameters are passed?

    Thanks,
    Chris

  • Hi,

    Please consider blestack/OAD_Off_Chip example. I added the code below to the end of the "SimplePeripheral_init" function in the "simple_pheripheral_oad_offchip.c";

      /* Call driver init functions */
      Board_initGeneral();
    
      GPIO_init(); //used for whole gpio input and output
      I2C_init();
      SPI_init();
    
      /* Create I2C for usage */
      I2C_Params_init(&glb_i2cParams);
      glb_i2cParams.bitRate = I2C_400kHz;
      glb_i2c = I2C_open(Board_I2C_TMP, &glb_i2cParams);
      if (glb_i2c == NULL)
      {
          while(1){}
      }
    
    
      uint8_t rxBuffer[1];
      uint8_t LSM9DS1_AG_ADDR = GetAccelGyroAddressOfSelectedSensor(MASTER_DoF);
      uint8_t LSM9DS1_M_ADDR  = GetMagnetoAddressOfSelectedSensor(MASTER_DoF);
    
    
      if (ND_ReadBytes(&glb_i2c, LSM9DS1_AG_ADDR, WHO_AM_I_XG, rxBuffer, 1) == false)
      {
          while(1){}
      }
      else
      {
          if(rxBuffer[0] != (uint8_t)WHO_AM_I_AG_RSP)
          {
              while(1){}
          }
      }

    Also here are my global variables that are defined in "simple_pheripheral_oad_offchip.c";

    static int16_t gx = 0, gy = 0, gz = 0;
    static bool timerStarted = false;
    
    static I2C_Handle      glb_i2c;
    static I2C_Params      glb_i2cParams;

    Also I added the code below in the "SimplePeripheral_taskFxn" and just under the "SimplePeripheral_init();" line in "simple_pheripheral_oad_offchip.c";

      //clock init, reference link is: coecsl.ece.illinois.edu/.../SYSBIOS_usersguide.pdf
      Clock_Params clockParams;
      Clock_Handle myClock;
      Error_Block eb;
      Error_init(&eb);
      Clock_Params_init(&clockParams);
      clockParams.period = DD_SYSTEM_GENERAL_TIMER_PERIOD;
      clockParams.startFlag = TRUE;
      clockParams.arg = (UArg)DD_TIMER_ARG;
      myClock = Clock_create(DD_SystemGeneralTimerHandle, DD_CLOCK_TIMEOUT, &clockParams, &eb);
      if (myClock == NULL)
      {
          while(1){}
      }

    And here is my timer callback function below;

    static void DD_SystemGeneralTimerHandle(UArg arg)
    {
        GPIO_toggle(Board_GPIO_LED0);
        if(timerStarted == false)
            return;
        timerStarted = false;
    
        uint8_t LSM9DS1_AG_ADDR = GetAccelGyroAddressOfSelectedSensor(MASTER_DoF);
        uint8_t LSM9DS1_M_ADDR  = GetMagnetoAddressOfSelectedSensor(MASTER_DoF);
    
        uint8_t temp[6] = {0, 0, 0, 0, 0, 0}; // We'll read six bytes from the gyro into temp
        if(ND_ReadBytes(&glb_i2c, LSM9DS1_AG_ADDR, OUT_X_L_G, temp, 6) == false) // Read 6 bytes, beginning at OUT_X_L_G
        {
            while(1){}
        }
        else
        {
            (gx) = (int16_t)((temp[1] << 8) | temp[0]); // Store x-axis values into gx
            (gy) = (int16_t)((temp[3] << 8) | temp[2]); // Store y-axis values into gy
            (gz) = (int16_t)((temp[5] << 8) | temp[4]); // Store z-axis values into gz
        }
    }

    As you can see, int the timer callback, I2C communication started if "timerStarted" variable is true. I will make "timerStarted" variable true later. I will explain it.

    Also there is "ND_ReadBytes" function above and it is like that;

    bool ND_ReadBytes(I2C_Handle *i2c, uint8_t devaddress, uint8_t regaddress, uint8_t *buffer, uint8_t len)
    {
        uint8_t txBuffer[1];
        I2C_Transaction i2cTransaction;
    
        txBuffer[0] = regaddress;
        i2cTransaction.slaveAddress = devaddress;
        i2cTransaction.writeBuf = txBuffer;
        i2cTransaction.writeCount = 1;
        i2cTransaction.readBuf = buffer;
        i2cTransaction.readCount = len;
    
        return I2C_transfer((*i2c), &i2cTransaction);
    }

    And lastly, I just want to read sensor variables with user's wish. It means that, if a user send (let's say) 0xAA value via characteristic 1, then I make "timerStarted " variable true. By this way, successive sensor reading process can be started. By the way, I am sure that sensor reading time is not exceeding timer period. I just make "timerStarted" variable true just under the "SimplePeripheral_processCharValueChangeEvt" function's case "CUSTOM_PROFILE_CHAR1"

    With this method, "ND_ReadBytes" function returns false in the timer callback.

    If I do not make "timerStarted " variable true in the code place that I explained above and I just write here the following code (just like in the timer callback);

        uint8_t LSM9DS1_AG_ADDR = GetAccelGyroAddressOfSelectedSensor(MASTER_DoF);
        uint8_t LSM9DS1_M_ADDR  = GetMagnetoAddressOfSelectedSensor(MASTER_DoF);
    
        uint8_t temp[6] = {0, 0, 0, 0, 0, 0}; // We'll read six bytes from the gyro into temp
        if(ND_ReadBytes(&glb_i2c, LSM9DS1_AG_ADDR, OUT_X_L_G, temp, 6) == false) // Read 6 bytes, beginning at OUT_X_L_G
        {
            while(1){}
        }
        else
        {
            (gx) = (int16_t)((temp[1] << 8) | temp[0]); // Store x-axis values into gx
            (gy) = (int16_t)((temp[3] << 8) | temp[2]); // Store y-axis values into gy
            (gz) = (int16_t)((temp[5] << 8) | temp[4]); // Store z-axis values into gz
        }

    then the "ND_ReadBytes" function returns true.

    Thanks,

    Dogus

  • Sorry for the delay. A couple of us thought the other person was handling this thread. Someone will reach out soon.

  • Hi Dogus,

    I have a few suggestions that may fix the issue:

    1) In your clock callback function you declare your I2C_Transaction and buffers as local stack variables. Instead you should declare then as global static variables or declare them in your thread and pass them to the callback via the arg parameter. The reason for this is that these variables must be persistent until the i2c_transfer call finishes, and right now they are likely to be overwritten because Swis and Hwis share the same stack.

    2) I2C opens in blocking mode by default which means that a call to I2C_transfer will block until the requested operation is complete. You are calling I2C_transfer from a clock callback function (DD_SystemGeneralTimerHandle) which runs in Swi or Hwi context. It is not allowed to call blocking functions in Swi or Hwi contexts, this may result in undefined behavior. Instead, you should open the I2C in callback mode which will allow you to make I2C_transfer calls from your clock callback function safely.

    Here is some pseudo code to demonstrate what I mean:

    // Declare static variables
    I2C_Transaction i2cTrans;
    uint8_t readBuffer[BUFLEN];
    uint8_t writeBuffer[BUFLEN];
    
    // I2C callback is called when the I2C transfer is complete
    myI2CCallback(I2C_Handle handle, I2C_Transaction *msg, bool status)
    {
        if (status) {
            // Store received data etc...
            someGlobalVar0 = readBuffer[0];
            someGlobalVar1 = readBuffer[1];
    
            // Maybe post a semaphore here to notify someone of new data
            Semaphore_post(semHandle);
        }
        else {
            // Some error handler
            logError();
        }
    }
    
    // Your clock callback function
    myClockCallback(UArg arg)
    {
        // Timer went off, start an i2c transaction
        i2cTrans.writeBuf = &writeBuffer;
        i2cTrans.writeCount = BUFLEN;
        i2cTrans.readBuf = &readBuffer;
        i2cTrans.readCount = BUFLEN;
        i2cTrans.slaveAddress = SLAVE_ADDRESS;
        // This call will not block because we set I2C_Params.transferMode = I2C_MODE_CALLBACK at I2C_open()
        I2C_transfer(i2c, &i2cTrans);
    }

    Hope that helps, let me know if you have further questions!

  • Hi,

    I will convert my structure as you describe. 

    Thanks for your reply.

    Dogus