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/CC2640R2F: Problem using UART and I2C simultaneously

Part Number: CC2640R2F

Tool/software: TI-RTOS

I am having an issue trying to perform multiple I2C transfers while also using the UART in a build derived from the simple peripheral example in simplelink_cc2640r2_sdk_1_30_00_25. An additional task was added that interacts with an external piece of HW via TI UART driver. Meanwhile, the main app task is using the TI I2C driver to periodically perform multiple I2C devices. Both the UART and I2C accesses work fine individually. However, a problem occurs when the UART performs a blocking read in the one task. If there is a switch to the app task to perform I2C transfers, only the first transfer succeeds. All subsequent accesses fail until the UART is closed. The way the code is currently written, each I2C_transfer() is enclosed within an I2C_open() & I2C_close() pair. This problem persists until UART_read either completes or times-out, at which point the first I2C access will succeed, but any that immediately follow fail. This condition repeats (with sufficient time between the multiple transfer set) as long as the UART is open. If I add a small amount of delay (3 msec) between each I2C transfer, they will all succeed. When the UART is closed, all I2C transfers succeed with or without delay between them. This problem occurs with different types of I2C devices, so I do not think it has anything to do with a particular slave device.

I started logging the MSTAT register after I2C_open and before I2C_close, and found that for transfers that succeeded, MSTAT == 0x20 at open and close. For the transfers that fail, MSTAT == 0x60 at open and close. So for some reason the BUSBSY bit is stuck ON. I tried writing a 0 to MCTRL after I2C_open() to see if that any effect, but it did not. Based on another forum thread, I also tried temporarily disabling standby and idle while performing the transfers, but that did not seem to have an effect either (especially standby, since that was disabled by the UART read anyway)..

Any ideas? Is this an expected result based on underlying CC26xx HW interactions that I missed?

Thanks for your help.

