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/CC2650: I2C bus usage in Hwi/Swi context issue

Part Number: CC2650
Other Parts Discussed in Thread: Z-STACK, PCA9534A, PCA9534,

Tool/software: TI-RTOS

Hi,

I'm using a custom board which contains CC2650MODA module running Z-Stack 1.2.2a (bios_6_41_02_41 , driverlib cc26xxware_2_20_06_14829,  xdctools_3_30_06_67). I'm trying to communicate to TI PCA9534A 8-bit I2C IO-expander. My custom PCA9534 device driver uses bsp_i2c module located in \tirtos_simplelink_2_11_01_09\packages\ti\boards\SensorTag\Interfaces respectively the sensor sw-module implementation which comes with CC2650 z-stack demo code. sensor.h/.c is just a wrapper for bsp_i2c.h/.c.

The communication runs perfectly as long as I call communication functions in task context, as soon as I'll try to read a registers in Hwi/Swi context readIOs() returns false. The Hwi is configured dynamically during PCA9534 initialization. Register read in Hwi context is triggered by PCA8534 interrupt pin which is connected to CC2650MODA module. As soon as PCA9534 drives a falling edge GPIO_InterruptISR(...) is called, but sensorReadReg(...) (which is just a wrapper for bspI2cWriteRead() always returns false.

I already checked if I2C frames are sent/received as it should be, and physical communication shows the right request/respons frame on I2C for Hwi/Swi based calls and for frequently calls in task context. So the issue has to be located somewhere in TI-RTOS context

BTW.: IAR Embedded Workbench IDE ARM v7.70.2 is used.

#include "bsp_i2c.h"
#include "sensor.h"
#include "expander_pca9534a.h"
#include "Board.h"
#include <stddef.h>
#include <ti/sysbios/family/arm/m3/Hwi.h>
#include <xdc/cfg/global.h>
#include <ti/drivers/pin/PINCC26xx.h>
#include <ti/sysbios/knl/Swi.h>

/* ------------------------------------------------------------------------------------------------
*                                           Constants
* ------------------------------------------------------------------------------------------------
*/

/* Slave address */
#define DEVICE_I2C_ADDRESS              0x38

/* Register addresses */
#define REG_INPUT												0x00
#define REG_OUTPUT											0x01
#define REG_POL_INVERSION								0x02
#define REG_CONFIGURATION								0x03	

#define REG_LENGTH											0x01	// register length

#define DEVICE_SELECT()                 bspI2cSelect(BSP_I2C_INTERFACE_0,DEVICE_I2C_ADDRESS)
#define DEVICE_DESELECT()               bspI2cDeselect()

static uint8_t gIoConfig = 0;
static uint8_t gInputRegVal = 0x00;
static bool gInit = false;


/* interrupt pin */
PIN_Handle interruptPinHandle;
static PIN_State interruptPinState;
static PIN_Config interruptPinTable[] =
{
    Board_INTERRUPT_IN | PIN_INPUT_EN | PIN_NOPULL | PIN_IRQ_NEGEDGE | PIN_HYSTERESIS, // expander interrupt pin
    PIN_TERMINATE /* Terminate list */
};

/* ------------------------------------------------------------------------------------------------
*                                         Local Functions
* ------------------------------------------------------------------------------------------------
*/

/* ------------------------------------------------------------------------------------------------
*                                         Public Functions
* ------------------------------------------------------------------------------------------------
*/

// Hwi
void GPIO_InterruptISR(PIN_Handle interruptPinHandle, PIN_Id interruptPinId){
	Swi_post(swiExpanderInterrupt);
}

