Help with initial FlexIO task

shawn

Well-known member
Hello all you FlexIO people. I've started diving into this, and I believe I understand about half of how to use this subsystem.

I could use some help because I can't quite figure out how to accomplish a certain task.

The goal:
Detect a high-to-low transition on a pin and trigger a timer output that stays asserted when the input stays low for at least 50us.

I figured that these must be the details for a FlexIO timer configuration (these are my working assumptions):
1. Enable on negative input edge (i.e. reverse trigger polarity)
2. Disable on positive input edge (i.e. reverse trigger polarity)
3. Use a 20MHz frequency (480MHz / 8 / 3) (50ns cycle time)
4. 50us is 1000 timer cycles (that's 20000 complete iterations a second)

I wrote a program that watches interrupts so that each time the timer status flag is set due to a timer reset, a counter is incremented. In the main loop, I print the interrupt count and I'm seeing the number increment by about 20000 per second, which is expected if the timer keeps running when the input signal is low, but I'd rather have the interrupt trigger once, once the input low time reached 50us.

What my program does correctly:
1. Start the timer on negative input edge
2. Stop the timer on positive input edge
3. Each second shows about 20000 interrupt counts (correct if I wanted the timer to keep running, and I don't). The count should be 1 if there's been one pulse >= 50us.

What my program does not do correctly:
1. Interrupts are triggered every 50us (i.e. every complete timer cycle) and I want it to be one-shot until the input goes low again. i.e. if the low pulse only happens once, even if it's longer than 50us, then the interrupt count should be 1.

What I don't understand:
1. The FLEXIO_TIMCFG_TIMOUT configuration talks about things like "Timer output is logic zero when enabled and is not affected by timer reset" and "Timer output is logic zero when enabled and on timer reset". This is confusing language because the "Timer 16-bit Counter Mode" section says that "When the 16-bit counter equals zero and decrements, the timer output toggles and the counter reloads from the compare register. A timer compare event occurs when the 16-bit counter equals zero and decrements. The timer status flag is set on a timer compare event."

If the timer output toggles, then what does "Timer output is logic zero when enabled and on timer reset" mean? Does it mean the output is logic zero on reset, or is that sentence supposed to read "Timer output is logic zero when enabled and toggles on timer reset"? Also, should "Timer output is logic zero when enabled and is not affected by timer reset" read as: "Timer output is logic zero when enabled and is not toggled by timer reset"?

In other words, how does a timer reset affect toggling its output? I'm confused by how TIMOUT is supposed to work.

2. Shouldn't TIMRST set to "Never reset" stop the timer from resetting?

3. Is it possible to treat the timer as a one-shot and to somehow disable it when a condition is met, such as the timer running out? How do I get the status to stop triggering the interrupt?

Here's my test code:

Code:
// See also:
// * https://forum.pjrc.com/threads/66201-Teensy-4-1-How-to-start-using-FlexIO

// Goal:
// When an input goes low for a certain number of cycles, have a timer
// assert and stay asserted until the input goes high.

// Details:
// 1. Use FlexIO2 pin 0 (Teensy 4.1 pin 10)
// 2. Want to detect low for at least 1000 cycles at 20MHz
//    (That's 20000 interrupts a second if the timer was
//     continuously resetting)
// 3. Enable the timer when the pin goes low
// 4. Disable the timer when the pin goes high
// 5. Keep timer asserted and stop triggering interrupts after timer
//    output goes high, and before the input pin goes high

#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))

// Forward declarations
void isr();

// Main program setup.
void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 4000) {
    // Wait for serial
  }
  delay(2000);
  printf("Starting...\r\n");

  // Configure frequency for 20MHz (480MHz/24)
  CLRSET(CCM_CS1CDR,
         CCM_CS1CDR_FLEXIO2_CLK_PODF(7) | CCM_CS1CDR_FLEXIO2_CLK_PRED(7),
         CCM_CS1CDR_FLEXIO2_CLK_PODF(7) | CCM_CS1CDR_FLEXIO2_CLK_PRED(2));  // /3 then /8

  // Turn on FlexIO2 clock
  CCM_CCGR3 |= CCM_CCGR3_FLEXIO2(CCM_CCGR_ON);

  // Configure Timer 4
  FLEXIO2_TIMCTL4 = FLEXIO_TIMCTL_TRGSEL(0) |  // Pin 0 trigger
                    FLEXIO_TIMCTL_TRGSRC |     // Internal trigger
                    FLEXIO_TIMCTL_TRGPOL |     // Trigger active low
                    FLEXIO_TIMCTL_PINCFG(0) |  // Pin output disabled
                    FLEXIO_TIMCTL_PINSEL(0) |  // Which pin
                    FLEXIO_TIMCTL_TIMOD(3);    // Single 16-bit counter
  FLEXIO2_TIMCFG4 = FLEXIO_TIMCFG_TIMOUT(3) |  // Zero when enabled, and on reset (is this correct?)
                    FLEXIO_TIMCFG_TIMDEC(0) |  // Decrement on clock
                    FLEXIO_TIMCFG_TIMRST(0) |  // Never reset
                    FLEXIO_TIMCFG_TIMDIS(6) |  // Disabled on trigger falling edge (low->high for inverted)
                    FLEXIO_TIMCFG_TIMENA(6);   // Enabled on trigger rising edge (high->low for inverted)
  FLEXIO2_TIMCMP4 = 1000;
  CLRSET(FLEXIO2_TIMIEN, 0xff, (1 << 4));  // Enable interrupts for Timer 4

  // Pin control - configure Teensy pin 10 as FlexIO2 pin 0
  // Start experiment with nothing connected and with the pin at high
  // There's no interrupts when the pin is high, as expected
  // There are regular interrupts when the pin is low. Why do
  // interrupts keep getting generated if the timer is configured to
  // never reset?
  pinMode(10, INPUT_PULLUP);
  IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_00 = 4;  // ALT4, FLEXIO2_FLEXIO00

  // Attach FlexIO2 interrupt
  attachInterruptVector(IRQ_FLEXIO2, isr);
  NVIC_ENABLE_IRQ(IRQ_FLEXIO2);

  // Turn FlexIO2 on
  FLEXIO2_CTRL |= FLEXIO_CTRL_FLEXEN;
}

