Teensy LC timers

Status
Not open for further replies.

P Dobson

Active member
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.
 
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.
 
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.
 

Attachments

  • Resolver_phase_reader_ver3.ino
    1.3 KB · Views: 87
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 !
    }
}
 
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.
 
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.
 
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.
 
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:
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.
 
Teensy LC timers.

Hi Theremingenieur,

I am still having trouble with I2C so have put that to one side for the moment and instead I am outputting the Resolver angle as an analogWrite. Then in the other teensy 3.5 card I am analogReading the value to get the effect I need. This works as long as the Teensy LC is plugged into a PC USB port. But as soon as I plug the Teensy LC into an external +5v supply the analogue out PWM appears in blocks with gaps between the blocks (i.e. not continuous PWM) this of course make a nonsense of the output from a low pass filter I have placed downstream of the PWM output. I have commented out all serialPrint statements, anything to do with the Wire library and have checked for noise on the D+ and D- lines all to no avail. Also tried different PWM pins controlled by different timers, no change.

So what could possibly causing this issue?? Code attached. The external +5v supply is 5.12v so I think this is fine too. Also tried a second power supply.

Confused Peter D.


View attachment TeeensyLC_resolver_to_analogue.ino
 
Can you attach a photo of your wiring? Do you have common ground between LC, T3.5, square wave generators, and external 5v power supply?

when you power LC with external 5v to Vin, i assume you disconnected LC from USB power ?

Teensy LC is not 5v tolerant, so i'm assuming square wave pins are 3.3v max.

EDIT: androgynous test

Lacking your PWM generators, I modified your sketch to use two of the LC PWM pins (9 and 10, @488 Hz) connected to pins 2 and 3 of the LC. Pin 9 had a 12-bity duty value of 200, and pin 10 I varied from 300 to 4000 in steps of 100 every second. With pin 16 on a scope, I confirmed that the pin 16 duty cycle was varying as the delta between the falling edges of pins 9 and 10. The modified sketch:
Code:
//#include<Wire.h>

elapsedMillis ms;
int duty = 300;

#define NUM_CHANNELS 1


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;

float resolverScaled = 0; //definition
int resolverAnglePin = 16; //PWM pin 16.


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() {

  pinMode(led, OUTPUT);
  pinMode (resolverAnglePin, OUTPUT);

  refMicros = micros();

  analogWriteFrequency(16, 11718.75 ); //sets pin 29 to PWM frequency of 14648.437KHz
  analogWriteResolution(12); //set analogwrite to 12 bit resolution.
  analogWrite(9, 200);
  analogWrite(10, duty);

  attachInterrupt(2, IsrRef, FALLING);
  attachInterrupt(3, IsrCh0, FALLING);
}

void loop() {
  if (ms > 1000) {
    duty += 100;
    if (duty > 4000) duty = 300;
    analogWrite(10, duty);
    ms = 0;
    Serial.println(resolverAngle);
  }
  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;

    resolverScaled = resolverAngle * 11.377;
    analogWrite (resolverAnglePin, resolverScaled);

    //No more need for delay since all that is only executed after a valid capture !
  }

}

The test sketch worked powered from USB, and it worked if I disconnected LC USB and powered 5v to LC Vin with GND from power supply to LC GND.
 
Last edited:
Teensy LC misbehaving.

Thanks very much for the code worked a treat and it has moved me forward a long way. I am pretty happy with the hardware. A single 0v every where, the input comparators are supplied from the Teensy 3.3v so should not be any issues there. The sine wave inputs are all transformer coupled so no common mode noise, nice capacitor across the external 5v supply. But having used your code and got it to work OK with an external +5v regulated supply, I have now found that swapping the transformer secondary winding over i.e. change the input by 180 degrees causes the same problem whether on external power or PC USB connection. So the only difference between your code (which continues to work) and what I am doing is that the two input signals I have are asynchronous to the Teensy clocks and I am wondering if some how this is causing the problem. I understand that to read data across from the timers it is better to inhibit interrupts so that a change in value during the data read does not corrupt the data value in case an interrupt occurs exactly at that point. At the moment I have two interrupts every 2.5mS and at certain angles they occur at identical times, How does the Teensy hardware cope with that or does software protection need to be put in place???

Next trick for me is to try and lock the external Sin/Cos generator to the Teensy clock, and also try running a second Teensy as the signal source as the second Teensy will not be clock phase locked to the first. I'll see what that does. So thank you for your help, I have moved forward but not quite got there yet. It is a very strange bug. More ideas always welcome. The circuit diagrams are enclosed.

BR Pete D.
 

Attachments

  • Resolver IF sheeet 2.pdf
    50.9 KB · Views: 87
  • Resolver IF sheeet 1.pdf
    55.1 KB · Views: 101
So the only difference between your code (which continues to work) and what I am doing is that the two input signals I have are asynchronous to the Teensy clocks and I am wondering if some how this is causing the problem...

FWIW, I used a 2nd Teensy (3.2) with my same PWM control on pins 9 and 10 jumpered to LC pins 2 and 3. LC pin 16 on scope still looks fine. So maybe that shows independent clocking is not the issue. T3.2 is powering the LC
 
