FreqMeasureMulti on the T4

Status
Not open for further replies.

xenington

Well-known member
OK. So I have had look into porting FreqMeasureMulti for the t4 and it is all very confusing. Can someone give me some pointers please?

I am not sure which timer to use: flexpwm or GPT? Both can be triggered by waveform features. From the datasheet, flexpwm4 is attached to pins 22 (AD_B1_08 as used by FreqMeasure), 23 (AD_B1_09) and 3 (EMC_05). That gives 3 readings only. GPT1 is not attached to any pins and GPT2 is only attached to pin 15 (AD_B1_03). Can 1 pin take multiple readings? Or can a variety of timers be used across multiple pins?

My goal is to measure 4 square-wave frequencies in the range 1KHz to 100KHz, up to 16 times each per second. I would prefer to use FreqMeasure if possible as FreqCount probably won't provide enough resolution at this sample rate. Might the t4 push FreqMeasure up that high?

The waves are produced by 4 x fgm3 magnetic sensors https://www.fgsensors.com/documentation. The signals are level-shifted by an Adafruit TXB0108 https://www.adafruit.com/product/395 before entering the t4.

Thanks for your help.
 
The TXB0108 level shifter was producing loads of interference so I replaced it with resistor voltage dividers instead (10k + 15k).
 
Over the last few days I have been playing around with this library for the T4.x boards.

I have a version up at: https://github.com/KurtE/FreqMeasureMulti/tree/T4_X_WIP
Which I issued a Pull Request for: https://github.com/PaulStoffregen/FreqMeasureMulti/pull/10

I implemented it using pins connected to FlexPWM timers. Which on the T4 is : 0-9, 22-25, 28, 29, 33, 34-39

I would not be surprised if it might need a few tweaks, but I think it is reasonable.

Here is an example showing initial testing versus a T3.6 for the different capture modes:
Some of the comments at the start of example shows a results line for each type of capture.

Code:
/* FreqMeasureMulti - Example with serial output
   http://www.pjrc.com/teensy/td_libs_FreqMeasure.html

   This example code is in the public domain.
*/
#include <FreqMeasureMulti.h>

// Measure 3 frequencies at the same time! :-)
FreqMeasureMulti freq1;

// Try different values for which type of measure:
#define MEASURE_PIN 39

#define MEASURE_TYPE FREQMEASUREMULTI_RAISING
#if defined(__IMXRT1062__)
#define PWM_OUT_PIN 10   // not a flexPWM pin 
#else
#define PWM_OUT_PIN 7  // not an FTM0 pin... 
#endif
//    FREQMEASUREMULTI_RAISING 
// T3.6: 60.00(2:1000000:16.67 2:1000000:16.67 2:1000000:16.67 2:1000000:16.67 )
// T4:   60.02(2:2490368:16.60 2:2490368:16.60 2:2490368:16.60 2:2490368:16.60 )
//    FREQMEASUREMULTI_FALLING 
// T3.6:  60.0(1:1000000:16.67 1:1000000:16.67 1:1000000:16.67 1:1000000:16.67 )
// T4:   60.02(1:2490368:16.60 1:2490368:16.60 1:2490368:16.60 1:2490368:16.60 )
//    FREQMEASUREMULTI_INTERLEAVE 
// T3.6: 60.00(2:1000000:16.67 1:1000000:16.67 2:1000000:16.67 1:1000000:16.67 )
// T4   60.02(1:2490368:16.60 2:2490368:16.60 1:2490368:16.60 2:2490368:16.60 )
//    FREQMEASUREMULTI_SPACE_ONLY 
// T3.6:63.08(0:951184:15.85 0:951184:15.85 0:951184:15.85 0:951184:15.85 )
// T4:  63.05(0:2359296:15.73 0:2359296:15.73 0:2424832:16.17 0:2424832:16.17 )
//    FREQMEASUREMULTI_MARK_ONLY 
// T3.6: 1229.11(3:48816:0.81 3:48816:0.81 3:48816:0.81 3:48816:0.81 )
// T4: 1226.16(3:65536:0.44 3:131072:0.87 3:131072:0.87 3:131072:0.87 )
//    FREQMEASUREMULTI_ALTERNATE 
// T3.6: 120.00(3:48816:0.81 0:951184:15.85 3:48816:0.81 0:951184:15.85 )
// T4  : 120.04(3:131072:0.87 0:2359296:15.73 3:131072:0.87 0:2359296:15.73 )

