CC1311R3: Trigger GPTimer with external input

Part Number: CC1311R3

I am trying to configure a GPTimer as a delay timer (e.g. 1us resolution) started by an external event, i.e. edge or level on a DIO input. This is one of the most common use cases for an MCU timer, but neither TI Driver nor Driverlib documentation are forthcoming on the question.

Section 15.4.1 of the TRM instructs me to "configure the GPT:TnMR TnSNAPS, TnWOT, TnMTE, and TnCDIR register bits to ... use an external trigger to start counting, configure an additional trigger or interrupt...", but TnWOT seems only to set a trigger from the timer in the previous position in the daisy chain—and there is no further mention of the TnMTE register bits in the TRM or hw_gpt.h.

Nor, when configured as a oneshot timer (GPT_MODE_ONESHOT_UP), does the pin defined by hwAttrs->pinMux act as a start trigger.

  • Hi Roger,

    There's a misunderstanding here. There's no register to set to enable a timer starting on an external event. You will need to implement this in some kind of event handler/isr. 
    I don't think a 1 us resolution is realistic. At best you might capture the time when the external event happened, then add it to your timer. 
    Cheers,
    Marie H
  • Indeed, the "TnMTE register bits" are mentioned in all of the Chipcon TRMs, but I guess that they never existed or didn't work. I ended up using a conventional INT_AON_GPIO_EDGE interrupt for the external trigger. Then to reduce the time needed to start the delay timer as much as possible, I set its TAWOT bit to stop it from counting until the GPIO_EDGE callback cleared TAWOT and allowed the delay timer to start counting. Measured latency is ~8us ± 2us.

    GPTimerCC26XX_Handle delay_timer;
    
    /* This ISR is called by dly_timer timeout.
       It starts the delayed process. */
    void delay_callback(GPTimerCC26XX_Handle handle,
    		    GPTimerCC26XX_IntMask interruptMask)
    {
    	/* Trigger process from delayed external input */
    }
    
    /* This ISR is called by falling edge of TRIG_IN.
       It starts the one-shot dly_timer. */
    void trigger_callback()
    {
    	uint32_t gpt_base = delay_timer->hwAttrs->baseAddr;
    	
    	/* Start delay_timer by clearing GPTMn[TAWOT] bit */
    	TimerWaitOnTriggerControl(gpt_base, TIMER_A, false);
    	
    	// Disable GPIO interrupt
    	IntDisable(INT_AON_GPIO_EDGE);
    }
    
    /* Latency (in addition to the loaded timer value)
       between TRIG_IN edge and delay_callback was
       determined empirically to be 8 +/-2 us. */
    #define DLY_LATENCY 8
    
    /* This ISR is called by falling edge of TRIG_IN.
       It starts the one-shot dly_timer. */
    int setup_delay_timer()
    {
    	/* Set up GPT as one-shot delay */
    	GPTimerCC26XX_Params params;
    	GPTimerCC26XX_Params_init(&params);
    	
    	params.width          = GPT_CONFIG_16BIT;
    	params.mode           = GPT_MODE_ONESHOT;
    	params.direction      = GPTimerCC26XX_DIRECTION_UP;
    	params.debugStallMode = GPTimerCC26XX_DEBUG_STALL_ON;
    	
    	delay_timer = GPTimerCC26XX_open(DELAY_GPT, &params);
    	if (delay_timer == NULL) {
    		return ENXIO;
    	}
    	/* Register interrupt at GPT timeout */
    	GPTimerCC26XX_registerInterrupt(delay_timer,
    					delay_callback,
    					GPT_INT_TIMEOUT);
    
    	/* Enable input on TRIG_IN */
    	IOCIOInputSet(TRIG_IN, IOC_INPUT_ENABLE);
    
    	/* Set up falling GPIO edge as interrupt */
    	IOCIOIntSet(TRIG_IN, IOC_INT_ENABLE, IOC_FALLING_EDGE);
    	IOCIntEnable(TRIG_IN);
    
    	/* Register GPIO_EDGE handler in dynamic vector table */
    	IntRegister(INT_AON_GPIO_EDGE, trigger_callback);
    	
    	/* Disable for now */
    	IntDisable(INT_AON_GPIO_EDGE);
    
    	return 0;
    }
    
    void arm_trigger(uint32_t delay)
    {
    	uint32_t gpt_base = delay_timer->hwAttrs->baseAddr;
    	
    	/* Load timer with specified microsecond delay */
    	uint32_t dly_load_value = 48UL * (delay - DLY_LATENCY);
    	GPTimerCC26XX_setLoadValue(delay_timer, dly_load_value);
    
    	/* Set GPTMn[TAWOT] bit (wait-for-trigger mode) to
    	   temporarily block delay_timer from counting. */
    	TimerWaitOnTriggerControl(gpt_base, TIMER_A, true);
    	
    	/* Start delay_timer */
    	GPTimerCC26XX_start(delay_timer);
    
    	/* Clear interrupt for TRIG_IN pin */
    	IOCIntClear(TRIG_IN);
    	
    	/* Clear any pending GPIO_EDGE interrupts */
    	IntPendClear(INT_AON_GPIO_EDGE);
    	
    	/* Enable GPIO_EDGE interrupts */
    	IntEnable(INT_AON_GPIO_EDGE);
    }