KL25Z Free Running Timer

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

KL25Z Free Running Timer

3,972 Views
ttahut
Contributor III

Hello,

 

I am trying to get a free running timer going on the FRDM KL25Z board.  I have included the component FreeCntr32 and attached it to TPM1.  I have been unsuccessful in getting it to run as a free running counter. No matter how I configure things, it stops after the counter rolls back to 0.  In addition, the OnCounterReset event gets fired everytime I call GetTimeMS() so using that as a trigger to reset and restart the counter won't work.  The third issue I have is that the counter seems to be counting at a 1ms rate when I have configured it for 10ms (it rolls back to zero after about a minute- or 65535 msecs).  What am I missing??? I have attached the KDS project that exhibits this behavior- thanks for any help.

 

Tom

Original Attachment has been moved to: TimerTest.zip

0 Kudos
Reply
5 Replies

1,895 Views
marek_neuzil
NXP Employee
NXP Employee

Hello Tom,

Let me explain how the application works:

You have FC321:FreeTimer32 working on TPM1 counter that is not fully allocated for this component  (see the property Component uses entire timer  = no). Therefore the TPM1 counter is running at period 10.923ms as you can see in the TU2:TimerUnit_LDD component (this component is used by the FC321:FreeCounter32). To count precisely ticks per 10ms as you have selected, the TPM1_C1V - channel 1 compare register is updated during each interrupt (when the compare register match the counter), i.e. it allows counting 10ms tick precisely.

FreeCount32 use RealTimeLDD component that implements TU2_OnChannel0() event routine that is invoked by the TPM1_C1V interrupt request (match of channel 1 compare register with the counter register). This routine counts TimerTicks (each 10ms) and also contains Overflow flag:

void TU2_OnChannel0(LDD_TUserData *UserDataPtr)
{
  RealTimeLdd1_TDeviceData *DeviceDataPrv = PE_LDD_DeviceDataList[PE_LDD_COMPONENT_RealTimeLdd1_ID];
  uint16_t Ticks;

  (void)UserDataPtr;                  /* Parameter is not used, suppress unused argument warning */
  (void)TU2_GetOffsetTicks(DeviceDataPrv->LinkedDeviceDataPtr, CHANNEL, &Ticks);
  Ticks += DeviceDataPrv->CmpVal;
  (void)TU2_SetOffsetTicks(DeviceDataPrv->LinkedDeviceDataPtr, CHANNEL, Ticks);
  DeviceDataPrv->TimerTicks++;         /* Increment counter of timer ticks */
  if (DeviceDataPrv->TimerTicks == 0U) { /* Testing counter overflow */
  DeviceDataPrv->Overflow = TRUE;    /* If yes then set overflow flag */
  }
}

The GetTimeMS method of FreeCounter32 return value in milisecond that is computed by using TimerTicks, see below:

LDD_TError RealTimeLdd1_GetTimeMS(LDD_TDeviceData *DeviceDataPtr, uint16_t *TimePtr)
{
  RealTimeLdd1_TDeviceData *DeviceDataPrv = (RealTimeLdd1_TDeviceData *)DeviceDataPtr;
  uint32_t CopyTicks;                  /* Working copy of variable TimerTicks */
  bool CopyOverflow;                  /* Working copy of variable Overflow */
  LDD_RealTime_Tfloat rtval;          /* Result of multiplication */

  /* {Default RTOS Adapter} Critical section begin, general PE function is used */
  EnterCritical();
  CopyTicks = DeviceDataPrv->TimerTicks; /* Loading actual number of timer ticks */
  CopyOverflow = DeviceDataPrv->Overflow; /* Loading actual state of "overflow flag" */
  /* {Default RTOS Adapter} Critical section end, general PE function is used */
  ExitCritical();
  if (CopyOverflow) {                  /* Testing counter overflow */
    return ERR_OVERFLOW;              /* If yes then error */
  }
rtval = CopyTicks * 10.0F;          /* Multiply ticks and clock configuration 0 coefficient */
  if (rtval > 0xFFFFUL) {              /* Is the result greater than 65535 ? */
    return ERR_MATH;                  /* If yes then error */
  }
  else {
    *TimePtr = (uint16_t)rtval;
  }
  return ERR_OK;
}

When the value of time in milisecond cannot be stored in 16bits value it is returned ERR_MATH. When the TimerTicks value overflow it is returned ERR_OVERFLOW. In both error cases the TimePtr value is not returned. Therefore you must execute the FC321_Reset() until ERR_MATH is returned by GetTimeMS method - it happens after 0xFFFF miliseconds (uint16_t type).

You can modify the code of the loop in the main function by the following way:

while (1)

  {

  if (lticksDiff(wTimeStart,lticksGetMS()) > 500)

  {

    printf(" Blinking...\n\r");

    PWM1_SetRatio16(wBrightness);

    if (wBrightness > 0)

    wBrightness= 0;

    else

    wBrightness= 5000;

    //wTimeStart= lticksGetMS();

    FC321_Reset();

    wTimeStart=0;

  }

  }

This change enable running the application for unlimited time.

Best Regards,

Marek Neuzil

0 Kudos
Reply

1,895 Views
mjbcswitzerland
Specialist V

Tom

With the KL25 you can use the SYSTICK, TPM, LPTRM or PITs as free running timers.

See http://www.utasker.com/docs/uTasker/uTaskerHWTimers.PDF

I had a quick look at your project but don't think that you have a problems with the TPM but instead have errors in the loop that you use because it is based on a 16 bit counter that looks to fail once it overfows.

I don't know the code used but, although named as FreeCntr32, it doesn't look to support more than 16 bit counting.

