Forum Rule: Always post complete source code & details to reproduce any issue!
Page 11 of 12 FirstFirst ... 9 10 11 12 LastLast
Results 251 to 275 of 295

Thread: TeensyTimerTool

  1. #251
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    7,429
    @luni
    I did a kludge to DCCWaveform.cpp to use the IntervalTImer for T3.x and GPT for T4.1. I just did a test with the arduino motor shield with a T3.5 and hook up a LogicAnalzer to pin2 power and pin 12 (signal) and got the following waveform:
    Click image for larger version. 

Name:	Capture.jpg 
Views:	54 
Size:	31.2 KB 
ID:	23453

    The short pulses are 58.1us and the long about 116us - think within specs. 58 = digit 1 and 116 = digit 0. And Power pin goes high when I turn on track power. So think the kludge worked. Tomorrow may hook up a test track and see what happens. Here is the kludge (looking forward to see what you come up with):

    Code:
    /*
     *  ę 2020, Chris Harlow. All rights reserved.
     *  ę 2020, Harald Barth.
     *  
     *  This file is part of Asbelos DCC API
     *
     *  This is free software: you can redistribute it and/or modify
     *  it under the terms of the GNU General Public License as published by
     *  the Free Software Foundation, either version 3 of the License, or
     *  (at your option) any later version.
     *
     *  It is distributed in the hope that it will be useful,
     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     *  GNU General Public License for more details.
     *
     *  You should have received a copy of the GNU General Public License
     *  along with CommandStation.  If not, see <https://www.gnu.org/licenses/>.
     */
    #include <Arduino.h>
    
    #include "DCCWaveform.h"
    #include "DIAG.h"
     
    const int NORMAL_SIGNAL_TIME=58;  // this is the 58uS DCC 1-bit waveform half-cycle 
    const int SLOW_SIGNAL_TIME=NORMAL_SIGNAL_TIME*512;
    
    DCCWaveform  DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true);
    DCCWaveform  DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false);
    
    
    bool DCCWaveform::progTrackSyncMain=false; 
    bool DCCWaveform::progTrackBoosted=false;
    
    #if !defined(TEENSYDUINO)
    VirtualTimer * DCCWaveform::interruptTimer=NULL;      
    
    
    void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver, byte timerNumber) {
      mainTrack.motorDriver=mainDriver;
      progTrack.motorDriver=progDriver;
    
      mainTrack.setPowerMode(POWERMODE::OFF);      
      progTrack.setPowerMode(POWERMODE::OFF);
      switch (timerNumber) {
        case 1: interruptTimer= &TimerA; break;
        case 2: interruptTimer= &TimerB; break;
    #ifndef ARDUINO_AVR_UNO  
        case 3: interruptTimer= &TimerC; break;
    #endif    
        default:
          DIAG(F("\n\n *** Invalid Timer number %d requested. Only 1..3 valid.  DCC will not work.*** \n\n"), timerNumber);
          return;
      }
      interruptTimer->initialize();
      interruptTimer->setPeriod(NORMAL_SIGNAL_TIME); // this is the 58uS DCC 1-bit waveform half-cycle
      interruptTimer->attachInterrupt(interruptHandler);
      interruptTimer->start();
    
    }
    
    void DCCWaveform::setDiagnosticSlowWave(bool slow) {
      interruptTimer->setPeriod(slow? SLOW_SIGNAL_TIME : NORMAL_SIGNAL_TIME);
      interruptTimer->start(); 
      DIAG(F("\nDCC SLOW WAVE %S\n"),slow?F("SET. DO NOT ADD LOCOS TO TRACK"):F("RESET")); 
    }
    
    #else
    
    void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) {
      mainTrack.motorDriver=mainDriver;
      progTrack.motorDriver=progDriver;
    
      mainTrack.setPowerMode(POWERMODE::OFF);      
      progTrack.setPowerMode(POWERMODE::OFF);
    
      // Initialise timer1 to trigger every 58us (NORMAL_SIGNAL_TIME)
      TimerA.beginPeriodic(interruptHandler, NORMAL_SIGNAL_TIME);
    
    
    
    }
    
    void DCCWaveform::setDiagnosticSlowWave(bool slow) {
      TimerA.beginPeriodic(interruptHandler, slow? SLOW_SIGNAL_TIME : NORMAL_SIGNAL_TIME);
      DIAG(F("\nDCC SLOW WAVE %S\n"),slow?F("SET. DO NOT ADD LOCOS TO TRACK"):F("RESET")); 
    }
    #endif
    
    
    
    
    
    void DCCWaveform::loop() {
      mainTrack.checkPowerOverload();
      progTrack.checkPowerOverload();
    }
    
    
    // static //
    void DCCWaveform::interruptHandler() {
      // call the timer edge sensitive actions for progtrack and maintrack
      bool mainCall2 = mainTrack.interrupt1();
      bool progCall2 = progTrack.interrupt1();
    
      // call (if necessary) the procs to get the current bits
      // these must complete within 50microsecs of the interrupt
      // but they are only called ONCE PER BIT TRANSMITTED
      // after the rising edge of the signal
      if (mainCall2) mainTrack.interrupt2();
      if (progCall2) progTrack.interrupt2();
    }
    
    
    // An instance of this class handles the DCC transmissions for one track. (main or prog)
    // Interrupts are marshalled via the statics.
    // A track has a current transmit buffer, and a pending buffer.
    // When the current buffer is exhausted, either the pending buffer (if there is one waiting) or an idle buffer.
    
    
    // This bitmask has 9 entries as each byte is trasmitted as a zero + 8 bits.
    const byte bitMask[] = {0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
    
    
    DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) {
      // establish appropriate pins
      isMainTrack = isMain;
      packetPending = false;
      memcpy(transmitPacket, idlePacket, sizeof(idlePacket));
      state = 0;
      // The +1 below is to allow the preamble generator to create the stop bit
      // fpr the previous packet. 
      requiredPreambles = preambleBits+1;  
      bytes_sent = 0;
      bits_sent = 0;
      sampleDelay = 0;
      lastSampleTaken = millis();
      ackPending=false;
    }
    
    POWERMODE DCCWaveform::getPowerMode() {
      return powerMode;
    }
    
    void DCCWaveform::setPowerMode(POWERMODE mode) {
    
      // Prevent power switch on with no timer... Otheruise track will get full power DC and locos will run away.  
      if (!interruptTimer) return; 
      
      powerMode = mode;
      bool ison = (mode == POWERMODE::ON);
      motorDriver->setPower( ison);
    }
    
    
    void DCCWaveform::checkPowerOverload() {
      
      static int progTripValue = motorDriver->mA2raw(TRIP_CURRENT_PROG); // need only calculate once, hence static
    
      if (millis() - lastSampleTaken  < sampleDelay) return;
      lastSampleTaken = millis();
      int tripValue= motorDriver->getRawCurrentTripValue();
      if (!isMainTrack && !ackPending && !progTrackSyncMain && !progTrackBoosted)
        tripValue=progTripValue;
      
      switch (powerMode) {
        case POWERMODE::OFF:
          sampleDelay = POWER_SAMPLE_OFF_WAIT;
          break;
        case POWERMODE::ON:
          // Check current
          lastCurrent = motorDriver->getCurrentRaw();
          if (lastCurrent <= tripValue) {
            sampleDelay = POWER_SAMPLE_ON_WAIT;
    	if(power_good_counter<100)
    	  power_good_counter++;
    	else
    	  if (power_sample_overload_wait>POWER_SAMPLE_OVERLOAD_WAIT) power_sample_overload_wait=POWER_SAMPLE_OVERLOAD_WAIT;
          } else {
            setPowerMode(POWERMODE::OVERLOAD);
            unsigned int mA=motorDriver->raw2mA(lastCurrent);
            unsigned int maxmA=motorDriver->raw2mA(tripValue);
            DIAG(F("\n*** %S TRACK POWER OVERLOAD current=%d max=%d  offtime=%l ***\n"), isMainTrack ? F("MAIN") : F("PROG"), mA, maxmA, power_sample_overload_wait);
    	power_good_counter=0;
            sampleDelay = power_sample_overload_wait;
    	if (power_sample_overload_wait >= 10000)
    	    power_sample_overload_wait = 10000;
    	else
    	    power_sample_overload_wait *= 2;
          }
          break;
        case POWERMODE::OVERLOAD:
          // Try setting it back on after the OVERLOAD_WAIT
          setPowerMode(POWERMODE::ON);
          sampleDelay = POWER_SAMPLE_ON_WAIT;
          break;
        default:
          sampleDelay = 999; // cant get here..meaningless statement to avoid compiler warning.
      }
    }
    
    
    
    
    
    // process time-edge sensitive part of interrupt
    // return true if second level required
    bool DCCWaveform::interrupt1() {
      // NOTE: this must consume transmission buffers even if the power is off
      // otherwise can cause hangs in main loop waiting for the pendingBuffer.
      switch (state) {
        case 0:  // start of bit transmission
          setSignal(HIGH);
          state = 1;
          return true; // must call interrupt2 to set currentBit
    
        case 1:  // 58us after case 0
          if (currentBit) {
            setSignal(LOW);
            state = 0;
          }
          else  {
            setSignal(HIGH);  // jitter prevention
            state = 2;
          }
          break;
        case 2:  // 116us after case 0
          setSignal(LOW);
          state = 3;
          break;
        case 3:  // finished sending zero bit
          setSignal(LOW);  // jitter prevention
          state = 0;
          break;
      }
    
      // ACK check is prog track only and will only be checked if 
      // this is not case(0) which needs  relatively expensive packet change code to be called.
      if (ackPending) checkAck();
    
      return false;
    
    }
    
    void DCCWaveform::setSignal(bool high) {
      if (progTrackSyncMain) {
        if (!isMainTrack) return; // ignore PROG track waveform while in sync
        // set both tracks to same signal
        motorDriver->setSignal(high);
        progTrack.motorDriver->setSignal(high);
        return;     
      }
      motorDriver->setSignal(high);
    }
          
    void DCCWaveform::interrupt2() {
      // set currentBit to be the next bit to be sent.
    
      if (remainingPreambles > 0 ) {
        currentBit = true;
        remainingPreambles--;
        return;
      }
    
      // beware OF 9-BIT MASK  generating a zero to start each byte
      currentBit = transmitPacket[bytes_sent] & bitMask[bits_sent];
      bits_sent++;
    
      // If this is the last bit of a byte, prepare for the next byte
    
      if (bits_sent == 9) { // zero followed by 8 bits of a byte
        //end of Byte
        bits_sent = 0;
        bytes_sent++;
        // if this is the last byte, prepere for next packet
        if (bytes_sent >= transmitLength) {
          // end of transmission buffer... repeat or switch to next message
          bytes_sent = 0;
          remainingPreambles = requiredPreambles;
    
          if (transmitRepeats > 0) {
            transmitRepeats--;
          }
          else if (packetPending) {
            // Copy pending packet to transmit packet
            for (int b = 0; b < pendingLength; b++) transmitPacket[b] = pendingPacket[b];
            transmitLength = pendingLength;
            transmitRepeats = pendingRepeats;
            packetPending = false;
            sentResetsSincePacket=0;
          }
          else {
            // Fortunately reset and idle packets are the same length
            memcpy( transmitPacket, isMainTrack ? idlePacket : resetPacket, sizeof(idlePacket));
            transmitLength = sizeof(idlePacket);
            transmitRepeats = 0;
            if (sentResetsSincePacket<250) sentResetsSincePacket++;
          }
        }
      }  
    }
    
    
    
    // Wait until there is no packet pending, then make this pending
    void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repeats) {
      if (byteCount >= MAX_PACKET_SIZE) return; // allow for chksum
      while (packetPending);
    
      byte checksum = 0;
      for (int b = 0; b < byteCount; b++) {
        checksum ^= buffer[b];
        pendingPacket[b] = buffer[b];
      }
      pendingPacket[byteCount] = checksum;
      pendingLength = byteCount + 1;
      pendingRepeats = repeats;
      packetPending = true;
      sentResetsSincePacket=0;
    }
    
    int DCCWaveform::getLastCurrent() {
       return lastCurrent;
    }
    
    // Operations applicable to PROG track ONLY.
    // (yes I know I could have subclassed the main track but...) 
    
    void DCCWaveform::setAckBaseline() {
          if (isMainTrack) return;
          int baseline = motorDriver->getCurrentRaw();
          ackThreshold= baseline + motorDriver->mA2raw(ackLimitmA);
          if (Diag::ACK) DIAG(F("\nACK baseline=%d/%dmA Threshold=%d/%dmA Duration: %dus <= pulse <= %dus"),
    			  baseline,motorDriver->raw2mA(baseline),
    			  ackThreshold,motorDriver->raw2mA(ackThreshold),
                              minAckPulseDuration, maxAckPulseDuration);
    }
    
    void DCCWaveform::setAckPending() {
          if (isMainTrack) return; 
          ackMaxCurrent=0;
          ackPulseStart=0;
          ackPulseDuration=0;
          ackDetected=false;
          ackCheckStart=millis();
          ackPending=true;  // interrupt routines will now take note
    }
    
    byte DCCWaveform::getAck() {
          if (ackPending) return (2);  // still waiting
          if (Diag::ACK) DIAG(F("\n%S after %dmS max=%d/%dmA pulse=%duS"),ackDetected?F("ACK"):F("NO-ACK"), ackCheckDuration, 
               ackMaxCurrent,motorDriver->raw2mA(ackMaxCurrent), ackPulseDuration);
          if (ackDetected) return (1); // Yes we had an ack
          return(0);  // pending set off but not detected means no ACK.   
    }
    
    void DCCWaveform::checkAck() {
        // This function operates in interrupt() time so must be fast and can't DIAG 
        
        if (sentResetsSincePacket > 6) {  //ACK timeout
            ackCheckDuration=millis()-ackCheckStart;
            ackPending = false;
            return; 
        }
          
        lastCurrent=motorDriver->getCurrentRaw();
        if (lastCurrent > ackMaxCurrent) ackMaxCurrent=lastCurrent;
        // An ACK is a pulse lasting between minAckPulseDuration and maxAckPulseDuration uSecs (refer @haba)
            
        if (lastCurrent>ackThreshold) {
           if (ackPulseStart==0) ackPulseStart=micros();    // leading edge of pulse detected
           return;
        }
        
        // not in pulse
        if (ackPulseStart==0) return; // keep waiting for leading edge 
        
        // detected trailing edge of pulse
        ackPulseDuration=micros()-ackPulseStart;
                   
        if (ackPulseDuration>=minAckPulseDuration && ackPulseDuration<=maxAckPulseDuration) {
            ackCheckDuration=millis()-ackCheckStart;
            ackDetected=true;
            ackPending=false;
            transmitRepeats=0;  // shortcut remaining repeat packets 
            return;  // we have a genuine ACK result
        }      
        ackPulseStart=0;  // We have detected a too-short or too-long pulse so ignore and wait for next leading edge 
    }

  2. #252
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,594
    Great, looks like it works! Let the trains departure :-)

    Just a comment: If this is only for you, your method of hacking the teensy timers into the code is certainly fine. If you want to have it integrated to the library it might be better to just use the interface they provide for extension to other timers. It is easy to use: you only need to add a teensy folder containing a Timer.h file where you derive a Timer class from the abstract VirtualTimer class and fill in the 4 or 5 functions (initialize, setPeriod etc) they need. Since IntervalTimer and the timers from the TimerTool provide all this functionality you just need to relay their calls to the corresponding Teensy Timer calls. The rest of the library can stay more or less untouched (at least that seems to be the idea).
    The pull request I sent you yesterday shows how to do this. If you want to have different timers for different purposes (like you did), you can do that as well, just distinguish which one you create by the number they pass in the Timer constructor.

  3. #253
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    7,429
    Quote Originally Posted by luni View Post
    Great, looks like it works! Let the trains departure :-)

    Just a comment: If this is only for you, your method of hacking the teensy timers into the code is certainly fine. If you want to have it integrated to the library it might be better to just use the interface they provide for extension to other timers. It is easy to use: you only need to add a teensy folder containing a Timer.h file where you derive a Timer class from the abstract VirtualTimer class and fill in the 4 or 5 functions (initialize, setPeriod etc) they need. Since IntervalTimer and the timers from the TimerTool provide all this functionality you just need to relay their calls to the corresponding Teensy Timer calls. The rest of the library can stay more or less untouched (at least that seems to be the idea).
    The pull request I sent you yesterday shows how to do this. If you want to have different timers for different purposes (like you did), you can do that as well, just distinguish which one you create by the number they pass in the Timer constructor.
    Well ran a test this morning and the train has departed: https://forum.pjrc.com/threads/65956...and-Teensy-4-x !

    Am planning to redo what I did to use a Teensy specific Time.h file to conform to their format but just wanted to test to see if it would work. So guess next up is to work on your merge and see what I can do. Really appreciate the help. Also glad with the EEPROM lib updated as well. So guess there was added value in me trying to port over the lib

  4. #254
    Junior Member
    Join Date
    Mar 2020
    Location
    Bristrol - UK
    Posts
    8
    Hi Luni,
    Sorry to bother you again with what could be a Mat 101 issue. I have been experimenting more with timing routines and the Timer Tool, moving from the LC to a Teensy 3.5. with an Audio board.

    The project is Radio based. I have a user programmable time delay (from 5 mins to about 15-20 minutes). When this timer expires it transmits some Morse code then starts a new count down.
    There is another counter also running at a rate of 10ms.

    My code uses a periodic timer set to 10ms from this I generate my 1s, 20ms and 10ms timers - my original code used the Periodic t1 (TCK); This works just fine and my code is doing as expected, when the countdown expires Morse is generated.

    As my "project" (in a lose terms) grows I need to move away from the software interrupts. I started using the FTMx interrupt source. However it seams the Audio (I used the Audio design tool) breaks when I select FTMx. With a scope I see the data IN line (pin 22 A8PWM) becomes stuck until I change the code back to TCK. Is there any way around this FTMx resource issue?

    As per my last post this is a steep learning curve for me so let me thank you in advance for any advise/guidance you can give.

    Regards.
    Mat.

  5. #255
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,594
    AFAIK the audio library uses some FTM timers. Should be documented somewhere. Did you try to use another FTM module?

  6. #256
    Member Van's Avatar
    Join Date
    Mar 2018
    Location
    Dresden, Germany
    Posts
    54
    Hi there!
    How can I change frequency without having to restart it?
    i'm trying to use it as a BPM Clock.
    Cheers!

  7. #257
    @luni
    I was using 0.1.10 Release till date and today I updated to 0.3.2 release.
    I see that my code is not working anymore. Here is the example that I was using earlier.
    I have this code working with correct timing on 0.1.10 Release + 600/528/150 Mhz

    Instead of 10mSec I am gettting this function called at every 1.5uSec Approx.
    Click image for larger version. 

