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.

CC2605 ADC TI-RTOS 2.20

Other Parts Discussed in Thread: CC2650

Hello !

I'm actually working on the CC2650 with the TI-RTOS 2.20 on CCS 6.1.3.

I saw in datasheet the ADC can convert at 200kSamples/s, so 5microseconde/sample. Unfortantely I'm unable to get this time with the RTOS. 

I have arround 22 microsecondes unless 5. Is the TI-RTOS slowing the ADC that much ?

Here is the code i used, I took the faster convert (2.7us) and have no other task. I thought this issue came from the clock but it seems we haevn't the choice about the clock.

#include "Board.h"
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Clock.h>
#include <ti/sysbios/family/arm/m3/Hwi.h>
#include <ti/drivers/power/PowerCC26XX.h>
#include <ti/sysbios/BIOS.h>

#include <ti/drivers/PIN/PINCC26XX.h>
#include <ti/drivers/UART.h>

#include <driverlib/aux_adc.h>
#include <driverlib/aux_wuc.h>
#include <inc/hw_aux_evctl.h>


#include <ti/mw/display/Display.h>
#include <xdc/runtime/System.h>

#define TASK_STACK_SIZE 512
#define TASK_PRI        1


char taskStack[TASK_STACK_SIZE];
Task_Struct taskStruct;

Semaphore_Struct sem;
Semaphore_Handle hSem;

Hwi_Struct hwi;

#define SAMPLECOUNT 100
#define SAMPLETYPE uint16_t
#define SAMPLESIZE sizeof(SAMPLETYPE)

#define ALS_POWER   IOID_26
#define ALS_OUTPUT  IOID_23

SAMPLETYPE adcSamples[SAMPLECOUNT];
SAMPLETYPE singleSample;

// Analog light sensor pins
const PIN_Config alsPins[] = {
  ALS_POWER       | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH   | PIN_PUSHPULL | PIN_DRVSTR_MAX,
  ALS_OUTPUT      | PIN_INPUT_DIS | PIN_GPIO_OUTPUT_DIS ,
  PIN_TERMINATE
};

PIN_Handle pinHandle;
PIN_State  pinState;


//CUSTOM
static PIN_Handle ledPinHandle;
static PIN_State ledPinState;
/*
 * Application LED pin configuration table:
 *   - All LEDs board LEDs are off.
 */
PIN_Config ledPinTable[] = {
    Board_LED0 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    Board_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    PIN_TERMINATE
};


UART_Handle uHandle;

void taskFxn(UArg a0, UArg a1);
void adcIsr(UArg a0);

int main(void) {

  //Initialize pins, turn on GPIO module
  PIN_init(BoardGpioInitTable);

  //Initialize task
  Task_Params params;
  Task_Params_init(&params);
  params.priority = TASK_PRI;
  params.stackSize = TASK_STACK_SIZE;
  params.stack = taskStack;

  Task_construct(&taskStruct, taskFxn, &params, NULL);

  // Construct semaphore used for pending in task
  Semaphore_Params sParams;
  Semaphore_Params_init(&sParams);
  sParams.mode = Semaphore_Mode_BINARY;

  Semaphore_construct(&sem, 0, &sParams);
  hSem = Semaphore_handle(&sem);


  ledPinHandle = PIN_open(&ledPinState, ledPinTable);
  System_printf("ADC INITIALIZED\n");

  System_flush();

  BIOS_start();
}