// Interrupt counter.
static volatile uint32_t counter = 0;

// Handles FlexIO interrupt.
void isr() {
  uint32_t status = FLEXIO2_TIMSTAT & 0xff;
  FLEXIO2_TIMSTAT = status;  // Clear all the interrupts

  if (status & (1 << 4)) {  // Timer 4 triggered?
    counter++;
  }

  asm volatile ("dsb");
}

// Main program loop.
void loop() {
  // Print the interrupt count every second
  printf("%" PRIu32 "\r\n", counter);
  delay(1000);
}

Note: Setting TIMOUT to this doesn't work either:
Code:
FLEXIO_TIMCFG_TIMOUT(1) |  // Zero when enabled, not affected by reset

Thank you in advance for any suggestions or assistance.
 
Last edited:
What's the maximum time the input will stay low?
There's a few different ways to do this but if the input is going to stay low for an indefinite amount of time I don't think it's possible with only one timer... unless it's possible to make that timer reset on its own output, which is something I've personally never tried.
 
It could be a long time; technically no limit (it’s actually a reset condition, so it’s valid if it stays low). But if it did have a limit, where were you going with that thought? A second, stopping condition, timer or something?
 
It could be a long time; technically no limit (it’s actually a reset condition, so it’s valid if it stays low). But if it did have a limit, where were you going with that thought?

Set the FlexIO clock speed to something around 10us, configure the timer as PWM, set the low cycle count (the upper 8 bits in TIMCMP) to 5-1 for 50us and set the high cycle count (lower 8 TIMCMP bits) to max (255). Set TIMOUT to 1 so the PWM cycle starts low instead of high.
On RESET the timer would stay low for 50us then go high for 256*10us (2.56ms), then loop...

A safer option is using two shifters in state mode, with a basic timer that counts 50us like you already had. The first shifter outputs nothing and switches to the second state (which outputs the signal you want) when the timer goes high. The second state reverts back to the first when the RESET line goes inactive.
Flow would be like this:
RESET triggers timer
timer counts 50us then asserts PIN (this doesn't have to be a physically present pin, FlexIO has internal connections so you can make use of the pins that aren't broken out)
State0 shifter changes to State1 due to PIN going high
State1 shifter asserts one of pin0-pin7 (state shifters can only control these pins) as function output, transitions back to State0 when RESET clears.

For extra safety I guess you could configure State0 to only transition when both the timer pin and RESET are in the correct state.
 
Thanks. I’ll give the shifter state mode a shot.

Do you happen to know why TIMRST set to "Never reset" doesn’t actually stop the timer from resetting?
 
TIMRST is just a way to trigger a reload of the timer's counter. The only way to do a one-shot timer is set TIMDIS to one of the timer compare settings, otherwise the timer will always rollover (which toggles its output).

Thinking about it some more, it might be possible with just one timer:
- TRGSEL set to the RESET pin
- PINCFG = 3 (output mode)
- PINSEL set to the output pin
- PINPOL set (so when the timer output is HIGH the output pin is LOW)
- TIMOD = 16-bit counter
- TIMOUT = 2 (output goes high when enabled and when reset)
- TIMDEC = 0 (source clock = FlexIO clk)
- TIMRST = 3 (reset when timer output and trigger match)
- TIMDIS = 2 (disable on timer compare)
- TIMENA = 2 (enable when trigger high)
- TSTOP + TSTART disabled
- TIMCMP set to a 16-bit value equal to 50us

When the RESET line is high the timer would activate, with its output high (inverted output pin = LOW). Because RESET and the timer output are equal, the counter would constantly reload i.e. it wouldn't count up, until RESET goes low. Then it would count the 50us and the timer would stop, with its output going low (inverted output pin = HIGH). It would stay this way indefinitely until RESET went high again, triggering the timer enable.
Basically the timer is held in reset while RESET is high, then it counts 50us and stops.

The reason I set TIMENA to 2 is because I don't know what state RESET will be when the timer is configured, you can set it to 6 to enable the timer on the rising edge of RESET but obviously it won't catch that edge if RESET is already high. The difference is the timer will restart one FlexIO tick later (holding the output pin HIGH one extra tick after RESET goes high) because it uses level matching instead of edge matching.
 
Back
Top