Problems Synchronizing an Interval Timer to a PWM on a Teensy 4

Status
Not open for further replies.

Dave47

Member
For some reason, when I set up the interval timer, I am only getting it to fire once. The code I am trying to update for use with the T4 already works on T3.2, T3.5, and T3.6 with minor timing tweaks between them, but not yet on the T4. The new program is an update my Commodore 6581 (SID) player.

https://forum.pjrc.com/threads/42587-David-s-MOS6581-SID-network-player-(Teensy-3-2)?highlight=SID

I need a 1MHz clock and a timed chip select that lines up with the clock's rising edge and doesn't "walk" away from the clock between SID writes. That looks like the following:

IMG_2569.JPG

The code needed to do this on previous Teensys is this:

Code:
#define CLK 4
#define CS1 3
#define NOP __asm__("nop\n\t")



volatile long timetonext;
volatile byte dataavailable;
volatile unsigned long cycles_per_us;

static inline void delayCycles(uint32_t cycles)
{ // MIN return in 7 cycles NEAR 20 cycles it gives wait +/- 2 cycles - with sketch timing overhead
  uint32_t begin = ARM_DWT_CYCCNT - 12; // Overhead Factor for execution
  while (ARM_DWT_CYCCNT - begin < cycles) ; // wait
}

  
IntervalTimer myTimer;

void setup() {
  Serial.begin(115200);
  pinMode(CLK, OUTPUT);
  digitalWrite(CLK, HIGH);
  pinMode(CS1, OUTPUT);
  digitalWrite(CS1, HIGH);


  analogWriteFrequency(CLK, 1000000);               // set clock frequency to 1MHz
  analogWriteResolution(5);                         // PWM resolution
  analogWrite(CLK, 15);                             // PWM dutycycle


  myTimer.begin(DATA, 10);
  //myTimer.priority(64);
  cycles_per_us = F_BUS_ACTUAL / 1000000;
}

FASTRUN void loop() {                               
   while (1) {
    timetonext = 3;                                  //Change this to vary the number of CLK pulses between CS1 pulses                                   
    dataavailable = 1;
    PIT_LDVAL0 = timetonext * cycles_per_us - 1;     // reset the PIT timer in myTimer to the delay until the next write
    while (dataavailable == 1) {}                    // wait for myTimer to fire interrupt
  }
}

FASTRUN void DATA(void)
{
  if (dataavailable >= 1) {
 
    digitalWriteFast(CS1, LOW);

    // 200ns Pulse on Teensy 4 @ 600MHz
    delayCycles(45);
 
    dataavailable = 0;
    digitalWriteFast(CS1, HIGH);
  }
}

The variable "timetonext" is the number of microseconds to wait before firing the interrupt and can be any number between 0 and 65535. I used PIT_LDVAL0 to change the start of the timer on the fly and it worked very well. Also, don't worry if the chip select doesn't line up with the clock in the example since that can be fixed with different amounts of delayCycles either in the interrupt routine or the main routine.

Any ideas?

David
 
Repeating what was noted in https://forum.pjrc.com/threads/57609-Teensyduino-1-48-Beta-1?p=216399&viewfull=1#post216399
One problem you have is using F_BUS_ACTUAL. The PIT timer for T4 IntervalTimer runs at 24MHz, not F_BUS_ACTUAL. Also if you are not running 1.48-beta1, the first PIT channel you get for IntervalTimer is channel 2, so you need to use PIT_LDVAL2.
To avoid explicit register usage note that the IntervalTimer has a method myTimer.update(micros).

If you want a 200ns pulse on CS1, then you want delayCycles(120).

FASTRUN has no effect on T4 since the T4 startup code copies all of the program's FLASH to RAM.
 
Last edited:

Manitou, thanks for your help!

If you want a 200ns pulse on CS1, then you want delayCycles(120).

This was the issue... delayCycles(25) does NOT result in a 42ns pulse at pin 3. 25 looks more like 4-5ns, 60 only looks like 20ns, and 130 does look like 200ns. I was measuring this at a testpoint after it is level shifted by an NTE74HC139 decoder and a 5ns input pulse is effectively filtered out. I should have measured at the Teensy pin and I would have seen that it was only a blip.

To avoid explicit register usage note that the IntervalTimer has a method myTimer.update(micros).

I've checked this on the other Teensys and using myTimer.update(micros) took longer than the explicit register write. Sometimes, I need timetonext=1 and this doesn't work on T4 without the explicit call. timetonext=2 does work, though.

FASTRUN has no effect on T4 since the T4 startup code copies all of the program's FLASH to RAM.

Noted and removed.

I can now get a waveform with timetonext=3 that repeats like the one above. There is one new issue I see, though. When I restart the Teensy4 repeatedly, I do not get the same phase relationship between the PWM and the interval timer every time. I assume they are running off different clock domains with different startup characteristics. Is there any way to make them use the same clock and thus always start with the same relationship?

David
 
Status
Not open for further replies.
Back
Top