// Swi
void interruptStatusSwi(){
	uint8_t readVal = 0;
	if(!readIOs(&readVal)){
// here we return :/ return; }
// some stuff to do } // initialization bool setIOCfg(eBoardID const config){ bool success = false; uint8_t cfg; uint8_t buf; // ... some initialization // // Hwi config interruptPinHandle = PIN_open(&interruptPinState, interruptPinTable); if(interruptPinHandle == NULL){ return false; } if (PIN_registerIntCb(interruptPinHandle, GPIO_InterruptISR) != PIN_SUCCESS){ PIN_close(interruptPinHandle); return false; } } gInit = success; return success; } bool getIOCfg(uint8_t * const data){ if(data == NULL){ return false; } bool success; DEVICE_SELECT(); success = sensorReadReg(REG_INPUT, data, REG_LENGTH); DEVICE_DESELECT(); *data = ~(*data); // return 1 for input, 0 for output -> inverted is more intuitive. return success; } #pragma optimize=none bool readIOs(uint8_t * data){ if(data == NULL){ return false; } bool success; if(!DEVICE_SELECT()){ return false; } success = sensorReadReg(REG_INPUT, data, REG_LENGTH);// wrapper for bspI2cWriteRead(...); DEVICE_DESELECT(); *data = *data & gIoConfig; // mask out output pins. return success; } bool writeIOs(uint8_t const data, uint8_t const mask){ // read current values bool success; uint8_t readReg; uint8_t writeReg = data; // read current output register DEVICE_SELECT(); success = sensorReadReg(REG_OUTPUT, &readReg, REG_LENGTH); maskOutValues(readReg, mask, &writeReg); writeReg &= ~gIoConfig; // mask out all inputs success &= sensorWriteReg(REG_OUTPUT, &writeReg, REG_LENGTH); DEVICE_DESELECT(); return success; }
static swi initialization in config file 

/* ================= SWI configuration ================ */
 var expanderInterruptParams = new Swi.Params();
 expanderInterruptParams.arg0 = 1;
 expanderInterruptParams.arg1 = 0;
 expanderInterruptParams.priority = 1;
 expanderInterruptParams.trigger = 0;
 Program.global.swiExpanderInterrupt = Swi.create("&interruptStatusSwi", expanderInterruptParams);

It doesn't matter if I call register read in Hwi or Swi context, both return false.

My question is, is there any configuration of Hwi/Swi I missed? Are there any preconditions I have to take into account, so I can use I2C communication in interrupt context?

  • Hi Robert,

    Have you tried increasing the size of your stack?
  • Indeed, that was also the first point I thought of. As far as I know Hwi/Swi use its own common interrupt stack, whereas I couldn't find any option to specifically increase Hwi stack size in IAR workbench linker configuration. So I hoped the symbol STACK_SIZE in linker .icf file does the trick. I increased it from originally 1024 byte to 1536 byte, unfortunately I see the same behaviour. 

    ICF-file:

    ////////////////////////////////////////////////////////////////////////////////
    // Memory Sizes
    ////////////////////////////////////////////////////////////////////////////////
    
    if ( isdefinedsymbol(CC2630) ||
         isdefinedsymbol(CC2650) )
    {
      define symbol RAM_SIZE            = 0x00005000;  // 20K
      define symbol FLASH_SIZE          = 0x00020000;  // 128K
      define symbol ROM_SIZE            = 0x0001C000;  // 115K
    }
    ////////////////////////////////////////////////////////////////////////////////
    // Default
    else
    {
      define symbol RAM_SIZE            = 0x00004000;  // 16K
      define symbol FLASH_SIZE          = 0x00020000;  // 128K
      define symbol ROM_SIZE            = 0x00018000;  // 96K
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // Memory Definitions
    ////////////////////////////////////////////////////////////////////////////////
    
    ////////////////////////////////////////////////////////////////////////////////
    // RAM
    //
    
    define symbol RAM_START             = 0x20000000;
    
    if ( isdefinedsymbol(CC2650) )
    {
      if ( isdefinedsymbol(ICALL_RAM0_ADDR) )
      {
        define symbol RAM_END           = (ICALL_RAM0_ADDR-1);
      }
      else // default
      {
        define symbol RAM_END           = 0x200037FF;
      }
    }
    else // CC2650 PG1
    {
      if ( isdefinedsymbol(ICALL_RAM0_ADDR) )
      {
        define symbol RAM_END           = (ICALL_RAM0_ADDR-1);
      }
      else // default
      {
        define symbol RAM_END             = 0x200029FF;
      }
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // Flash
    //
    
    define symbol FLASH_START           = 0x00000000;
    
    if ( isdefinedsymbol(ICALL_STACK0_ADDR) )
    {
      define symbol FLASH_END           = (ICALL_STACK0_ADDR-1);
    }
    else // default
    {
      define symbol FLASH_END           = 0x00007FFF;
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // Stack
    //
    
    define symbol STACK_SIZE           = 0x600;
    define symbol STACK_START          = RAM_END + 1;
    define symbol STACK_END            = STACK_START - STACK_SIZE;
    define block CSTACK with alignment = 8, size = STACK_SIZE { section .stack };
    //
    define symbol STACK_TOP            = RAM_END + 1;
    export symbol STACK_TOP;
    
    ////////////////////////////////////////////////////////////////////////////////
    // Heap
    //
    
    define symbol HEAP_SIZE            = 0x800;
    define block  HEAP with alignment  = 8, size = HEAP_SIZE { };
    
    ////////////////////////////////////////////////////////////////////////////////
    // Flash Interrupt Vector Table
    //
    
    define symbol INTVEC_NUM_ENTRIES   = 50 + 1; // first entry is stack location
    define symbol INTVEC_SIZE          = INTVEC_NUM_ENTRIES + 4;
    
    ////////////////////////////////////////////////////////////////////////////////
    // Customer Configuration Area (CCA)
    //
    
    define symbol CCA_NUM_ENTRIES      = 21;
    define symbol CCA_SIZE             = (CCA_NUM_ENTRIES * 4);
    define symbol CCA_START            = (FLASH_SIZE-1) - CCA_SIZE + 1;
    define symbol CCA_END              = (FLASH_SIZE-1);
    
    ////////////////////////////////////////////////////////////////////////////////
    // Memory Regions
    ////////////////////////////////////////////////////////////////////////////////
    
    define memory mem with size = 4G;
    
    define region RAM        = mem:[from RAM_START   to RAM_END];
    define region FLASH      = mem:[from FLASH_START to FLASH_END];
    define region FLASH_CCA  = mem:[from CCA_START   to CCA_END];
    
    ////////////////////////////////////////////////////////////////////////////////
    // Memory Placement
    ////////////////////////////////////////////////////////////////////////////////
    
    // CCA
    place at start of FLASH_CCA { readonly section .ccfg };
    keep                        { readonly section .ccfg};
    
    // Interrupt Vector Table
    place at address mem:FLASH_START { readonly section .intvec };
    keep                             { readonly section .intvec };
    
    // RAM Vector Table
    place at start of RAM { section .vtable_ram };
    
    // Stack
    place at end of RAM { block CSTACK };
    
    // Heap
    place in RAM { block HEAP };
    
    place in FLASH { readonly };
    place in RAM   { readwrite };
    
    ////////////////////////////////////////////////////////////////////////////////
    // Initialization
    ////////////////////////////////////////////////////////////////////////////////
    
    initialize by copy { readwrite };
    
    do not initialize
    {
      section .noinit,
      section .stack,
    };
    

    Is there any way to see Hwi stack usage in IAR workbench? Unfortunately I'm just able to monitor statically created stacks from iCall task, (my)main task and idle task, but no interrupt.

    As far as I know option

    /* main() and Hwi, Swi stack size */
    Program.stack = 1024; 

    in app.cfg is ignored by IAR compiler/linker.

  • Hi Robert,

    I looked into this more and it seems that you cannot make blocking function calls (like this i2c driver makes) inside a HWI or a SWI with TI-RTOS, you only can within a task context.

    If you still want some sort of notification with an interrupt context, I would recommend posting to a task semaphore within this SWI (or a HWI).

  • Thanks Jason for clarification. I found documentation describing this non-blocking limitation for SWIs in kernel user guide meanwhile. I'll follow your advice for using a task controlled by semaphores posted by hwi routine.

    BR

    Robert