Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 8 of 8

Thread: Problems with the duration of PITimer1 pulses on Teensy 3.1

  1. #1
    Member
    Join Date
    Feb 2017
    Location
    A Coruņa, Spain
    Posts
    28

    Problems with the duration of PITimer1 pulses on Teensy 3.1

    Good afternoon,

    I wrote a simple code with 3 pwm that generates a 0V signal during 55,55us (PWM1) after that 1,5us high and 54us low (PWM2) and finally 2us high and 53,5us low (PWM3) and these three PWMs are repeated non-stop.

    A brief explanation of my code. I load a value of the timer at the PIT_LDVAL1 and I wait for the set of the flag at PIT_TFLG1. When the flag is set clear the flag and the timer counts with a new value.

    I calculated the values for the PITimer1 in hexadecimal as PWM 1 (55us low), PWM2_1 (1,5us high), PWM2_2 (53us low), PWM3_1 (2us high) and PWM3_2 (53,5us low) with the Teensy at 72MHz, however the high pulses last around 3 and 4 us. For this reason I guess the PITimer1 of my Teensy is working at 36MHz not 72MHz. Why? How can I choose the frequency of the timer? The compiler configures the Teensy at 72MHz, doesn't it? Am I doing anything wrong?

    Thanks for the help!.




    #include <stdint.h>
    #include <mk20dx128.h>

    //Number of the pins
    #define MOSFET1 13
    #define MOSFET2 15
    #define MOSFET3 22
    #define MOSFET4 23

    //Duration of the pulses
    #define PWM1 0xFA0 // 55,55us at 72MHz

    #define PWM2_1 0x68 // // aprox.1,5us at 72MHz
    #define PWM2_2 0xF35 //

    #define PWM3_1 0x88 //// aprox.2us at 72MHz
    #define PWM3_2 0xF18 //





    void setup() {
    // put your setup code here, to run once:

    //Pins as output
    pinMode(MOSFET1,OUTPUT);
    pinMode(MOSFET2,OUTPUT);
    pinMode(MOSFET3,OUTPUT);
    pinMode(MOSFET4,OUTPUT);


    //Enable the clock for ports and timers
    SIM_SCGC6 |= (1<<24);
    SIM_SCGC6 |= (1<<25);
    SIM_SCGC6 |= (1<<23);
    SIM_SCGC5 |= (1<<13);
    SIM_SCGC5 |= (1<<12);
    SIM_SCGC5 |= (1<<11);
    SIM_SCGC5 |= (1<<10);
    SIM_SCGC5 |= (1<<9);
    SIM_SCGC5 |= (1<<5);

    //Pin 0,1,2,5 of PORTC as GPIO
    PORTC_PCR0=PORT_PCR_MUX(0x1);
    PORTC_PCR1=PORT_PCR_MUX(0x1);
    PORTC_PCR2=PORT_PCR_MUX(0x1);
    PORTC_PCR5=PORT_PCR_MUX(0x1);


    digitalWriteFast(MOSFET1,LOW);
    digitalWriteFast(MOSFET2,LOW);
    digitalWriteFast(MOSFET3,LOW);
    digitalWriteFast(MOSFET4,LOW);

    PIT_MCR=0x00; //PIT on
    PIT_TFLG1=0x01;//Clear the flag




    }

    void loop() {
    // put your main code here, to run repeatedly:



    PIT_LDVAL1=PWM1; //First value of the timer
    PIT_TCTRL1=0x03; //Start PIT1 and interrupts


    while(1)
    {

    /*---------------------------------------------------------------------------
    ----------------------------------PWM 1--------------------------------------
    ---------------------------------------------------------------------------*/
    PIT_LDVAL1=PWM2_1;//Load a new value for the next pulse
    digitalWriteFast(MOSFET3,LOW);
    digitalWriteFast(MOSFET1,HIGH);

    while((PIT_TFLG1 & (1<<0))==0)
    {
    //Wait until PIT overflows
    }

    /*---------------------------------------------------------------------------
    ----------------------------------PWM 2--------------------------------------
    ---------------------------------------------------------------------------*/
    digitalWriteFast(MOSFET3,HIGH);

    PIT_TFLG1=0x01;//Clear the flag TOF
    PIT_LDVAL1=PWM2_2;//Load a new value for the next pulse

    while((PIT_TFLG1 & (1<<0))==0)
    {
    //Wait until PIT overflows
    }

    digitalWriteFast(MOSFET3,LOW);

    PIT_TFLG1=0x01;//Clear the flag TOF
    PIT_LDVAL1=PWM3_1;//Load a new value for the next pulse


    while((PIT_TFLG1 & (1<<0))==0)
    {
    //Wait until PIT overflows
    }


    /*---------------------------------------------------------------------------
    ----------------------------------PWM 3--------------------------------------
    ---------------------------------------------------------------------------*/
    digitalWriteFast(MOSFET3,HIGH);

    PIT_TFLG1=0x01;//Clear the flag TOF
    PIT_LDVAL1=PWM3_2;//Load a new value for the next pulse

    while((PIT_TFLG1 & (1<<0))==0)
    {
    //Wait until PIT overflows
    }

    digitalWriteFast(MOSFET3,LOW);

    PIT_TFLG1=0x01;//Clear the flag TOF
    PIT_LDVAL1=PWM1;//Load the first value to restart the cycle of PWM's


    while((PIT_TFLG1 & (1<<0))==0)
    {
    //Wait until PIT overflows
    }

    PIT_TFLG1=0x01;//Clear the flag TOF
    }
    }


    Click image for larger version. 

