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: Semaphore / Event multiple pend

Part Number: CC2640R2F

Tool/software: TI-RTOS

Hello everyone,

I am using CC2640R2 Launchpad to develop a BLE application for a custom board. The board will integrate a few I2C and SPI sensors.

I would like to discuss my approach to understand if it's reasonable.

I started from SimpleBlePeripheral application. I added 3 services (one for each sensor) and I can actually see them on my phone, along with all the characteristics. I'm thinking to add one task for each sensor which will be in charge of collecting data and process them. Then, the original SimpleBLEPeripheral task will be in charge of transmitting processed data to the smartphone. Hence, just one task will be registered with ICall.

This choice poses a few problems:

  1. I need to initialize I2C and SPI drivers and all the pins required for interrupts: I'm currently doing it in the ICall enabled task. Once that's done, I post a semaphore (lets' call it SEM1) to let the pther tasks initialize the sensors.
  2. I2C and SPI need to be shared among all tasks (except ICALL enabled task). How do I proceed here? My first thought is a semaphore (let's call it SEM2 for I2C and SEM3 for SPI). At this point al the sensors tasks should pend on at least 2 semaphores: is this possible? I haven't found any clue on either TI-RTOS manual or Semaphore API.
  3. One specific sensor (MAXIM MAX30102, it's a oulso-oxymeter) poses a further problem. It can issue many interrupts on the same pin. In particular I am interested in the fo,llowing: A) when a given threshold is exceeded (meaning the finger is on the sensor and the acquisition can start) and B) when the sensor FIFO is almost FULL and a I2C read must be performed to continue with the acquisition. Here, I am using a fourth semaphore (SEM4) to pend first on the proximity interrupt. Then, after disabling the proximity interrupt and enabling the fifo almost full interrupt,  inside a loop I pend again on SEM4 to collect the required number of samples. Again, is this reasonable?

I found the Event module which seems to be appropriate for my situation. However, I am not sure how to proceed with the MAX30102 double interrupt. Is it possible to Event_pend twice in a task?

Best regards,

Jack

  • I tried to remove all the BLE stuff by plugging my code for the I2C sensor into an empty project. basically, it's a single task with a HWI ISR. I did that to try out the Event module. Well, the interrupt of my sensor works great when I remove Event_pend and Event_post calls. As soon as I step on those, the task hangs. Any idea of what's going on here?
  • Hi Giacomo,

    Your initial approach with handling the sensors in their own task makes sense.

    1) Is there a particular reason that you need to initialize all the drivers in the BLE task and not in the task related to that particular driver? For example, can't the I2C task handle the I2C (and related pins) initialization? If you would do it in the "BLE Task" and then post a semaphore, I suggest using a counting one that allows you trigger multiple pending tasks. 

    2) I assume one senor only uses one of the two interfaces? In that case you should be able to use a semaphore approach as suggested. I don't see why you would need to pend on two semaphores as there should be one / interface.

    You can not pend on two semaphores at once, you would have to do two back-to-back pends.

    3) Do you mean that you would re-use the semaphore for both the interrupts? I would say this is reasonable but it all depends on how you implement the logic in practice.

    The event module could very well be appropriate in your situation, depending on what you mean with pending twice in a task. There is a limitation to one task at a time that can pend on a particular event handle.

    There is a good example on how you can use the event module found in the BIOS user guide, this could potentially help you with you hanging task:

    dev.ti.com/.../Bios_User_Guide.pdf

  • Hi M-W,

    1) I haven't initialized the drivers in the sensor tasks because more than one task needs the same driver: where should I put the initialization code than to make sure it executes before anything else? I suppose i could place it in main, before BIOS_start().

    2) You're correct. However, I can imagine at least 2 semaphores: one to manage I2C driver access between tasks and one (for each task) to let the task know sensors ISR triggered.

    What do you mean by back-to-back pends?

    3) I thought about using one semaphore for each interrupt because there is one-to-one relation between ISR and sensor tasks.

    Best regards,
    Jack
  • Just a clarification: by "pending on 2 semaphore at once" you mean:

    1) make 2 calls to semaphore pend, with different semaphore objects, in the same task, one after the other

    2) make 2 calls to semaphore pend, with the same semaphore object, in the same task, one after the other

    3) make one call to senaphore pend which returns if 2 different semaphore objects are greater than 0 at the same time

    I understand that 3) is not possible. What I ideally would need is 1). Can you confirm that's not supported? I implemented it to make sure sensors tasks loops start after driver init functions AND to switch context from ISR to tasks when ISR are received. Moreover, as explained in the first post, MAX30102 sensor task needs to pend FIRSY on proximity interrupt to make sure the finger is touching the sensor and ONLY THEN loop to collect the required number of samples from fifo

    Best regards,

    Jack

  • Hi Jack,

    I was leaning more towards 3) when writing my answer. Both 1) and 2) is possible but you would still only be pending on one of the semaphores at a time.

    Regarding the need for two semaphores, I miss-interpret the initial reason for it, using one semaphore for the driver and one for the sensor (to post for ISR) makes sense.

    If you have multiple task using one driver then it could be a good idea to make the initialization in the first task that runs (as you already do). Don't do the initialization (if this includes calling any other API function other then "init") in the main function as this in many cases could crash the RTOS as it is not yet started (and many driver depends on this).
  • Hi M-W,

    Then I really surprised I'm finding so many problems.

    I attach a diagram of the structure of my code (sensor 2 - task 2 - isr 2 is not operational yet but clearly this diagram has to scale well with many sensors).

    As you can see, I shouldn't need to pend on multiple semaphore objects AT THE SAME TIME. I only need to be able to pend on the same and/or different semaphore objects ONE AFTER THE OTHER. It is acceptable to wait the I2C bus is free after having received the interrupt from the ISR. Sensors are slow with respect to I2C and SPI.

    To read from sensor 1 (MAX30102) I am using a driver I wrote using TI I2C driver. The same driver has been used to read from the sensor using empty project as a start. In this case, I just created a task and an HW interrupt. A semaphore is posted in the ISR and pended in the task. As explained previously, in the task I first pend once for understanding when the user place its finger on the sensor; then, I pend repeatedly in a loop to gather enough data. I need 1000 and had no problem in collecting them all within expected time.

    Of all that fails when the code is plugged in SimpleBLEPeripheral. Collection of 1000 samples never finishes. I ruled out tasks stack overflow (both BLE task and sensor task) by looking at memory occupation during a debug session.

    I'm willing to share the relevant files of both projects, maybe you guys can see something I'm missing. 

    Best regards,

    Jack

  • Hi Jack,

    If you share the code I will take look at it and see if anything sticks out.
  • Hi M-W,

    I believe I found the cause of the problem I was facing: it's related to the proximity functionality of the sensor.

    However, I'm now facing an other problem related to events and semaphores.

    What I would like to achieve is the following:

    1. when sensor task finishes the acquisition, I post an event to be received by SimplePeripheral main task. This is the function I am using:
    static uint8_t SimplePeripheral_enqueueMsg(uint8_t event, uint8_t state,
                                               uint8_t *pData)
    {
      // Allocate space for the message.
      sbpEvt_t *pMsg = ICall_malloc(sizeof(sbpEvt_t));
    
      // Create dynamic pointer to message.
      if (pMsg)
      {
        pMsg->hdr.event = event;
        pMsg->hdr.state = state;
        pMsg->pData = pData;
    
        // Allocated space for queue node.
        queueRec_t *pRec = ICall_malloc(sizeof(queueRec_t));
    
        if (pRec)
        {
          pRec->pData = (uint8_t *) pMsg;
    
          // Enqueue the message. This is an atomic operation
          Queue_put(appMsgQueue, &pRec->_elem);
    
          // Wake up the application thread event handler.
          if (syncEvent)
          {
            Event_post(syncEvent, SBP_QUEUE_EVT);
          }
    
          return TRUE;
        }
    
        // Free the message.
        ICall_free(pMsg);
    
        return FALSE;
      }
    
      return FALSE;
    }

    and the call:

    SimplePeripheral_enqueueMsg(SBP_MAX30102_DATA_READY, 0, NULL);

    where SBP_MAX30102_DATA_READY is a redefinition of Event_Id_05.

    2. SimplePeripheral task receives the event correctly and 

    SimplePeripheral_processAppMsg(sbpEvt_t *pMsg)

    is called. In this function, I added a new case (SBP_MAX30102_DATA_READY ) to the switch where I call the the function to update the characteristics of the service related to the sensor. The case newly added never gets triggered because pMsg->hdr.event is not equal to SBP_MAX30102_DATA_READY.

    What is happeing here? Am I not allowed to add new events? What is the recommended way of achieving this?

    Best regards,

    Jack

  • Hi Jack,

    You should be able to add additional events, if the pMsg is not what you expect, what is the value of it then? Also, what SDK version are you using?
  • HI M-W,

    I'm using SimpleLink CC2640R2 SDK - v:2.20.00.49. I'm importing SimplePeripheral app and stack from Resource Explorer. CCSv8 and compiler TI v18.1.2.LTS.

    The values I see there are actually pretty weird. This is the function I'm talking about:

    static void SimplePeripheral_processAppMsg(sbpEvt_t *pMsg)
    {
      switch (pMsg->hdr.event)
      {
        case SBP_STATE_CHANGE_EVT:
          {
            SimplePeripheral_processStateChangeEvt((gaprole_States_t)pMsg->
                                                    hdr.state);
          }
          break;
    
        case SBP_CHAR_CHANGE_EVT:
          {
            SimplePeripheral_processCharValueChangeEvt(pMsg->hdr.state);
          }
          break;
    
        // Pairing event
        case SBP_PAIRING_STATE_EVT:
          {
            SimplePeripheral_processPairState(pMsg->hdr.state, *pMsg->pData);
    
            ICall_free(pMsg->pData);
            break;
          }
    
        // Passcode event
        case SBP_PASSCODE_NEEDED_EVT:
          {
            SimplePeripheral_processPasscode(*pMsg->pData);
    
            ICall_free(pMsg->pData);
            break;
          }
    
    	case SBP_CONN_EVT:
          {
            SimplePeripheral_processConnEvt((Gap_ConnEventRpt_t *)(pMsg->pData));
    
            ICall_free(pMsg->pData);
            break;
    	  }
    
        case SBP_MAX30102_DATA_READY:
          {
            SimplePeripheral_processMAX30102DataReadyEvt();
    
            ICall_free(pMsg->pData);
            break;
          }
    
        default:
          // Do nothing.
          break;
      }
    }

    where I added the last case which, as mentioned earlier, never gets triggered.

    I put a breakpoint at the beginning of the function to check 

    pMsg->hdr.event

    and the values are above 100. Moreover, I checked the same variable before inserting it in the queue and the values are ok. Specifically, I put a breakpoint in this function:

    static uint8_t SimplePeripheral_enqueueMsg(uint8_t event, uint8_t state,
                                               uint8_t *pData)
    {
      sbpEvt_t *pMsg = ICall_malloc(sizeof(sbpEvt_t));
    
      // Create dynamic pointer to message.
      if (pMsg)
      {
        pMsg->hdr.event = event;
        pMsg->hdr.state = state;
        pMsg->pData = pData;
    
        // Enqueue the message.
        return Util_enqueueMsg(appMsgQueue, syncEvent, (uint8_t *)pMsg);
      }
    
      return FALSE;
    }

    Any more thoguhts on this?

    Best regards,

    Jack




  • I apparently solved the problem. For future reference, it turns out that I cannot just add an event flag the Application events and exploiting the function which is already there. The correct way seems to add a flag to 

    SBP_ALL_EVENTS

    What I did was then copy the structure already used for periodicClock. 

    // Application events
    #define SBP_STATE_CHANGE_EVT                  0x0001
    #define SBP_CHAR_CHANGE_EVT                   0x0002
    #define SBP_PAIRING_STATE_EVT                 0x0004
    #define SBP_PASSCODE_NEEDED_EVT               0x0008
    #define SBP_CONN_EVT                          0x0010
    
    
    // Internal Events for RTOS application
    #define SBP_ICALL_EVT                         ICALL_MSG_EVENT_ID // Event_Id_31
    #define SBP_QUEUE_EVT                         UTIL_QUEUE_EVENT_ID // Event_Id_30
    #define SBP_PERIODIC_EVT                      Event_Id_00
    #define SBP_MAX30102_DATA_READY               Event_Id_05   // 0x0020
    
    
    // Bitwise OR of all events to pend on
    #define SBP_ALL_EVENTS                        (SBP_ICALL_EVT        | \
                                                   SBP_QUEUE_EVT        | \
                                                   SBP_PERIODIC_EVT     | \
                                                   SBP_MAX30102_DATA_READY)

  • That is correct, I should not have assumed that you had done this from the start, my bad.
    You would have to add the event to the "ALL EVENTS" mask to make the application pend for those flags as well.