Implementation of PSI5 protocol using Teensy 4.1 - Timing issues

mgdosreis

New member
Hi, All.

I have been working in a project that timing is critical. I have to implement the PSI5 protocol using teensy 4.1. The objective is to have four independent channels and the data for each channel must be updated through Ethernet.

About the PSI5 protocol:
1. It is a 189kbps protocol. So I am using a timer to run at 2.65us (Period of 5.3us).
2. Each channel starts transmitting information after a sync pulse in the power line. After the sync pulse is detected using a falling edge in a pin, the timer is enabled. After 47us, the bit pattern is transmitted. 21 bits need to be transmitted after each sync pulse. After the bit transmission, the timer is turned off until the next sync pulse.
3. The PSI5 protocol is Manchester-enconded. So I am using two pins to generate the current modulation through an additional circuit.

What I have so far:
1. I am able to generate at the four channels the pattern that I want using one interrupt (Interval Timer or Periodic Timer). In this case, I can one have on sync pulse for synchronization for the four channels (not ideal, because each channels need to be independent). I am not using Ethernet at this part.
2. The next test was trying to make just two channels to work independently from each other. In this case, I tried to use two timers (Interval Timer or Periodic Timer). I have the expected pattern from two different sync pulses. However, I have a problem with timing for the two channels. When the second timer is enabled and the first one is already enabled (or the other way around). The two channels start to have a small delay/shifting at the pattern that is being transmitted. Even though, I establish different priorities I still get this undesirable behavior. The device that is reading this pattern does not accept any delays or small shifting in the transmitted bits.
3. The other test was running one channel with one timer. The signal is transmitted perfectly without any delays or shifting and the external equipment can read without any errors the PSI5 bit transmission for a timer running at 2.63us. However, when I enable the configuration of the Ethernet port using Native Ethernet (TCP/IP or UDP), the timer for the signal transmission starts to get a delay by the Ethernet port and the equipment starts to give errors saying that the signal is not respecting the timing. You can see on the oscilloscope that the Signal from time to time has a noticeable shifting. In this case, the Ethernet is not even sending or receiving bits from a external computer. It is just connected to the router. I am assuming here that the Ethernet is just checking status from time to time and this is messing with the timing for the timer I am using for signal generation.

Code:
// Set as global variables.
PeriodicTimer timers[4] = {PeriodicTimer(), PeriodicTimer(), PeriodicTimer(), PeriodicTimer()};

// This function is called when the raising edge of the Sync Pulse is detected. This function starts the timer for the bit generation
void ISR_Channel_0()
{
static int ch_idx = 0;
digitalWrite( channels[ch_idx].flipflopResetPin, HIGH);
if ( !channels[0].enabled )
{
timers[0].begin( generateSignalsCh0, channels[ch_idx].tp_div_2_us );
channels[ch_idx].counterPeriodGreater = 0, channels[ch_idx].outputCounter = -1;
channels[ch_idx].enabled = true, channels[ch_idx].newBitPattern = channels[ch_idx].confSetStop = false;
}
}

// This is the function for the signal generation. We have tried to reduce as much as we can. In one version for this function, we only had the digitalWrite to update the edges for the transmission. But when adding a second timer, we still had
// problems with one timer interfering with the other.
void generateSignalsTest ( uint8_t idx )
{
if ( channels[idx].enabled )
{
if ( !channels[idx].confSetStop)
{
channels[idx].initialized = channels[idx].confSetStop = true;
channels[idx].counterPeriodGreater = delayAfterPulse;
}

if ( channels[idx].initialized )
{
if ( channels[idx].counterPeriodGreater == 0x0 )
{
channels[idx].outputCounter++;

digitalWrite( channels[idx].outputPinA, CH0_bufferPatternDecoded[channels[idx].outputCounter] );

if ( channels[idx].outputCounter == 41 )
{
channels[idx].outputCounter = -1, channels[idx].counterPeriodGreater = 1;
channels[idx].endFrame = true;

}
}
else
{
channels[idx].counterPeriodGreater--;
if ( channels[idx].endFrame )
{
channels[idx].outputCounter = -1;
channels[idx].initialized = channels[idx].enabled = channels[idx].endFrame = false;
digitalWrite( channels[idx].flipflopResetPin, LOW);
timers[idx].stop();
}
}
}
}
}

In the setup I have:

for (int i = 0; i < 4; i++ )
{
ch_idx = i;
// Setting initial state for all channels' outputs: pins that drive the current modulation.
pinMode( channels[ch_idx].outputPinA, OUTPUT ), digitalWrite( channels[ch_idx].outputPinA, HIGH);
pinMode( channels[ch_idx].outputPinB, OUTPUT ), digitalWrite( channels[ch_idx].outputPinB, HIGH );
pinMode( channels[ch_idx].isrPin, INPUT ) , pinMode( channels[ch_idx].isrPin, FALLING );
pinMode( channels[ch_idx].flipflopDPin, OUTPUT) , digitalWrite( channels[ch_idx].flipflopDPin, LOW );
pinMode( channels[ch_idx].flipflopResetPin, OUTPUT), digitalWrite( channels[ch_idx].flipflopResetPin, LOW);
}

