I've continued to play with using the GPT timers for frequency counting and am starting to get some working code together. I hope to publish it here as a mini-library when its more formed to help those doing the same thing, if my limited understanding of C++ classes permits . This is more of a discussion that a real problem I guess, just interested to get outside thoughts. Anyway the results from using one counter to time counts for the other using interrupts are interesting, at best impressive and I'd be interested in comments from those more expert! I've coded counter initialisation routines that both set up the counters and install a choice of interrupts for rollover and compare1 events. For compare1 there is a freeze option that freezes the count of the other counter in the first line of the compare1 ISR code. So for single measurement frequency or period counts the GPT compare1 value will freeze counting after a set gate time or cycle count for period counting. I'm testing with 10 MHz PWM Teensy output jumpered to both GPT external count inputs so far.
Results: If I configure GPT1 as the gate time counter, with its compare 1 value set at 10,000,000 (1 second) gate time and then count frequency input on GPT2 I get a result of 10,000,000 counts usually, very occasionally with +1 error. The error is arithmetically added, same results with either 1 sec or 100 sec gated counts. Incredible accuracy really. But ... this is with GPT1 started first as shown in the attached code, GPT1 stops the GPT2 count in the "freeze" ISR as described. If I reverse the counter start order using startGPTCounter(GPT_GPT2FIRST) in my code then starting GPT2 first results in an error consistently at +8 counts.
Whats happening given the difference? I think the compare1 interrupt latency I'm getting on the ARM is a little more than I expected, somewhere under 1uS tho (<8 periods@10MHz). I think that's being compensated by starting GPT1 first with the C code: {GPT1_CR |= GPT_CR_EN; GPT2_CR |= GPT_CR_EN;}. There appears to be a significant fraction of a uS delay between the two starts when this method is used. I think this exactly compensates out the interrupt latency that delays GPT2 freeze. Be interested if anyone agrees with this thinking. Obviously the two delays might vary at different clock frequencies and processor overheads so I'm wary of relying on the apparent compensation. Guess in a real system raising interrupt priority might help things to be more consistent.
I know nothing about ARM assembler but wonder if inline assembler to replace my C, {GPT1_CR |= GPT_CR_EN; GPT2_CR |= GPT_CR_EN;}, might tighten up the "simultaneous" counter start? Not sure if just GPTnCR =x; is faster when compiled, but that creates possible bugs if undesireable bit operations result. I suppose the other way at this problem is to start GPT2 here earlier, read while counting then read it on count completion and subtract the startup count later, but I'm thinking there are errors in doing it that way too. I suppose both counters could be read while running. But it complicates the code and am not sure it would end up as more accurate. I may do this later anyway to code some continuous count routines. I wish the ARM had a simple simultaneous startup option for both counters, would all be pretty easy if the hardware supported that. Anyway if anyone can point me at some inline assembler to achieve fast counter start/stops Id be very interested to try it out. I've pasted relevant code fragments below,
Steve
Results: If I configure GPT1 as the gate time counter, with its compare 1 value set at 10,000,000 (1 second) gate time and then count frequency input on GPT2 I get a result of 10,000,000 counts usually, very occasionally with +1 error. The error is arithmetically added, same results with either 1 sec or 100 sec gated counts. Incredible accuracy really. But ... this is with GPT1 started first as shown in the attached code, GPT1 stops the GPT2 count in the "freeze" ISR as described. If I reverse the counter start order using startGPTCounter(GPT_GPT2FIRST) in my code then starting GPT2 first results in an error consistently at +8 counts.
Whats happening given the difference? I think the compare1 interrupt latency I'm getting on the ARM is a little more than I expected, somewhere under 1uS tho (<8 periods@10MHz). I think that's being compensated by starting GPT1 first with the C code: {GPT1_CR |= GPT_CR_EN; GPT2_CR |= GPT_CR_EN;}. There appears to be a significant fraction of a uS delay between the two starts when this method is used. I think this exactly compensates out the interrupt latency that delays GPT2 freeze. Be interested if anyone agrees with this thinking. Obviously the two delays might vary at different clock frequencies and processor overheads so I'm wary of relying on the apparent compensation. Guess in a real system raising interrupt priority might help things to be more consistent.
I know nothing about ARM assembler but wonder if inline assembler to replace my C, {GPT1_CR |= GPT_CR_EN; GPT2_CR |= GPT_CR_EN;}, might tighten up the "simultaneous" counter start? Not sure if just GPTnCR =x; is faster when compiled, but that creates possible bugs if undesireable bit operations result. I suppose the other way at this problem is to start GPT2 here earlier, read while counting then read it on count completion and subtract the startup count later, but I'm thinking there are errors in doing it that way too. I suppose both counters could be read while running. But it complicates the code and am not sure it would end up as more accurate. I may do this later anyway to code some continuous count routines. I wish the ARM had a simple simultaneous startup option for both counters, would all be pretty easy if the hardware supported that. Anyway if anyone can point me at some inline assembler to achieve fast counter start/stops Id be very interested to try it out. I've pasted relevant code fragments below,
Steve
#define GPT1_ROLLOVER_ISR GPT1RollISR // ISR names defined
#define GPT2_ROLLOVER_ISR GPT2RollISR
#define GPT1_COMPARE1_ISR GPT1Compare1ISR
#define GPT2_COMPARE1_ISR GPT2Compare1ISR
#define GPT1_COMPARE1FREEZE_ISR GPT1CompareFreezeISR
#define GPT2_COMPARE1FREEZE_ISR GPT2CompareFreezeISR
#define GPT1 1
#define GPT2 2
#define GPT_BOTH 3
#define GPT_BOTH_GPT2FIRST 4
#define GPT_CLOCK_SOURCE 3
#define GATE_CLOCK_FREQ 10000000
#define NO_INTERRUPT 0 // Counter ISR setup control variable
#define ROLLOVER 1 // ISR setup
#define COMPARE_RESET 2 // ISR setup
#define COMPARE_FREEZE 3 // ISR setup
void initGPTCounter(uint8_t GPTn, uint8_t clkSource, uint32_t compare1=0, uint8_t interrupt=NO_INTERRUPT);
// Sets up a GPT timer. Parameters: 1/2=GPT1/2, clkSource for timer 1..3, compare1 = count up comparison value, interrupt parameter
// controls which ISR is setup: NO_INTERRUPT 0, ROLLOVER 1, COMPARE_RESET 2, COMPARE_FREEZE 3. Defaults for compare 1 and interrupt
// Initiate single frequency count task:
case FREQ_COUNT_SINGLE:
{
taskClearCounts();
float gateTime = 10; // *** Test value for gating in seconds
uint32_t gateCount = (uint32_t)((float)GATE_CLOCK_FREQ*gateTime); // ***** THIS CODE OUT FOR NON_TEST SETUP
initGPTCounter(GPT2,GPT_CLOCK_SOURCE,0,NO_INTERRUPT); // Sets up GPT1 with clock input on pin 14, clock source options 1..3.
initGPTCounter(GPT1,GPT_CLOCK_SOURCE,gateCount,COMPARE_FREEZE); // Sets up GPT2 with clock input on pin 25, params: GPTn, clkSource, compare1 value,interrupt type)
startGPTCounter(GPT_BOTH);
// **** This code starts GPT1 first (GPT_BOTH==3)produces minimal error +1 count, error is +8 counts if GPT2 started first ***
break;
}
void startGPTCounter(uint8_t GPTn)
{
if (GPTn == GPT_BOTH) {GPT1_CR |= GPT_CR_EN; GPT2_CR |= GPT_CR_EN; return;} // Enable both GPT 1 and 2 near-simultaneously, GPT1 first
if (GPTn == GPT_BOTH_GPT2FIRST) {GPT2_CR |= GPT_CR_EN; GPT1_CR |= GPT_CR_EN; return;} // GPT 2 enabled first
if (GPTn == GPT1) {GPT1_CR |= GPT_CR_EN; return; } // Enable only GPT1. Counter will reset to start from zero assuming GPT_CR_ENMOD is set by init()
if (GPTn == GPT2) {GPT2_CR |= GPT_CR_EN; return;} // Enable only GPT2
}
// Compare freeze ISR for the GPT1 compare 1 counter interrupt
void GPT1CompareFreezeISR()
{
GPT2_CR &= ~GPT_CR_EN; // Freeze the other GPT counter (GPT2), 64 bit gated counts can then be read from the static counter with minimal error
GPT1_CR &= ~GPT_CR_EN; // Freeze GPT1
GPT1_SR |= GPT_SR_OF1; // Clear SR reg compare 1 flag
GPT1Flags |= GPT_SR_OF1; // Interrupt flag variable is global volatile bool type, set the compare 1 flag
while (GPT1_SR & GPT_SR_OF1);
asm volatile ("dsb"); // Prevents ISR firing twice
}