Using a timer on a Kinetis processor

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

Using a timer on a Kinetis processor

Jump to solution
4,889 Views
garylynch
Contributor IV

I wish to use a timer on a Kinetis processor. In particular I wish to equip my MK10FX512VLQ12 with a crystal or external oscillator and program it in such a way that I can generate an interrupt every millisecond (or 50 Hz cycle or 60 Hz cycle, or at 10:37:51 am EST every February 29). When I am done, I want to understand what I have constructed, so that when the thing doesn't work, I can troubleshoot it.

I don't want a debugged source deck that solves the problem for me, as it teaches me nothing.

I started with the "K10 Sub-Family Reference Manual" (Document Number: K10P32M50SF0RM, Rev. 2, Feb 2012). Within I found a number of peripherals that could be called timers:

  • The System Oscillator,
  • The RTC Oscillator,
  • The System Tick Timer,
  • The Programmable Delay Block,
  • The Multi-Purpose Clock Generator,
  • The FlexTimer,
  • The Low Power Timer.

There are other peripherals that affect the distribution of clock signals, especially:

  • The System Integration Module.

Each of these peripherals has a chapter showing how to program it, but I can find no bird's eye view showing how they are connected to each other, who provides a source signal for whom, in what order can I program them to get maximum flexibility?

Figure 5-1 on page 134 comes close to this goal, as it shows connections between 4 of the sub-systems listed above in an easy-to-grasp graphic.

Where do I find out how the rest interconnect?

This is part of a broader question which looks something like "How does NXP support engineers who want to understand this chip?"

Advance thanks for your insights.

Labels (1)
1 Solution
4,292 Views
mjbcswitzerland
Specialist V


Hi Gary

To cut down to what you have specified that you want to do in this case (ms interrupt and interrupt at a date/time) you can use a PIT (missing from your list and driven by bus clock so nothing else to worry about) and the RTC.

If you don't want to see any working code the best thing to do is connect with the debugger and set up the registers according to details in the user's manual until it does what you want (then you can learn all details) and subsequently write the code to do the same.

If you don't mind a bit of additional help just get the open source uTasker project from GitHub and use its simulator (which simulates the chip, the timers, interrupts, DMA, etc.) and enable its PIT and RTC so that you have the operation and then step the code and study the simulation so that you also get to know the internal operating details.

The timer and RTC operation (with UTC and Gregorian date, time zones and daylight saving) are detailed in
http://www.utasker.com/docs/uTasker/uTaskerHWTimers.PDF
and
http://www.utasker.com/docs/uTasker/uTasker_Time.pdf

Once you have mastered these you can move on to USB and TCP/IP or advanced topics like encryption and signal processing with the features in the Kinetis part in use or other more powerful ones.

Regards

Mark

Complete Kinetis solutions, training and support: http://www.utasker.com/kinetis.html
Kinetis K60/K10:
- http://www.utasker.com/kinetis/TWR-K60N512.html
- http://www.utasker.com/kinetis/TWR-K60D100M.html
- http://www.utasker.com/kinetis/TWR-K60F120M.html
- http://www.utasker.com/kinetis/ELZET80_NET-KBED.html
- http://www.utasker.com/kinetis/ELZET80_NET-K60.html

View solution in original post

11 Replies
4,292 Views
garylynch
Contributor IV

Thanks, Mark!

Using the Butcher algorithm (Have you patented it yet?) I was able to construct the following simplest possible program:

int main(void) {

    uint32_t counterVal;

    /* + + + + + + + + + + + + + + + + + + + */
    /*                                       */
    /* Assume clock frequency is 50 MHz.     */
    /*                                       */
    /* 1 msec = 50,000,000 x 0.001 = 50,000  */
    /* LDVAL gets 1 less than this. (§35.2)  */
    /*                                       */
    /* + + + + + + + + + + + + + + + + + + + */

  SIM->SCGC6 |= 0x800000u;       /* Turn on clock to PIT */

  PIT->MCR = 0x1;                /* Enable PIT timers                 */
                                 /* Let PIT timers stop in debug mode */

  PIT->CHANNEL[0].TFLG  = 0x1;   /* Reset the timer interrupt flag (TIF)  */
  PIT->CHANNEL[0].LDVAL = 49999; /* Set reload value to run 1 msec        */
  PIT->CHANNEL[0].TCTRL = 0x01;  /* Turn PIT timer 0 on, interrupts off  */

  for(;;) {
      counterVal = PIT->CHANNEL[0].CVAL; /* Read out counter value */
  }     /* END for(,,) */

  return 0;
}

