Overflow interrupts with Flex timer in quadrature mode

Hello everyone,

For my application I want to use a quadrature encoder on a motor, but I have found that the mbed library does not use the hardware support for quadrature decoding on the board I am using (NXP/Freescale K64F).
I have figured out most of what I needed with the help of the user manual of the K64F, however, I have become stuck at the handling of the interrupt.

For the K64F, flex timer 1 (FTM1) and FTM2 support quadrature decoding. For the code below FTM2 is used as its inputs are available directly on the headers, to which the A and B phase signals are connected (PTB18 and PTB19 respectively).

The code is confirmed to work properly for counting the encoder up and down, but locks up and goes into an infinite loop when a timer overflow occurs. If I don’t enable interrupts (remove FTM2->SC |= FTM_SC_TOIE(1):wink: the code simply loops back from 64 to 0, and visa versa.
My steps for enabling the interrupts are:

  1. Enable interrupts for FTM2: FTM2->SC |= FTM_SC_TOIE(1);
  2. Tell the NVIC to handle FTM2 interrupts: NVIC_EnableIRQ(FTM2_IRQn);
  3. Handle the interrupt by overriding the weak callback function: void FTM2_IRQHandler(void){ … }

I suspect that either I am missing an initialization step of the interrupt, or that somehow using the IRQHandler is not working well with mbed. Of course I am leaning more towards the former than the latter.

For completion sake, I am using mbed studio with mbed OS 6.8.0 on the FRDM-K64F.

#include "mbed.h"

const unsigned int CONST_ENC_MOD_SIZE = 65;

static BufferedSerial           pc(USBTX, USBRX);

void FTM2_IRQHandler(void);

// main() runs in its own thread in the OS
int main()
{
    pc.set_baud(115200);
    // Register reference: https://os.mbed.com/questions/77426/Why-cant-I-access-FTM2-MODE-on-the-FRDM-/
    // https://www.nxp.com/docs/en/application-note/AN5142.pdf
    // RF chapter 40 for FTM modules
    //
    // In the reference manual of k64F, page 1100 [40.4.25 QUadrature Decoder mode]
    //                                  page 142 [3.8.2.1 (FTM) Instantiation information] -> FTM1 and FTM2 are used for quadrature decoders
    //                                  page 249 [10.3.1 K64 Signal Multiplexing and Pin assignments] -> pin connections for the timers
    //
    // This gives the following possible pins for the two timers (A; B), tqfp 100
    // FTM1
    // (PTB0, PTB1) on ALT6 function (PCR MUX)
    // (PTA12, PTA13) on ALT7 function (PCR MUX)
    //
    // FTM2
    // (PTB18, PTB19) on ALT6 function (PCR mux)

    printf("Initializing...\n");

    // Enable the clock for FTM
    SIM->SCGC6 |= SIM_SCGC6_FTM2_MASK;
    // Enable the counter
    FTM2->MODE |= FTM_MODE_FTMEN_MASK;
    // Enable the counter to run in the BDM mode
    FTM2->CONF |= FTM_CONF_BDMMODE(3);
    // Load the modulo register (max is 2^16 -1, 65535)
    FTM2->MOD = CONST_ENC_MOD_SIZE;
    // Load initial value into the timer register
    FTM2->CNTIN = 0; // Note that writing a value to the 'count' register doesn't updata it, but instead updates it with the initial value.
    // Config quadrature mode
    FTM2->QDCTRL |= FTM_QDCTRL_QUADEN_MASK;
    // Start the timer clock with src as external (RF P 993)
    FTM2->SC |= FTM_SC_CLKS(3); // Clock source as external
    FTM2->SC |= FTM_SC_TOIE(1); // Enable interrupts on overflow
    // Configuring the input pins
    PORTB->PCR[18] = PORT_PCR_MUX(6); // Select pin alt. function, quadrature mode FTM2 CHA
    PORTB->PCR[19] = PORT_PCR_MUX(6); // Select pin alt. function, quadrature mode FTM2 CHB

    //NVIC->ISER[FTM2_IRQn / 32] |= (1 << (FTM2_IRQn% 32));

    NVIC_EnableIRQ(FTM2_IRQn);


    while (true) {
        wait_us(200000); // 200ms wait
        uint32_t cnt = FTM2->CNT;
        printf("%d\n", cnt);
    }
}


void FTM2_IRQHandler(void){
    
    if(FTM2->CNT > (CONST_ENC_MOD_SIZE/2)){ // Negative overflow
        printf("Negative overflow\n");
        // TODO handle overflow
    }else{ // Positive overflow
        printf("Positive overflow\n");
        // TODO handle overflow
    }

    FTM2->SC &= ~FTM_SC_TOF(1); // Clear timer overflow bit
}

To add to the above, I have tried to ‘override’ the interrupt vector

NVIC_SetVector(FTM2_IRQn, (uint32_t)&FTM2_IRQHandler);

This however instead of freezing the MCU, causes an error (flashing red light). (UPDATE: See below)

Some more information that might give a hint as to what is going on:

If I go into the debugger and ‘pause’ on the system freeze, it indicated to be in ‘WDOG_EWM_IRQHandler’ which points at that there is an unhandled interrupt. At the very least, it does confirm that an interrupt is triggered.

I have tried the following names for the ISR, none of which seem to work:

// This seems to be the correct default, as it also has mentions in the rest of the mbed codebase
void FTM2_IRQHandler(void){ .... } 
// The following 4 are name variations that are used in various FTM implementations on the internet
void FTM2_ISR(void){ .... } 
void ftm2_isr(void){ .... } 
void flexTimer2Isr(void){ .... }  
// Seems to be the ISR to catch unhandled interrupts, doesn't work either
void DefaultISR(void){ .... } 

Am I correct in assuming that FTM2_IRQHandler(){} is the default interrupt handler? And if so, what is going wrong that this ISR is not being called?

Alternatively, how can I override this function in the interrupt vectors without causing the mcu to crash and burn?

UPDATE:
The above code for overwriting the interrupt vector did work, the mcu crashed because of the printf code inside the ISR (understandably). So for now the problem is solved, I am however still curious as to what the default IRQ handler is.