void setup() {
  Serial.begin(57600);
  while (!Serial) ; // wait for Arduino Serial Monitor
  analogWriteResolution(10);
  analogWriteFrequency(PWM_OUT_PIN, 60);
  analogWrite(PWM_OUT_PIN, 50);

  delay(10);
  Serial.println("FreqMeasureMulti Begin");
  delay(10);
  freq1.begin(MEASURE_PIN, MEASURE_TYPE);
}

float sum1 = 0;
int count1 = 0;
int freq1Reads[4];
uint8_t levels[4];
elapsedMillis timeout;

void loop() {
  if (Serial.available()) {
    uint16_t analog_write_val = 0;
    uint16_t val = 0;
    int ch;
    while ((ch = Serial.read()) != -1) {
      if ((ch == ' ') || (ch == ',')) {
        analog_write_val = val;
        val = 0;
      } else if ((ch >= '0') && (ch <= '9')) {
        val = val * 10 + ch - '0';
      }
    }
    // Yah quick and dirty
    analogWriteFrequency(PWM_OUT_PIN, val);
    if (analog_write_val) {
      analogWrite(PWM_OUT_PIN, analog_write_val);
    }
  }
  if (freq1.available()) {
    for (uint8_t i = 3; i > 0; i--) {
      freq1Reads[i] = freq1Reads[i - 1];
      levels[i] = levels[i - 1];
    }
    freq1Reads[0] = freq1.read();
    levels[0] = freq1.readLevel();
    sum1 = sum1 + freq1Reads[0];
    count1 = count1 + 1;
  }
  // print results every half second
  if (timeout > 500) {
    if (count1 > 0) {
      Serial.print(freq1.countToFrequency(sum1 / count1));
      Serial.print("(");
      for (uint8_t i = 0; i < 4; i++)
        Serial.printf("%u:%u:%.2f ", levels[i], freq1Reads[i], freq1.countToNanoseconds(freq1Reads[i])/1000000.0);
      Serial.print(")");
    } else {
      Serial.print("(no pulses)");
    }
    Serial.println();
    sum1 = 0;
    count1 = 0;
    timeout = 0;
  }
}
 
Thank you, thank you, thank you, thank you, thank you.

This has been doing my nut in for ages. I will do some testing tomorrow and report back.
 
@ KurtE

Excellent work. The library works very well. I can take readings from 4 sensors producing square wave frequencies around 50KHz in less than 1 microsecond! Using pulseIn() or digitalRead() takes much longer - over 100 milliseconds.

I have been trying to get this to work with TeensyTimerTools as I want to take readings every so often. However I am getting strange results such as 0 or inf or 65535. Could this be conflicting interrupts or is the buffer overflowing?
 
It may depend on which timer you choose? I did my quick and dirty testing using PWM from QT pins...
 
I tried them all: TCK, GPT and TMR.

The example sketch you gave above in post #3 prints the following:

Code:
41981.53(2:4294967295:0.00 2:4294967295:0.00 2:0:0.00 2:4294967295:0.00 )
42016.81(2:4294967295:0.44 2:4294967295:0.00 2:4294967295:0.00 2:4294967295:0.00 )
42028.58(2:4294967295:0.00 2:4294967295:0.00 2:0:0.00 2:4294967295:0.00 )
41981.53(2:4294967295:0.00 2:4294967295:0.00 2:4294967295:0.00 2:4294967295:0.00 )
42016.81(2:4294967295:0.00 2:4294967295:0.44 2:0:0.00 2:4294967295:0.00 )
42040.36(2:4294967295:0.00 2:4294967295:0.00 2:4294967295:0.00 2:4294967295:0.00 )

The freq.read() is either at the upper limit of uint32_t or 0. However, freq.countToFrequency() seems accurate at 42KHz. Weird.
 
If I use the output of pin10, this is printed:

Code:
59.97(2:2490368:16.60 2:2490368:16.60 2:2555904:17.04 2:2490368:16.60 )
60.03(2:2490368:16.60 2:2490368:16.60 2:2490368:16.60 2:2490368:16.60 )
59.97(2:2490368:16.60 2:2555904:17.04 2:2490368:16.60 2:2490368:16.60 )
60.02(2:2490368:16.60 2:2490368:16.60 2:2490368:16.60 2:2490368:16.60 )
59.97(2:2555904:17.04 2:2490368:16.60 2:2490368:16.60 2:2490368:16.60 )