I loaded this into my board and was able to see with the debugger that CVAL counts down, and when it rolls over, sets the timer interrupt flag.

I will attempt to use the same algorithm to enable interrupts, deploy an interrupt handler and do something I can measure inside: maybe toggle an LED at a frequency I can see.

Aldo,

Are you saying if I install your new IDE it will provide me with graphics that aren't in the data sheet? Does KDS 3.1 offer me anything? I already have it installed an know how to use it.

I will report back after I try this. Thanks again.

0 Kudos
Reply
4,292 Views
mjbcswitzerland
Specialist V

Gary

This turns it into interrupt driven.

Regards

Mark

    int main(void) {
        uint32_t counterVal;

        /* + + + + + + + + + + + + + + + + + + + */
        /*                                       */
        /* Assume clock frequency is 50 MHz.     */
        /*                                       */
        /* 1 msec = 50,000,000 x 0.001 = 50,000  */
        /* LDVAL gets 1 less than this. (§35.2)  */
        /*                                       */
        /* + + + + + + + + + + + + + + + + + + + */

      SIM->SCGC6 |= 0x800000u;       /* Turn on clock to PIT */

      PIT->MCR = 0x1;                /* Enable PIT timers                 */
                                     /* Let PIT timers stop in debug mode */

      _CONFIG_DRIVE_PORT_OUTPUT_VALUE(A, (PORTA_BIT28), (PORTA_BIT28), (PORT_SRE_SLOW | PORT_DSE_HIGH));; // configure and drive LED output to 1

      fnEnterInterrupt(irq_PIT0_ID, 3, _PIT0_Interrupt); // enter interrupt handler for this PIT at priority 3

      PIT->CHANNEL[0].TFLG  = 0x1;   /* Reset the timer interrupt flag (TIF)  */
      PIT->CHANNEL[0].LDVAL = 49999; /* Set reload value to run 1 msec        */
      PIT->CHANNEL[0].TCTRL = 0x03;  /* Turn PIT timer 0 on, interrupts on   */

      for(;;) {
          counterVal = PIT->CHANNEL[0].CVAL; /* Read out counter value */
      }     /* END for(,,) */

      return 0;
    }

static void _PIT0_Interrupt(void)
{
    PIT->CHANNEL[0].TFLG= 0x00000001;      // clear the interrupt flag
    _TOGGLE_PORT(A, PORTA_BIT28);          // toggle to show periodic interrupt
}
0 Kudos
Reply
4,292 Views
garylynch
Contributor IV

Ok, I have a first draft of my own creation.

All the Kinetis documentation I have found says you don't have to do anything
special to write an ISR--that it can look just like a function. But this doesn't
tell me how the processor associates one of my functions with a particular
interrupt vector.

I found a student help file at the University of Texas that said the interrupt
vector table was in file Startup.s. My environment has a pre-defined assembly
language file called startup_MK10F12.s which contains such a table and the entry
for PIT channel 0 is named PIT0_IRQHandler, so that is the name I used for my
handler, below.

