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.

Unstable low frequency interrupts occurring GPTM timer edge capture counts.

Guru 54057 points

GPTM down count mode capturing positive edge counts and event interrupt cycle unable to keep RPM stable during high NVIC traffic inside a pre set amount of CCP input edge match counts in the one second interrupt time intervals.

Updated Source 10-03-2016:

Oddly GPTM clock source changed from 120Mhz SYSCLK to 16Mhz PIOS. PIOS clock source reduced false RPM ramping to over 300k RPM in the one second one shot timer interrupt intervals into the edge counts. That was occurring NVIC servicing high amount of ADC/PWM interrupts and only has few hundred RPM miscalculations with PIOS. Original code was interrupting every 2 edges, with code changes below the edges counted rise much higher prior to interrupting and constrained via TnMATCHR at the Oneshot timer reload interval of 1 second. 

Shouldn't the edge count timer interrupts and 1 second one shot timer occur at the same clock time if the interrupt priority never changes?

Edge count GPTM-0B = INT36 priority 0x40 and Oneshot GPTM-3A INT 51 priority 0x40.

Example of interrupt period code:

#define TIMER_TICKS_PER_HZ   0x3c //60hz
void
TMCCP1TacoEdgeCountInt(void)
{

	/* Clear the CnMIM CnMEIN/CnMCENT interrupt.
	 * To be sure NVIC unpends INT clear twice*/
	ROM_TimerIntClear(TIMER0_BASE, TIMER_CAPB_MATCH);
	ROM_TimerIntClear(TIMER0_BASE, TIMER_CAPB_MATCH);

	/* Phase lock the interrupt loop of CCP1 input edges
	 * accumulating the frequency of captured edges from
	 * the most recent tick update of TnTBV ISR value. */
	g_ui32TimerFrequency += ui32EdgeCount;

	/* Average pulse times dividing the number of interrupts
	 * incremented per-revolution this captured frequency. */
	ui32NewEdgeTime = g_ui32TimerFrequency / 2;

	/* Update the new taco frequency derived from
	 * the average of edge count interrupt cycles. */
	g_ui32TimerFrequency = g_ui32TimerFrequency - ui32NewEdgeTime;

    /* Trigger GPTM3 oneshot 1 second taco ticks.
     * Further process the CCP1 edge counts into RPM. */
    HWREG(TIMER3_BASE + TIMER_O_CTL) |= TIMER_CTL_TAEN;

	/* Re-enable GPTM edge counts starting a new edge
	 * capture sequence for processing all the counted
	 * edges in 1 second intervals of count frequency. */
	HWREG(TIMER0_BASE + TIMER_O_CTL) |= TIMER_CTL_TBEN;
}

/**********************************************
 * ISR source is 1 second OneShot timer 
 **********************************************/