Name:	Screenshot_16.png 
Views:	33 
Size:	10.6 KB 
ID:	23659

    Code:
    /********************************************************
     * Basic usage of the timer
     * 
     * Generates a timer from the timer pool and
     * starts it with a period of 250ms. 
     * The timer callback simply toggles the built in LED
     * 
     ********************************************************/
    
    #include "TeensyTimerTool.h"
    
    using namespace TeensyTimerTool;
    PeriodicTimer t1(PIT);
    
    void vStartTimer()
    {
      t1.begin(callback, 10'000); //10mSec
    }
    
    void vStopTimer()
    {
      t1.begin([] {}, 10'000); //10mSec  
    }
    
    void callback()
    {
        digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));    
    }
    
    void setup()
    {
        pinMode(LED_BUILTIN,OUTPUT);   
        vStartTimer();
    }
    
    void loop()
    {   
    }

  8. #258
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,594
    @luni
    I was using 0.1.10 Release till date and today I updated to 0.3.2 release.
    I see that my code is not working anymore. Here is the example that I was using earlier.
    I have this code working with correct timing on 0.1.10 Release + 600/528/150 Mhz

    Instead of 10mSec I am gettting this function called at every 1.5uSec Approx.
    Strange, this is the most simple application and should work of course. I'm currently not at home and don't have a T4 with me for testing I can have a closer look on Friday. Does it work with other timers (e.g. GPT1, TCK)?

  9. #259
    I'm on a T3.2, so I unless I've misunderstood something, I only have FTM0, FTM1 and FTM2 to use?
    (and now realizing that you probably replied to @HallMark and not me ;-) )

  10. #260
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,594
    Quote Originally Posted by jensa View Post
    @luni TeensyStep works great, but I've run into a conflict with the PWM timer (as mentioned here https://github.com/luni64/TeensyStep/issues/51). I've tried doing as you suggest in this issue by changing

    Code:
    #define USE_TIMER TIMER_DEFAULT
    to

    Code:
    #define USE_TIMER TIMER_FTM1
    I'm on a T3.2, so I have limited timers available. I think the problem is that I'm using one RotateControl and one StepControl that need to work independently?
    My project needs PWM, so I need to not use TIMER_FTM0 at all.

    So I wonder:
    - Is it so that each instance will grab a new timer?
    - Do you have a suggestion as to how to solve this?

    Thanks!
    Looks like you are in the wrong thread? This is TeensyTimerTool, not TeensyStep :-)
    Anyway, on a T3.2 FTM1 only has 2 channels. A controller needs one PIT and two FTMs so, you can only use one controller if you selected FTM1 (same with FTM2). Couldn't you switch your PWM pins so that you can use FTM0 for the Steppers and FTM1/FTM2 for PWM?

  11. #261
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,594
    Quote Originally Posted by HallMark View Post
    @luni
    I was using 0.1.10 Release till date and today I updated to 0.3.2 release.
    I see that my code is not working anymore. Here is the example that I was using earlier.
    I have this code working with correct timing on 0.1.10 Release + 600/528/150 Mhz

    Instead of 10mSec I am gettting this function called at every 1.5uSec Approx.
    Click image for larger version. 