attachInterrupt( digitalPinToInterrupt( channels[0].isrPin ), ISR_Channel_0, FALLING);
attachInterrupt( digitalPinToInterrupt( channels[1].isrPin ), ISR_Channel_1, FALLING);
attachInterrupt( digitalPinToInterrupt( channels[2].isrPin ), ISR_Channel_2, FALLING);
attachInterrupt( digitalPinToInterrupt( channels[3].isrPin ), ISR_Channel_3, FALLING);

At this point, I have nothing in the main loop and the ethernet is not configured. And using two timers is enough to have timing issues. The ethernet is configured as the examples provided by the Native ethernet.

I appreciate all the help.

Thank you.
 
Code:
// Set as global variables.
PeriodicTimer timers[4] = { PeriodicTimer(), PeriodicTimer(), PeriodicTimer(), PeriodicTimer() };

// This function is called when the raising edge of the Sync Pulse is detected. This function starts the timer for the bit generation
void ISR_Channel_0()
{
	static int ch_idx = 0;
	digitalWrite(channels[ch_idx].flipflopResetPin, HIGH);
	if (!channels[0].enabled)
	{
		timers[0].begin(generateSignalsCh0, channels[ch_idx].tp_div_2_us);
		channels[ch_idx].counterPeriodGreater = 0, channels[ch_idx].outputCounter = -1;
		channels[ch_idx].enabled = true, channels[ch_idx].newBitPattern = channels[ch_idx].confSetStop = false;
	}
}

// This is the function for the signal generation. We have tried to reduce as much as we can. In one version for this function, we only had the digitalWrite to update the edges for the transmission. But when adding a second timer, we still had
// problems with one timer interfering with the other.
void generateSignalsTest(uint8_t idx)
{
	if (channels[idx].enabled)
	{
		if (!channels[idx].confSetStop)
		{
			channels[idx].initialized = channels[idx].confSetStop = true;
			channels[idx].counterPeriodGreater = delayAfterPulse;
		}

		if (channels[idx].initialized)
		{
			if (channels[idx].counterPeriodGreater == 0x0)
			{
				channels[idx].outputCounter++;

				digitalWrite(channels[idx].outputPinA, CH0_bufferPatternDecoded[channels[idx].outputCounter]);

				if (channels[idx].outputCounter == 41)
				{
					channels[idx].outputCounter = -1, channels[idx].counterPeriodGreater = 1;
					channels[idx].endFrame = true;

				}
			}
			else
			{
				channels[idx].counterPeriodGreater--;
				if (channels[idx].endFrame)
				{
					channels[idx].outputCounter = -1;
					channels[idx].initialized = channels[idx].enabled = channels[idx].endFrame = false;
					digitalWrite(channels[idx].flipflopResetPin, LOW);
					timers[idx].stop();
				}
			}
		}
	}
}

//In the setup I have :
void SetUp(){
	for (int i = 0; i < 4; i++)
	{
		ch_idx = i;
	// Setting initial state for all channels' outputs: pins that drive the current modulation.
		pinMode(channels[ch_idx].outputPinA, OUTPUT), digitalWrite(channels[ch_idx].outputPinA, HIGH);
		pinMode(channels[ch_idx].outputPinB, OUTPUT), digitalWrite(channels[ch_idx].outputPinB, HIGH);
		pinMode(channels[ch_idx].isrPin, INPUT), pinMode(channels[ch_idx].isrPin, FALLING);
		pinMode(channels[ch_idx].flipflopDPin, OUTPUT), digitalWrite(channels[ch_idx].flipflopDPin, LOW);
		pinMode(channels[ch_idx].flipflopResetPin, OUTPUT), digitalWrite(channels[ch_idx].flipflopResetPin, LOW);
	}

	attachInterrupt(digitalPinToInterrupt(channels[0].isrPin), ISR_Channel_0, FALLING);
	attachInterrupt(digitalPinToInterrupt(channels[1].isrPin), ISR_Channel_1, FALLING);
	attachInterrupt(digitalPinToInterrupt(channels[2].isrPin), ISR_Channel_2, FALLING);
	attachInterrupt(digitalPinToInterrupt(channels[3].isrPin), ISR_Channel_3, FALLING);
}
In future can you put your code between CODE tags using the # button. I think you will agree that it makes the code so much more easy to read for anyone that is going to help you.
 
One quick suggestion is use digitalWriteFast() rather than digitalWrite(). It's faster. Can you provide a screen shot or even a sketch of what your output signals look like? For signal generation, rather than using a timer interrupt and digital output writes, you should investigate the output compare functions of the FlexTimers and QuadTimers. I'm assuming that you know the bit (edge) pattern you want to produce at the beginning of the period. If you can generate an array containing the times of edge transitions, you can use an output compare to schedule the first edge and generate an interrupt on that edge. When that interrupt occurs, you schedule the next edge, etc. As long as you have enough time between edges to execute your ISR(s) and schedule the next edge for each channel, your edges will be precisely timed, as opposed to having the variation you get when the edge timing depends on the path through the logic as you have now.
 