My board has an LED on bit 19 of Port E.

  uint32_t lEDState, n;

  /* ===================================================== */
  /*                                                       */
  /* Function:                                             */
  /*   int main(void)                                      */
  /*                                                       */
  /* Purpose:                                              */
  /*   Initialize hardware & wait for interrupt.           */
  /*                                                       */
  /* ===================================================== */

  int main(void) {

    uint32_t counterVal;

    SystemCoreClockUpdate();

    SIM->SCGC5 |= SIM_SCGC5_PORTE_MASK;   /* Enable clock to PORTE */
    SIM->SCGC6 |= 0x800000u;              /* and PIT               */

            /* + + + + + + + + + + + */
            /* Configure PORTE, bit  */
            /* 19 as output          */
            /* + + + + + + + + + + + */
    PTE->PDDR = 0x000800000;

        /* + + + + + + + + + + + + + + + + + + + + */
        /*                                         */
        /* Configure PORTE, bit 19 PCR as:         */
        /* - Int stat flag to off (0)              */
        /* - Int config to disabled (000)          */
        /* - Lock reg to unlocked (0)              */
        /* - MUX to alt 001 (GPIO)                 */
        /* - Drive strength enable to low (0)      */
        /* - Open drain enable to off (0)          */
        /* - Passive filter enable to off (0)      */
        /* - Slew rate enable to fast (0)          */
        /* - Pull enable to off (0)                */
        /* - Pull select to down (0)               */
        /*                                         */
        /* 3322 2222 2222 1111 1111 11             */
        /* 1098 7654 3210 9876 5432 1098 7654 3210 */
        /* ---- ---- ---- ---- ---- ---- ---- ---- */
        /* ···· ···· ···· IIII ···· ···· ···· ···· */
        /* ···· ···· ···· RRRR ···· ·MMM ···· ···· */
        /* ···· ···I ···· QQQQ ···· ·UUU ·DOP ·S·· */
        /* ···· ···S ···· CCCC L··· ·XXX ·SDF ·RPP */
        /* ···· ···F ···· 3210 K··· ·210 ·EEE ·EES */
        /* –––– –––– –––– –––– –––– –––– –––– –––– */
        /* 0000 0000 0000 0000 0000 0001 0000 0000 */
        /*                                         */
        /*            = 0x0000 0100                */
        /* + + + + + + + + + + + + + + + + + + + + */
    PORTE->PCR[19] = 0x00000100;

    n = 0;
    lEDState = LED_OFF;              /* Start with LED off */
    PTE->PCOR = 0x000800000;         /* Turn all LEDs off     */

    PIT->MCR = 0x1;                  /* Enable PIT timers                 */
                                     /* Let PIT timers stop in debug mode */

        /* + + + + + + + + + + + + + + + + + + + */
        /* Assume clock frequency is 50 MHz.     */
        /*                                       */
        /* 1 msec = 50,000,000 x 0.1 = 5,000,000 */
        /* LDVAL gets 1 less than this. (§35.2)  */
        /* + + + + + + + + + + + + + + + + + + + */
    PIT->CHANNEL[0].LDVAL = 4999999; /* Set reload value to run 100 msec     */
    PIT->CHANNEL[0].TFLG  = 0x1;     /* Reset the timer interrupt flag (TIF) */

    NVIC->ICPR[0] = 0x40000000;      /* Clear pending PI timer, ch 0 interrupt */
    NVIC->ISER[0] = 0x40000000;      /* Enable PI timer, ch 0 interrupt        */

    PIT->CHANNEL[0].TCTRL = 0x03;    /* Turn PIT timer 0 on, interrupts on     */

    for(;;) {
      counterVal = PIT->CHANNEL[0].CVAL; /* Read out counter value */
      if(counterVal < 100) {
          n = n + 1;         /* Address to set a breakpoint */
      }
    }   /* END for(,,) */

    return 0;
  } /* END main() */

  /* ===================================================== */
  /*                                                       */
  /* Function:                                             */
  /*   void PIT0_IRQHandler(void)                          */
  /*                                                       */
  /* Purpose:                                              */
  /*   Turns red LED on or off every 100 msec.             */
  /*                                                       */
  /* ===================================================== */

  void PIT0_IRQHandler(void) {

      PIT->CHANNEL[0].TFLG  = 0x1;     /* Reset the timer interrupt flag (TIF) */

    if(lEDState == LED_OFF) {
      PTE->PSOR = 0x000800000; /* LED is on when output high */
      lEDState = LED_ON;       /* Indicate LED on            */
    } else {
      PTE->PCOR = 0x000800000; /* LED is off when output low */
      lEDState = LED_OFF;      /* Indicate LED off           */
    }
    return;

  } /* END PIT0_IRQHandler() */

As before, the counter counts down to zero. When it re-loads bit 0 of the
PIT->CHANNEL[0].TFLG register gets set.

