Phase shift measurement with Tennsy 3.6

Status
Not open for further replies.

Christophe

New member
Hello,
I need to measure the phase shift between two square waveforms that have the same frequency and a 50% duty cycle (few Khz). The reference signal is on pin 10 and the signal measured on pin 5. I need to determine the phase shifting (+/- PI) between thoses 2 signals with a good accuracy (~20ns).
I am not an engineer and manipulate the registers of a μcontroller is out of my reach.

I think there are two ways to do it:

1 - Use the quadrature decoder mode but I suspect that it acts as a simple XOR gate that only provides information on a phase shift of +/- PI/2

2- Use the library of Paul Stoffregen "PulsePosition Library".
Instead of measuring the period of the signal, it would be possible to measure the time between the rising edge
of the reference signal (which gives the start signal) and that of the rising edge of the measured signal.
Knowing the period of the reference signal it would then be possible to determine the phase shift.

Thank you very much for your help
 
You will need to work with the FTM timer registers. The PulsePosition code can help, but it does not accomplish everything you want. You will need to add more work to achieve your goal.

You might also look at the FreqMeasure & FreqMeasureMulti code. It uses the same "input capture" technique, where the rapidly counting timer is copied to a register when the voltage on the pin changes. If you do this with 2 pins from the same timer, you will get 16 bit numbers for the exact moment each signal changed.
 
As suggested, your best option is to use a "Flexible Timer Module" - alias FTM. There are three available FTM0, FTM1 and FTM2. They really are great fun to work with, but it will need some mental "commitment" in order to understand and master what you are dealing with. The principles are simple, but sadly the manuals are not easy for newcomers.

The FTM mode appropriate for your requirement is a simple counter, which counts indefinitely (and loops around to zero if it overflows). The clock for the counter is one of the T3.6 internal clocks (sometimes called the peripheral clock) and you can set this to run at 120 MHz, which gives you a clock period of 8.3 nanoSecs - ample enough accuracy for ~20nS.

You can then use external "trigger" pins to "read" that counter when your signal makes a transition (either positive transitions, negative or both - you choose). The FTM has the ability to "freeze" a value of the counter at the instant of the transition. It stores this separately, and will hold it until you reset it in code. So by "freezing" the counter values coincident with your reference signal and measured signal, then a simple subtraction will give you the phase between them as a number of peripheral clock pulses. How often you perform these reads is again up to your decision - and you can fairly easily arrange for this to be done by an "interrupt service routine" so that you get regular updates before another transition happens.

Making a "read" of the counter by one of these external signals does not affect the basic counting operation. The FTM counting operation is oblivious to the fact that any read has taken place- it carries on regardless, so the accuracy of your readings are solely dependant on the accuracy of the clock pulses and the instants that your signals make the transitions.

This general idea is the basis for a phase detector, often used in a phase locked loop. There are many examples of code in the forum to illustrate and I'm happy to guide you in using the FTM registers if you are prepared to make the mental commitment to study. If you are, get yourself a copy of the Freescale Reference manual for the chip in ready for a journey into the world of FTM timers.
 
Hello,
Thank you for your answers.
Excuse my language but I go through google translation.
The last time I wrote code was in GW BASIC .... now I use LABVIEW instead. It took me a while to get back into this universe.


It is indeed to achieve a simple lock-in that I sought your help. After studying a little mechanics I got my way but it seems to me that I still have some bugs.

I have no merit ! To do this, I used Paul Stoffregen's library and simplified it for my particular needs.
I also helped myself to an excellent AN:
https://www.nxp.com/docs/en/application-note/AN5142.pdf

I have not yet grasped all the subtleties of the FTM but I am beginning to understand how it works.


Here is an example of the "bug" that I encounter when I read the raw value of the counter (Ref -Mes) every 10 ms. The returned value is false and that in a way that seems random.


17391;-1019.00
17402;64508.00 < ------
17413;-1021.00
17424;-1030.00
17435;-1022.00
17446;-1030.00
17457;-1024.00
17468;-1025.00
17479;-1026.00
17490;-1019.00
17501;-1025.00
17512;-1019.00
17523;-1028.00
17534;-1019.00
17545;-1027.00
17556;64514.00 <----------
17567;-1024.00


Here is the code in cpp :

#include "FreqMeasure.h"
#include "util/FreqMeasureCaptureMoi.h"


#define FREQMEASURE_BUFFER_LEN 12
static volatile uint32_t buffer_value[FREQMEASURE_BUFFER_LEN];
static volatile uint8_t buffer_head;
static volatile uint8_t buffer_tail;

static uint32_t captureRef;
static uint32_t captureMes;
static int32_t Dephasage;

void FreqMeasureClass::begin(void)
{
capture_init();
buffer_head = 0;
buffer_tail = 0;
capture_start();
}

uint8_t FreqMeasureClass::available(void)
{
uint8_t head, tail;

head = buffer_head;
tail = buffer_tail;
if (head >= tail) return head - tail;
return FREQMEASURE_BUFFER_LEN + head - tail;
}

int32_t FreqMeasureClass::read(void)
{
uint8_t head, tail;
int32_t value;

head = buffer_head;
tail = buffer_tail;
if (head == tail) return 0xFFFFFFFF;
tail = tail + 1;
if (tail >= FREQMEASURE_BUFFER_LEN) tail = 0;
value = buffer_value[tail];
buffer_tail = tail;
return value;
}


float FreqMeasureClass::countToNanoseconds(int32_t count)
{
return (float)count * (1000000000.0f / (float)F_BUS);
}

