Teensyduino 1.56 Beta #2

Status
Not open for further replies.
I don't understand. FreqCount is still included with the installer. Its 3 examples should still be in the Examples menu.

Not sure what timing constraint you're talking about. On each Teensy model, that library can count at fast as the external clocking of hardware timers allows.

@Paul
Thank you for replying to my query.

My system consists of :
Teensy 4.1 plus custom PC board and touchscreen.
Windows 10 PC
Recently installed Arduino 1.8.15 and Teensyduino 1.56-beta1
The Library manager indicates I have FreqCount 1.3.0
I see no FreqCount() examples.

My application includes a frequency meter for RF purposes up to 60 MHz with a programmable gate period the user can set to any decade value between 10uSec and 10Sec. The input buffering is achieved with a high speed Schmidt trigger and some other routing. The input to the T4.1 is full amplitude and nice and clean.

The Teensy FreqCount function works well when tested with the original sample code but gives strange results when the period between measurements is made longer than the gate period. The interval between measurements should not influence the value of the measurement.

With a gate time of 1000 uSec and a delay of 13mSec simulating the other functions a 10MHz input displays 13,0001. (Error)

With a gate time of 1000 uSec and a delay of 900uSec simulating the other functions a 10MHz input displays 10,000. (Correct)

The attached code clearly shows the problem I'm experiencing, please see the comments.
Regards,
RichardL

PS: I tried an alternative to delayMicroseconds() just in case that was causing a conflict; no change.

Code:
#include <FreqCount.h>
long FCount;


// Frequency measurement gate time which is 
// user configurable between 10uS to 10Sec.
long GateTime = 1000;     //Ex: 1000 for a 1mSec gate


// uSec taken by several other functions, SD card buffer writes, serial, etc.
// Typically in range 5 to 15 mSec.
long SimTime  = 13000;    // Ex: 13000 for a 13mSec aggregate delay


// What I see... 
// With any input frequency between 1MHz and 25MHz. Broadly:
//    If SimTime > GateTime the value of FCount is in error and looks
//    like the gate period is controlled by SimTime. 


//    If GateTime > SimTime FCount value is OK.


void setup() {
  Serial.begin(57600);
  FreqCount.begin(GateTime);  // Gate time is in uSec
}
void loop() {
  delayMicroseconds(SimTime); // Simulates lots of other things happening.
  // MyDelay(13);
  if (FreqCount.available()) {
    FCount = FreqCount.read();
    Serial.println(FCount);
   }
}


void MyDelay(long WaitTime){
  unsigned long ExitTime = millis() + WaitTime;
    while (millis() < ExitTime){};
  }
 
Complete aside:
your MyDelay isn't safe from wraparound, code it like this:
Code:
void MyDelay(unsigned long WaitTime){
  unsigned long StartTime = millis();
  while (millis() - StartTime < WaitTime)  // safe at wraparound due to the subtraction.
  {}
}
 
Complete aside:
your MyDelay isn't safe from wraparound, code it like this:
Code:
void MyDelay(unsigned long WaitTime){
  unsigned long StartTime = millis();
  while (millis() - StartTime < WaitTime)  // safe at wraparound due to the subtraction.
  {}
}