Name:	IMG_6423.jpg 
Views:	50 
Size:	131.4 KB 
ID:	10125
    Click image for larger version. 

Name:	Captura de pantalla 2017-03-26 a las 19.15.04.jpg 
Views:	74 
Size:	130.0 KB 
ID:	10126
    Attached Thumbnails Attached Thumbnails Click image for larger version. 

Name:	IMG_6421.jpg 
Views:	24 
Size:	125.3 KB 
ID:	10124  

  2. #2
    Member
    Join Date
    Aug 2013
    Location
    Ohio
    Posts
    88
    Assuming several things here: 1) Teensy 3.1 or 3.2; 2) No special clock settings (there are several overclocking options). Given those two assumptions:

    The PIT runs at the bus clock frequency (36 MHz), which is half of the system clock frequency (72 MHz). Thus your report corresponds exactly with expectations. This is documented in the reference manual: In my copy ("K20 Sub-Family Reference Manual, Rev. 1.1, Dec 2012") the relevant table is on page 160, in section 5.7 "Module Clocks".

    Given that your code is using a software-based technique to set the timing of your output signal, there will inevitably be (sometimes serious) timing jitter due to various other system interrupt sources such as the SYSTICK 1 kHz system timer and possibly USB interrupts also near 1 kHz but scheduled by the USB host.

    Upon becoming more familiar with the Teensy environment, you may want to consider building a timing chain (still using the PIT if you wish) using interrupts; if you try that approach, remember to set the PIT interrupt service priority high enough that it is not itself interrupted by the other system interrupt sources.

  3. #3
    Member
    Join Date
    Feb 2017
    Location
    A Coruņa, Spain
    Posts
    28
    Thanks for the response Len.

    I read that section and the PITimer works with the bus clock and the default frequency is 36 MHz, so I can divide by 2 the numbers of the timers and the pulses will last what I want.

    About the interrupts I read the section NVIC but I don't know how to place on the top of priority the PITimers interrupts. How can I do it?

  4. #4
    Member
    Join Date
    Aug 2013
    Location
    Ohio
    Posts
    88
    There are a few things to know, the first of which is that a PIT control library exists which will help:

    <arduino_root>/hardware/teensy/avr/cores/teensy3/IntervalTimer.h -- Header file defining the InttervalTimer object, and several member functions

    To understand a bit more about it, you can also study the source, IntervalTimer.cpp, in the same directory. I strongly suggest that you look it over. It is not very complicated and is very informative. Look at IntervalTimer::begin() and IntervalTimer::beginCycles() to see what it does.

    In the NVIC, smaller numbers are higher priority, so an interrupt service function whose priority is a smaller number cannot be interrupted by an interrupt service function whose priority has a higher number. The number is in the range [0, 255] and only the high order 4 bits are used by the hardware when deciding whether and when to service an interrupt. The default interrupt priority for PIT timers is 128 (as can be seen in IntervalTimer.h). To set the priority, call IntervalTimer:riority(newpri).

    Using the Teensy IntervalTimer class does present one challenge: The library does not allow the caller to choose which of the 4 timers to use in the begin() function, so you may need to adjust the library code accordingly, or alternatively, construct your own class that does what you need using the knowledge to be learned from examining Paul's library.

    Our city is getting a thunderstorm right now (under a tornado watch too! :-) ) so I am ending this message now. Good luck with your project!

  5. #5
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,606
    Quote Originally Posted by Borja View Post
    About the interrupts I read the section NVIC but I don't know how to place on the top of priority the PITimers interrupts. How can I do it?
    You should really use IntervalTimer.

    But if you really want to do this the direct register way, use this to assign the priority:

    NVIC_SET_PRIORITY(IRQ_PIT_CH1, 0);

    and this to actually enable the interrupt with the NVIC:

    NVIC_ENABLE_IRQ(IRQ_PIT_CH1);

    You also need to enable the TIE bit in PIT_TCTRL1.

    Don't forget to put a pit1_isr() function somewhere in your program, and be sure to clear the flag so it interrupt doesn't keep getting called in an infinite loop.

    If you use IntervalTimer, all these details are handled automatically.

  6. #6
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    Keep in mind that interrupts have a fair amount of latency. Even if your interrupt has the highest priority, it can't run while interrupts are disabled.

    Below is test sketch that measures PIT timer interrupt latency / jitter. A typical measurement cycle on Teensy 3.1 running at 72MHz is:


    Measurements: 1000000
    cycles: min 37 max 43 avg 41
    Jitter: 0.08us


    ("cycles" are CPU clock cycles.)

    Latency is quite stable, but around 0.5us.

    Code:
    volatile uint32_t t_start = 0;
    volatile uint32_t t_end = 0;
    
    FASTRUN void setup() {
        Serial.begin(9600);
        delay(2000);
    
        Serial.println("Starting");
        
        SIM_SCGC6 |= SIM_SCGC6_PIT;
        PIT_MCR = 0x00;
        NVIC_ENABLE_IRQ(IRQ_PIT_CH1);
        NVIC_SET_PRIORITY(IRQ_PIT_CH1, 0); // highest priority
        
        PIT_LDVAL1 = 1; // set timer count
    
        uint32_t measurement_count = 0;
        uint32_t min_cycles = -1;
        uint32_t max_cycles = 0;
        uint32_t total_cycles = 0;
    
        while(true) {
            if(t_end == 0) {
                if(t_start == 0) {
                    noInterrupts()
                    t_start = SYST_CVR;
                    PIT_TCTRL1 |= 0x00000003; // enable Timer 1 interrupts (TIE = 1) & start Timer 1 (TEN = 0)
                    interrupts();
                    if(!t_start) {
                        // t_start is used as flag, we would hang if by coincidence t_start == 0
                        while(!t_end) ;
                        t_start = 0; t_end = 0;
                        continue;
                    }
                    continue;
                } else {
                    continue;
                }
            }
            uint32_t cycles = t_start - t_end;
            
            t_start = 0; t_end = 0;
            // throw away invalid measurements (SYST_CVR overflow)
            if(cycles > 10000) continue;
            measurement_count++;
            total_cycles += cycles;
            if(cycles < min_cycles) min_cycles = cycles;
            if(cycles > max_cycles) max_cycles = cycles;
            if(measurement_count >= 1000000) {
                Serial.print("Measurements: ");
                Serial.println(measurement_count);
                Serial.print("cycles: min "); 
                Serial.print(min_cycles);
                Serial.print(" max ");
                Serial.print(max_cycles);
                Serial.print(" avg ");
                Serial.println(total_cycles / measurement_count);
                Serial.print("Jitter: ");
                Serial.print(1000000.0f * (max_cycles - min_cycles) / F_CPU);
                Serial.println("us");
    
                min_cycles = -1;
                max_cycles = 0;
                measurement_count = 0;
                total_cycles = 0;
            }
        }
    }
    
    FASTRUN void pit1_isr(void){
        PIT_TCTRL1 = 0;  // stop timer
        t_end = SYST_CVR;
        PIT_TFLG1 = 1;   // ack interrupt
    }
    
    void loop() {}
    Last edited by tni; 03-28-2017 at 09:56 AM. Reason: Previous version had high jitter, since PIT start could be delayed by interrupt

  7. #7
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    I've updated the previous post. It is actually possible to get very low jitter for the PIT timer.

  8. #8
    Member
    Join Date
    Feb 2017
    Location
    A Coruņa, Spain
    Posts
    28
    Thank you for the responses.

    I try to update my code when I have a little free time.

    By the way writing 0x03 in PIT_TCTRL1 I am activating Interrupts for PIT1.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •