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

Thread: Teensy4 QuadTimer as 64-bit counter

  1. #1
    Junior Member
    Join Date
    Jan 2018
    Posts
    9

    Teensy4 QuadTimer as 64-bit counter

    I am cascading a QTimer into a 64-bit counter, and it works. But I am having a strange thing happen on startup, that I would like to understand.

    I am posting the full code at the end, but the most important thing are the the CNTR initializations:
    Code:
      TMRx->CH[0].CNTR = 0;                   // set count to 0
      TMRx->CH[1].CNTR = 0;                   // set count to 0
      TMRx->CH[2].CNTR = 0xffff;              // set count to 0xffff
      TMRx->CH[3].CNTR = 0;                   // set count to 0
    Using there, here is what I get from the serial monitor:
    Code:
    0
    0
    65535
    0
    
    17897
    49
    0
    0

    so, at first the values are bad, then they turn fine and work as intended. I can also just add a 100ms delay after the timer setup and then the values are ok in the main program.

    If I set all CNTR to 0, then the 3rd and 4th values start out at 0 as expected, but then change to 1, like this:

    Code:
    0
    0
    0
    0
    
    3397
    49
    1
    1

    I guess a carry over bit needs to be specified or something...
    The 0xffff and wait a few ms workaround functions, but I do want to understand what is going on though, so does anyone here know?

    Code:
    #include <U8x8lib.h>
    
    U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(/* reset=*/U8X8_PIN_NONE);
    
    IMXRT_TMR_t * TMRx = (IMXRT_TMR_t *)&IMXRT_TMR4;
    
    void setup()
    {
      Serial.begin(9600);
      // initialize and clear display
      u8x8.begin();
      u8x8.setPowerSave(0);
      u8x8.setFont(u8x8_font_chroma48medium8_r);
      u8x8.drawString(0, 0, "Started !");
      
      CCM_CCGR6 |= CCM_CCGR6_QTIMER4(CCM_CCGR_ON); // enable QTMR4
      IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_11 = 1;        // QT4 Timer2 on pin 9
    
      TMRx->CH[0].CTRL =0;                    // stop
      TMRx->CH[1].CTRL =0;                    // stop
      TMRx->CH[2].CTRL =0;                    // stop
      TMRx->CH[3].CTRL =0;                    // stop
      TMRx->CH[0].CNTR = 0;                   // set count to 0
      TMRx->CH[1].CNTR = 0;                   // set count to 0
      TMRx->CH[2].CNTR = 0xffff;              // set count to 0xffff
      TMRx->CH[3].CNTR = 0;                   // set count to 0
      TMRx->CH[0].SCTRL = 0;                  // clear all overflow etc flags
      TMRx->CH[1].SCTRL = 0;                  // clear all overflow etc flags
      TMRx->CH[2].SCTRL = 0;                  // clear all overflow etc flags
      TMRx->CH[3].SCTRL = 0;                  // clear all overflow etc flags
      TMRx->CH[3].CTRL  = TMR_CTRL_CM (7);    // Count Mode:           Cascaded counter mode
      TMRx->CH[3].CTRL |= TMR_CTRL_PCS(6);    // Primary Count Source: CH[2] output  
      TMRx->CH[2].CTRL  = TMR_CTRL_CM (7);    // Count Mode:           Cascaded counter mode
      TMRx->CH[2].CTRL |= TMR_CTRL_PCS(5);    // Primary Count Source: CH[1] output  
      TMRx->CH[1].CTRL  = TMR_CTRL_CM (7);    // Count Mode:           Cascaded counter mode
      TMRx->CH[1].CTRL |= TMR_CTRL_PCS(4);    // Primary Count Source: CH[0] output
      TMRx->CH[0].CTRL  = TMR_CTRL_CM (1);    // Count Mode:           Count rising edges of primary source
      TMRx->CH[0].CTRL |= TMR_CTRL_PCS(2);    // Primary Count Source: Counter 2 input pin
      //delay(100);                           // settle timer???
    }
    
    char buffer[100];
    
    void loop()
    {
      sprintf(buffer, "%u", TMRx->CH[0].CNTR);  // regular read of CH CNTR causes HOLD registers to be set with corresponding values of other channels;
      u8x8.drawString(0, 2, "     ");
      u8x8.drawString(0, 2, buffer);
      Serial.println(buffer);
      sprintf(buffer, "%u", TMRx->CH[1].HOLD);  // regular read of CH CNTR causes HOLD registers to be set with corresponding values of other channels;
      u8x8.drawString(0, 3, "     ");
      u8x8.drawString(0, 3, buffer);
      Serial.println(buffer);
      sprintf(buffer, "%u", TMRx->CH[2].HOLD);  // regular read of CH CNTR causes HOLD registers to be set with corresponding values of other channels;
      u8x8.drawString(0, 4, "     ");
      u8x8.drawString(0, 4, buffer);
      Serial.println(buffer);
      sprintf(buffer, "%u", TMRx->CH[3].HOLD);  // regular read of CH CNTR causes HOLD registers to be set with corresponding values of other channels;
      u8x8.drawString(0, 5, "     ");
      u8x8.drawString(0, 5, buffer);
      Serial.println(buffer);
      Serial.println();
      delay(1000);
    }

  2. #2
    Junior Member
    Join Date
    Jan 2018
    Posts
    9
    After much more testing, I would like to add that no matter what I try I cannot get the 3rd 16 bit counter to overflow into the 4th one. So instead of a 64-bit counter, the maximum I can get to work is 48 bits. Is that an errata in the chip, or am I doing something wrong?

  3. #3
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,574
    it looks like you are using pin 9 as "clock source" (quad timer 4 timer 2), what do you have connected to pin 9?
    your last init should be start quadtimer 4 timer 2: TMRx->CH[2].CTRL = TMR_CTRL_CM(1) | TMR_CTRL_PCS(2) | TMR_CTRL_LENGTH ;
    Or maybe you are using timer 0 input pin ?? or do you need to set PCS() to use the IP bus clock?
    some QTMR discussions in huge T4 beta test thread, e.g. https://forum.pjrc.com/threads/54711...l=1#post211019

    and my pin 9 counting example (only 32 bits)
    https://github.com/manitou48/teensy4...qtmr_count.ino

    chain example, 48 bits (32-bit counter of microseconds)
    https://github.com/manitou48/teensy4...mr_cascade.ino

    EDIT: i extended chain example to 64-bits, and i'm seeing what you were seeing, highest timer goes to 1 ??
    Code:
    0 ticks 0 ms   20007 us 46552007
    ch1 20013 ch2 0 ch3 0
    0 ticks 0 ms   1020012 us 47552013
    ch1 36979 ch2 15 ch3 1
    0 ticks 0 ms   2020018 us 48552019
    ch1 53945 ch2 30 ch3 1
    Last edited by manitou; 06-21-2020 at 04:40 PM.

  4. #4
    Junior Member
    Join Date
    Jan 2018
    Posts
    9
    Quote Originally Posted by manitou View Post
    it looks like you are using pin 9 as "clock source" (quad timer 4 timer 2), what do you have connected to pin 9?
    your last init should be start quadtimer 4 timer 2: TMRx->CH[2].CTRL = TMR_CTRL_CM(1) | TMR_CTRL_PCS(2) | TMR_CTRL_LENGTH ;
    some QTMR discussions in huge T4 beta test thread, e.g. https://forum.pjrc.com/threads/54711...l=1#post211019

    and my pin 9 counting example (only 32 bits)
    https://github.com/manitou48/teensy4...qtmr_count.ino
    Thanks for the help

    The clock source was an external 5 Mhz signal, but I have since gone over to doing what you did and just connected a pin(to test a faster clock):
    Code:
      analogWriteFrequency(8, 75000000);  //  max 75mhz at 150 mhz bus speed
      analogWrite(8, 128);
    Trying to understand what is going on here...
    - why do I have to set up ch2 last?
    - if I just want to roll over at 0xffff why set TMR_CTRL_LENGTH ?

    So my thought is
    - external signal goes into pin9 which goes to timer 2 input. Hence
    TMR4->CH[0].CTRL = TMR_CTRL_CM (1); // Count Mode: Count rising edges of primary source
    TMR4->CH[0].CTRL |= TMR_CTRL_PCS(2); // Primary Count Source: Counter 2 input pin

    -then the other channels are just cascading. So timer2 input gets re-routed to timer0 and timer1 cascades into timer2 instead of using the original timer2 input

    Is that wrong?

    I just swapped timer 0 and 2, so that 2 gets the initial clock and then cascades into the others - same behavior.

    I did look at the thread you suggested and also looked at the qtmr_count.ino qtmr_capture.ino and qtmr_cascade.ino and this: https://github.com/mjs513/FreqCount/...ount-T4-Branch
    as well as the full datasheet.

    I can cascade 2 timers easily, and 3 with the 0xffff workaround, but not 4...

  5. #5
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,574
    i changed my qtmr_cascade sketch to config 48-bit microsecond counter like the following
    Code:
      int cnt;
    
      cnt = 150 ; // 1 us
      TMRx->CH[0].CTRL = 0; // stop
      TMRx->CH[0].CNTR = 0;
      TMRx->CH[0].LOAD = 0;  // start val after compare
      TMRx->CH[0].COMP1 = cnt - 1;  // count up to this val and start again
      TMRx->CH[0].CMPLD1 = cnt - 1  ;
      TMRx->CH[0].CTRL = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8 ) | TMR_CTRL_LENGTH ;  // no prescale
      cnt = 65536;
      TMRx->CH[1].CTRL = 0; // stop
      TMRx->CH[1].LOAD = 0;  // start val after compare
      TMRx->CH[1].COMP1 = cnt - 1;
      TMRx->CH[1].CMPLD1 = cnt - 1;
      TMRx->CH[1].CTRL = TMR_CTRL_CM(7) | TMR_CTRL_PCS(4) | TMR_CTRL_LENGTH ;  //clock from clock 0
    
      TMRx->CH[2].CTRL = 0; // stop
      TMRx->CH[2].LOAD = 0;  // start val after compare
      TMRx->CH[2].COMP1 = cnt -1  ;
      TMRx->CH[2].CMPLD1 = cnt - 1;
      TMRx->CH[2].CTRL = TMR_CTRL_CM(7) | TMR_CTRL_PCS(5)  ;  //clock from clock 1
    
      TMRx->CH[3].CTRL = 0; // stop
      TMRx->CH[3].LOAD = 0;  // start val after compare
      TMRx->CH[3].COMP1 = cnt -1 ;
      TMRx->CH[3].CMPLD1 = cnt -1;
      TMRx->CH[3].CTRL = TMR_CTRL_CM(7) | TMR_CTRL_PCS(6)  ;  //clock from clock 2
    timer3 doesn't flip from 0 to 1 at start, so it seems to be working. channel 0 timer uses IP bus clock (150mhz) prescaled by (150-1) to count microseconds
    Last edited by manitou; 06-21-2020 at 04:39 PM.

  6. #6
    Junior Member
    Join Date
    Jan 2018
    Posts
    9
    Quote Originally Posted by manitou View Post
    i changed my qtmr_cascade sketch to config 48-bit microsecond counter like the following
    Code:
      int cnt;
    
      cnt = 150 ; // 1 us
      TMRx->CH[0].CTRL = 0; // stop
      TMRx->CH[0].CNTR = 0;
      TMRx->CH[0].LOAD = 0;  // start val after compare
      TMRx->CH[0].COMP1 = cnt - 1;  // count up to this val and start again
      TMRx->CH[0].CMPLD1 = cnt - 1  ;
      TMRx->CH[0].CTRL = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8 ) | TMR_CTRL_LENGTH ;  // no prescale
      cnt = 65536;
      TMRx->CH[1].CTRL = 0; // stop
      TMRx->CH[1].LOAD = 0;  // start val after compare
      TMRx->CH[1].COMP1 = cnt - 1;
      TMRx->CH[1].CMPLD1 = cnt - 1;
      TMRx->CH[1].CTRL = TMR_CTRL_CM(7) | TMR_CTRL_PCS(4) | TMR_CTRL_LENGTH ;  //clock from clock 0
    
      TMRx->CH[2].CTRL = 0; // stop
      TMRx->CH[2].LOAD = 0;  // start val after compare
      TMRx->CH[2].COMP1 = cnt -1  ;
      TMRx->CH[2].CMPLD1 = cnt - 1;
      TMRx->CH[2].CTRL = TMR_CTRL_CM(7) | TMR_CTRL_PCS(5)  ;  //clock from clock 1
    
      TMRx->CH[3].CTRL = 0; // stop
      TMRx->CH[3].LOAD = 0;  // start val after compare
      TMRx->CH[3].COMP1 = cnt -1 ;
      TMRx->CH[3].CMPLD1 = cnt -1;
      TMRx->CH[3].CTRL = TMR_CTRL_CM(7) | TMR_CTRL_PCS(6)  ;  //clock from clock 2
    timer3 doesn't flip from 0 to 1 at start, so it seems to be working. channel 0 timer uses IP bus clock (150mhz) prescaled by (150-1) to count microseconds
    Thanks! With that I was able to track down the problem. Not setting the COMP value was my mistake. The datasheet clearly states that the OFLAG is the clock for the next cascaded unit, so it has to be set to 0xffff or will cause the next counter to count too early. I don't think CMPLD1 needs to be set if TMR_CTRL_LENGTH is not set. Would you agree with that assessment?

Posting Permissions

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