void taskFxn(UArg a0, UArg a1) {

  Hwi_Params hwiParams;
  Hwi_Params_init(&hwiParams);
  hwiParams.enableInt = true;

  Hwi_construct(&hwi, INT_AUX_ADC_IRQ, adcIsr, &hwiParams, NULL);

  // Set up pins
  pinHandle = PIN_open(&pinState, alsPins);

  // Enable clock for ADC digital and analog interface (not currently enabled in driver)
  AUXWUCClockEnable(AUX_WUC_MODCLKEN0_ANAIF_M|AUX_WUC_MODCLKEN0_AUX_ADI4_M);

  // Set up ADC
  AUXADCEnableSync(AUXADC_REF_FIXED, AUXADC_SAMPLE_TIME_2P7_US, AUXADC_TRIGGER_MANUAL);

  // Disallow STANDBY mode while using the ADC.
  Power_setConstraint(PowerCC26XX_SB_DISALLOW);

  uint8_t currentSample = 0;

  while(1)
  {
	  for(currentSample = 0; currentSample < SAMPLECOUNT; currentSample++)
	  {

		//Sleep 100ms in IDLE mode
		//Task_sleep(4 / Clock_tickPeriod);


		AUXADCSelectInput(ADC_COMPB_IN_AUXIO7);

		// Trigger ADC sampling
		AUXADCGenManualTrigger();
		// Wait in IDLE until done

		Semaphore_pend(hSem, BIOS_WAIT_FOREVER );


		adcSamples[currentSample] = singleSample;

		PIN_setOutputValue(ledPinHandle, Board_LED1, !PIN_getOutputValue(Board_LED1));

	  }

	  	 System_printf("Value AD1 : %d \t \n",adcSamples[currentSample-1]);
		 System_flush();

  }

}


void adcIsr(UArg a0) {

  // Pop sample from FIFO to allow clearing ADC_IRQ event
  singleSample = AUXADCReadFifo();
  // Clear ADC_IRQ flag. Note: Missing driver for this.
  HWREGBITW(AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR, AUX_EVCTL_EVTOMCUFLAGSCLR_ADC_IRQ_BITN) = 1;

  // Post semaphore to wakeup task
  Semaphore_post(hSem);

}

Thank you by advance.

Bets regards,