Well that fixed it.

Thanks Manitou, your work on dual Teensys got me thinking down different routes and overnight it became obvious. I am getting multiple falling edges on the interrupt lines. Checked it out this morning and sure enough there they were approx 1uS apart. That'll teach me to build birds nests instead of ground plane mounted prototype circuits for HF work. So I added a little positive feed back to the LM339s and improved supply and bias decoupling. Interestingly I am also getting a bit of cross talk between output channels so as one falling edge gets close to the other time wise the other responds with multiple edges. I can fix that with a proper board layout too. So I have not tried it yet but I guess I can also add a couple of lines to the interrupt routine so that a test for values close to the previous value is done. If the value has not changes much do not update the read timer value (bit like de-bounce). Because it is for an aircraft system the base frequency is always going to be close to 400Hz and there should never be more than 1 interrupt per channel every 2.5mS.

Why the problem manifested itself the way it did I am still not sure, supply decoupling possibly? Any how a good dose of ground plane, decent HF layout and decoupling will fix it all.

I'll go back and look again at the I2C interface, it may well have been affected by the same issues.

Thanks again, I am over the moon and now cracking on with a proper board layout.

BR Pete D.
 
Teensy LC timers.

Since last posting I have moved on a bit. Got all 6 channels to work and print out, but do not have the knowledge to format the data into a form that can be sent via i2c. can any one help? I don't understand "union" for example or how to use it in this application. I need to compile 6 off uint32_t variables into a single frame to send over i2c.

Also need to understand the sort of code needed at the i2c receiving end to break the data back into individual resoverAngles

there are some complications; if an interrupt for a give channel does not come in on a particular IsrRef cycle then I need to set the data to an out of range value that can be detected by the receiving i2c and prevent servo runaway because of out of date data. I have tried to include lots of notes with the code. See top of thread for hardware set up.

Any help very much appreciated. Pete D.
 

Attachments

  • Resolver_reader_Ver7_multichannel.ino
    4.6 KB · Views: 90
In C & C++, a union means you can access data as more than 1 type. It's still the same binary data. Usually it's used the way you have it, to access the raw bytes.

Code:
union {
  float fData;
  uint8_t bData[4];
} transmitConv[NUM_CHANNELS]; // what does this do? presumably formats the data for i2c transmission, but don't undestand how to use it.

Regarding this line:

Code:
    transmitConv[0].fData = resolverAngle0; //?? what does this do

The resolverAngle0 variable is a 32 bit integer. This code converts it to a float and stores it into the first item of the transmitConv array.

If you're not familiar with how floating point numbers are stored, which is normally the sort of detail you could ignore, you'll need to learn just a bit to understand what's going on. Here's one of many pages on the subject.

https://www.trimble.com/OEM_ReceiverHelp/V4.44/en/FloatingPointDataTypes.html

Here's a page which shows you the conversion.

http://www.binaryconvert.com/convert_float.html

The important point is the float data isn't stored anything like an ordinary integer. For example, if you store the number 7, the actual bytes put into the float are 40 E0 00 00.

Once you understand what bytes should actually be in the float, hopefully the rest of this code can make more sense. If it's still unclear, maybe add some code like this:

Code:
  Serial.print("int was: ");
  Serial.println(resolverAngle0);

  Serial.print("float was: ");
  Serial.println(transmitConv[0].fData);

  Serial.print("bytes: ");
  Serial.print(transmitConv[0].bData[0], HEX);
  Serial.print(",");
  Serial.print(transmitConv[0].bData[1], HEX);
  Serial.print(",");
  Serial.print(transmitConv[0].bData[2], HEX);
  Serial.print(",");
  Serial.print(transmitConv[0].bData[3], HEX);
  Serial.println();

Hopefully you'll be able to see the union is pretty simple. It's just letting you access the same exact thing in 2 different ways.

The tricky part is understanding the float data format, how something like 40 E0 00 00 actually means the number 7.00.
 
Teensy LC timer read by DMA?

Thank you Paul.

Moved on a bit (with your help) and have uncovered another problem. If the phasing of the two signals are pretty much in sync then the channel interrupt (ISR Ch X) occurs whilst the reference interrupt (ISR Ref) is still running (Measured to be 2 to 3 us). This produces a hole in the data where it is not possible to work out the phase angle when the phase relationship is any where between 0 degrees and 10 degrees (2us). I can't see how to fix this using interrupts (which will always occupy time). So I was now thinking of somehow getting timer readings via hardware (DMA??) so that when an incoming edge occurs the current timer reading is transferred by hardware to a memory location. Is this possible?? I guess this required machine code type solution which would be way above my skill set. Currently using a Teensy LC but could easily use a Teensy 3.2 instead. Any advse or help very welcome.

The enclosed sketch works except when the phase difference between reference and the measured channel is less than approx 2uS.

BR Peter D.
 

Attachments

  • Resolver_reader_Ver7_multichannel_PD.ino
    2 KB · Views: 70
Status
Not open for further replies.
Back
Top