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.

Using CC2650 GPTimers to measure narrow pulses

Other Parts Discussed in Thread: CC2650, CC2640, SYSBIOS

I am working on a CC2650 application to measure pulse widths in the range of 1us.  I found that setting up an edge-triggered interrupt and reading SysTick in the interrupting resulted in highly inaccurate measurements (as well as missed edges) because of the varying delay between the edge and the actual ISR execution.

I read in the tech reference that you can use GPTimers to capture the time that an edge occurred and trigger an interrupt.  It looks to me like I need to do the following:

1) Use IOCFGx register to set the GPIO pin for edge detection (both edges) and set PORT EVENT x to be triggered when this happens

2) Set up two GPTimers for edge capture - one set for rising edge, one for falling.

Where I am confused, is in the how to map EVENT x to the GPTimer edge trigger.  It looks like the MCU event fabric is the place to do this, but I can't make sense of the mapping.

I'm also wondering if can configure TWO timers to track edge events for the SAME input signal, one capturing rising edges, the other capturing falling edges.

Any help here, or example code, would be helpful.

  • Hi Todd,

    Please refer to this post: e2e.ti.com/.../1493831
    An example will be coming soon.

    Best wishes
  • I'm aware of the post you linked to, but it doesn't directly answer my question. In fact, it states that time periods less than 150ns cannot be measured, but the sysTick (and GPTimers) on a CC2650 running with an internal clock of 48MHz have resolutions of around 20ns/tick. I verified this resolution using the sysTick function.

    It also says that there are no GPTimer drivers, but I found some useful function prototypes at ti\tirtos_simplelink_2_11_01_09\products\cc26xxware_2_00_06_14829\driverlib\timer.

    Using these prototypes, I have set up my timers, but my interest at this point is in how to map the edge detection event from the GPIO, through the MCU event fabric, to trigger the timer event capture.

    Secondly, I would like to understand the feasibility of measuring a very small pulse by configuring the edge detect logic for a single pin to trigger a timer event capture on BOTH the rising and falling. Can I assign ONE pin to trigger a rising edge event capture on one GPtimer, and a falling edge event capture on another? Would these two events be handled by the SAME or DIFFERENT ISRs? If the same, is there a way to determine when the ISR runs that more than one event has occurred and needs to be processed?
  • Hi Todd,

    The way you potentially could achieve this would be:

    • Set up your PIN_Config structure with your IO and run PIN_open for the application to get a handle and "own" that pin.
      • Your pin configuration can create an event on either rising, falling or both.
    Use PINCC26XX_setMux to route the pin as input to event fabric using the mux named IOC_PORT_MCU_TIMER0
    • This is equivalent to PORT_EVENT0 but our internal "renaming" has apparently not caught all of these.
    • The PORT_EVENT0 signal can then be set as GPT0A/B capture source in EVENT:GPT0ACAPTSEL
    • Configure the timer to do a capture.

    I do not know the timer very well but I think it is the IO's which sets the limitation on minimum periods of a signal being detected. You might be able to get around it by connecting the signal to two different IO's and have one detect a rising and one a falling event.

    The same ISR will be taken and returned to the user from the PIN driver if the PIN handle has configured a callback function to do so. 

    Regards,

    Svend

  • Svend,

    Thanks for your help!  I am able to successfully trigger interrupts on the rising/falling edge of my input signal, but for some reason, I can't seem to get the timers to capture the clock value at the moment the edge occurred - it always reads zero.

    I'm tying together GPIO pins 26 and 27 and using the former for positive edges, the later for falling  Here is my initialization sequence:

    // Set inputs for edge detection
    HWREG(IOC_BASE + IOC_O_IOCFG26) |= (IOC_IOCFG26_EDGE_DET_POS | IOC_IOCFG26_PORT_ID_PORT_EVENT0);
    HWREG(IOC_BASE + IOC_O_IOCFG27) |= (IOC_IOCFG27_EDGE_DET_NEG | IOC_IOCFG27_PORT_ID_PORT_EVENT1);

    // Set the pins to trigger PORT_EVENT0 and PORT_EVENT1 respectively

    PINCC26XX_setMux(hPinHandle, Board_VXBOOST_CMP, IOC_PORT_MCU_TIMER0);
    PINCC26XX_setMux(hPinHandle, Board_VXBOOST_CMP2, IOC_PORT_MCU_TIMER1);

    // Set EVENT0 and EVENT1 to trigger the edge capture of the GP timers.
    HWREG(EVENT_BASE + EVENT_O_GPT0ACAPTSEL) = EVENT_GPT0ACAPTSEL_EV_PORT_EVENT0;
    HWREG(EVENT_BASE + EVENT_O_GPT0BCAPTSEL) = EVENT_GPT0BCAPTSEL_EV_PORT_EVENT1;

    // Enable the free-running clock...
    HWREG(CPU_SCS_BASE + CPU_SCS_O_STRVR) = 0x00FFFFFF;
    HWREG(CPU_SCS_BASE + CPU_SCS_O_STCSR) = 0x01;

    // Enable GPT0
    HWREG(PRCM_BASE + PRCM_O_GPTCLKGR) |= PRCM_GPTCLKGR_CLK_EN_GPT0;
    PRCMLoadSet();

    // Sets timers of GPT0 to be edge capture and up-counting timer
    TimerConfigure(GPT0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP | TIMER_CFG_B_CAP_TIME_UP);

    // Sets timer A to save value on rising edge.
    TimerEventControl(GPT0_BASE, TIMER_A, TIMER_EVENT_POS_EDGE);

    // Sets timer B to store value on falling edge.
    TimerEventControl(GPT0_BASE, TIMER_B, TIMER_EVENT_NEG_EDGE);

    // Sync the timers together
    TimerSynchronize(GPT0_BASE, TIMER_0A_SYNC | TIMER_0B_SYNC);

    // Initialize this to zero.
    TimerPrescaleSet(GPT0_BASE, TIMER_BOTH, 0);

    // Start at zero
    TimerLoadSet(GPT0_BASE, TIMER_BOTH, 0);

    // Enable both timers
    TimerEnable(GPT0_BASE, TIMER_BOTH);

    // Register the interrupts...
    TimerIntRegister(GPT0_BASE, TIMER_A, processRisingEdge);
    TimerIntRegister(GPT0_BASE, TIMER_B, processFallingEdge);

    >>> When we are ready to collect data,  the software enables interrupts like this:

    // Clear any pending interrupt flags
    HWREG(GPIO_NONBUF_BASE+GPIO_O_EVFLAGS31_0) = ( (1 << 27) | (1 << 26));

    // Enable the interrupts
    TimerIntEnable(GPT0_BASE, TIMER_CAPA_EVENT | TIMER_CAPB_EVENT);

    Inside the rising-edge interrupt, I attempt to grab the timer values like this

    ticks = ( (HWREG(GPT0_BASE + GPT_O_TAPR) & 0x000000FF) << 16) + HWREG(GPT0_BASE + GPT_O_TAR);

    Similarly, in the falling edge interrupt I do this:

    ticks = ( (HWREG(GPT0_BASE + GPT_O_TBPR) & 0x000000FF) << 16) + HWREG(GPT0_BASE + GPT_O_TBR);

    However, ticks always ends up as zero.  Can you spot anything I missed above?

  • I found my problem. This line was resetting the "reload" value to zero:
    TimerLoadSet(GPT0_BASE, TIMER_BOTH, 0);

    I had thought this loaded the initial value. Once I removed this line, everything is now working.

    Thanks!
  • Hi Todd,

    Good to hear you have it working but reading through your code I am surprised this works on a system level with TI RTOS. A few comments below:

    Todd Witters said:
    HWREG(IOC_BASE + IOC_O_IOCFG26) |= (IOC_IOCFG26_EDGE_DET_POS | IOC_IOCFG26_PORT_ID_PORT_EVENT0);
    HWREG(IOC_BASE + IOC_O_IOCFG27) |= (IOC_IOCFG27_EDGE_DET_NEG | IOC_IOCFG27_PORT_ID_PORT_EVENT1);

    This is partially what PIN_setMux is doing as well and enabling interrupts as well should be done through the PIN_Config given to PIN_open. 

    Todd Witters said:
    // Enable the free-running clock...
    HWREG(CPU_SCS_BASE + CPU_SCS_O_STRVR) = 0x00FFFFFF;
    HWREG(CPU_SCS_BASE + CPU_SCS_O_STCSR) = 0x01;

    Enabling the SysTick timer is unrelated to the GP Timers so unnecessary, it just uses current.

    Todd Witters said:
    // Enable GPT0
    HWREG(PRCM_BASE + PRCM_O_GPTCLKGR) |= PRCM_GPTCLKGR_CLK_EN_GPT0;
    PRCMLoadSet();

    TI RTOS API Power_setDependency should be used instead.

    Todd Witters said:
    // Register the interrupts...
    TimerIntRegister(GPT0_BASE, TIMER_A, processRisingEdge);
    TimerIntRegister(GPT0_BASE, TIMER_B, processFallingEdge);

    The IntRegister functions in here will copy the vector table from Flash to RAM and patch the vector table in RAM. This will overwrite any interrupts TI RTOS had configured up in RAM already. TI RTOS API Hwi_construct should be used instead.

    Also you will need to disallow the system from going to standby when the timers are running using Power_setConstraing(POWER_SB_DISALLOW).

    Best regards,
    Svend

  • Todd..

    We at Safecast () are starting up to get a geiger tube connected to the CC2650 and would like to know if we could have a look at or share the code you use.

    regards

    Rob Oudendijk

  • Hi,Todd Witters 

    I had a problem,When I call the function  "TimerIntRegister()  " to register  callback  function,compiler hints:

    "Warning[Lp023]: absolute placement (in [0x20000000-0x200000c7]) overlaps with reserved areas: C:\ti\simplelink\ble_cc26xx_2_00_00_42893\Projects\ble\SimpleBLEPeripheral\CC26xx\IAR\Application\CC2640\..\..\..\..\..\common\cc26xx\IAR\cc26xx_ble_app.icf 139  [0x20000000-0x200000c7] "ti_sysbios_family_arm_m3_Hwi_ramVectors" "

    TimerIntRegister(GPT0_BASE,TIMER_A,Timer_callBackFun);   // "Timer_callBackFun"  is defined by myself.

    Can you tell me how to solve this problem?

     

  • The issue is that you are using a low-level driverLib calls to set up interrupts. This will try to place a different vector table at start of RAM than what TI RTOS has placed there. You should use Hwi_construct to setup interrupts instead in TI RTOS and enable timer interrupts on the source side using TimerIntEnable.

    Regards,
    Svend
  • Thank you very much!
  • Hello Todd,

    Good to see your post~

    I am newbie to CC2650/40. I need your help/suggestion regarding implementing counting/delay timer to my application. As you have implemented it well, please suggest me to implement it~

    I need to count the incoming pulses/interrupt at one GPIO Pin. The pulse could be readable at rising edge or falling edge. I need some sample example to implement~
    1. Implementing timer construct~
    2. Pin_config to read the incoming interrupt/pulse count~
    3. whenever count == ??, do some operation~

    Thanks in advance~

    Regards
  • Hello Svendbt,

    Nice to see your post related ti timer implementing for pulse period measurement. I am also struggling with this. I just need to measure the pulse duty (period) in a simple way so that I can understand this step by step, how it works. Please help me by sharing a simple example for this application. 

    Please suggest~

    Thanks n Regards

  • Hi Todd,
    Can you help me about what  to include the definition in you code.
    For example, i have already include <driverlib/ioc.h> in my code but it still have wrong about IOC_PORT_MCU_TIMER0 for no definition.
    Can you help me?

  • Hi,

    What's your TI-RTOS version and BLE stack version?
    There has been naming changes along the way, so you should check the what's the value is and at which file. Then find the corresponding value and files in the new TI-RTOS
  • Hi,
    how to look my TI-RTOS version and BLE stack version?

    below is the example (Sensor Tag) I use to development
    ti\simplelink\ble_cc26xx_2_01_01_44627\Projects\ble

    thank you for your help

    furthermore,  I already #include <driverlib/ioc.h>

    ioc.h

  • Hi,
    I found that IOC_PORT_MCU_PORT_EVENT0 is same as IOC_PORT_MCU_TIMER0.
    thank you very much
  • hi ,
    How to defined CPU_SCS_O_STRVR and CPU_SCS_O_STCSR?
  • Hi Anna,

    These registers are defined in hw_cpi_scs.h within the /inc folder of the cc26xxware install. I highly recommend taking a look at the documentation within the /doc folder of cc26xxware (included in TI-RTOS) so that this layout makes more sense.
  • Hi Todd,
    1) I try to decrease two ticks from below tickA -tickB.
    Do you know what's the meaning of the result.
    for example : HEX(0000d6ae - 0000d697) = DEC (23)
    Is it meaning 23*(1/48 micro sec) = 0.479 ?
     Why it doesn't match the input signal for about 10 micro second.

    tickA = ( (HWREG(GPT0_BASE + GPT_O_TAPR) & 0x000000FF) << 16) + HWREG(GPT0_BASE + GPT_O_TAR);
    tickB = ( (HWREG(GPT0_BASE + GPT_O_TAPR) & 0x000000FF) << 16) + HWREG(GPT0_BASE + GPT_O_TAR);

    2) Do you know the difference between
    ( (HWREG(GPT0_BASE + GPT_O_TAPR) & 0x000000FF) << 16) + HWREG(GPT0_BASE + GPT_O_TAR);
    and
    HWREG(GPT0_BASE + GPT_O_TAR);


    best regards,
    Anna

  • How do I set the value of the timer to zero?