Teensy 4.1 Hardware counter with external input

LNLN

Member
Hi

I am porting an application from ESP32 to Teensy 4.1 and I am getting stuck on this. What I want to achieve is this:
* Use a hardware counter to count pulses on a pin (external source)
* Use either a comparator or overflow to call an ISR when target count is reached
* Not conflict with onboard Ethernet
* Reset/restart counter when target has been reached

I do not want to read/compare the value of the hardware counter in software. I would really want this comparison to be made in hardware if possible. Does anyone have a working example on achieving this? I am very flexible on pin selection since I am making an adapter board switching from the previos ESP32.
 
What's the pulses frequency ? Hz, kHz, MHz ???
The Teensy runs at 600MHz. If the frequency is low, you could accept the little overhead introduced by an interrupt on each pulse.
 
Maybe start with the FreqCount library. It's automatically installed, so in Arduino IDE click File > Examples > FreqCount to get going quickly. Of course you can dive into the source code if you really want to get into the details of how it uses the hardware, but first just running examples is a quick and easy way to get going.
 
Hi Paul
I had a brief look at the FreqMeasure library which seems to count a certain amount of pulses rather than counting for a certain time. I could however not see that I can attach an interrupt handler, but things would of course clear up a bit if I had a look at the source code.

Edit: I have realized that probably the only way forward is deep diving into the NXP manual. I read my fair share of hardware manuals at the time where I thought using Arduino type platforms was for noobs now I have obviously gotten a bit too comfortable :)
 
Last edited:
I have now dived into the manual. Cannot really get this working.
I try following 52.6.1 "Selecting clock source" from the manual (page 3023).

First, I am not interested in compare events. I just want to get the counter up and running.
52.6.1 Selecting the Clock Source
The Clock Source field in the Control Register (CR[CLKSRC]) selects the clock source.
CLKSRC should be changed only after disabling the GPT (CR[EN]=0). To change the
clock source, follow this sequence:
1. Disable the GPT: CR[EN]=0
2. Disable GPT interrupts: Clear the Interrupt Register (IR)
3. Configure the Output Mode to unconnected/ disconnected for all channels:
CR[OMn]=0
4. Disable the Input Capture Modes: CR[IMn]=0
5. Change the clock source CR[CLKSRC] to the desired value.
6. Assert a GPT software reset: CR[SWR]=1
7. Clear the flags in the GPT Status Register (SR) by writing 1s to them.
8. Reset the GPT counter to zero: CR[ENMOD]=1
9. Enable the GPT: CR[EN]=1
10. Enable GPT interrupts: For the desired interrupts, write 1s to their enable bits in IR.

Pad B0_13 is supposedly pin 25 on the Teensy 4.1 board.

The code below is executed in setup()

I then simply check the GPT1_CNT register to see if anything happens, but it's stuck at zero.
I have verified that pulses can be read on pin 25 by just setting it as a digital input in code, and polling it (which I am not doing while testing the counter).

C:
 // Enable GPT1 clock
  // Configure pad settings: input with pull-up
  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_13 = 
    IOMUXC_PAD_PKE |           // Keeper enabled
    IOMUXC_PAD_PUE |           // Pull enabled
    IOMUXC_PAD_PUS(3) |        // 22K pull-up
    IOMUXC_PAD_SPEED(2) |      // Medium speed
    IOMUXC_PAD_DSE(6) |        // Drive strength
    IOMUXC_PAD_HYS;            // Hysteresis enabled

  // STEP 1 - Disable GPT1 before configuration
  bitClear(GPT1_CR, 0);
  
  // STEP 2 - Disable GPT interrupts
  GPT1_IR = 0;    // Manual page 3023

  // STEP 3 - Configure output mode to unconnected/disconnected for all channels
  bitClear(GPT1_CR, 20);
  bitClear(GPT1_CR, 21);
  bitClear(GPT1_CR, 22);
  bitClear(GPT1_CR, 23);
  bitClear(GPT1_CR, 24);
  bitClear(GPT1_CR, 25);
  bitClear(GPT1_CR, 26);
  bitClear(GPT1_CR, 27);
  bitClear(GPT1_CR, 28);

  // STEP 4 - Disable GPT input capture modes   //Manual page 3023
  bitClear(GPT1_CR, 16);
  bitClear(GPT1_CR, 17);
  bitClear(GPT1_CR, 18);
  bitClear(GPT1_CR, 19);

  // STEP 5 - Set GPT clock source //According to instruction in Manual page 3023
  // Configure GPT1 Control Register:
  // - CLKSRC = 011 (External clock, counts on rising edge)
  // - FRR = 1 (Free-Run mode - counter continues after compare)
  // - ENMOD = 1 (Counter resets to 0 when enabled)
  GPT1_CR = GPT_CR_CLKSRC(3);

  // STEP 6 - Reset GPT1 //Manual page 3023
  GPT1_CR |= GPT_CR_SWR;
  while (GPT1_CR & GPT_CR_SWR);
  
  // STEP 7 - Clear the flags in GPT Status register by writing 1s to them
  GPT1_SR |= 0b11111;

  // STEP 8 - Reset GPT counter to zero (Manual page 3023)
  GPT1_CR |= GPT_CR_ENMOD;

  // Select input path
  IOMUXC_GPT1_IPP_IND_CLKIN_SELECT_INPUT = 0;   // 0 = pin25 (B0 13) (according to NXP manual and Teensy 4.1 schematic)
  
  // Step 9 - Enable GPT1
  GPT1_CR |= GPT_CR_EN;
 
I eventually got this up and running based on a few snippets I found here and there, and with a little insight into the manual. Even though I seem to get the result I wanted, I am a bit frustrated that I don't really get this. Anyway; this is the working code:


C:
void configureCounter()
{
  CCM_CCGR1 |= CCM_CCGR1_GPT(CCM_CCGR_ON) ;  // enable GPT1 module
  GPT1_CR = 0;
  GPT1_SR = 0x3F; // clear all prior status
  GPT1_CR =  GPT_CR_CLKSRC(3);// | GPT_CR_FRR ;// 3 external clock
  *(portConfigRegister(25)) = 1;  // ALT 1

  GPT1_OCR1 = 399;    // Count target

  attachInterruptVector(IRQ_GPT1, gpt1_isr);
  NVIC_ENABLE_IRQ(IRQ_GPT1);

  GPT1_CR |= GPT_CR_EN; // enable
  GPT1_IR |= 1; // Interrupt enable
}

C:
void gpt1_isr() {
  // Clear the interrupt flag
  GPT1_SR = GPT_SR_OF1;

  isrCalled = true;
}
 
Thank you. I have used some older Teensy prior to this, but not on this hardware close level. They have incredible performance, and as I pushed the ESP32 to the limits I decided to migrate.
 
I eventually got this up and running based on a few snippets I found here and there, and with a little insight into the manual. Even though I seem to get the result I wanted, I am a bit frustrated that I don't really get this. Anyway; this is the working code:
GPT is the best choice for what you've done, but just FYI, both FlexPWM and QDC (quadrature decoder) can be used to do something similar. In FlexPWM, the compare value is only 8 bits, but in QDC it is 32 bits, the same as GPT.
 
Back
Top