I can't see how that is safe. By way of example lets assume that millis starts at 0, goes to 100 then wraps round to 0 again. I know it doesn't but this is to demonstrate the problem with your code above.
OK, continuing.
Let's assume that at the time of entry to the routine that millis is 90 and the long wait time is 30. The spreadsheet below shows what happens.
There is NO exit from the routine.
Code:
Millis	StartTime WaitTime (millis-StartTime) (millis-startTime) <WaitTime
90	90	  30	        0	              TRUE
91	90	  30	        1	TRUE
92	90	  30	        2	TRUE
93	90  	  30	        3	TRUE
94	90	  30	        4	TRUE
95	90	  30	        5	TRUE
96	90	  30	        6	TRUE
97	90	  30	        7	TRUE
98	90	  30	        8	TRUE
99	90	  30	        9	TRUE
100	90	  30	       10	TRUE
0	90	  30	      -90 	TRUE
1	90	  30	      -89	TRUE
2	90	  30	      -88	TRUE
3	90	  30	      -87	TRUE
4	90	  30	      -86	TRUE
5	90	  30	-85	TRUE
6	90	  30	-84	TRUE
7	90	  30	-83	TRUE
8	90	  30	-82	TRUE
9	90	  30	-81	TRUE
10	90	  30	-80	TRUE
11	90	  30	-79	TRUE
12	90	  30	-78	TRUE
13	90	  30	-77	TRUE
14	90	  30	-76	TRUE
15	90	  30	-75	TRUE
16	90	  30	-74	TRUE
17	90	  30	-73	TRUE
18	90	  30	-72	TRUE
19	90	  30	-71	TRUE
20	90	  30	-70	TRUE
21	90	  30	-69	TRUE
22	90	  30	-68	TRUE
23	90	  30	-67	TRUE
24	90	  30	-66	TRUE
25	90	  30	-65	TRUE
26	90	  30	-64	TRUE
27	90	  30	-63	TRUE
28	90	  30	-62	TRUE
29	90	  30	-61	TRUE
30	90	  30	-60	TRUE
31	90	  30	-59	TRUE
32	90	  30	-58	TRUE
33	90	  30	-57	TRUE
34	90	  30	-56	TRUE
35	90	  30	-55	TRUE
36	90	  30	-54	TRUE
37	90	  30	-53	TRUE
38	90	  30	-52	TRUE
39	90	  30	-51	TRUE
40	90	  30	-50	TRUE
41	90	  30	-49	TRUE
42	90	  30	-48	TRUE
43	90	  30	-47	TRUE
44	90	  30	-46	TRUE
45	90	  30	-45	TRUE
46	90	  30	-44	TRUE
47	90	  30	-43	TRUE
48	90	  30	-42	TRUE
49	90	  30	-41	TRUE
50	90	  30	-40	TRUE
51	90	  30	-39	TRUE
52	90	  30	-38	TRUE
53	90	  30	-37	TRUE
54	90	  30	-36	TRUE
55	90	  30	-35	TRUE
56	90	  30	-34	TRUE
57	90	  30	-33	TRUE
58	90	  30	-32	TRUE
59	90	  30	-31	TRUE
60	90	  30	-30	TRUE
61	90	  30	-29	TRUE
62	90	  30	-28	TRUE
63	90	  30	-27	TRUE
64	90	  30	-26	TRUE
65	90	  30	-25	TRUE
66	90	  30	-24	TRUE
67	90	  30	-23	TRUE
68	90	  30	-22	TRUE
69	90	  30	-21	TRUE
70	90	  30	-20	TRUE
71	90	  30	-19	TRUE
72	90	  30	-18	TRUE
73	90	  30	-17	TRUE
74	90	  30	-16	TRUE
75	90	  30	-15	TRUE
76	90	  30	-14	TRUE
77	90	  30	-13	TRUE
78	90	  30	-12	TRUE
79	90	  30	-11	TRUE
80	90	  30	-10	TRUE
81	90	  30	-9	TRUE
82	90	  30	-8	TRUE
83	90	  30	-7	TRUE
84	90	  30	-6	TRUE
85	90	  30	-5	TRUE
86	90	  30	-4	TRUE
87	90	  30	-3	TRUE
88	90	  30	-2	TRUE
89	90	  30	-1	TRUE
90	90	  30	0	TRUE
Oh, I gave up trying to format the output, but I am sure you can see what happens.
 
I can't see how that is safe. By way of example lets assume that millis starts at 0, goes to 100 then wraps round to 0 again. I know it doesn't
And exactly this is your understanding problem. The trick is the unsigned arithmetic.
 
I can't see how that is safe. By way of example lets assume that millis starts at 0, goes to 100 then wraps round to 0 again.

integer variables in C are modular, so you need to do the subtraction modulo the relevant power of two. For instance with uint32_t:
Code:
millis      start       millis-start
0xFFFFFFF0  0xFFFFFFF0  0x00000000
0xFFFFFFFF  0xFFFFFFF0  0x0000000F
0x00000000  0xFFFFFFF0  0x00000010
Carry/borrow bits are simply discarded, so the operations are modulo 2^32
 