If I had programmed everything correctly, I would simultaneously see bit 30 of the
NVIC->ICPR[0] or NVIC->ISPR[0] registers get set, (the circuit from the PIT to the
NVIC is one of those paths that I can't find) but this doesn't happen, and my
handler never gets entered.

That's as much as I've been able to figure out on my own.

0 Kudos
Reply
4,292 Views
mjbcswitzerland
Specialist V

Gary

Ensure that you enable interrupts globally using

asm("cpsie   i");

or similar.

Regards

Mark

0 Kudos
Reply
4,292 Views
mjbcswitzerland
Specialist V

Further tips:


In NVIC IRQ51 priority register is written with the interrupt priority ((0..15 << 4)
And unmasked with
IRQ32_63_SER = 0x00010000;

When IRQ fires it is visible in NVIC IRQ32_63_CPR as 0x00010000
and causes the PIT handler to be called.

You can also use
PTE->PTOR
to toggle the output rather than two different instructions.

Regards

Mark

0 Kudos
Reply
4,292 Views
garylynch
Contributor IV

Thanks, Mark.

I got the "cpsie i" instruction to 'compile' (but had to change your directive to __asm()). Sadly, it did not beget any interrupts.

I cannot follow your math regarding the interrupt priority register used by the PI timer.

The K10 Sub-Family Reference Manual has a table beginning on page 61 which lists 46 sources to which an IRQ number is assigned. The PI timer is assigned IRQ number 30. It then states you have to calculate the interrupt priority register number from the equation IPR# = IRQ div 4. For 30 this would be 7. It implies you access the priority registers indirectly via the following registers:

  • NVICISPRx
  • NVICICPRx
  • NVICIABRx
  • NVICIPRx

although it does not explain what the acronyms mean or what the registers do: anywhere.

It pictures the IP registers as 32 bits wide.

It also admits its treatment of the NVIC is incomplete and if I want to know the details I should get them from http://www.arm.com.

From ARM I tracked down a Cortex M4 Generic User Guide. On page 219 it presents a table of these registers with slightly different abbreviations but defines what the acronyms mean:

  • NVIC_ISERx  Interrupt Set-enable Registers
  • NVIC_ICERx  Interrupt Clear-enable Registers
  • NVIC_ISPRx  Interrupt Set-pending Registers
  • NVIC_ICPRx  Interrupt Clear-pending Registers
  • NVIC_IABRx  Interrupt Active Bit Registers
  • NVIC_IPRx   Interrupt Priority Register

Here as well, the IP registers are described as 32-bit registers.

In both sources, there are 8 registers in each set, numbered 0-7, with the exception of the NVIC_IPRx, which the Generic User Guide numbers at 60: index 0-59. It gives no clue what peripherals are mapped to what bits. I presume this is because each manufacturer configures it differently.

So ARM was no help with the PI timer at all.

Finally, I brought up the debugger under KDS and looked at the peripheral registers offered under NVIC. I found:

  • NVICISER0-3
  • NVICICER0-3
  • NVICISPR0-3
  • NVICICPR0-3
  • NVICIABR0-3
  • NVICIP0-105.

In this depiction the registers are only 8 bits wide.

There is quite a lot of discord among my sources. Before I can get this straight I have to decide whom to believe.

0 Kudos
Reply
4,292 Views
mjbcswitzerland
Specialist V

Gary

The Kinetis parts use Cortex processor cores from ARM and the ARM documents are what need to be followed apart from when the user's manual overrides it - generally there is little Cortex information in the user manuals since it would otherwise just be a duplication of the ARM details (and unnecessarily take up masses of additional space).

The priority registers can be accessed as byte or long word so you can chose how you want to do it. It is practical to do it as byte because then you can set a pointer to the first register location and increment it by the interrupt ID number to get to the priority byte of interest.

However there is a twist: this is true for your K10 processor with Cortex-M4 core but will not work on a Cortex-M0+ core (as used in KL parts, for example) since the priority registers can be read a bytes or words but ONLY written as long words (!).....this needs to be respected when designing code that is compatible across all the parts.

Regards

Mark

0 Kudos
Reply
4,292 Views
garylynch
Contributor IV

Mark,

I have held off replying because I wanted to try my own solution first. But it's not working yet so I'll make a couple of comments.
 
 1. Your solution does a lot inside of function calls which are invisible to me. I can't tell how much work is hiding behind
      "fnEnterInterrupt(irq_PIT0_ID, 3, _PIT0_Interrupt);".

 2. The statement above implies that you are changing the interrupt vector table, which my research says defaults to flash. Does this mean you have copied the table to RAM before entering main(), or is it inside one of those other functions?

I will have more to say once I have tested my own version.

0 Kudos
Reply
4,292 Views
mjbcswitzerland
Specialist V

Gary

You can find the function calls and macros in the open source uTasker project at Github if you want to compare them.

fnEnterInterrupt() sets the interrupt priority and enables it in the NVIC and so is needed whether you have interrupt vectors located in Flash or in SRAM. Passing a vector for insertion in SRAM is optional but is more flexible (and faster in operation) than fixed ones in Flash, in which case the fixed handler has to be hard coded there. The vector table offset register are changed to point to the SRAM location before main(), as well as priming default handlers to be called in case one forgets to correctly set peripheral ones, plus hard fault error handlers.

Regards

Mark

0 Kudos
Reply
4,292 Views
AldoG
NXP TechSupport
NXP TechSupport

Hello,

 

Another option could be to use MCUXpresso Config tools with our SDK to evaluate different NXP products,

 

Pins Tool

Assigns internal signals to external pins, sets electrical properties, I/O conflict resolution options and generates ANSI-C source code that drops into the MCUXpresso SDK environment.

 

Clocks Tool

For a graphical representation of the MCU clock tree system and interactive user controls as well as assistance with system fine-tuning.

 

Peripherals Tool

Enables selection of desired peripherals (i.e. UART, ADC, SPI, etc.) and generates initialization code, and configures higher level application code for USB projects.

 

All tools and runtime software are provided free-of-charge. The assembly and C source code are provided under permissive open-source licensing.

 

As I see you are interested in the clock configuration, you can check this out at the Clocks Tool, you can also take a look to our SDK in which you can find open source drivers, middleware, and reference example applications for your target MCU.

 

You can check this out at:

MCUXpresso config tools

MCUXpresso Config Tools|Software Development for NXP Microcontrollers (MCUs) | NXP 

 

MCUXpresso SDK builder

Welcome | MCUXpresso SDK Builder 

 

Regards,

Aldo.

0 Kudos
Reply
4,293 Views
mjbcswitzerland
Specialist V


Hi Gary

To cut down to what you have specified that you want to do in this case (ms interrupt and interrupt at a date/time) you can use a PIT (missing from your list and driven by bus clock so nothing else to worry about) and the RTC.

If you don't want to see any working code the best thing to do is connect with the debugger and set up the registers according to details in the user's manual until it does what you want (then you can learn all details) and subsequently write the code to do the same.

If you don't mind a bit of additional help just get the open source uTasker project from GitHub and use its simulator (which simulates the chip, the timers, interrupts, DMA, etc.) and enable its PIT and RTC so that you have the operation and then step the code and study the simulation so that you also get to know the internal operating details.

The timer and RTC operation (with UTC and Gregorian date, time zones and daylight saving) are detailed in
http://www.utasker.com/docs/uTasker/uTaskerHWTimers.PDF
and
http://www.utasker.com/docs/uTasker/uTasker_Time.pdf

Once you have mastered these you can move on to USB and TCP/IP or advanced topics like encryption and signal processing with the features in the Kinetis part in use or other more powerful ones.

Regards

Mark

Complete Kinetis solutions, training and support: http://www.utasker.com/kinetis.html
Kinetis K60/K10:
- http://www.utasker.com/kinetis/TWR-K60N512.html
- http://www.utasker.com/kinetis/TWR-K60D100M.html
- http://www.utasker.com/kinetis/TWR-K60F120M.html
- http://www.utasker.com/kinetis/ELZET80_NET-KBED.html
- http://www.utasker.com/kinetis/ELZET80_NET-K60.html