This seems OK. Maybe 42KHz is too fast and freq.read() reaches its limit too quickly.
 
@KurtE

Hi Kurt

Can you please explain how FreqMeasureMulti for the Teensy 4 actually works? Regarding rising edges only.

Does it measure the time between 2 edges? Take an average of several periods?

Thanks

BTW the above problem turned out to be competing interrupts.
 
Sorry I did not see this question earlier.

I am not a full expert on a lot of this. I started off from what Paul did for one pin in the other library and generalized it some to work with the other pins. Note in all cases like it, it is best to take a look at the actual code.

So a lot of main setup stuff was done by Paul. But I did learn a lot while doing the converting.

The code uses the eFlexPWM (chapter 54 of the IMXRT1060RM) Each of these pins that are supported here are pins that can be Input pins one of the channels of the 4 submodules of the 4 modules)

For example T4 pin 0 is: FlexPWM1_1_X Module 1 sub module 1 X channel. If you look at the library source file FreqMeasureMultiIMXRT.cpp you will see a table with all of the pins and the stuff that makes them work...

Example Pin 2:
Code:
&IMXRT_FLEXPWM4, M(4,2), 1, 1 | 0x10, IRQ_FLEXPWM4_2, &FreqMeasureMulti::flexpwm4_2_isr, &IOMUXC_FLEXPWM4_PWMA2_SELECT_INPUT, 0},  // FlexPWM4_2_A   2  // EMC_04

Probably has more details in it than you are interested in, but includes things like: it is FlexPWM4 so first thing is pointer to that objects hardware registers. The (4,2) is encoding that it is FlexPWM 2 sub module 2, the 1 is saying that it is the A channel. The 1 | 0x10 says we need to change this pins MODE to this to be a flexPWM input pin. Next is systems ISR number for this, the next is a pointer to the ISR function that will be called, and the next two are saying that I might have to tell the system that for this actual function MAP the SELECT INPUT, using the mentioned register address to the value 0 in this case...

So the begin method uses all of that above data, to first initialize the timer for the module/submodule . Note: there can be a complication that more than one pin might use the same timer module/sub-module as again three channels (A, B, X), The code tries to handle it by link listing the objects to each other and handle all of them in same ISR.

But again the code than changes the IO pin mentioned into the MUX value specified plus if needed set the pin SELECT_INPUT PIN as to route that signal from that pin.

Note: For each of these channels, the Module/SubModule can capture two time of the two possible edges. But in the Falling or rising case it only sets up to capture one of these in the first of the two.
It then tells the module to interrupt us on that channel capture as well as overflows of the timer. The ISR when triggered sees if there was an overlow and if so updates a count in the object.

And it detects if there was an edge captured. If so it calculates the actual timing and saves it out on the output queue, which the client can then get...
 
Hi KurtE

Thanks for the info. It was very useful in getting started in understanding how this works.

One thing I have discovered is that it appears (I may be wrong) that freqmeasuremulti for T4 is more of a period counter than a period measurer. I am still investigating...
 
New FreqMeasureMulti

Hi KurtE (and everyone else)

Unfortunately, I was right about FreqMeasureMulti being a cycle counter and not a cycle measurer.

So, I had a go at writing my own! It doesn't have the broad functionality of the original, but it does accurately measure the time taken for 1 period to pass and it does what I need it to. Perhaps it can be expanded or the method integrated into the original?

Anyway, see what you think. You will need to provide a frequency source for testing.

ATB.
 

Attachments

  • FreqMeasureMulti_X.ino
    4.3 KB · Views: 61
FreqMeasureMulti_X

Hello all

Attached is the final version of FreqMeasureMulti_X. The version attached to message #12 above is now redundant.

This new version measures the period of 4 digital signals at a certain, specified rate. This frees up time to do other stuff inbetween readings, such as saving the data to an SD card.

It uses the PIT timer to trigger an isr where the flexPWM timer captures the time between 2 consecutive rising edges. The flexPWM timer ticks over at 6.66667 nanoseconds, so resolution is excellent.

The time taken from the start of the isr to the end of copying the volatile buffer to a non-volatile buffer is about 1.2 microseconds. In theory read rates of more than 800,000 per second should be possible! I have no way of measuring this though.

Also, my testing was carried out on a ~40KHz signal. I don't know what the signal frequency limit might be (less than 150MHz presumably).

Hope you like it and find it useful. :cool:

Any suggestions or queries are welcome.
 

Attachments

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