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.

What is the Task concept in TI RTOS

Other Parts Discussed in Thread: CC2650, SYSBIOS

Hi,

I need 2 different functionalities, that can run parallel.

My use case is like this,

I wanted to read ADC readings from cc2650 with a predefined interval and store the value to a global buffer. When ever a global buffer of size 256 Byte is completely filled, the SPI Flash write functionality get invoked and write the buffer to flash memory. But Flash write should not block the ADC reading operation. 

I don't know if the threading concepts in c programming are applicable in TI RTOS. 

I see that all the Tasks in TI RTOS are created with a priority and TI RTOS having  preemptive scheduling, which means low priority tasks will be in a queue until a high priority task finishes. 

What happens if I am giving same priority to the tasks, which I have explained above. Will it run parallel or one task will queue the other and take turns to execute.

Anyone having good understanding about the Task execution and scheduling please help me and let me know if running tasks parallel is possible or not

  • Hello,
    I will move this thread to the TI-RTOS forums where the experts there can answer best.

    Thanks
    ki
  • Hi Joseph,

    Since the CC2650 has a single core (I haven't considered the cortex-m0 dedicated for the radio), multi-threading implies that tasks will run one after the other. That's what TI-RTOS manages for you.

    If two tasks have the same priority, they will be queued one after the other.
    If one task has a higher priority than another, only the higher priority task will run.

    That being said, you will need to use synchronization modules (ex: semaphores, mailboxes, events, etc.) which will allow your lower priority tasks to run while the higher priority task is waiting to be ready.

    In your case, I see two different solutions:
    1- Two tasks with pointers to buffer and two buffers: The ADC task would have a pointer to one of the buffers while the flash will point to the other. When the ADC is done filling its buffer, it alerts the Flash task that it finished filling. The same goes for the flash. Once it finished writing to flash, it alerts the ADC task that it has completed its writing. When both tasks reach the end, you can then swap the pointers. (you would disable the Hwi to ensure that you don't get interrupted by either the ADC or flash during that process).
    2- Two tasks, one buffer and a mailbox: Your flash task pends on a mailbox while the ADC fills its buffer. When the ADC task has finished filling its buffer, it posts the buffer to the mailbox. (The mailbox module copies the data into its own buffer so your ADC buffer can be re-used). You would also disable the Hwi so you don't get interrupted by the ADC of the flash.

    I prefer the first solution because it doesn't require any "copying", just swapping of pointers. But the second one is simpler to implement.

    Another thing to consider is whether you just want to store the value of the ADC or do some calculations. If you're just storing the values, you don't need two tasks since everything can be handled in the Hwi. I would beware of the post to the mailbox inside the ADC Hwi. The copy operation to the mailbox could be very long and you could miss some ADC "reads".

    Hope this helps

  • Just want to add that if you use Mailbox_post inside the Hwi, you need to set the Timeout to 0.
    Check this thread for more details:
    e2e.ti.com/.../532525
  • The 1st solution suggested by Michael is called "ping-pong buffer" which is widely used in data streaming. In general, this should be what you need.

    But for CC26xx, there is something special to flash memory. During erasing/programming flash memory, no op-code can be executed from the flash memory. That means, your code including RTOS can't be placed in on-chip flash memory. How to do it? RTOS is probably placed in ROM. But where to put your code? So, the only way is to disable interrupts to prevent task-switching & ISR when erasing/programming flash memory.

    This induces another issue since your ADC task/ISR will be disabled for a period of time. And the time is pretty long, e.g. the erase time of a page/sector is 8ms. Your ADC sampling frequency must be less than 128Hz. If you need a faster sampling frequency then you will need and external flash chip to store data. Or use the ultimate solution, Sensor Controller, to sample ADC data.
  • Thanks for the reply.

    Robert Chen,

    Actually I forgot to mention it. Its external flash memory I am using to store the data.

    Michel Solecki,

    Yes, I am implementing some kind of algorithm after reading the ADC values, then only it will be copied to external flash memory


    I will try out the logic that you guys suggested and will post more queries and problems if have any during the implementation.
  • Hi Michel,

    Currently I am trying the 1st method that you are suggested with Event as synchronizing module. 

    See the code below

    		for (;;)
    			{
    				count++;
    
    				if(count > 2)
    				{
    					adcVal = 0;
    					count  = 0;
    				}
    
    				// Get the ticks since startup
    				uint32_t tickStart = Clock_getTicks();
    
    				// Trigger ADC sampling
    				AUXADCGenManualTrigger();
    
    				/* Reading ADC 12 bit register */
    				singleSample = AUXADCReadFifo();
    
    
     		        /* Converting 2 12 bit data to 3 byte */
    				if (count == 1)
    					adcVal |= singleSample;
    				else if (count == 2)
    				{
    
    					adcVal = (adcVal << 12) | singleSample;
    
        				/* Converts 4 byte data to 3 byte array */
    					ptr = (char *) &adcVal;
    					i = 3;
    					do
    					{
    						i--;
    						txBuf[3 - (i+1)] = *(ptr +i);
    					}while(i != 0);
    					/*Conversion ends here */
    
    					/* 3 byte to 255 byte conversion */
    					if (buffer_swap == 0)
    					{
    						for (i = 0; i < 3; i++)
    						{
    							FlashBuf1[bufferSize] = txBuf[i];
    							bufferSize++;
    						}
    					}
    					else if (buffer_swap == 1)
    					{
    
    						for (i = 0; i < 3; i++)
    						{
    							FlashBuf2[bufferSize] = txBuf[i];
    							bufferSize++;
    						}
    					}
    
    					if (bufferSize >= 255)
    					{
    						if (buffer_swap == 0)
    						{
    							UART_write(UART_handle, "Buffer 1 Full\n\r", 15);
    							
    							buffer_swap = 1;
    							arrayPointer = FlashBuf1;			/* Passing pointer to the first array */
    							
    						}
    						else if (buffer_swap == 1)
    						{
    							UART_write(UART_handle, "Buffer 2 Full\n\r", 15);
    							
    							buffer_swap = 0;
    							arrayPointer = FlashBuf2;
    							
    						}
    
    						Event_post(myEvent, Event_Id_00);
    						bufferSize = 0;
    					}
    
    					Task_sleep(12500);
    				}

    Here I am able to receive the event if the FlashBuf1 is full. But I am not getting the event for FlashBuf2. 

    Can I generate the same event again or do I have to clear the previous event or something like that ?

  • Hi Joseph,

    An event will not tell you how many times the event occurred, only that it has occurred.

    And when you do the Event_pend, all events are cleared. Therefore, you should store the result of the Event_pend function in a variable.

    I'm sure you've seen it, but just in case here's the general idea of the Events:

    // You need to define the andMask and or Mask
    // You can also change the timeout value
    UInt events = Event_pend(myEvent, andMask, orMask, BIOS_WAIT_FOREVER);
    
    // check the mask if you used an orMask, otherwise, you don't need to do this since all events have to occur for the pend to exit if (events & Event_Id_00) { // Do stuff here for event 00 } if (events & Event_Id_01) { // Do stuff here for event 01 }

    As for the events not occurring, I would need to see the code where you post the event and where you pend. In short, you have to post the same event; in this case, myEvent has to be global to where you post and pend.

    I'm also wondering about the algorithm that you're using: you increment count (count++) and then you reset it to zero (if greater than 2). Shouldn't it be the other way around? Reset to zero if greater than 2 and then increment. Right now, as I see it, you'll skip every third sample because your count will be zero at that moment. And you don't need to check on whether your count is equal to 1 or 2.

    And just a suggestion for your code. You're adding many variables to handle your buffers. Here's a code snippet that would use the power of multi-threading (and the corrections mentioned above).

    // Somewhere global you have two buffers defined
    Char Buffer1[255];
    char Buffer2[255];
    
    // You also have two pointers (one in ADC task and the other in your flash task)
    Char* AdcBufferPtr;
    Char* FlashBufferPtr;
    
    // When your tasks are started you assign one of the buffers to your pointers
    AdcBufferPtr = Buffer1;
    FlashBufferPtr = Buffer2;
    
    for (;;)
    {
       if (count > 2)
       {
          adcVal = 0;
          count = 0;
       }
       count++;
    
       // Get the tick and ADC sample here
    
       adcVal = (adcVal << 12) | singleSample;
    
       if (count == 2)
       {
          // Do the 4 to 3 conversion here
          // Protect the access to your buffer variables
          IArg key = GateMutexPri_enter(someGateMutex);
          for (i = 0; i < 3; i++)
          {
             AdcBufferPtr[bufferSize] = txBuf[i];
             bufferSize++;
          }
    
          if (bufferSize >= 255)
          {
             bufferSize = 0;
             Event_post(myEvent, Event_Id_00);
          }
          GateMutexPri_leave(someGateMutex, key);
    
          // I assume you want to sleep during the copy to flash
          Task_slep(12500);
       }
    }
    
    //Flash copy task
    for(;;)
    {
       // do the copy here
       // Don't forget to protect access to your buffer variables with GateMutexPri_enter and leave
       // You should use the same GateMutexPri variable in all tasks (someGateMutex in this case)
    
       // Once flash copy is complete, post event
       Event_post(myEvent, Event_Id_01);
    }
    
    // Here is the sample for your pointer swap
    UInt AdcBufferFull_AND_FlashCopyComplete = Event_Id_00 | Event_Id_01;
    for (;;)
    {
       Char* temporaryPtr;   
    
       UInt events;
       // This will wait until the ADC buffer is full and until the copy to flash is complete
       events = Event_pend(myEvent, AdcBufferFull_AND_FlashCopyComplete, 0, BIOS_WAIT_FOREVER);
    
       // You don't need to check whether both events have occurred since you already know that
       
       // Protect access to buffer variables
       IArg key = GateMutexPri_enter(someGateMutex);
       temporaryPtr = AdcBufferPtr;
       AdcBufferPtr = FlashBufferPtr;
       FlashBufferPtr = temporaryPtr;
       GateMutexPri_leave(someGateMutex, key);
    }

    So with this code, you accomplish a few things. You don't really need to think which buffer you have to fill, since the "pointer" swap occurs when you finish filling up the buffer and only once your finished copying your data to flash. You also allow all your tasks to run concurrently as if they were handled by different processors.

    You could also use a clock instead of Task_sleep to synchronize your ADC task. That would ensure that your ADC sample always occurs at the same interval. Right now, the interval between each sample will depend on what instructions are executed in your code (depends on your if statements). To do that, create a clock with the desired period. Remove the Task_sleep and add Semaphore_pend just below the for(;;) statement. Add your clock timeout function which will call Semaphore_post.

    For priorities, your pointer swap should have the highest priority and your two other tasks can have the same priority as long as the Flash task "releases" control from time to time. Otherwise, your ADC sample will not occur until the flash task has completed its operation.

    Hope this helps

  • This is much appreciated response from you.

    I am using GateMutexPri_enter(someGateMutex) and GateMutexPri_leave(someGateMutex, key); that you psted above and I am getting an unresolved symbol error
    unresolved symbol ti_sysbios_gates_GateMutexPri_leave__E

    How can I avoid such an error ?
    GateMutexPri.h is also included in my code.
  • Can I close this thread out?
  • Writing

    var GateMutexPri = xdc.useModule('ti.sysbios.gates.GateMutexPri');      to my appBLE.cfg file creates following errors

    How can I avoid these errors?

    gmake[1]: *** [mangled_ti.sysbios.BIOS_config_lib.pp] Error 2
    
    gmake[1]: *** Waiting for unfinished jobs....
    
    gmake[1]: *** [mangled_ti.sysbios.BIOS_RtsGateProxy_config_lib.pp] Error 2
    
    C:/Users/DANISH~1.JOS/AppData/Local/Temp/make5760-e.sh: 1: Syntax error: Unterminated quoted string
    
    gmake[1]: *** [mangled_ti.sysbios.knl.Clock_config_lib.pp] Error 2
    
    gmake[1]: *** [mangled_ti.sysbios.knl.Mailbox_config_lib.pp] Error 2
    
    gmake[1]: *** [mangled_ti.sysbios.knl.Queue_config_lib.pp] Error 2
    
    gmake[1]: *** [mangled_ti.sysbios.knl.Semaphore_config_lib.pp] Error 2
    
    gmake[1]: *** [mangled_ti.sysbios.knl.Swi_config_lib.pp] Error 2
    
    gmake[1]: *** [mangled_ti.sysbios.knl.Task_config_lib.pp] Error 2
    
    gmake[1]: *** [mangled_ti.sysbios.knl.Clock_TimerProxy_config_lib.pp] Error 2
    
    gmake[1]: *** [mangled_ti.sysbios.knl.Task_SupportProxy_config_lib.pp] Error 2
    
    gmake[1]: *** [mangled_ti.sysbios.family.arm.m3.Hwi_config_lib.pp] Error 2
    
    gmake[1]: *** [mangled_ti.sysbios.family.arm.cc26xx.Timer_config_lib.pp] Error 2
    
    C:/Users/DANISH~1.JOS/AppData/Local/Temp/make5760-f.sh: 1: Syntax error: Unterminated quoted string
    
    gmake[1]: *** [mangled_ti.sysbios.family.arm.cc26xx.TimestampProvider_config_lib.pp] Error 2
    
    C:/Users/DANISH~1.JOS/AppData/Local/Temp/make5760-d.sh: 1: Syntax error: Unterminated quoted string
    
    C:/Users/DANISH~1.JOS/AppData/Local/Temp/make5760-10.sh: 1: Syntax error: Unterminated quoted string
    
    C:/Users/DANISH~1.JOS/AppData/Local/Temp/make5760-1.sh: 1: Syntax error: Unterminated quoted string
    
    gmake[1]: *** [mangled_ti.sysbios.hal.Hwi_HwiProxy_config_lib.pp] Error 2
    
    gmake[1]: *** [mangled_ti.sysbios.family.arm.m3.TaskSupport_config_lib.pp] Error 2
    
    gmake[1]: *** [mangled_ti.sysbios.hal.Hwi_config_lib.pp] Error 2
    
    gmake: *** [C:/ti/simplelink/ble_cc26xx_2_01_01_44627/Projects/ble/SimpleBLEBroadcaster/CC26xx/CCS/Config/src/sysbios/sysbios.aem3] Error 2
    
    error: xdc.cfg.SourceDir: "C:/ti/xdctools_3_31_01_33_core/packages/xdc/cfg/SourceDir.xs", line 209: xdc.cfg.SourceDir : Build of generated source libraries failed: exit status = 2:
    
    js: "C:/ti/xdctools_3_31_01_33_core/packages/xdc/cfg/Main.xs", line 160: Error: Configuration failed!
    
    xdctools_3_31_01_33_core\gmake.exe: *** [package/cfg/appBLE_pem3.xdl] Error 1
    
    xdctools_3_31_01_33_core\gmake.exe: *** Deleting file `package/cfg/appBLE_pem3.xdl'
    
    xdctools_3_31_01_33_core\gmake.exe: *** [package/cfg/appBLE_pem3.xdl] Deleting file `package/cfg/appBLE_pem3.h'
    
    xdctools_3_31_01_33_core\gmake.exe: *** [package/cfg/appBLE_pem3.xdl] Deleting file `package/cfg/appBLE_pem3.c'
    
    js: "C:/ti/xdctools_3_31_01_33_core/packages/xdc/tools/Cmdr.xs", line 51: Error: xdc.tools.configuro: configuration failed due to earlier errors (status = 2); 'linker.cmd' deleted.
    
    gmake: *** [configPkg/linker.cmd] Error 1
    
    gmake: Target 'all' not remade because of errors.
  • This seems to be a compiler issue that I don't know how to resolve.
    But if it's a problem which occurs during the TI-RTOS build process, you should find an file called .errorlog (which might give you more details). In the TI-MAC stack, it was located in a ConfigPkg (not sure whether that file is also created in BLE stack or where it would be located).

    But since the original issue seems to be have been resolved, I would recommend that you post a new question. You will get more people looking into this issue.
    Sorry I'm not much more help in this matter.