PITimer: PIT (Periodic Interrupt Timer) library for Teensy 3.0

Status
Not open for further replies.
You could always try editing the code to simply remove that 40 cycle limit.

I'm curious to hear how fast you can get the interrupt to run and still have even a little CPU left over for the main program? 40 cycles was merely a guess on my part, which has never been experimentally verified. If you do investigate this, please share your findings?
 
Recurring interrupt with interval < 1 microsecond is too frequent to be viable or useful. Anything on the order of 100 microseconds or less, recurring, would cost too much CPU time, and have too much latency.

If so, I might use the wrong approach in my code. But what's the right way to do it then?
The problem I have to solve is to switch a Laser on/off according to data in a bitfield buffer (i.e. not simple PWM but an arbitrary pattern). The switch timing is crucial.
During the switching the main program does virtually nothing, so it isn't an issue when the CPU doesn't have any time left to do much else.
Given this, what would be the best approach to switch a digital pin on/off (according to a bit value in a memory buffer) *exactly* each -say- 700ns?
In my current approach, I use IntervalTimer wich works fine so far down until ~950ns (I attached the source below).
If there's a different/better approach to do such thing (especially if it enables me to do shorter timing), please let me know.
Thanks!

---

This is my test program. It fills a buffer with sample data and then "outputs" the data once on a digitalPin:

Code:
// Create an IntervalTimer object 
IntervalTimer myTimer;

const int laserPin = 5;  // the pin with a LED

uint8_t scanBuffer[150];
const int kBufferSize = 150;

volatile uint8_t scanTemplate;
volatile uint8_t scanIndex;
volatile uint8_t nextPixelState = LOW;
volatile uint8_t nextBitIndex = 0;

void setup(void) {
  pinMode(laserPin, OUTPUT);

  // Fill scanBuffer with sample data
  for(int i=0;i<kBufferSize;i++)
    scanBuffer[i] = 0b10101010;
  
  // Start output
  startLaserLine();
  myTimer.begin(switchLaser, 0.822916667); 
}

void startLaserLine() {
  noInterrupts();
  scanIndex = 0;
  scanTemplate = scanBuffer[scanIndex];
   
  nextBitIndex = 7;
  nextPixelState = (scanTemplate & (1<<nextBitIndex))?HIGH:LOW;
  
  interrupts();
}

// ISR
void switchLaser(void) {
  // Output prepared bit on digital pin as first thing after the timer fired 
  digitalWriteFast(laserPin, nextPixelState);

  // Prepare the next bit
  if(nextBitIndex>0) {
    nextBitIndex--;
    nextPixelState = (scanTemplate & (1<<nextBitIndex))?HIGH:LOW;
  } else if(scanIndex<kBufferSize) {
    scanTemplate = scanBuffer[scanIndex++];
    nextBitIndex = 7;
    nextPixelState = (scanTemplate & (1<<nextBitIndex))?HIGH:LOW;
  } else {
    nextPixelState = LOW;
  }
}

// Do nothing in the main loop
void loop(void) {
}
 
I was wondering what the best way to change the PITimer interval period would be.

Per the datasheet, on page 904, it says
Timer Load Register (PIT_LDVALn) sets the timer start value. The timer will count down until it reaches 0, then it will generate an interrupt and load this register value again. Writing a new value to this register will not restart the timer; instead the value will be loaded after the timer expires. To abort the current cycle and start a timer period with the new value, the timer must be disabled and enabled.

On page 907,
The timers generate triggers at periodic intervals, when enabled. The timers load the start values as specified in their LDVAL registers, count down to 0 and then load the respective start value again. Each time a timer reaches 0, it will generate a trigger pulse and set the interrupt flag.

On page 909,
LDVAL trigger = (period / clock period) - 1

In their example they write to the register in hexadecimal.
50 MHz clock = 2 ns clock period
Interrupt @ 5.12 ms = 5.12 ms / 20 ns = 256,000 = 0x0003E7FF
PIT_LDVAL1 = 0x0003E7FF;

My first question is, can I write to the register with a decimal number and have it be the same as writing to the register in hexadecimal?

Is PIT_LDVAL1 = 0x0003E7FF the same as PIT_LDVAL1 = 256000 ?

Because I have tried to write the register with a decimal formatted number and have received very odd results. I am wonder if I am writing to the register correctly and if not, how do I write to the register?
 