dave

  • Hi Dave,

    I will have to dive deeper into this, I'll come back with an answer as soon as possible!

    In the mean while, could you maybe provide some code snippets of the two tasks where you setup and perform the I2C and UART operations?
  • Hi M-W,

    Thanks for helping me look into this. Here's how I'm using the device drivers (only the app task is using the I2C driver currently; obviously in this example I do not need to open and close the I2C repeatedly, but there are circumstances where I will need to do that):

    main app task:

    bool I2CCommand(uint8_t address, const void *txbuf, uint8_t txlen, void *rxbuf, uint8_t rxlen)

    {

        I2C_Params i2c_params;

        I2C_Params_init(&i2c_params);

        i2c_params.bitRate = I2C_400kHz;

        I2C_Handle i2c = I2C_open(Board_I2C, i2c_params);

        if (i2c == NULL)

        {

            System_printf("I2CCommand: Error Initializing I2C\n");

            return FALSE;

        }

        System_printf("OPEN MSTAT=%x\n",HWREG(I2C0_BASE + I2C_O_MSTAT));

        I2C_Transaction i2c_transaction;

        i2c_transaction.slaveAddress = address;

        i2c_transaction.writeBuf = (void *)txbuf;

        i2c_transaction.writeCount = txlen;

        i2c_transaction.readBuf = rxbuf;

        i2c_transaction.readCount = rxlen;

     

        bool rval = I2C_transfer(i2c, &i2c_transaction);

     

        System_printf("CLOSE MSTAT=%x\n",HWREG(I2C0_BASE + I2C_O_MSTAT));

        I2C_close(i2c);

     

        return rval;

    }

    int16_t GetMeasurement(void)

    {

        uint8_t tx_buffer[2];

        int8_t real;

        // wake device

        tx_buffer[0] = 0x01; // register 0x01

        tx_buffer[1] = 0x00;

        bool i2c_stat = I2CCommand(&board_i2c_pins, I2CADDR, tx_buffer, 2, NULL, 0);

        DELAY_MS(150);

        // read value

        tx_buffer[0] = 0x00; // register 0x00

        i2c_stat &= I2CCommand(&board_i2c_pins, I2CADDR, tx_buffer, 1, &rval, 1);

        // shutdown device

        tx_buffer[0] = 0x01; // register 0x01

        tx_buffer[1] = 0x01;

        i2c_stat &= I2CCommand(&board_i2c_pins, I2CADDR, tx_buffer, 2, NULL, 0);

        return rval;

    }

    secondary interface task:

    UART_Params uparams;

    UART_Handle uhandle = NULL;

    UART_Params_init(&uparams);

    uparams.writeDataMode = UART_DATA_BINARY;

    uparams.readDataMode  = UART_DATA_BINARY;

    uparams.baudRate = 115200;

    uparams.readTimeout = TICKS_PER_MS(200); // in ticks

    uhandle = UART_open(Board_UART0, uparams);

    .

    .

    .

    // UART_read gets call many times because of the timeout; the data read in buf is used

    // to determine when to stop reading. The multiple breaks also allow switching to the

    // app task, so the I2C transactions will occur randomly during the UART_reads.

    int len = UART_read(uhandle, buf, resize);

    UARTCC26XX_Object *object = uhandle->object;

    System_printf("rdst=%02x, len=%d", object->status, len);

    .

    .

    .

    UART_close(uhandle);

    Please let me know if I can provide any other information.

    dave

  • Hi Dave,

    There should be no hidden HW interactions between the UART and I2C as far as I know. When you have MSTAT = 0x60, what is the status of the MCTRL register?

  • Hi M-W,

    OK, thanks for the update. I did want to provide some additional information anyway. For the I2C, .sdaPin = IOID_13 and .sclPin = IOID14. The UART is using HW flow control, with .txPin = IOID_16, .rxPin = IOID_15, .ctsPin = IOID_17, and .rtsPin = IOID_18.

    How do I check the "status" of the MCTRL register? It is write-only, correct? (If I try to read it after reading MSTAT, I just get the same value as MSTAT).

    BTW, here is an example of the problem occurring during those 3 I2C transfers above:

    [UART in blocking read, first attempt to access I2C device]

    OPEN MSTAT=20

    I2C_transfer return: 1

    CLOSE MSTAT=20

    OPEN MSTAT=60

    I2C_transfer return: 0

    CLOSE MSTAT=60

    OPEN MSTAT=60

    I2C_transfer return: 0

    CLOSE MSTAT=60

    dave

  • Hi Dave,

    My bad, I forgot that the MCTRL register is write only. I will try to put a test together and see if I can reproduce this problem, I'll get back to you when I have this setup.
  • Hi Dave,

    I have now setup a test using the empty project where I did the following:
    * Created two tasks, one for I2C and one for UART.
    * Using your UART setup, I perform a blocking UART read inside a while(1) loop in the UART task (never closing the uart).
    * Using your I2C setup I perform open -> read -> close operations in a while(1) loop.

    I'm unable to see the problem you are experiencing as all my I2C transfers are successful.

    Copying your code I noticed you are not passing the parameters as pointers to the I2C and UART open function. Based on the snippets above you should be passing them as such:
    UART_open(Board_UART0, &uparams);
    I2C_open(Board_I2C, &i2c_params)

    What are priorities of the two tasks you are using?
  • Hi M-W,

    Sorry, that is just a typo; I am passing the a pointer to the parameter structure.

    Currently the main app task is priority == 2 and the interface task with the UART is priority == 1.

    In your test, are you performing a single blocking read that never completes and/or have you verified that multiple I2C open-transfer-close sets are occurring while that read is active? Just want to confirm that the conditions are the same. Something like this:

    task P=1: blocking UART_read()

    [idle function*, task switch to task P=2]

    task P=2: I2C-open-transfer-close-1, I2C-open-transfer-close-2, I2C-open-transfer-close-3

    [idle function, task switch to task P=1]

    task P=1: UART_read completes with data or timeout or still blocks

    [idle function, task switch to task P=2] etc.

    I have not had time, but probably should add my own test to the v1.30.00.25 simple peripheral example this was derived from (but heavily modified since). As described earlier, I could actually reproduce this from within a single task by just opening the UART with the main task and performing the I2C transactions (obviously the other task was unable to function because it could not open the UART). Same symptoms--first transaction succeeds, others fail with stuck bus busy bit--but in that case, I was able to fix the problem by adding 3ms delay between I2C_close and I2C_open calls.

    dave

  • *Oops, forgot to say that there is an added idle function that resets the watchdog and logs itself so that I can tell when it is being called. I do not think it matters, but wanted to mention it anyway.
  • Hi Dave,

    I can verify that the sequence I'm running are the same including the multiple open-close calls:
    task P=1: blocking UART_read()

    [idle function*, task switch to task P=2]

    task P=2: I2C-open-transfer-close-1, I2C-open-transfer-close-2, I2C-open-transfer-close-3 etc (it does this forever communicating with a I2C slave)

    [idle function, task switch to task P=1]

    task P=1: UART_read completes with data or timeout or still blocks

    [idle function, task switch to task P=2] etc.

    I have tried with different task priority and with the same priority. Same result.

    Could you try lowering your app main task down to priority 1 and see if it makes a difference?
    Also, could you try recreating this using the "empty" example?
  • Hi M-W,

    OK, I will try to recreating it in a Simple Peripheral example.

    dave

  • Hi M-W,

    Like you, I was unable to recreate the problem within the SP example, so I must have goofed up something somewhere in my development. I will have to dig deeper into an older build to see if I can figure out where I went wrong. Thanks for your help anyway.

    dave
  • Good luck with figuring it out.

    I will close this thread for now. If you are able to solve your problem, or if any other problem pops up, feel free to post about it here or create a new forum post about it!
  • Hi M-W,

    OK, the reason why the modified version of the SP example (with added periodic I2C transfers in simple_peripheral.c and new simple task that opens the UART and does repeated blocking reads with 3 second timeout) worked was because I (and perhaps you) was using the default  I2C and UART pins for the CC2640R2F LaunchPad board, i.e.:

    SDA = IOID_5

    SCL = IOID_4

    UART_TX = IOID_3

    UART_RX = IOID_2

    I found this still worked with my regular SW compiled to run on that platform. However, we are using a custom development board that in practice uses a different set of pins, specifically:

    SDA = IOID_13

    SCL = IOID_14

    UART_TX = IOID_3

    UART_RX = IOID_4

    That set fails in the way I described even for the modified SP example on the LaunchPad. (On the LP board, this combination works because pin 4 is still pulled high for I2C use, and pins 13 & 14 are tied to the left and right buttons, which are open by default. External pull-ups are required, but the cheap little luma sensor board I have for testing has them built onto it.)

    Can you please try this combination of pins with your setup and let me know how it goes? I can send you my modified SP example source code, if that would help.

    dave

  • I could try it out if you sent me your SP example project. I think it only makes sense to try it on a confirmed problematic project instead of trying to reproduce it myself :)
  • Hi M-W,

    OK, here it is. Sorry for the size. To make sure, I have included everything without doing a clean first, including the BLE Stack library, so that you would have everything I have. This code also includes a bit-bang debug logging UART tied to System_printf via app_ble.cfg. It is outputting at 921600 BAUD to pin DIO29 (which I jumpered to the debugger side of TXD). This makes it easier to follow what is happening (and I verified that it had no effect on the outcome by disabling it completely). I also indicate the I2C transfer status with the green and red LEDs (green on initially, and stays on unless there is a I2C transfer failure, in which case it switches to red on). The secondary task loops performing 3 second timeout blocking reads. I hijacked the 5 second periodic SimpleBLEPeripheral_performPeriodicTask function (which is now enabled immediately during init) to perform an I2C measurement.

     For HW, I used a CC2640R2F LaunchPad with nothing connected to it except jumpered debug UART and one of these: www.amazon.com/.../B06XHKTL58 connected to it with SCL on DIO14 and SDA on DIO13. Nothing is connected to the designated UART pins DIO4 and DIO3. This is just for testing purposes. I observed the problem on our custom board with different I2C devices, so I would not expect whatever you might have to matter.

    There is a #if 1/0 in the secondary task in main.c to easily switch between having the UART open or closed. For completeness, I also included uart_open.hex and uart_close.hex in the FlashROM_StackLibrary directory.

    Thanks for trying this. Please let me know if there is anything else I can provide or if you have any questions.

    dave

    simple_peripheral_cc2640r2lp_uart_i2c_issue.zip

  • Hi Dave,

    I'm unable to try your project out as it contains a lot of linked resources with changes that I don't have locally (such as your board file).

    When you exported the workspace, did you check the "Resolve and export linked resources" option?
    File -> Export -> General -> Archive File -> Check "Resolve and export linked resources" -> Finish
  • OK, here it is exported with that checked. BTW, all I did was copied a local version of CC2640R2_LAUNCHXL.c into the Startup directory. The only mods to it are changing the 2 sets of pins per the earlier discussion.

    dave

    simple_peripheral_cc2640r2lp_uart_i2c_issue_2.zip

  • Hi M-W,

    Any updates? Did you get a chance to try this test yet?

    Thanks!

    dave

  • Hi Dave,

    I've been a little busy but I'm working on it. I'm at this moment having some trouble running it at all and I'm trying to figure out why this is.
    I will get back as soon as I have any results on this.
  • Hi Dave,

    I've been able to reproduce the problem you are having and what is causing it. The problem is due to the pins you are using are defined to have a default state (they have a state defined inside CC2640R2_LAUNCHXL.c::BoardGpioInitTable) which causes an error on the I2C bus inside I2C_close. This as the PIN handled are closed and the pins are reverted to the default value before the I2C is disable.

    The reason you are seeing this problem only when UART is active as the UART keeps the power domain on, when UART is not active, the power driver is allowed to disable the power domain in between transactions which effectively causes a hard reset of the I2C module. If the power domain is not turned of, the state of the module is retained.

    This is a bug in the I2C driver and you should be able to work around it by a small change in the I2C driver:
    * Inside the I2CCC26XX.c::I2CCC26XX_close() - Move the PIN_close() statement to after the I2C Master has been disabled.
  • Fixed!

    Thanks, M-W!

    dave