void
FanSpeedTickHandler(void)
{
	uint32_t ui32TBVEdgeCount;
	uint32_t ui32TBILREdgeCount;

	/* Clear the periodic 1Hz TIMEOUT interrupt.*/
    ROM_TimerIntClear(TIMER3_BASE, TIMER_TIMA_TIMEOUT);

	/* In down-count mode, the current count of the input
	 * events can be obtained by subtracting the GPTMTnR
	 * or GPTMTnV from the value made up of the GPTMTnPR
	 * and GPTMTnILR register combination. */

    /* Get GPTMTBV free running count value. */
	ui32TBVEdgeCount = HWREG(TIMER0_BASE + TIMER_O_TBV);
	/* Get GPTMTBILR current matching edge counts */
	ui32TBILREdgeCount = HWREG(TIMER0_BASE + TIMER_O_TBILR);
	/* Determine the total number of counted edges up to ISR */
	ui32EdgeCount = ui32TBILREdgeCount - ui32TBVEdgeCount;

	/* Disable GPTM-B0 to update TnMATCHR count register */
	HWREG(TIMER0_BASE + TIMER_O_CTL) &= ~(TIMER_CTL_TBEN);

	/* Load number of counted edges 1 second later */
	 ROM_TimerMatchSet(TIMER0_BASE, TIMER_B, ui32EdgeCount);
	/* Re-enable GPTM-B0 edge counts. */
	HWREG(TIMER0_BASE + TIMER_O_CTL) |= TIMER_CTL_TBEN;
	/* Reprogram GPTM-3 wide 32bit oneshot timer for 1 second. */
	HWREG(TIMER3_BASE + TIMER_O_TAILR) = 0xF42400;;

	/* Set the Low pass filter taco speed update
	 * of 96EPS in the captured edge counts */
	if(ui32EdgeCount > TIMER_TICKS_PER_HZ)
	{
		/* Update the Taco RPM formula with the
		 * frequency derived in the counted edges */
		TacoSpeedUpdate(g_ui32TimerFrequency);

		/* Clear the edge counts & frequency */
		g_ui32TimerFrequency = 0;

		return;
	}
	else
	{
		/* Set the fan speed to zero in the absence
		 * of edge count interrupts, thus indicating
		 * the fan blade has stopped moving. */
		ui32FanSpeedSend = ui32FanSpeed = 0;
		g_ui32TimerFrequency = 0;
		ui32EdgeCount = 0;
	}
}

 
  • One thing discovered; Setting TIMER_TICKS_PER_HZ above 120 seems to stop calls to TacoSpeedUpdate(). How ever GPIO frequency counter measures from 47Hz - 101.5Hz (IF) as the interrupt rate increases and decreases from 101Hz-212Hz.

  • Timer configuration posted for those who want to try edge captures derived into an intermediate frequency. Attempts to integrate edge time captures into the counted edges period are not much better at this low frequency. Main reason to remove another timer from the formula, where RPM =  frequency * 30.

    /**********************************************************
     * 
     * Testing: GPTM-0B/CCP1 Pin92
     *
     * Fan taco CCP1 edge capture mode, 16 bit down count.
     * No upper bits [23:16] or use of the 8 bit Prescaler,
     *
     **********************************************************/
            /* Disable GPTM-0B */
    	HWREG(TIMER0_BASE + TIMER_O_CTL) &= ~(TIMER_CTL_TBEN);
    
            /* Configure GPTM-0B 16Mhz clock source.*/
    	ROM_TimerClockSourceSet(TIMER0_BASE, TIMER_CLOCK_PIOSC)
    	/* Set GPTM-0B control register for
    	 * split half wide 16 bit timers */
    	HWREG(TIMER0_BASE + TIMER_O_CFG) = 0x4;
    	/* Set GPTM-0B mode register for down count: Edge count mode.
    	 * Set edge count capture interrupt on CCP1 input pin event both edges */
            HWREG(TIMER0_BASE + TIMER_O_TBMR) |=
       	(TIMER_TBMR_TBMR_CAP|TIMER_TBMR_TBMIE|TIMER_TBMR_TBILD|TIMER_TAMR_TAMRSU);
        /* Set load value GPTMTbILR to 960EPS(0x3C0) maximum edges.*/
        ROM_TimerLoadSet(TIMER0_BASE, TIMER_B, 0x3C0);    
        /* Set GPTM-0B TnMATCHR value. Count down
         * to TnMATCHR sets the timeout interrupt. */
        ROM_TimerMatchSet(TIMER0_BASE, TIMER_B, 0x1);
        /* Configure edge polarity capture events for CCP1. */
        ROM_TimerControlEvent(TIMER0_BASE, TIMER_B, TIMER_EVENT_BOTH_EDGES);
        /* Register the global interrupt in the controller */
        TimerIntRegister(TIMER3_BASE, TIMER_A, TMCCP1TacoEdgeCountInt);
        /* Set GPTM0-CnMIM=1 Capture Edge Counter reload interrupt.
         * Set TMaEN=1, (PWM down count match sets TMaEN=1). */
        ROM_TimerIntEnable(TIMER0_BASE, TIMER_CAPB_MATCH);
        ROM_IntEnable(INT_TIMER0B);
        ROM_TimerEnable(TIMER0_BASE, TIMER_B);
    
        /*****************************************************
         * GPTM-3: 32 bitwide 1000ms Oneshot timer
         * for generating 1 second Taco tick intervals.
         *****************************************************/
        /* Set GPTM-3A clock source 16Mhz */
        ROM_TimerClockSourceSet(TIMER3_BASE, TIMER_CLOCK_PIOSC);
        /* Set GPTM-3A for OneShot next timeout.  */
        ROM_TimerConfigure(TIMER3_BASE, TIMER_CFG_ONE_SHOT);
        /* Set GPTM-3A load value for 1000ms or 1 second. */
        ROM_TimerLoadSet(TIMER3_BASE, TIMER_A, 0xF42400);
        /* Enable interrupt to occur on timer timeout.  */
        ROM_TimerIntEnable(TIMER3_BASE, TIMER_TIMA_TIMEOUT);
        /* Enable 1 second oneshot taco tick timer */
        ROM_TimerEnable(TIMER3_BASE, TIMER_A);
        /* Register the global interrupt in the controller */
        TimerIntRegister(TIMER3_BASE, TIMER_A, FanSpeedTickHandler);
        ROM_IntEnable(INT_TIMER3A);
    
        /*****************************************************
         * Configures 102-213Hz PWM pulse generator:
         * Used for fan speed taco pulse (loop back) test.
         *
         * GPTM-4: 24 bitwide PWM CCP0 timer mode for
         * testing CCP GPTM0A-TnMATCHR interrupt handler.
         *******************************************************/
    	/* Disable GPTM-4A */
    	HWREG(TIMER4_BASE + TIMER_O_CTL) &= ~(TIMER_CTL_TAEN | TIMER_CTL_TBEN);
    	/* Set GPTM-4A clock source 16Mhz */
    	ROM_TimerClockSourceSet(TIMER4_BASE, TIMER_CLOCK_PIOSC);
    	/* Set GPTM-4A CTRL for a split half wide 16 bit timer */
    	HWREG(TIMER4_BASE + TIMER_O_CFG) = 0x4;
        /* GPTM 16 bit split prescaler 8 bit upper extension 23:16/24 bits
         * 120mHz:20Tc*0.548258ms=10.965ms | 16mHz:20Tc*62.5us=1.25ms */
        ROM_TimerPrescaleSet(TIMER4_BASE, TIMER_A, 0x0001);//102Hz=2
    	/* Set GPTM-4A mode register for periodic PWM down count. */
        HWREG(TIMER4_BASE + TIMER_O_TAMR) |=
        		(TIMER_CFG_A_PWM|TIMER_TAMR_TAAMS);//|TIMER_TAMR_TAMRSU
        /* Set GPTM-4A 102.7Hz Pulse rate: 0x64BE=3072 RPM. */
        /* Set GPTM-4A 213.7Hz Pulse rate: 0x26d0=6350 RPM. */
        ROM_TimerLoadSet(TIMER4_BASE, TIMER_A, 0x26d0);
    
        /** Disable timer when Not using taco wrap test **/
        ROM_TimerDisable(TIMER4_BASE, TIMER_A);
        /* Disable the NOT required PWM interrupt */
        ROM_IntEnable(INT_TIMER4A);
    	/* Set GPTM-4A match character value GPTMTaMATCHR
    	 * with PWM TM4 duty cycle (100%=9.740ms) where (N=240/95% duty)
    	 * Makes active high 15us NEG edge pulse width, 30ns rise/fall */
    	ROM_TimerMatchSet(TIMER4_BASE, TIMER_A, 240);
    

  • Arduino simplifies fan taco RPM conversion process into pin interrupts incrementing a single variable count.  While Cypress implements a 30kHz timer into the process of captured and comparing counts.  I tried to including the edge times into the above  edge counts which stabilize the low side frequency. How ever the best low to high RPM spread achievable even with added edge times was  only 3896 and often had out of range edge counts entering the RPM print out reading. There was no gain in adding edge timer reprogram TnILR during the edge counts interrupt other than the GPIO 53hz lower speed count had far less wandering from 49-62Hz. I think it may require what Cypress shows a 30kHz clock to work properly for RPM conversion in timer edge counting mode. 

    Offloading the taco signal into accumulated edge count and or edge times is about the hardest thing I have ever done with DSP.

    http://playground.arduino.cc/Main/ReadingRPM

    Fan Taco Cypress AN2087.pdf

  • How Arduino ever got a valid RPM reading after decrementing the (half_rotation) value backwards in the interrupt handler is simply stunning.

    It didn't work for me but is seems calling the RPM update from the interrupt was causing some timing issues. The alternative is to use another timer to call the RPM update function using the local frequency variable that was incremented in the count interrupt handler.

    The updated RPM code is posted in the first message body above and the edge count timer code is listed further down.

  • Modifications made to fan speed timers help yet may lack stability of 32Khz RTC being in the closed loop timer cycles.

    Both GPTM-0A and 3A were changed to use more stable clock source, PIOSC 16Mhz. Also reloading TnMATCHR register in the 1 second closed loop has reduced taco (RPM) attempting to following NVIC interrupt speed. A divergence between GPTM-0A edge counts and GPTM-3A One shot timer interrupts falsely add several hundred RPM onto measured speed when PWM0/ADC0 peripherals may add more NVIC interrupt traffic.

    The fan taco frequency load TbILR 960(0x3C0) roughly twice the number of matched edges/2 per second (TnMATCHR 480(0x1E0)) closed loop interrupts remain a constant in the 1 second taco tick interrupts and calls to convert revolutions per minute.

    What can be causing seemingly NVIC divergence in the closed timer loop and will the RTTCLK clock source to GPTM3 produce a more stable 1 second timer?

    BTW: Both GPTM interrupt priority are 0x40 where GPTM3A 32bit one shot timer has higher interrupt than 16bit GPTM0B edge counts timer.