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

Thread: Teensy LC timers

  1. #1
    Junior Member
    Join Date
    Oct 2016
    Location
    Farnborough, UK
    Posts
    19

    Teensy LC timers

    I have a working script for Teensy ++2 but when I change to the Teensy LC the timer register values are not recognized:
    TCCR3A = 0;
    TCCR3B = 0;
    I also want to read the timer which for Teensy ++2 works:
    timerRef = TCNT3;
    What text format should I use to access the Teensy LC timer (don't mind which one)?
    BR PD.

  2. #2
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    1,624
    The Teensy LC (and 3.x) timers are completely different. Thus, there is no simple way to just rename the register in code to get the similar functionality.

    unfortunetely, you don’t respect the forum rule and post complete code, so without knowing what you want to achieve with that timer (delay,? interrupt?, PWM?) nobody can point you towards a solution on the more modern platforms.

  3. #3
    Junior Member
    Join Date
    Oct 2016
    Location
    Farnborough, UK
    Posts
    19
    Quote Originally Posted by Theremingenieur View Post
    The Teensy LC (and 3.x) timers are completely different. Thus, there is no simple way to just rename the register in code to get the similar functionality.

    unfortunetely, you donít respect the forum rule and post complete code, so without knowing what you want to achieve with that timer (delay,? interrupt?, PWM?) nobody can point you towards a solution on the more modern platforms.


    Thanks for your very quick reply. The reason for no attachment is that I could not see how to attach a file. But now I return to the forum the WEB layout is different so have attached the file in question.

    Basically I am trying to read the timing of interrupts when they arrive to allow me to calculate phase angle between two square waves. It works very nicely on the Teensy ++2 but I would like to develop it to work with 6 resolvers all using the same reference phase, so I need a total of 7 interrupts eventually. The enclosed script when compiled for the Teensy LC produces errors as you summise above. But would love to know how to rewrite the script to make it work on Teensy LC.....

    Hope you can help.

    BR PD.
    Attached Files Attached Files

  4. #4
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    1,624
    A few thoughts before:
    First, the resolution of that code is not the best. The timer is clocked by the CPU clock of 16MHz divided by 64, thus with 250kHz (4us resolution) which is not great, but which prevents the overflowing of the small 16bit timer registers. I suggest to use a 32bit register and to not reset it, so that it might overflow, but since the interrupts capture only the difference to the previous status, overflow won't hurt any longer. I'd take the internal 32bit microsecond counter which needs more than 1 hour to overflow and which allows 1us resolution independent of the CPU clock.
    Second, the conversion of the float value into 4 bytes is a little clumsy and CPU cycle eating, so using a union{} definition does this in zero time.
    Third, your loop is not very efficient, it does not check if new interrupts have happened since the last calculation and transmit, it risks to redo all the work although no new interrupts have happened.

    So, please take the code below as my suggestion, it compiles without errors, but I had no square waves for a live test.

    Code:
    #include<Wire.h>
    
    #define NUM_CHANNELS 1
    
    union{
        float fData;
        uint8_t bData[4];
    } transmitConv[NUM_CHANNELS];
    
    volatile uint32_t refMicros;
    volatile uint32_t refCapture;
    volatile uint32_t chnCapture[NUM_CHANNELS];
    volatile uint8_t capStatus = 255;
    
    float resolverAngle;
    byte led = LED_BUILTIN;
    int Address = 2;
    
    void IsrRef(){
        uint32_t now = micros();
        refCapture = now - refMicros;
        refMicros = now;
        capStatus = 0;    //Drop all channel statuses so that the measurement starts always with the ref signal
    }
    
    void IsrCh0(){
        chnCapture[0] = micros() - refMicros;
        capStatus = (capStatus | (1<<0));    //Set the first bit to indicate cpature on ch1
    }
    
    void setup(){
    
    
        Wire.begin();
    
    
        pinMode(led, OUTPUT);
    
    
        refMicros = micros();
    
    
        attachInterrupt(2, IsrRef, FALLING);
        attachInterrupt(3, IsrCh0, FALLING);
    }
    
    
    void loop(){
        if((capStatus & (1 << 0)) && (refCapture > 0)) {    //Did a valid capture happen on ch1? Prevent div by 0
            digitalWrite(led, !digitalRead(led));    // toggle the internal LED to check program execution speed
            resolverAngle = 360.0f * (float) chnCapture[0] / (float) refCapture;
    
            transmitConv[0].fData = resolverAngle;
    
            Wire.beginTransmission(1);
            Wire.write(transmitConv[0].bData, 4);
            Wire.endTransmission();
    
            Serial.print(refCapture);
            Serial.print("  ");
            Serial.print(chnCapture[0]);
            Serial.print("  ");
            Serial.println(resolverAngle);
    
            //No more need for delay since all that is only executed after a valid capture !
        }
    }

  5. #5
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    17,596
    Agreed, calling micros() on Teensy LC will probably give performance similar or better than that AVR code.

    But if you really want to mess with timers, the register similar to TCNT3 on Teensy LC is FTM0_CNT. And FTM0_MOD is similar to OCR3A, giving you the maximum-1 count number before it rolls over to zero (or FTM0_MOD is zero if the count goes all the way up to 65535).

    Here's a very simple little program you can run to see them in action.

    Code:
    void setup() {
    }
    
    void loop() {
      uint32_t count, maximum;
      count = FTM0_CNT;
      maximum = FTM0_MOD;
      Serial.print("Count=");
      Serial.print(count);
      Serial.print(", Max=");
      Serial.println(maximum);
    }
    Regarding TCCR3A and TCCR3B, I'd recommend using analogWriteFrequency() to configure the timer. If you choose one of the "ideal" frequencies it will count all the way to 65535.

    https://www.pjrc.com/teensy/td_pulse.html

    If you *really* want to mess with those registers too, FTM0_SC is used to configure the timer, including prescaler, and of course you use FTM0_MOD to configure how high it will count.

  6. #6
    Junior Member
    Join Date
    Oct 2016
    Location
    Farnborough, UK
    Posts
    19
    Quote Originally Posted by PaulStoffregen View Post
    Agreed, calling micros() on Teensy LC will probably give performance similar or better than that AVR code.

    But if you really want to mess with timers, the register similar to TCNT3 on Teensy LC is FTM0_CNT. And FTM0_MOD is similar to OCR3A, giving you the maximum-1 count number before it rolls over to zero (or FTM0_MOD is zero if the count goes all the way up to 65535).

    Here's a very simple little program you can run to see them in action.

    Code:
    void setup() {
    }
    
    void loop() {
      uint32_t count, maximum;
      count = FTM0_CNT;
      maximum = FTM0_MOD;
      Serial.print("Count=");
      Serial.print(count);
      Serial.print(", Max=");
      Serial.println(maximum);
    }
    Regarding TCCR3A and TCCR3B, I'd recommend using analogWriteFrequency() to configure the timer. If you choose one of the "ideal" frequencies it will count all the way to 65535.

    https://www.pjrc.com/teensy/td_pulse.html

    If you *really* want to mess with those registers too, FTM0_SC is used to configure the timer, including prescaler, and of course you use FTM0_MOD to configure how high it will count.

    Thank you all for your help. I apologizes the most important piece of information I forgot to add is that this is driving aircraft system instruments (made by Frasca) so all the signals to and from the resolver are 400Hz. So a timer that last just a little longer that 2.5mS before overflow would give me the best resolution (I'll readjust the pre-scaling for the 16Mhz CPU clock). I am actually after a resolution of 0.1 degrees i.e. 1 part in 3600 so a 16 bit timer should be fine, but I like the idea of a 32 bit timer. I don't think I am clever enough to cope with overflow and the consequential math to cope with that and from my simplistic point of view it just seemed easier to reset the timer at the end of every reference cycle.

    Completely take on board your point about the loop just hacking away even when it does not need to. I will alter that (thanks). Finally I want to output all 6 resolver timings (only 1 channel written so far) to a Teensy 3.5 using I2C and there is the issue of converting the float to a format that is I2C compatible. I think you have shown me how to do that too. (I meant to send a version of the script without the Wire call, but that is extra helpful). I found another thread on that too so if I get stuck I will follow that through.

    Alternatively I can output the resolver angle as an analogue (analogWrite) set up for 10 bit (plus a Low pass filter) to match the analogRead in the Teensy 3.5. (not as good as 0.1 degrees though).

    Thanks again much appreciate you help. Basically I am a hardware engineer so circuitry is fine, software, not very clever at .

    BR PD.

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    17,596
    Quote Originally Posted by P Dobson View Post
    I am actually after a resolution of 0.1 degrees i.e. 1 part in 3600 so a 16 bit timer should be fine, but I like the idea of a 32 bit timer. I don't think I am clever enough to cope with overflow and the consequential math to cope with that and from my simplistic point of view it just seemed easier to reset the timer at the end of every reference cycle.
    Maybe the FreqMeasureMulti library could help? It has this sort of code, to extend the timer to 32 bit using the overflow interrupt. It gives the elapsed time between each rising edge of the waveform, to 1/48e6 precision. It uses timer input capture too, so it doesn't get small errors due to varying interrupt latency.

  8. #8
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,440
    Unsigned int math takes care of overflow with no real work. As long as all vars and compares and operations work with the same type.

    This works with millis() or micros() or any unsigned 8,16,32 integer when that type is maintained.

    This example shows it with unsigned 8 bit math - and adding a Prime 101 number causing overflow to show as -155 when not constrained to uint8_t:
    Code:
    void setup() {
      Serial.begin(38400);
      while (!Serial && (millis() <= 3000));
      Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
    }
    
    uint8_t Old=0, New=0, Diff=0;
    void loop() {
      New += 101;
      Serial.print( "New=" );
      Serial.print( New );
      Serial.print( "\tOld=" );
      Serial.print( Old );
      Serial.print( "\tNew-Old=" );
      Diff = New-Old;
      Serial.print( Diff );
      Serial.print( "\t\tNew-Old=" );
      Serial.print( New-Old ); // will show -155 as it isn't uint8_t constrained
      Old = New;
      Serial.println();
      delay( 800 );
    }
    Code:
    T:\tCode\FORUM\UnsignedMath\UnsignedMath.ino Jun 30 2018 16:28:12
    New=101	Old=0	New-Old=101		New-Old=101
    New=202	Old=101	New-Old=101		New-Old=101
    New=47	Old=202	New-Old=101		New-Old=-155
    New=148	Old=47	New-Old=101		New-Old=101
    New=249	Old=148	New-Old=101		New-Old=101
    New=94	Old=249	New-Old=101		New-Old=-155
    New=195	Old=94	New-Old=101		New-Old=101
    New=40	Old=195	New-Old=101		New-Old=-155
    Last edited by defragster; 07-01-2018 at 12:56 AM. Reason: removed dupe line

  9. #9
    Junior Member
    Join Date
    Oct 2016
    Location
    Farnborough, UK
    Posts
    19
    Thank you Paul, I will have a look at the FreqMeasureMulti. Will also check out unsigned Integers to cope with overflow.

    I ran your code Theremingenieur, and as expected it works with the hardware just fine. The test Print output is perfect. The serial data output is present and the number of data packets sent changes as the resolverAngle changes, but the data within each data packet remains fixed (i.e. the same character is being transmitted every time). The number of words transmitted seems to change at approx every 10 degree increment. The next step for me is to produce a script that will read the I2C data being sent on a Teensy 3.5. so I can investigate further. The interrupt coding you use has left me cold, but works, so will continue to use that if I may. Still trying to understand how union{ } works and how to use it. I'll press on.

    Thanks again, BR PD.

Tags for this Thread

Posting Permissions

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