Teensy 4.1 GPT CAPTURE input / interrupt troubles

Status
Not open for further replies.

mweber

New member
I'm trying to measure the elapsed time between two logic pulses with the GPT capture functions as described in the reference manual section 52.5.2.1. I found Paul's example code elsewhere in the forum (https://forum.pjrc.com/threads/5426...B-(600-Mhz-M7)?p=193217&viewfull=1#post193217 ) from the time he was doing crystal measurements. I have twiddled it a little bit per the reference manual, but I could not get it to (1) increment GPTx_CNT or (2) capture a count into GPTx_CNT in either its original form or my modified form. Code further below. I'm using the Arduino 1.8.13 environment with TeensyDuino 1.8.5. My development host is running debian 10.5 on an x86-64. I've commented out the capture pin mapping to focus first on the counter running issue, but you can see what options I was fiddling with. None worked.

I've run a few of the TeensyTimerTool library examples, and they work as expected, so I believe the hardware is still OK. Nice library, BTW. :) Interestingly, when I add Serial code to dump GPT1_CNT every second in a loop in one of the demos, the count has incremented only 25 to 30, which seems... really small. But the timers themselves work, so... IDK.

Pins: I stared at the Teensy 4.1 schematic (https://www.pjrc.com/teensy/schematic.html) and the ref manual (https://www.pjrc.com/teensy/IMXRT1060RM_rev2.pdf) a long time before I decided that GPT1_CAPTURE was not usable because none of the pads it could be muxed to were available as Teensy pins, but that I could use either GPT2_CAPTURE1 on GPIO_AD_B1_04, Teensy 4.1 pin 40 (A16) or GPT2_CAPTURE1 on GPIO_AD_B1_03, Teensy 4.1 pin 15 (A1). I tried both of these pins and didn't see any ISR calls, but, since the GPT2_CNT register itself did not appear to be incrementing, that is probably the first problem in line.

Any ideas? Thanks so much for any help!

Code:
#include <imxrt.h>

void setup() {
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  delay(1000);
  digitalWrite(13, LOW);
  Serial.begin(9600);
  while (!Serial);
  delay(5000);
  digitalWrite(13, HIGH);
  Serial.println("ok");
  delay(1000);

//  IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_03 = 8; // GPT2_CAPTURE1 on GPIO_AD_B1_04, Teensy 4.1 pin 40 (A16)
//  IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_04 = 8; // GPT2_CAPTURE1 on GPIO_AD_B1_03, Teensy 4.1 pin 15 (A1)

//  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_03 = 0x13000; //Pullup 22k & Hyst

//  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_04 = 0x13000; //Pullup 22k & Hyst
  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_04 = 0x10000 | 0x2000; //Pullup 100k & Hyst
//  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_04 = 0x2000; //Pullup 100k

  CCM_CCGR0 |= CCM_CCGR0_GPT2_BUS(CCM_CCGR_ON) | CCM_CCGR0_GPT2_SERIAL(CCM_CCGR_ON);
//  CCM_CCGR1 |= CCM_CCGR1_GPT1_BUS(CCM_CCGR_ON) | CCM_CCGR1_GPT1_SERIAL(CCM_CCGR_ON);

  // 52.6.1: Init and select clock source
  // See imxrt.h for macro definitions and register bit values
  GPT2_CR &= ~(GPT_CR_EN); // Set EN = 0
  GPT2_IR = 0; // Disable GPT interrupt register
  GPT2_CR &= ~(GPT_CR_OM1(0x07)); // Disable OM1
  GPT2_CR &= ~(GPT_CR_OM2(0x07)); // Disable OM2
  GPT2_CR &= ~(GPT_CR_OM3(0x07)); // Disable OM3
  GPT2_CR &= ~(GPT_CR_IM1(0x03)); // Disable IM1
  GPT2_CR &= ~(GPT_CR_IM2(0x03)); // Disable IM2
  GPT2_CR |=   GPT_CR_SWR;     // Assert SWR

  Serial.print("GPT2_CR after SWR is ");
  Serial.println(GPT2_CR);
  
  GPT2_PR = GPT_PR_PRESCALER(2); // divide-by-(2+1) = 8MHz clock
  
  GPT2_SR |=  0x3F;            // Clear flags in status register (write 1 to clear bottom 6 bits) 
  GPT2_CR |=  GPT_CR_ENMOD;    // bring GPT counter to 0x00000000
  GPT2_CR |=  GPT_CR_EN |          // Enable GPT 
              GPT_CR_EN_24M |      // Enable 24MHz crystal input
              GPT_CR_CLKSRC(5) |   // use 24MHz crystal clock source
              GPT_CR_IM1(2) |      // with capture on falling edge (2) (or, rising edge=1, or both edges=3)
              GPT_CR_FRR;          // in free run mode
              
  Serial.print("GPT2_CR after setup is ");
  Serial.println(GPT2_CR);

  GPT2_IR |=  GPT_IR_IF1IE;    // Enable CAPTURE1 interrupt

  attachInterruptVector(IRQ_GPT2, capture);
  
  NVIC_ENABLE_IRQ(IRQ_GPT2);
}

volatile uint32_t ticks;
volatile uint32_t count;

void capture() {
  GPT2_SR = GPT_SR_IF1; // clear capture flag bit
  static uint32_t last = 0;
  count++;
  uint32_t t = GPT2_ICR1;
  uint32_t n = t - last;
  last = t;
//  asm("dsb"); // data synchronization barrier
  Serial.printf("t=%u, delta=%u\n", t, n);
}

void loop() {
  uint32_t now1 = GPT1_CNT;
  uint32_t now2 = GPT2_CNT;
  Serial.print(now1);
  Serial.print("  ");
  Serial.print(now2);
  Serial.print("  ");
  Serial.println(count);
  delay(1000);
}

Output:

Code:
18:22:46.683 -> 0  0  0
18:22:47.677 -> 0  0  0
18:22:48.672 -> 0  0  0
18:22:49.666 -> 0  0  0
18:22:50.661 -> 0  0  0
18:22:51.655 -> 0  0  0

Best,
Mike
 
Last edited:
Again assuming that you are using AD_B1_03 As your example:
You will need to set it to mode 8... which you have the line commented out. Sometimes I might cheat a little like:

pinMode(40, INPUT); // sets up the pad, depending o things could maybe do INPUT_PULLUP (or down...)
IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_03 = 8; // GPT2_CAPTURE1 on GPIO_AD_B1_04, Teensy 4.1 pin 40 (A16)

But you may also need to set the appropriate select input daisy register:
I think it is this one: IOMUXC_GPT2_IPP_IND_CAPIN1_SELECT_INPUT = 1;

(Page 937)... These select input registers have hit enough times that is what I look at first when inputs are not coming through.
 

This works! It looks very similar to https://github.com/manitou48/teensy4/blob/master/gpt_capture.ino, which I tried modifying yesterday to use GPT2, but clearly I missed something. Thank you very much! I notice you are using a different clock source than I was, but it's still plenty fast (looks like 24MHz). I may follow up on KurtE's suggestion and read about the daisy chains to understand what's going on there. Not sure if it's related to the clock source issue or not. For curiosity's sake I would like to try to understand how to set up the other clock source, but for now my immediate problem is solved.

I was also able to tweak that example to work on a falling edge with a pullup instead of a rising edge with a pulldown, so at least I'm reading the manual correctly.

Thanks again!
 
yep, the example runs GPT2 @24mhz. You can configure GPT to 150MHz for CLKSRC(1) by adding
CCM_CSCMR1 &= ~CCM_CSCMR1_PERCLK_CLK_SEL; // turn off 24mhz mode
in setup() before line CCM_CCGR0 |= ....

changing the underlying clock to 150MHz also affects PIT timer (and breaks intervalTimer())
 
changing the underlying clock to 150MHz also affects PIT timer (and breaks intervalTimer())

This is the reason I was attempting to use a different clock source. The OSC clock is also 24 MHz and should be able to be connected to GPTs, though I have not yet been successful in doing that. When I figure it out, I will post back.
 
This is the reason I was attempting to use a different clock source. The OSC clock is also 24 MHz and should be able to be connected to GPTs, though I have not yet been successful in doing that. When I figure it out, I will post back.

Sorry I may be confused here? But the OSC clock Is the the one that we default to using (24mhz), which is set by the setting that @manitou mentioned about turning off in order to go to 150mhz.

And also as shown in the clock tree, PERCLK_CLK_ROOT is used by both PIT and GPT. So again any change you make to this branch of the clock tree will effect both sub-systems.
See the clock tree Page 1016 of current manual.
 
Sorry I may be confused here? But the OSC clock Is the the one that we default to using (24mhz), which is set by the setting that @manitou mentioned about turning off in order to go to 150mhz.

Roger page 1016, and roger the behavior of CCM_CSCMR1_PERCLK_CLK_SEL. What I'm looking at is on page 2961, the description for GPTx_CR CLKSRC bits 8-6, which indicate there is another path to the 24M xtal oscillator that does not come from per_clk but directly from the xtal_clk itself. The working code that @manitou provided sets these bits to 001, which as you've pointed out selects ipg_clk. Setting these bits to 5 should select ipg_clk_24M instead. Page 2951 describes ipg_clk_24M and on page 2947 it's the top line in the clock selection block 52-2. These clocks are also referred to in the clock table on page 1093 as "gpt1_ipg_clk_24m" and "gpt2_ipg_clk_24m," but these names don't appear anywhere else in the manual when I search for them. Per this table, there is no gating or enable that needs to be set anywhere for these clocks to be available.

It would be handy to be able to use the apparently independent ipg_clk_24M prescaler that is part of the GPT instead of having to change the peripheral clock tree.

I haven't made this work yet, so I may be super confused, but... that's how I'm interpreting the manual at the moment. BTW, thanks for the continued effort to clarify this and run it to ground.
 
It would be handy to be able to use the apparently independent ipg_clk_24M prescaler that is part of the GPT instead of having to change the peripheral clock tree.

Both paths are based on the same 24MHz crystal. My original sketch in post #4 uses CLKSRC(1) to reference the 24 mhz clock. If you want to use CLKSRC(5) for 24mhz, then you must enable the 150MHZ clock and change GPT2_CR. Here is the sketch modified to use CLKSRC(5) for 24 MHz (changes in red).
Code:
void setup() {
  Serial.begin(9600);
  while (!Serial);
  delay(2000);
  Serial.println("ok");
  pinMode(13, OUTPUT);

  analogWriteFrequency(14, 100);  // test with PWM
  analogWrite(14, 128); // jumper pwm 14  to pin 15  Serial3 on T4B2 breakout
  // Connect GPS 1PPS signal to pin 15 (GPIO_AD_B1_03)
  IOMUXC_GPT2_IPP_IND_CAPIN1_SELECT_INPUT = 1;  // remap GPT2 capture 1
  IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_03 = 8; // GPT2 Capture1
  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_03 = 0x13000; //Pulldown & Hyst
[COLOR="#FF0000"]  // uncomment following for 150mhz
  CCM_CSCMR1 &= ~CCM_CSCMR1_PERCLK_CLK_SEL; // turn off 24mhz mode[/COLOR]
  CCM_CCGR0 |= CCM_CCGR0_GPT2_BUS(CCM_CCGR_ON) |
               CCM_CCGR0_GPT2_SERIAL(CCM_CCGR_ON);  // enable clock
  GPT2_CR = 0;
  GPT2_PR = 0;
  GPT2_SR = 0x3F; // clear all prior status
  GPT2_IR = GPT_IR_IF1IE;
  GPT2_CR = GPT_CR_EN |[COLOR="#FF0000"] GPT_CR_EN_24M | GPT_CR_CLKSRC(5)[/COLOR] |
            GPT_CR_FRR | GPT_CR_IM1(1);
  attachInterruptVector(IRQ_GPT2, capture);
  NVIC_ENABLE_IRQ(IRQ_GPT2);
}

#define LEN  10  // was 124

volatile uint32_t ticks;

void capture() {
  static uint32_t prior = 0;
  static uint32_t list[LEN];
  static uint32_t count = 0;
  static int index = 0;
  uint32_t now = GPT2_ICR1;
  GPT2_SR = GPT_SR_IF1;
  uint32_t n = now - prior;
  prior = now;
  asm("dsb");
  ticks++;
  //  Serial.println(index);
  if (index >= LEN) index = 0;
  list[index++] = n;
  count++;
  // GPIO2_DR_TOGGLE = (1 << 3);  // 1050
  if (index == LEN) {
    uint32_t sum = 0;
    for (int i = 0; i < LEN; i++) {
      sum = sum + list[i];
    }
    Serial.printf("capture=%u, sum=%u\n", n, sum);
  }
}

void loop() {
#if 0
  Serial.println(ticks);
  delay(1000);
#endif
}
As noted configuring the 150MHz clock also affects PIT timer and breaks intervalTimer(), so you are better off using the original configuration for 24 MHz with CLKSRC(1). Just for the record, using CLKSRC(4) will use the 32KHz crystal, and the GPT will count 32768 ticks/second. I've used a GPS PPS to pin 15 to measure frequency drift for either 24MHz crystal or 32KHz crystal.

Or use GPT@150mhz and attachIinterupt() to measure T4 cyrstal drift with GPS PPS:
https://github.com/manitou48/teensy4/blob/master/gpsgpt.ino
 
Last edited:
Status
Not open for further replies.
Back
Top