John

  • John,

    It looks like you are waking the task on every sample. I think a better approach would be to collect several samples (~100) before waking the task. Then have the task process the samples while the next batch is being collected. You will need two buffers, on for collecting new samples and one for processing the previous samples.

    I would also add a flag to let you know if the task is not keeping up.

    Also, be careful when calling System_printf and System_flush. Depending on which system proxy you are using, these functions could have a significant execution time.

    ~Ramsey

  • Hi John,

    I have never tested the max performance of the Sensor controller with the ADC, but you could offload all the sampling away from the M3 core to the Sensor Controller.

    But as Ramsey suggested, you should use the two buffer approach as well.

    You could also use the DMA to transfer your data.

    Michel
  • Hi Ramsey and Michel,

    First, thank you to both of you, you gave me precious informations here.

    I would like ton configure the ADC to work with buffers and give the work to the Sensor Controller without drivers, only by registers. I found some usefull registers here to do that but I don't know how to change it in CCS. Plus, I didn't find any register to give the work to the Sensor controller.

    If you can help me with more informations about that here it should be great.

    Best regards,
    John
  • Hi John,

    Using the Sensor Controller is a little hard to understand at first, but once you do, it is very easy to use.

    First, download and install Sensor Controller Studio (SCS) from the TI website: www.ti.com/.../sensor-controller-studio

    When you open SCS, you will see Tool /Documentation section. The Getting Started Guide / Tutorial is a great place to start. It shows you some examples and there are more examples in the Examples section when you first open it. The tuorial with the examples should be good to understand how to use it so you can insert it into your project.

    Let us know if you have any problems.

    Regards,
    Michel
  • Hi Michel,

    Thank you about this answer, I will work on it, that's very usefull.

    Best regards,
    John
  •  Hi Michel,

    I'm coming back to you, I worked few days on the SCS and understood most of it.

    Q1/ ==> A question is still remaining now, how to read the data sensored by the SC ? I read it's contained on RAM which is readable by CPU and DMA byut I don't find the adress of it.

    Q2/ ==> I would use a memcpy() to get it, do you think it's a good way ?

    Q3/ ==> I'm using the following code, does it seems a good way to you ?

    int main(void) {
    
      //Initialize pins, turn on GPIO module
      PIN_init(BoardGpioInitTable);
    
      // Construct semaphore used for pending in task
      Semaphore_Params sParams;
      Semaphore_Params_init(&sParams);
      sParams.mode = Semaphore_Mode_BINARY;
    
      Semaphore_construct(&sem, 0, &sParams);
      hSem = Semaphore_handle(&sem);
    
    
      ledPinHandle = PIN_open(&ledPinState, ledPinTable);
      pinHandle = PIN_open(&pinState, alsPins);
      scifOsalInit();
      scifOsalRegisterTaskAlertCallback(scTaskAlertCallback);
      scifInit(&scifDriverSetup);
      scifStartRtcTicksNow(0x00020000); // 2 secondes
    
    
    
      System_printf("ADC INITIALIZED\n");
    
      System_flush();
    
      BIOS_start();
    }

    void scTaskAlertCallback()
    {
        uint16_t result=0;
    
        // Clear the ALERT interrupt source
        scifClearAlertIntSource();
    
        memcpy(&result,"?????",sizeof(uint16_t));
    
        System_printf("Value AD1 : %d \t \n",result);
        System_flush();
    
        // Acknowledge the alert event
        scifAckAlertEvents();
    
        Semaphore_post(hSem);
    
    }

    And on the SC :

    // Power up the light sensor and wait for it to get ready
    gpioSetOutput(AUXIO_O_ALS_POWER);
    fwDelayUs(1000, FW_DELAY_RANGE_1_MS);
    
    // Enable the ADC
    adcEnableSync(ADC_REF_FIXED, ADC_SAMPLE_TIME_2P7_US, ADC_TRIGGER_MANUAL);
    
    // Sample the light sensor
    S16 adcValue;
    adcGenManualTrigger();
    adcReadFifo(adcValue);
    state.adcValue = adcValue;
    
    // Output the new bin value
    output.bin = adcValue;
    state.forceOutput = 0;
    
    // Notify the driver
    fwGenAlertInterrupt();
    //}
    
    // Schedule the next execution
    fwScheduleTask(1);

    Q4/ ==> Why with the code, I never go on my Callback ?

    Thank you by advance.

    Best regards,

    John

  • Hi John,

    John73 said:
    Q1/ ==> A question is still remaining now, how to read the data sensored by the SC ? I read it's contained on RAM which is readable by CPU and DMA byut I don't find the adress of it.

    Once you generate the output files, you should have a scif.h file. This file will contain the data structures that you have created in SCS.

    In my case, for example, I had an adc task with state and output variables.

    In the scif.h file, this looked like this:

    /// ADC: Task output data structure
    typedef struct {
        uint16_t adcout; ///< Buffer Output for ADC
    } SCIF_ADC_OUTPUT_T;
    
    
    /// ADC: Task state structure
    typedef struct {
        uint16_t alertEnabled; ///< boolean to communicate with MPU about alert state
        uint16_t count;        ///< number of collected samples
    } SCIF_ADC_STATE_T;
    
    
    /// Sensor Controller task data (configuration, input buffer(s), output buffer(s) and internal state)
    typedef struct {
        struct {
            SCIF_ADC_OUTPUT_T output;
            SCIF_ADC_STATE_T state;
        } adc;
    } SCIF_TASK_DATA_T;
    
    /// Sensor Controller task generic control (located in AUX RAM)
    #define scifTaskData    (*((volatile SCIF_TASK_DATA_T*) 0x400E00E6))

    As you can see from the code aboce, the location of the data is 0x400E0056.
    And to access my data in my code, I declared pointers to these variables using this code:

    volatile uint16* adcValue = &scifTaskData.adc.output.adcout;
    volatile uint16* adcSamples = &scifTaskData.adc.state.count;
    volatile uint16* alertEnabled = &scifTaskData.adc.state.alertEnabled;

  • Sorry for the incomplete thread, my browser crashed while writing the answer.

    John73 said:
    Q2/ ==> I would use a memcpy() to get it, do you think it's a good way ?

    It's up to you to decide how you want to handle the data. One thing you have to note is that the data will get overwritten if the SC task runs again, so yes, you should copy the data (or use it right away).

    If you have a large structure, then the memcpy will be optimized for your processor, but for simple uin16_t, for example, you can use simple arithmetic operators
    i.e. in my code:
    uint16_t temp = *adcValue;

    John73 said:
    Q3/ ==> I'm using the following code, does it seems a good way to you ?

    This seems good, but I would change two things:

    I also moved the scifStartRtcTicksNow() call after TI-RTOS is started (inside one of my tasks). If for whathever reason TI-RTOS is not started when you enter the callback and call the Semaphore_post statement, you could possibly crash your system. Not sure whether it can actually happen, but I prefer being on the cautious side, rather than looking for an odd bug.

    I moved the piece of code below at the end of my Alert callback function. I am not sure of the behaviour if another interrupt occurs while the alert callback is running.

    // Clear the ALERT interrupt source
    scifClearAlertIntSource();

    John73 said:
    Q4/ ==> Why with the code, I never go on my Callback ?

    Have you scheduled the first execution inside the init of your task? You should have code for the init and that's where you schedule the first execution. Optionally, that's also where you would initiliaze your variables and drivers if they remain static throughout the use of the sensor controller tasks.

    Regards,

    Michel

  • Hello Michel,

    First I want to say a big "Thank you". I understood really better how this machine is working and how you managed it.

    I followed your advices and created a function InitTask() like that : 

    void InitTask()
    {
    	scifOsalInit();
    	scifOsalRegisterCtrlReadyCallback(scCtrlReadyCallback);
    	scifOsalRegisterTaskAlertCallback(scTaskAlertCallback);
    
    	scifInit(&scifDriverSetup);
    
    	PIN_setOutputValue(ledPinHandle, Board_LED1,1);// !PIN_getOutputValue(Board_LED1));
    
    	scifStartRtcTicksNow(0x00020000); //2sec
    }

    with the function scCtrlReadyCallback)() empty. The function is called by a Task contructed.

    But unfortunately I still have an issue : I still never go on the scTaskAlertCallback() function. I can see I'm going on my InitTask() function becaus emy led is turning on on start but that's all. I don't really understand what do you mean by "Have you scheduled the first execution inside the init of your task?" I know how the Scheduler is working but I don't see how to "Scheduler" my function, with semaphore ?

    Best regards,

    John

  • Hi John,

    I don't really understand what do you mean by "Have you scheduled the first execution inside the init of your task?" I know how the Scheduler is working but I don't see how to "Scheduler" my function, with semaphore ?

    The initialization code is inside Sensor Controller Studio. Here a snapshot of what it looks like in my project:

    I followed your advices and created a function InitTask() like that : 

    You don't need to create an InitTask because you will then have to delete and recover the RAM used by that task.

    I presume that you have a task where you pend on the Semaphore. I would copy the code that you have up there inside the task where you do Semaphore_pend before the infinite loop.

    Here's what it would look like:

    void taskFxn(UArg a0, UArg a1) {
    {
      // Do you init stuff here
    
      // Sensor controller init
      scifOsalInit();
      scifOsalRegisterCtrlReadyCallback(scCtrlReadyCallback);
      scifOsalRegisterTaskAlertCallback(scTaskAlertCallback);
    
      scifInit(&scifDriverSetup);
    
      PIN_setOutputValue(ledPinHandle, Board_LED1,1);// !PIN_getOutputValue(Board_LED1));
    
      scifStartRtcTicksNow(0x00020000); //2sec
    
      while(1)
      {
        // Do some stuff here (if needed)
    
        Semaphore_pend(hSem, BIOS_WAIT_FOREVER ); // you can set the timeout to whatever you need
    
        // Do your other stuff here (if needed)
      }
    }

    This way you don't waste any ressources (heap and stack) and you know that TI-RTOS is fully initialized and modules are created and instantiated.

    Regards,

    Michel

  • Hi Michel,

    Thank you so much, this is finally working !

    I changed the InitTask() to a task like your taskFxn(UArg a0, UArg a1). Finnaly I saw I forgot to add

    #define BV(n) (1 << (n))

    and on the Init if this task I add :

    scifStartTasksNbl(BV(SCIF_ANALOG_LIGHT_SENSOR_TASK_ID));

    If it can help someone one day.

    Anyway, thank you again Michel, for your time and good advices.

    Best regards,

    John