Name:	Screenshot_16.png 
Views:	33 
Size:	10.6 KB 
ID:	23659
    Sorry for the delay, I finally found some time trying your code. I can not reproduce the problem. It generates nice 10ms calls here. Do you still have this problem?

  12. #262
    Junior Member
    Join Date
    Jul 2021
    Posts
    5

    Encapsulated timers

    Hi,
    I am having some trouble with using the timers encapsulated in a class. I am working on a simple LED control class that flashes LEDs. I am using a teensy 4.0 and teensytimertool V0.3.2.
    Here is the class

    Code:
    //LEDcontrol.h
    #include "Arduino.h"
    #include "TeensyTimerTool.h"
    using namespace TeensyTimerTool;
    class LED{
        PeriodicTimer PTimer;
        OneShotTimer OTimer;
        void callbackTriggerP1();
        void callbackP1();
    public:
        LED(int, int);
        int pin;
        int selftrig=0, flash=0;
        void begin();
        void end();
    };
    Code:
    //"LEDControl.cpp"
    LED::LED(int p, int f){
        flash = f;
        pin = p;
    }
    
    void LED::callbackP1(){
      OTimer.trigger(100ms);
    }
    void LED::callbackTriggerP1(){
      if(selftrig < flash)
      {
        OTimer.trigger(100ms);
        selftrig++;
        digitalWriteFast(pin, !digitalReadFast(pin));
      }
      else{
        selftrig = 0;
        digitalWriteFast(pin,LOW);
      }
    }
    
    void LED::begin(){
        pinMode(pin, OUTPUT);
        flash = (flash-1)*2;
        OTimer.begin([this]() {this->callbackTriggerP1(); });
        PTimer.begin([this]() {this->callbackP1(); }, 1000ms);
    }
    
    void LED::end(){
        PTimer.end();
    }
    This is pretty simple and similar to the Blinker class example, except with some self calls to trigger multiple flashes. This works fine when only one is being used, ie in this main function:
    Code:
    #include "Arduino.h"
    #include "LEDControl.h"
    LED L1(23, 4);
    //LED L2(22, 4);
    void setup()
    { 
      Serial.begin(9600);
      L1.begin();
      //L2.begin();
    }
    
    void loop()
    {
    
    }
    The problem occurs when you try to add a second LED (or more). The LED that was begun second will just come on and stay on, without flashing. This indicates that it is only triggering the callbacktrigger() function once.

    Any help here would be appreciated.

  13. #263
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,869
    not seeing a loop() making that a complete posted sketch?

    It may be after the first call it is getting called TWICE? Not sure if that was resolved here - but a fast short _isr() on T_4.0 can trigger twice as it exits before realizing the interrupt was serviced.

    Was a scope at hand to put on the LED?

  14. #264
    Junior Member
    Join Date
    Jul 2021
    Posts
    5
    I am not sure I follow. There is an empty loop after the setup function.

    I guess it is possible that the _isr is being called twice, although it is strange that the problem only occurs when the TeensyTimerTool class is being generated from within another class. For example here is a working piece of code that flashes 2 LEDs. All the timers are defined as globals. It is the same as before just doesn't use the class. as soon as I wrap it in a class, it stops working, I also cannot specify to use the tick timers if it is in a class, which might be related.

    Code:
    //main.cpp
    #include "Arduino.h"
    #include "TeensyTimerTool.h"
    using namespace TeensyTimerTool;
    
    #define LED1_PIN 23
    #define LED2_PIN 22
    
    PeriodicTimer PT1(TCK);
    OneShotTimer OST1(TCK);
    void callback1();
    void callbackTrigger1();
    int selftrig1=0, flash1=4;
    
    PeriodicTimer PT2(TCK);
    OneShotTimer OST2(TCK);
    void callback2();
    void callbackTrigger2();
    int selftrig2=0, flash2=4;
    
    void setup()
    { 
      Serial.begin(9600);
      flash1 = (flash1-1)*2;
      flash2 = (flash2-1)*2;
      pinMode(LED1_PIN, OUTPUT);
      pinMode(LED2_PIN, OUTPUT);
      PT1.begin(callback1, 1000ms);
      PT2.begin(callback2, 1000ms);
      OST1.begin(callbackTrigger1);
      OST2.begin(callbackTrigger2);
    }
    
    void loop()
    {
    
    }
    
    void callback1(){
      OST1.trigger(100ms);
    }
    
    void callbackTrigger1(){
        if(selftrig1 < flash1)
      {
        OST1.trigger(100ms);
        selftrig1++;
        digitalWriteFast(LED1_PIN, !digitalReadFast(LED1_PIN));
      }
      else{
        selftrig1 = 0;
        digitalWriteFast(LED1_PIN,LOW);
      }
    }
    
    void callback2(){
      OST2.trigger(100ms);
    }
    
    void callbackTrigger2(){
      if(selftrig2 < flash2)
      {
        Serial.println("led2");
        OST2.trigger(100ms);
        selftrig2++;
        digitalWriteFast(LED2_PIN, !digitalReadFast(LED2_PIN));
      }
      else{
        selftrig2 = 0;
        digitalWriteFast(LED2_PIN,LOW);
      }
    }
    I don't have access to a scope today, but will on Sunday and will post the results if we haven't worked it out by then.
    Last edited by Peaz; 07-09-2021 at 02:57 AM. Reason: scope info

  15. #265
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,869
    Somehow the empty loop() wasn't seen

    Ran the code and the behavior described seems to be there.

    Adding this to the callback didn't change the behavior to make sure the interrupt was cleared :: asm volatile ("dsb");

    The second LED L2 is never being called after the first instance - code below:
    {edit}: the SECOND one with .begin is called only once - swap .begin of L1 and L2 and it changes
    Code:
    T:\tCode\TimerTool\TimerTest\TimerTest.ino Jul  8 2021 20:20:21
    T:\tCode\TimerTool\TimerTest\TimerTest.ino Jul  8 2021 20:22:35
    millis=1000	PIN=22	count=0
    millis=1000	PIN=23	count=1
    millis=2000	PIN=22	count=3
    millis=2000	PIN=23	count=1
    millis=3000	PIN=22	count=9
    millis=3000	PIN=23	count=1
    millis=4000	PIN=22	count=15
    millis=4000	PIN=23	count=1
    millis=5000	PIN=22	count=21
    millis=5000	PIN=23	count=1
    millis=6000	PIN=22	count=27
    ...

    Code:
    #include "TeensyTimerTool.h"
    using namespace TeensyTimerTool;
    class LED {
      PeriodicTimer PTimer;
      OneShotTimer OTimer;
      void callbackTriggerP1();
      void callbackP1();
    public:
      void showcnt();
      LED(int, int);
      int pin;
      int selftrig = 0, flash = 0, acnt = 0;
      void begin();
      void end();
    };
    
    
    //"LEDControl.cpp"
    LED::LED(int p, int f) {
      flash = f;
      pin = p;
    }
    
    void LED::callbackP1() {
      OTimer.trigger(100ms);
    }
    void LED::showcnt() {
      Serial.print( "millis=" );
      Serial.print( millis() );
      Serial.print( "\tPIN=" );
      Serial.print( pin );
      Serial.print( "\tcount=" );
      Serial.println( acnt );
      //acnt = 0;
    }
    void LED::callbackTriggerP1() {
      if (selftrig < flash)
      {
        OTimer.trigger(100ms);
        selftrig++;
        //digitalWriteFast(pin, !digitalReadFast(pin));
        digitalToggleFast(pin);
        acnt++;
      }
      else {
        selftrig = 0;
        digitalWriteFast(pin, LOW);
      }
      asm volatile ("dsb");
    }
    
    void LED::begin() {
      pinMode(pin, OUTPUT);
      flash = (flash - 1) * 2;
      OTimer.begin([this]() {this->callbackTriggerP1(); });
      PTimer.begin([this]() {this->callbackP1(); }, 1000ms);
    }
    
    void LED::end() {
      PTimer.end();
    }
    
    
    
    
    //#include "LEDControl.h"
    LED L1(22, 4);
    LED L2(23, 4);
    void setup()
    {
      Serial.begin(9600);
      Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
      L1.begin();
      L2.begin();
    }
    
    void loop()
    {
      if ( !(millis() % 1000)) {
        L1.showcnt();
        L2.showcnt();
        while ( !(millis() % 1000)) yield();
    
      }
    
    }
    Last edited by defragster; 07-09-2021 at 03:56 AM.

  16. #266
    Junior Member
    Join Date
    Jul 2021
    Posts
    5
    The second LED L2 is never being called after the first instance - code below:
    Good to know it is repeatable and not just my machine...

    Any idea why this is? or what steps I can take to find the root cause?

    I have simplified the class to only call the periodic timer, and to start the OneShot timer, simply by changing the following:
    Code:
    void LED::callbackP1() {
      //OTimer.trigger(100ms);
      digitalToggleFast(pin);
    }
    And this works as expected. So it seems the issue is in either the OneShotTimer class, or in the logic in callbacktrigger().

    The logic in callbacktriggerP1() works outside the class so I can't see why it wouldn't work inside...

    Thanks heaps for your help.

  17. #267
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,594
    Short answer:
    You use 16bit hardware timers for the second LED which can not handle your 1s delay.


    Details:
    If you don't specify which timer module to use, the TimerTool chooses the next free timer from a timer pool which is defined in the config file. Here the default definition of the pool for a Teensy 4.0:

    Code:
    #if defined(ARDUINO_TEENSY40)
        TimerGenerator* const timerPool[] = {GPT1, GPT2, TMR1, TMR2, TMR3, TMR4, TCK};
    It starts using GPT1 and GPT2 (both 32bit) and then the first two channels of the TMR1 module (16bit).

    It is always a good idea to activate the error handling of the library during development (see https://github.com/luni64/TeensyTime...ror-Handling):
    Code:
    void setup()
    {
        while(!Serial){}
    
        TeensyTimerTool::attachErrFunc(ErrorHandler(Serial));
    
        L1.begin();
        L2.begin();
    }
    This will print the following warning messages
    Code:
    W-100: Period overflow, set to maximum
    W-100: Period overflow, set to maximum
    To fix this you can either adjust the timer pool definition in the config file (see here for instructions: https://github.com/luni64/TeensyTime.../Configuration), or (recommended) explicitly define which timer module to use in the constructor of your class. I recommend to use the TCK module which has 20 32bit channels and is implemented in software. In case you really need a hardware timer you can use the PIT module which has four 32bit channels.

    For a quick check you can change your timing from milliseconds to microseconds, i.e.

    Code:
    //"LEDControl.cpp"
    #include "LEDControl.h"
    
    LED::LED(int p, int f)
    {
        flash = f;
        pin   = p;
    }
    
    void LED::callbackP1()
    {
        OTimer.trigger(100us);
    }
    void LED::callbackTriggerP1()
    {
        if (selftrig < flash)
        {
            OTimer.trigger(100us);
            selftrig++;
            digitalWriteFast(pin, !digitalReadFast(pin));
        }
        else
        {
            selftrig = 0;
            digitalWriteFast(pin, LOW);
        }
    }
    
    void LED::begin()
    {
        pinMode(pin, OUTPUT);
        flash = (flash - 1) * 2;
        OTimer.begin([this]() { this->callbackTriggerP1(); });
        PTimer.begin([this]() { this->callbackP1(); }, 1000us);
    }
    
    void LED::end()
    {
        PTimer.end();
    }
    Which will generate:

    Click image for larger version. 

Name:	Screenshot 2021-07-09 070228.jpg 
Views:	24 
Size:	33.5 KB 
ID:	25214
    Last edited by luni; 07-09-2021 at 06:42 AM.

  18. #268
    Junior Member
    Join Date
    Jul 2021
    Posts
    5
    Thanks @luni this was the issue.

    I can't get specify the timer module in the class constructor, when I change the following I get an error.
    Code:
    class LED {
       PeriodicTimer PTimer(TCK);
       OneShotTimer OTimer(TCK);
      //PeriodicTimer PTimer;
      //OneShotTimer OTimer;
      void callbackTriggerP1();
      void callbackP1();
    public:
      void showcnt();
      LED(int, int);
      int pin;
      int selftrig = 0, flash = 0, acnt = 0;
      void begin();
      void end();
    };
    variable "TeensyTimerTool::TCK" is not a type name
    If I define the timer as a global this isn't an issue.

    Have I specified it incorrectly?

    Thanks,

  19. #269
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,594
    In c++ you can not initiate a member class directly in the parents class declaration if the member class has a constructor. You need to do this in the definition of the parent class (your LEDControl.cpp). Specifically, in the initializer list of the class constructor. This sounds much more complicated than it really is :-)

    I.e., leave your LEDControl.h header as it was and just add the following line to your constructor code in your LEDControl.cpp file:
    Code:
    LED::LED(int p, int f)
        : PTimer(TCK), OTimer(TCK)
    {
        flash = f;
        pin   = p;
    }
    Last edited by luni; 07-09-2021 at 06:50 AM.

  20. #270
    Junior Member
    Join Date
    Jul 2021
    Posts
    5
    That did the trick!

    you are a lifesaver. Thank you and @defragster for the quick and helpful replies!

  21. #271
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,594
    Glad it works, have fun with the TimerTool

  22. #272
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,594

    MicroMod compatibility

    FYI: I just uploaded a new TeensyTimerTool version which is compatible to the Teensy MicroMod boards.

    https://github.com/luni64/TeensyTimerTool

  23. #273
    Junior Member Croc's Avatar
    Join Date
    May 2020
    Location
    FR
    Posts
    6
    Luni,

    recently i've updated my libraries coz of a new computer, and i have an issue with your tool.
    here is my application : pulse generator

    the problem is, your timerTool is trigging some pulses directly after the incoming trigger signal, without any delay (but the width/duration is ok).
    "Some" pulses is maybe 1/100 or 1/1000, dont know exactly, it happens after some persistence on my oscilloscope, under a hi rep rate external trigger (> 1 khz). The others 99/100 or 999/1000 are ok.
    also, the jitter is about 2Ás !

    during my tests, i've generated pulses with delay from 2 to 5 Ás, and duration from 2 to 5 Ás.
    for one, or multiple channels : each channels are affected by this bug.

    i've tried each of your versions, and it works fine from the 0.1.11. But any newer version than 0.1.11 has the bug.
    and the jitter is better with this version ~1 Ás


    Another point:
    in the TCK.cpp, i use now an empty yield() function.
    and i'm calling the tick() directly from the loop(), that improve the jitter (~500ns)


    thanks!

  24. #274
    greetings dear..

    please tell a beginner, in 500 ms I need to make a request for Serial port,
    if I do so:
    Code:
    Serial7.print("IF;");
    Serial7.flush();
    delay(500);
    then everything works correctly, but I don't want to use Delay()
    if I do this:
    Code:
    if ((millis() - last_time) > 500)
    {
    Serial7.print("IF;");
    Serial7.flush();
    last_time = millis();
    }
    then this code does not work for me, what am I doing wrong, or how to do it right ?

    board Teensy 4.1

  25. #275
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,594
    @Croc: I used the code from your linked thread and added some timing functionality:

    Code:
    #include "Arduino.h"
    #include "TeensyTimerTool.h"
    using namespace TeensyTimerTool;
    
    uint32_t t0, t1, t2;
    elapsedMillis stopwatch;
    
    class PulseGen
    {
    public:
        PulseGen() : delayTimer(TCK), durationTimer(TCK) // use the tick timers for this task, we have 20 per default, can be increased in config
        {}
    
        void begin(unsigned pin, unsigned delay, unsigned duration)
        {
            this->pin = pin;
            this->delay = delay;
            this->duration = duration;
    
            pinMode(pin, OUTPUT);
            delayTimer.begin([this]    {t1 = ARM_DWT_CYCCNT; digitalWriteFast(this->pin, HIGH);  this->durationTimer.trigger(this->duration);});
            durationTimer.begin([this] {t2 = ARM_DWT_CYCCNT; digitalWriteFast(this->pin, LOW);});
            // t1: start of pulse, t2: end of pulse
        }
    
        void trigger() { delayTimer.trigger(delay); }
    
    protected:
        unsigned pin, delay, duration;
        OneShotTimer delayTimer, durationTimer;
    };
    
    //-------------------------------------
    PulseGen pg;
    
    void setup()
    {
      pinMode(0,OUTPUT);
      pg.begin(1, 2, 5);  // output on pin1 2Ás delay, 5Ás duration
    }
    
    void loop()
    {
      if(stopwatch > 100) // generate a pulse every 100ms
      {
          Serial.printf("delta: %.2f Ás, duration: %.2f Ás\n", (t1 - t0)/600.0f, (t2 - t1)/600.0f); // print out timing in microseconds
    
          digitalWriteFast(0, HIGH);   // marker for the LA
          t0 = ARM_DWT_CYCCNT;         // start time = trigger time
          pg.trigger();
          digitalWriteFast(0, LOW);
          stopwatch -= 100;
      }
    }
    Here the output of the LA:

    Click image for larger version. 

Name:	Screenshot 2021-08-10 201529.jpg 
Views:	10 
Size:	28.5 KB 
ID:	25559

    And here the printout: EDIT: (delta should read "delay" of course)
    Code:
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    delta: 2.42 Ás, duration: 5.17 Ás
    I don't see any jitter or wrong delay here.

    Can you prepare a minimal example showing your problem?
    Last edited by luni; 08-10-2021 at 06:29 PM.

Posting Permissions

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