Ok, Got it. I had to write a small sketch to prove it to myself.
You learn something new each day. Thanks.
 
@MarkT, @BriComp, @FrankB
Thanks for your comments relating to roll over / wrap around in MyDelay(), certainly something that needs to be taken account of in a developed program but not really relevant to the problem I'm seeing. (If you look at my code snip the call to MyDelay() was commented out anyway...)
RichardL
 
@Paul
I still find that FreqCount() fails to show a correct value when the interval between readings is longer than the gate period. It looks to me like a reset or a latching failure or something along those lines. The result is that FreqCount() is useless in any program where the loop() time is longer than the gate period. That is a big problem if an agile gate period is needed or a short gate time provides sufficient precision.

For my purposes, another irritation of the current version of FreqCount() is that there appears to be a data pipeline that needs to be flushed of each time the gate period is changed. Changing from a 10 Second gate period down to a shorter one is currently a tedious activity! It would be a distinct improvement if the pipeline were to be cleared (or able to be cleared) when the gate period was changed.

My preference would be to have an option that allows the program to initiate a frequency measuring cycle directly and for FreqCount() to NOT loop internally; this would provide gate period agility and the ability to initiate a frequency measurement at a user chosen instant.

You may be interested in this montage showing the project I have been working on for over a year now and would benefit from a solution to this problem.

Best regards,
RichardL

LIAB Montage.jpg
 
@RichardL, you're right that this is a problem with FreqCount. It's actually a pretty complex library because there are abstractions for all of the different platforms, from AVR to T4.x, which are quite different. It might not be easy to fix the FreqCount library, and since you are using T4.1, it might be better to focus on finding a solution specific to T4.x. What pin are you using for your frequency input?
 
Wondering if these statements that just poll and ask for known data (?) - not doing anything unsafe in an interrupt:
Code:
  if (FreqCount.available()) {
    FCount = FreqCount.read();

Could be called with an Interval timer with period shorter than the GateTime.

That would assure timely reading when .available().

Then in loop() when GateTime is changed the queue'd data could be emptied with : while ( .available() ) { .read(); }

And the next series could be started where the intervalTimer isr() does as needed to buffer the data locally ( in an array with volatiles as needed ) for processing in loop().
 
Yup, looks like the Teensy 4 code is handling the previous count substract inside the read function rather than the interrupt. I probably should have reviewed the pull request more carefully than just running the examples and seeing it working. Then again, it's an issue which just hasn't come up (as far as I know) until now.
 
Yup, looks like the Teensy 4 code is handling the previous count substract inside the read function rather than the interrupt. I probably should have reviewed the pull request more carefully than just running the examples and seeing it working. Then again, it's an issue which just hasn't come up (as far as I know) until now.

When we were testing those changes never seemed to be an issue on getting the correct counts that we could tell think all that was done in the T4 beta thread. Think you need to also look in FreqCountTimers.h on how the timers and callback is handled for the 1062 about line 360.
Code:
#elif defined(TIMER_USE_INTERVALTIMER_T4)
static IntervalTimer itimer;
volatile uint32_t count;

static void timer_callback()
{
  count = TMRx->CH[2].CNTR | TMRx->CH[3].HOLD << 16; // atomic
  count_ready = 1;
}

static inline uint16_t timer_init(uint32_t usec)
{
	itimer.begin(timer_callback, usec);  //timer correction
	return usec;
}
so when count is ready available returns true and count should return the lasted value.

You might start with this post: https://forum.pjrc.com/threads/54711-Teensy-4-0-First-Beta-Test?p=195935&viewfull=1#post195935 and follow the links to the other posts where we (me and manitou) changed things around). Was quite a bit of testing to get the timers correct way back when using a Adafruit clock generator to verify values.
 
so when count is ready available returns true and count should return the lasted value.

As Paul said, delta = count - prev and prev = count should be in the interrupt callback. The delta is computed in read(), so if more than one interrupt occurs between reads, the reported frequency will be higher than actual. It probably just didn't get tested with long delays between calls to read().
 
As Paul said, delta = count - prev and prev = count should be in the interrupt callback. The delta is computed in read(), so if more than one interrupt occurs between reads, the reported frequency will be higher than actual. It probably just didn't get tested with long delays between calls to read().

Note:
Code:
ISR(TIMER_ISR_VECTOR)

is not used for Teensy 4.x (__IMXRT1060__), that interrupt is ifdef out:
Code:
#if(!defined(__IMXRT1062__))
so all that Gate stuff is not applicable.

When the timer interrupt hits avail sets count_ready = 1. Which is predicated on the interval timer interrupt in util:
Code:
#elif defined(TIMER_USE_INTERVALTIMER_T4)
static IntervalTimer itimer;
volatile uint32_t count;

static void timer_callback()
{
  count = TMRx->CH[2].CNTR | TMRx->CH[3].HOLD << 16; // atomic
  count_ready = 1;
}

static inline uint16_t timer_init(uint32_t usec)
{
	itimer.begin(timer_callback, usec);  //timer correction
	return usec;
}
Note count is in the callback for the timer. Only the delta is computed in the read. Easy enough to fix though. Maybe just don't understand the issue - will have to test.
 
Note count is in the callback for the timer. Only the delta is computed in the read. Easy enough to fix though. Maybe just don't understand the issue - will have to test.

Yes, that's the issue. The delta should be computed in the callback. Let's say counter starts at 0, gate time is 100 ms, and read() is called every 200 ms. When read() is called, timer_callback() will have been called twice, and count will be 2 x freq. read() will compute delta = count - prev, which will be 2 x freq. If read() is called every 500 ms, it will return 5 x freq, etc. If delta is computed in callback, output will always be 1 x f, even if interrupt occurs multiple times between calls to read().
 
Yes, that's the issue. The delta should be computed in the callback. Let's say counter starts at 0, gate time is 100 ms, and read() is called every 200 ms. When read() is called, timer_callback() will have been called twice, and count will be 2 x freq. read() will compute delta = count - prev, which will be 2 x freq. If read() is called every 500 ms, it will return 5 x freq, etc. If delta is computed in callback, output will always be 1 x f, even if interrupt occurs multiple times between calls to read().

Actually if you do something like this based on what you said:
Code:
  analogWriteFrequency(8, 50000000);  // test jumper 11 to 25
  analogWrite(8, 128);
  
  FreqCount.begin(100000);  //Time in microseconds
}