See chapter 54 of the T4.x processor reference manual regarding QuadTimer. I think you could generate your waveforms using either the "Fixed-Frequency" or "Variable Frequency" PWM Modes. The variable-frequency mode simply means that you can define both a low time and a high time, and together those define the period of one PWM period. Each of those PWM periods could span multiple bit times when you have consecutive low or high bits.

For each of the 4 channels of each QuadTimer module, there are separate COMP (compare) and CMPLD (compare load) registers so that you can pre-set the to control the next pair of edges. There may be some constraints that I'm unaware of, but there are 8 pins (10-15,18-19) on T4.x that are associated with QuadTimer channels. 4 of those pins (14,15,18,19) are associated with a single QuadTimer module QTMR3, so those might be a good choice. With all 4 outputs on a single QuadTimer, they would all be on the same clock, so synchronization might be easier if that is necessary. QuadTimer counters and compare registers are 16 bits, so hopefully you could choose clock frequency and PWM periods that allow you to get your edges where you want them. You could probably also use FlexPWM, for which you have even more pins, and that module is even more complex. Not easy to figure out all of the details, but the capability is there.
 
More information about the issue

One quick suggestion is use digitalWriteFast() rather than digitalWrite(). It's faster. Can you provide a screen shot or even a sketch of what your output signals look like? For signal generation, rather than using a timer interrupt and digital output writes, you should investigate the output compare functions of the FlexTimers and QuadTimers. I'm assuming that you know the bit (edge) pattern you want to produce at the beginning of the period. If you can generate an array containing the times of edge transitions, you can use an output compare to schedule the first edge and generate an interrupt on that edge. When that interrupt occurs, you schedule the next edge, etc. As long as you have enough time between edges to execute your ISR(s) and schedule the next edge for each channel, your edges will be precisely timed, as opposed to having the variation you get when the edge timing depends on the path through the logic as you have now.

The following figure shows the sync pulse from the ECU. ECU is responsible to send this pulse through the power line for synchronization. When the teensy detects the pulse, it waits for around 50us to start sending the 21 bits (Manchester-Encoded). The pulse repeats every 500us. The pattern from teensy takes around 115us to be transmitted. I must have four independent channels doing this, because each channel will have its own pulse from the ECU for synchronization.

fig_FirstMsgSent.png

IMG_1426.jpg

Due to time, I have used a second teensy 4.1 on top of the one I was already using to be able to have at least one operational channel. One uC to deal with the Ethernet communication and a second one for signal transmission. This is working for now, but I have to fulfill the requirement for four channels and I would like to use teensy 4.1 for that. Another requirement is that the bit pattern has to be changed for each channel by the API through Ethernet every 10ms.

fig_Teensy.jpg

This is how I am measuring the modulated current by the Teensy 4.1. I am measuring the voltage at the resistor.

IMG_1424.jpg

I appreciate all the information that you have provided, but I do not have that much experience using teensy, so it will take me some time to go through your feedback, and test them out in my application.
 
The following figure shows the sync pulse from the ECU. ECU is responsible to send this pulse through the power line for synchronization. When the teensy detects the pulse, it waits for around 50us to start sending the 21 bits (Manchester-Encoded). The pulse repeats every 500us. The pattern from teensy takes around 115us to be transmitted. I must have four independent channels doing this, because each channel will have its own pulse from the ECU for synchronization.

50 us is a long time for T4.1, so you definitely have time to do necessary calculations and start your output on time. If you use input capture on a QuadTimer module for the trigger input, you'll know exactly when it arrived, and you'll be able to start the output at a very precise time relative to the input.

I appreciate all the information that you have provided, but I do not have that much experience using teensy, so it will take me some time to go through your feedback, and test them out in my application.

Yes, there is a lot to learn. The manual often does not become clear until you try things, scratch your head, read the same thing again, etc. Good luck.
 
Here's a T4.x example of rising-edge input capture (QTIMER1 ch1) and triggering a set of edges via output compare (QTIMER1 ch2). The first output edge occurs 47 us after the input edge, and after that output edges occur every 8 us (125 kHz). I think you could extend this to implement your PSI5 protocol with 4 separate pairs of QTIMER channels.

T41_IC_OC.png

Code:
//==============================================================================
// 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 );
  }
}
 

Attachments

  • T41_IC_OC.png
    T41_IC_OC.png
    3.1 KB · Views: 18
Here's a T4.x example of rising-edge input capture (QTIMER1 ch1) and triggering a set of edges via output compare (QTIMER1 ch2). The first output edge occurs 47 us after the input edge, and after that output edges occur every 8 us (125 kHz). I think you could extend this to implement your PSI5 protocol with 4 separate pairs of QTIMER channels.

View attachment 29336

Code:
//==============================================================================
// 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 );
  }
}

Thank you. I will test it. I have tried FlexTimers, but I had the same issue.
 
Back
Top