You can take a look at µTasker Kinetis FRDM-KL25Z support which will also work out-of-the-box with KDS but includes more powerful methods that those generated by PE and also allows you to simulate the KL25 (and its timers).

It is completely free for non-commerical work and there is fast-track support in the Kinetis forum.

Regards

Mark

Kinetis: µTasker Kinetis support

KL25: µTasker Kinetis FRDM-KL25Z support / µTasker Kinetis TWR-KL25Z48M support

For the complete "out-of-the-box" Kinetis experience and faster time to market

0 Kudos
Reply

1,895 Views
ttahut
Contributor III

The code simply flashes the LED on the FRDM board.  The flashing stops because when the counter rolls back to zero, it stops counting.  I'm perfectly fine with a 16 bit counter (at 1 ms or 10ms increments) and I can handle the roll over- that's not the issue.  The problem is it stops counting.

0 Kudos
Reply

1,895 Views
mjbcswitzerland
Specialist V

Tom

I ran the code and the TPM1 was still counting once the LED stopped blinking.

However when I look a litte deeper into the IDE project I have the feeling that it is not using the TPM but instead the real-time-clock.

In fact the code looks to be written to make it as difficult as possibe to actually follow what it is doing - if I expand the routine that you are using to get the elapsed time from a previous point in time (lticksGetMS()) it boils down to

uint16_t uiTimeNow;

RealTimeLdd1_GetTimeMS(((LDD_TDeviceData *)(PE_LDD_DeviceDataList[PE_LDD_COMPONENT_RealTimeLdd1_ID]), &uiTimeNow);

return(uiTimeNow);

whereby the initialisation code calls RealTimeLdd1_Init() which enters pointers and such to structs used by the RTC.

Reading the description in the RealTimeLdd1_GetTimeMS() header it says

** @brief
**     This method returns the time (as a 16-bit unsigned integer)
**    

in milliseconds since the last Reset method call.

and it looks like if the ms value is greater than 64k (ms) it won't return a new time value but instead will return an error "ERR_MATH" and the value returned by lticksGetMS() is effectively random.

Therefore

1. I don't think that you are using the TPM

2. the code involved is rather difficult to follow so not 100% sure, but the routine ltickGetMS() has no error handling and returning a random value if the RealTimeLdd1_GetTimeMS() fails - it should at least check the return value.

3. Finally it seems that the RTC call only works for up to 64s and need a reset (according to the methods header) so that it can be used again.

Looking at your code it seems like you really only want a time delay so there are much simpler methods to do this. The one presently used looks unsuitable and I would avoid it since its coding is of a quality which would not pass the most basic of reviews - returning random values is a huge issue even in non-safety critical applications!

I would use a decent method that allows the processor to use a low power mode between its switching operations and you can also reduce the processor's power consumption by easily 80% in the process.

Regards

Mark

Kinetis: µTasker Kinetis support

KL25: µTasker Kinetis FRDM-KL25Z support / µTasker Kinetis TWR-KL25Z48M support

For the complete "out-of-the-box" Kinetis experience and faster time to market


0 Kudos
Reply

1,895 Views
mjbcswitzerland
Specialist V

Tom

To illustrate how it is simple to do the same but much more efficienly I have configured the uTasker framework for the FRDM-KL25Z and set a timer task (this is using the LPTMR) to flash the LED at 1Hz (as you do). When the LED is on it also drives the PWM output at 500Hz with 90% duty cycle (the same as yours)

If you measure the current consumption of your code doing this it measures 11mA (36mW). This method requires 1.9mA (6.3mW > 80% power saving!!) to achieve the same.

Furthermode, if you connect the OpenSDA virtual COM that is writing "Blinking..." each 500ms you will find a menu and that the project inherits many more features from the framework without them needing to be configured.

Below is the code for your part.

Regards

Mark

Kinetis: µTasker Kinetis support

KL25: µTasker Kinetis FRDM-KL25Z support / µTasker Kinetis TWR-KL25Z48M support

For the complete "out-of-the-box" Kinetis experience and faster time to market



//Initialisation

//

PWM_INTERRUPT_SETUP pwm_setup;

pwm_setup.int_type = PWM_INTERRUPT;

pwm_setup.pwm_mode = (PWM_SYS_CLK | PWM_PRESCALER_16); // clock PWM timer from the system clock with /16 prescaler

pwm_setup.pwm_reference = (_TIMER_2 | 0); // timer module 2, channel 0 (red LED in RGB LED)

pwm_setup.pwm_frequency = PWM_TIMER_US_DELAY(TIMER_FREQUENCY_VALUE(500), 16);// generate 500Hz on PWM output

pwm_setup.pwm_value = _PWM_PERCENT(90, pwm_setup.pwm_frequency); // 90% PWM (high/low)

fnConfigureInterrupt((void *)&pwm_setup); // enter configuration for PWM

fnSetLowPowerMode(STOP_MODE); // dynamically set STOP mode when no activity

// Blink task (controlled by OS based on SYSTICK and configured to operate every 500ms)

//

extern void fnBlinkTask(TTASKTABLE *ptrTaskTable)

{

    if ((IS_POWERED_UP(5, SIM_SCGC5_PORTB) && (PORTB_PCR18 & PORT_MUX_MASK) == PORT_MUX_GPIO)) {

        _CONFIG_PERIPHERAL(B, 18, (PB_18_FTM2_CH0 | PORT_SRE_FAST | PORT_DSE_HIGH)); // configure PWM out

    }

    else {

        _CONFIG_DRIVE_PORT_OUTPUT_VALUE_FAST_HIGH(B, (PORTB_BIT18), (PORTB_BIT18), (PORT_ODE | PORT_SRE_SLOW | PORT_DSE_HIGH)); // drive '1' to output (LED off)

    }

}

0 Kudos
Reply