void FreqMeasureClass::end(void)
{
capture_shutdown();
}


void FTM_ISR_NAME (void)
{
uint8_t i = 0;
bool inc = false;

if (capture_overflow()) {
capture_overflow_reset();
inc = true;
}

if (capture_event_Ref()) {

captureRef = capture_read_Ref();

do {

} while (capture_event_Mes()==0);

captureMes = capture_read_Mes();

Dephasage = captureRef - captureMes;

i = buffer_head + 1;
if (i >= FREQMEASURE_BUFFER_LEN) i = 0;
if (i != buffer_tail) {
buffer_value = Dephasage;
buffer_head = i;
}
}
}


FreqMeasureClass FreqMeasure;

The header :

#define CAPTURE_USE_FTM1_CH0 3 // FTM1 CH0 sur pin 3. C'est la Référence
#define CAPTURE_USE_FTM1_CH1 4 // FTM1 CH1 sur pin 4. C'est la Mesure


#define FTM_SC_VALUE (FTM_SC_TOIE | FTM_SC_CLKS(1) | FTM_SC_PS(0))
#define FTM_ISR_NAME ftm1_isr

static inline void capture_init(void)
{
if (FTM1_MOD != 0xFFFF || (FTM1_SC & 0x7F) != FTM_SC_VALUE) {
FTM1_SC = 0;
FTM1_CNT = 0;
FTM1_MOD = 0xFFFF;
FTM1_SC = FTM_SC_VALUE;
FTM1_MODE = 0;
FTM1_MODE = FTM_MODE_WPDIS; //allow reconfiguring the CSC on the fly
}

NVIC_SET_PRIORITY(IRQ_FTM1, 48);
}

static inline void capture_start(void)
{
FTM1_C0SC = 0b01000100;
*portConfigRegister(CAPTURE_USE_FTM1_CH0) = PORT_PCR_MUX(3);
FTM1_C1SC = 0b01000100;
*portConfigRegister(CAPTURE_USE_FTM1_CH1) = PORT_PCR_MUX(3);
NVIC_ENABLE_IRQ(IRQ_FTM1);
}

static inline uint16_t capture_event_Ref(void)
{
return (FTM1_C0SC & 0x80) ? 1 : 0;
}

static inline uint32_t capture_read_Ref(void)
{
uint32_t val = FTM1_C0V;
FTM1_C0SC = 0b01000100;
return val;
}

static inline uint8_t capture_overflow(void)
{
return (FTM1_SC & FTM_SC_TOF) ? 1 : 0;
}

static inline void capture_overflow_reset(void)
{
FTM1_SC = FTM_SC_VALUE;
}

static inline void capture_shutdown(void)
{
FTM1_C0SC = 0;
*portConfigRegister(CAPTURE_USE_FTM1_CH0) = 0;
FTM1_C1SC = 0;
*portConfigRegister(CAPTURE_USE_FTM1_CH1) = 0;
NVIC_DISABLE_IRQ(IRQ_FTM1);
}

static inline uint16_t capture_event_Mes(void)
{
return (FTM1_C1SC & 0x80) ? 1 : 0;
}

static inline uint32_t capture_read_Mes(void)
{
uint32_t val = FTM1_C1V;
FTM1_C1SC = 0b01000100;
return val;
}

second header :

#ifndef FreqMeasure_h
#define FreqMeasure_h

#include <Arduino.h>

class FreqMeasureClass {
public:
static void begin(void);
static uint8_t available(void);
static int32_t read(void);
static float countToNanoseconds(int32_t count);
static void end(void);

};

extern FreqMeasureClass FreqMeasure;

#endif




Thank you very much for your comments or suggestions
 
Same bug in ns. the real result is ~ -17120 ns.

Another detail may be important I generate the reference frequency with analogWriteFrequency on pin 38. Logically, he uses the FTM 3 Timer and measures the FTM 1.

13.25;-17100.12
13.26;-17104.77
13.27;-17122.49
13.28;-17136.34
13.29;-17134.54
13.30;-17129.97
13.31;-17124.39
13.32;178451.11 <---
13.33;352392.09
13.34;249482.41
13.35;119157.32
13.36;47742.81
13.37;14367.64
13.38;-2031.92
13.39;-10160.82
13.41;-13975.37
13.42;-15703.94
13.43;-16484.77
13.44;-16822.19
13.45;-16969.42 -
13.46;-17049.04
13.47;-17099.36 <-----
13.48;-17123.52
13.49;-17124.65
13.50;-17118.12
13.51;-17101.33
13.52;-17097.17
13.53;-17107.18
13.54;-17110.11
13.55;-17100.50
13.56;-17091.92
 
Since this looks like a typical signed/unsigned overflow issue, I would recommend these two changes:

-Make Desphasage an unsigned int like the other variables:
Code:
static uint32_t Dephasage;
-Reverse the substraction:
Code:
Dephasage = captureMes - captureRef;

Marc
 
Thank you for your answer Marc but the problem is elsewhere. The phase shift must be positive or negative so in signed variable.
But I ended up finding the solution!
The variables must be in 16 bits and not in 32 which causes a buffer overflow.

Thanks for your help

Christophe
 
But I ended up finding the solution!

Now you have the solution, so problem solved.

That application note you quoted in post #4 is an excellent document - it duplicates a lot of the material contained in the "Reference Manual".

By the way, when adding your code, try using the CODE tags to wrap around (it makes it easier for others to assist).
 
Status
Not open for further replies.
Back
Top