//==============================================================================
// T4.x QTIMER TEST PROGRAM (input capture on CH2, output compare on CH1)
//==============================================================================
// Joe Pasquariello 09/09/22 adapted from program by @TelephoneBill
//
// Pin11 = input for TMR1 CH2 (input capture)
// Pin12 = output for TMR1 CH1 (output compare)
// Pin13 = built-in LED
// Pin23 = PWM for test (jumper to pin 11)
volatile uint32_t ISRTotalCount, ISRTotalPrev;
volatile uint32_t ISRComp1Count, ISRComp1Prev;
volatile uint32_t ISRCapt2Count, ISRCapt2Prev;
volatile uint16_t CH2_IC_Value, CH2_IC_Delta, CH2_IC_Prev;
volatile uint32_t CH1_OC_Count;
float CPUTemp;
elapsedMillis looptime;
uint32_t sec = 0;
// QTMR1 as alias for IMXRT_TMR1
#define QTMR1 (IMXRT_TMR1)
void setup()
{
Serial.begin(9600); // USB serial
pinMode(13, OUTPUT); // pin 13 dig out (LED)
// turn on clocks for QTMR1 (CG13)
CCM_CCGR6 |= CCM_CCGR6_QTIMER1(CCM_CCGR_ON);
// Disable all 4 channels of QTMR1
QTMR1.ENBL = 0;
//===================================================================
// configure TMR1 CH1 for OUTPUT COMPARE w/ interrupt on COMP1 match
//===================================================================
QTMR1.CH[1].CTRL = 0; // stop channel
// set SCTRL status and control register
QTMR1.CH[1].SCTRL = TMR_SCTRL_OEN; // output enable
QTMR1.CH[1].LOAD = 0; // counter starts counting from zero
QTMR1.CH[1].COMP1 = 1200-1; // 8 uS - count up to this value
QTMR1.CH[1].CMPLD1 = 1200-1; // load COMP reg with value from this reg
// set CSCTRL comparator status and control register
// enable Timer Compare Flag 1 interrupt, load from CMPLD1
QTMR1.CH[1].CSCTRL = TMR_CSCTRL_TCF1EN | TMR_CSCTRL_CL1(1);
// set pin 12 as TMR1 CH1 external pin (see R.M. page 309).
IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_01 = 1;
//===================================================================
// configure TMR1 CH2 for INPUT CAPTURE w/ interrupt on RISING EDGE
//===================================================================
QTMR1.CH[2].CTRL = 0; // stop channel
// set SCTRL status and control register
// enable Input Edge Flag interrupt, capture mode rising edges
QTMR1.CH[2].SCTRL = TMR_SCTRL_IEFIE | TMR_SCTRL_CAPTURE_MODE(1);
QTMR1.CH[2].LOAD = 0; // counter starts counting from zero
QTMR1.CH[2].COMP1 = 0xFFFF; // 16.7uS - count up to this value
QTMR1.CH[2].CMPLD1 = 0xFFFF; // load COMP reg with value from this reg
// set CSCTRL comparator status and control register
QTMR1.CH[2].CSCTRL = 0; // no compare function
// set pin 11 as TMR1 CH2 external pin (see R.M. page 309).
IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_02 = 1;
//===================================================================
// set CTRL control registers
//===================================================================
QTMR1.CH[1].CTRL = 0 // CH1 output compare
| TMR_CTRL_CM(0b001) // count rising edges of primary source
| TMR_CTRL_PCS(0b1000) // primary source = IP Bus Clock / 1
| TMR_CTRL_SCS(0b01) // secondary source = CH1 input pin
//| TMR_CTRL_ONCE // 0 -> count repeatedly
| TMR_CTRL_LENGTH // 1 -> re-init counter on compare match
//| TMR_CTRL_DIR // 0 -> count up
//| TMR_CTRL_COINIT // 0 -> no co-channel init
| TMR_CTRL_OUTMODE(0b011); // toggle OFLAG on compare match
QTMR1.CH[2].CTRL = 0 // CH2 input capture
| TMR_CTRL_CM(0b001) // count rising edges of primary source
| TMR_CTRL_PCS(0b1001) // primary source = IP Bus Clock / 2
| TMR_CTRL_SCS(0b10) // secondary source = CH2 input pin
//| TMR_CTRL_ONCE // 0 -> count repeatedly
//| TMR_CTRL_LENGTH // 0 -> free-running counter
//| TMR_CTRL_DIR // 0 -> count up
//| TMR_CTRL_COINIT // 0 -> no co-channel init
| TMR_CTRL_OUTMODE(0b011); // toggle OFLAG on compare match
// enable CH2 for input capture (CH1 gets enabled/disabled in ISR)
QTMR1.ENBL = 0b0100;
// attach interrupt vector and enable interrupt
attachInterruptVector(IRQ_QTIMER1, QTMR1_isr);
NVIC_ENABLE_IRQ(IRQ_QTIMER1);
// PWM on pin 23 for test
analogWriteFrequency( 23, 2000 ); // 2-kHz PWM
analogWrite( 23, 128 ); // 50% duty cycle
looptime = 0; // init elapsedMillis timer
}
void QTMR1_isr()
{
if (QTMR1.CH[2].SCTRL & TMR_SCTRL_IEF) { // if CH2 IEF (input edge flag)
QTMR1.CH[2].SCTRL &= ~(TMR_SCTRL_IEF); // clear IEF
ISRCapt2Count++; // increment Capture count
CH2_IC_Value = QTMR1.CH[2].CAPT; // read CH2 capture value
CH2_IC_Delta = CH2_IC_Value - CH2_IC_Prev; // compute delta since prev
CH2_IC_Prev = CH2_IC_Value; // save value for next capture
CH1_OC_Count = 0; // init OC edge count = 0
QTMR1.CH[1].CNTR = 0; // init CH1 counter = 0
QTMR1.CH[1].COMP1 = 150*47; // 1st compare match in 47 us
QTMR1.CH[1].CMPLD1 = 150*8; // next compare match in 8 us
QTMR1.ENBL |= 0b0010; // enable CH1
}
if (QTMR1.CH[1].CSCTRL & TMR_CSCTRL_TCF1) { // if CH1 TCFG flag set
QTMR1.CH[1].CSCTRL &= ~(TMR_CSCTRL_TCF1); // clear TCF1
ISRComp1Count++; // increment Compare count
if (++CH1_OC_Count >= 22) // if OC edge count >= 22
QTMR1.ENBL &= ~0b0010; // disable CH1
}
ISRTotalCount++; // increment total ISR count
asm volatile ("dsb"); // wait for clear memory barrier
}
void loop()
{
if (looptime >= 1000) {
looptime -= 1000;
sec++;
CPUTemp = tempmonGetTemp();
Serial.printf( "sec=%6lu ISR=%8lu OC(1)=%8lu IC(2)=%5lu DELTA=%5hu CPU=%1.1f(degC)\n",
sec,
ISRTotalCount-ISRTotalPrev,
ISRComp1Count-ISRComp1Prev,
ISRCapt2Count-ISRCapt2Prev,
CH2_IC_Delta,
CPUTemp );
ISRTotalPrev = ISRTotalCount;
ISRComp1Prev = ISRComp1Count;
ISRCapt2Prev = ISRCapt2Count;
digitalToggleFast( 13 );
}
}