Duty cycle measurement via Teensy 3.6

RRdae

Active member
I am working with a magnetic encoder that outputs a PWM waveform with the duty cycle proportional to the position for one rotation. The PWM frequency is 1khz and the minimum "ON" period before rolling over to a new rotation is 1.8us (the minimum "OFF" period is about 2x longer). Initially, I intended to pass the PWM through a low pass filter and read the result via ADC, but I am having an issue with noise on the output of the low pass due to noise in the amplitude of the output from the encoder (but not noise in the duty cycle).

Which brings me to where I am currently at, needing to measure a short period between rising and falling edges that ranges from 1.8us to 1ms with at least 10bit resolution. Any solution cannot impede the main loop for more than 100 microseconds, as the same controller monitors other functions that are time dependent (BLDC motor control, current sense, etc).

Searching and reading over the last day has led me to believe that FTM timers may be exactly what I need for this purpose, but I am having trouble grasping my mind around the lower level functions of the Teensy. This leads me to a few questions:

1. Does anyone know of a library that can already do what I need (calculate the length of a pulse without impeding the main program for the duration of each measurement)?

2. Is there a tutorial on accessing the FTM? My degree and background is in mechanical, so my experience with low level programming is limited.

3. Is there a list of variables for low level functions of the K66 family somewhere? I am looking through FreqCountTimers.h to try to understand FTM and am seeing variables accessed that seem to exist due to the core programming without indication of what their purpose is. I would like to know where these come from, or at least how they are determined using the K66 manual. For example, in FreqCountTimers.h, line 120, 121, variables TCCR1A and TCCR1B are accessed.

Thank you for reading.
 
Thank you!

I don't know how I missed that package, but FREQMEASUREMULTI_MARK_ONLY is exactly what I need. I will continue learning about the low level functions of the Teensy, but the purpose of this thread is fulfilled!
 
Happy to see that this extension to the library, allowing optionally the selection of different parts of the signal, which I contributed many years ago and which seems to be rarely used, is useful for you. :)
 
Thank you for your extension, it's very helpful!

I have created a short library to simplify the use of the AS5048 magnetic encoder via the PWM output. I am attaching it here in case anyone encounters a similar situation. It makes use of your library for time measuring, then conditions the results into a usable angle (over both a single turn and multiple turns) or duty cycle. A simple moving average can also be used, optionally. Hardware zeroing via I2C is not implemented; Since it is a permanent operation I did not want to accidentally trigger it until I am ready, so it will be added later.

Note: An I2C library for the AS5048A is available by another author, but I avoided it because, from what I have seen, I2C is a slow headache. This one works using the PWM output from the IC without I2C.

Note 2: It requires 2 pins, with identical inputs, for sensing. One to measure the duty cycle and one to measure the period. A single pin can be used, with modification, but the result will have more uncertainty as the PWM signal tends to fluctuate a bit. Recording the period for comparison gives much more accurate results. Accuracy is easily within a tenth of a degree, perhaps higher with filtration.

Note 3: Read the info at the top of the header file for details.
 

Attachments

  • AS5048.cpp
    6.6 KB · Views: 121
  • AS5048.h
    5.5 KB · Views: 203
  • Leg_OS_v0_1.ino
    556 bytes · Views: 105
Thank you for your extension, it's very helpful!

Note 3: Read the info at the top of the header file for details.

Hi,

Thank's for this as I think it is what I need.

But I'm very noob about code, I understand that I have to make a Pin_def.h file but I'have no idea what to put inside. Could you please add an example of it with your attached Files ?

I don't know if it's normal but I also notice that you call in your Leg_OS_v0__1.ino file:
#include "Pin_def.h"
And in your .ccp & .h :
#include "_def_Pins.h"

Does it suposed to be the same file ?

Thank's by advance.
 
I do not actually remember what I used _pin_def.h for, but it was a remnant from the larger program that I was writing at the time.

I do remember that Pin_def.h contained definitions for all the pin connections for my application. In my case it had 40ish definitions, but if you are only wanting to use it for this application, you will only need 2. I suggest the following:

1. In the cpp file, go to Setup and change ROT_PWM_HIGH and ROT_PWM_PER to be the values of the two pins you want to measure with. Alternatively, you could define those two values in the header file, similar to: #define ROT_PWM_HIGH pin_number_you_want_to_measure

Code:
void AS5048::Setup()
{
    //Initialize frequency measure objects with pins defined in _def_Pins.h
    MeasHigh.begin([B]ROT_PWM_HIGH[/B],FREQMEASUREMULTI_MARK_ONLY);
    MeasPeriod.begin([B]ROT_PWM_PER[/B]);

2. Remove the #include to _def_Pins.h from the h and cpp file (they dont belong in the latter, anyway), and remove the #include to Pin_def.h from the .ino file (it is a remnant from the larger program that should never have been left in the example code).
 
Thank's you very much for your fast answer.It's works nice.
This thing seems very noisy, maybe PWM is more sensitive to it instead of SPI/I2C.
But it could be a good opportunity to see the impact using a shielded cable !
 
I do not actually remember what I used _pin_def.h for, but it was a remnant from the larger program that I was writing at the time.

I do remember that Pin_def.h contained definitions for all the pin connections for my application. In my case it had 40ish definitions, but if you are only wanting to use it for this application, you will only need 2. I suggest the following:

1. In the cpp file, go to Setup and change ROT_PWM_HIGH and ROT_PWM_PER to be the values of the two pins you want to measure with. Alternatively, you could define those two values in the header file, similar to: #define ROT_PWM_HIGH pin_number_you_want_to_measure

Code:
void AS5048::Setup()
{
    //Initialize frequency measure objects with pins defined in _def_Pins.h
    MeasHigh.begin([B]ROT_PWM_HIGH[/B],FREQMEASUREMULTI_MARK_ONLY);
    MeasPeriod.begin([B]ROT_PWM_PER[/B]);

2. Remove the #include to _def_Pins.h from the h and cpp file (they dont belong in the latter, anyway), and remove the #include to Pin_def.h from the .ino file (it is a remnant from the larger program that should never have been left in the example code).

are ROT_PWM_HIGH and ROT_PWM_PER the same pin?
 
are ROT_PWM_HIGH and ROT_PWM_PER the same pin?

No, they are two separate pins. In the code below, which is the same as posted by RRdae, but shows a few more lines above Setup(), you can see that MeasHigh and MeasPeriod are variables of type FreqMeasureMulti. In Setup() are the calls to the begin() function. The first argument is a pin number, and they must be two different pins. If you passed the same pin number to both instances of FreqMeasureMulti, the second one would override the first one. I don't know if it would "work" or not, but it wouldn't be what you want. MeasHigh.begin() gets a second argument which is the measurement type. That second argument is not required for MeasPeriod.begin() because the default measurement type is period. Since you want to measure the period and the high time of one signal, on two different pins, you need to connect your signal to both of the pins you choose.

Code:
FreqMeasureMulti MeasHigh;
FreqMeasureMulti MeasPeriod;

AS5048::AS5048(){}

void AS5048::Setup()
{
    //Initialize frequency measure objects with pins defined in _def_Pins.h
    MeasHigh.begin(ROT_PWM_HIGH,FREQMEASUREMULTI_MARK_ONLY);
    MeasPeriod.begin(ROT_PWM_PER);
 
Back
Top