Problems with the duration of PITimer1 pulses on Teensy 3.1

Status
Not open for further replies.

Borja

Active member
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
}
}


IMG_6423.jpg
Captura de pantalla 2017-03-26 a las 19.15.04.jpg
 

Attachments

  • IMG_6421.jpg
    IMG_6421.jpg
    125.3 KB · Views: 80
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.
 
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?
 
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::priority(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!
 
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.
 
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:
I've updated the previous post. It is actually possible to get very low jitter for the PIT timer.
 
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.
 
Status
Not open for further replies.
Back
Top