void loop() {
  if (FreqCount.available()) {
    unsigned long count = FreqCount.read();
    Serial.println(count);
  }

  delay(500);
  
}
count is actually half of the frequency. Measures 25000000 vs 50000000. I am assuming this is what you are talking about then. Think we tested with long delays but we kept the begins to around 1sec.
 
Ok here is a potential fix - besides moving the delta to the timer_callback had to do one more thing to work with the default case as well. I am attaching an updated copy of the FreqCount library if some one wants to try it with their application:
 

Attachments

  • FreqCount.zip
    11 KB · Views: 64
count is actually half of the frequency. Measures 25000000 vs 50000000. I am assuming this is what you are talking about then. Think we tested with long delays but we kept the begins to around 1sec.

I put in the changes shown below, and it seems to work correctly, i.e. I do get 50000000. High frequencies other than 50MHz are not exact because they are close to the maximum for PWM. Are you clocking at 600 MHz?

Code:
static void timer_callback()
{
  count = TMRx->CH[2].CNTR | TMRx->CH[3].HOLD << 16; // atomic
  count_output = count - count_prev;
  count_prev = count;
  count_ready = 1;
}

uint32_t FreqCountClass::read(void)
{
    count_ready = 0;
    return count_output;
}
 
Actually - those are the exact same changes I put in as well :)

Yes I am clocked at 600Mhz.

High frequencies other than 50MHz are not exact because they are close to the maximum for PWM.
That is correct - we saw that in testing as well.

I tested the changes with the default sketch as well as the changes I posted above and they both seem to work well even with the available fairly reasonably if you wait for a x number of cycles or time is short enough.
 
I still don't see it, and I see no difference in results with/without count = 0 in read(). That's good, because setting count = 0 in read() should have no effect on the ISR.
 
Status
Not open for further replies.
Back
Top