@zaggo
I see I'm rather late in adding to this discussion, but if you truly need consistent timing, you need to use hardware. You can use something like a small FPGA to set up fixed timer/counters that increment/decrement on the smallest boundary of your switch timing and then set the CPU to use DMA to load the FPGA timer. You should double buffer the FPGA timer buffer so you stay ahead. You have probably solved this issue, but I thought I might throw in my $0.02.
 
Paul:
I am having a teensy problem with the IntervalTimer on Teensy3.1.
I have set up three instances of "IntervalTimer":

IntervalTimer calculateFuncTimer;
IntervalTimer printDataTimer;
IntervalTimer updateGPSTimeTimer;

status = calculateFuncTimer.begin(calculateFuncISR, 200000); // Calculate Wind Data ISR to run every 200 milliseconds.
if (status == false)
{
if (DEBUG) Serial.println("calculateFuncTimer initialization failed");
}
status = printDataTimer.begin(printDataISR, 500000); // Print Data ISR to run every 500 milliseconds.
if (status == false)
{
if (DEBUG) Serial.println("printDataTimer initialization failed");
}
status = updateGPSTimeTimer.begin(updateGPSTimeISR, 10000000); // Update GPS Time to run every 10 seconds.
if (status == false)
{
if (DEBUG) Serial.println("updateGPSTimeTimer initialization failed");
}

One with period set to 200000, one with period set to 500000, and one with 10000000 (10 million or 10 seconds). The first two work great, doing their thing at the proper time, I originally set this third one to 1000000 (1 million or 1 second) and it worked just fine.
But when I set the third for a 10 second period it does not work properly. It does not produce any error when the functions are set up. It fires continually every time thru the main loop.
I checked with the code in IntervalTimer.h to look a little closer at the Maximum number in microseconds allow and I have come up with this number as the Maximum allowed: 89,478,485 or 89 seconds.
The formula: MAX_PERIOD = UINT32_MAX / (F_BUS / 1000000.0) is from IntervalTimer.h, and when I plug these values (UINT32_MAX = 4,294,967,295) (F_BUS = 48000000) / 1000000 into it I come up with the above value.
Could you help me understand what I have done wrong?

Thanks,
Freddie
 
Yes, it does seem like up to 89 seconds should be allowed. I do not know why it's failing for you. It could be a bug in the code, or some other unexpected limitation.

As a general rule, I only investigate problems when a complete program is posted.
 
I was wondering what the best way to change the PITimer interval period would be.

Per the datasheet, on page 904, it says


On page 907,


On page 909,


In their example they write to the register in hexadecimal.


My first question is, can I write to the register with a decimal number and have it be the same as writing to the register in hexadecimal?

Is PIT_LDVAL1 = 0x0003E7FF the same as PIT_LDVAL1 = 256000 ?

Because I have tried to write the register with a decimal formatted number and have received very odd results. I am wonder if I am writing to the register correctly and if not, how do I write to the register?

Yes, you can write to it in decimal. Note that this value takes effect on the next timer cycle (from the F1RM: "Sets the timer start value. The timer will count down until it reaches 0, then it will generate an interrupt and load this register value again. Writing a new value to this register will not restart the timer; instead the value will be loaded after the timer expires. To abort the current cycle and start a timer period with the new value, the timer must be disabled and enabled again.");

I'm working on a very similar thing: I am trying to use the FrequencyCounter library, and have a suggestion to @loglow for an enhancement:

I'd like to calibrate the frequency counter (for variations in the 72 MHz Teensy crystal). My plan is to adjust this PIT_LDVALx value on just the 1st interrupt each cycle -- this gives me fine resolution (1 part in 36M, and a range of +/- 1000 ppm). I cloned the FreqCount.cpp files and inserted this:

index = gate_index + 1;
length = gate_length;
// 1st PIT interrupt can be adjusted ==> have option to finely calibrate by adjusting length
if (index == 1) PIT_LDVAL0=36000-1+20; else PIT_LDVAL0=36000-1;

if (index >= length) {
gate_index = 0;


In practice, the 36000 constant is variable depending on the Teensy clock frequency, (it's F_BUS); the '20' I have is just to show the impact of a change. In addition, I assume that FreqCount uses just the 1st PIT, but this is not generally true.

At present in the IntervalTimer lib, there is no supported way to return the index of the actual PIT used for the timer in FreqCount. It would be convenient for IntervalTimer() to return the index of the PIT used (now it just returns true or false); that would make my technique more general.
 
Status
Not open for further replies.
Back
Top