GPIO toggle jitter

Status
Not open for further replies.

canca87

New member
I am using a teensy 3.1 to toggle a GPIO (pin 0) as fast as possible. I have noticed that there is considerable jitter in the pulses which I measured to be ~350-440ns. If I had to guess, there is some kind of background interrupt running on the Teensy (an interrupt I did not set up).

WP_20160401_12_39_53_Pro.jpg

I do not need anything besides this GPIO pin and the SPI bus (interrupts enabled for receiving in slave mode using btmcmahan's SPI library). Is there a way to disable any background interrupts? Ideally, the only jitter/interrupt should be is when the SPI receives data (which I measured is ~1.2 us).

Code below:


Code:
 #include "t3spi.h" // Library code to use the hardware SPI in slave mode

 T3SPI SPI_SLAVE; //initalise the T3SPI class as SPI_SLAVE

 #define dataLength 2 //only two integer per SPI packet

 volatile uint16_t data[dataLength] = {}; //initalise empty data array
 volatile uint16_t delayCount = 15; //initalise the delay counter (time the pulse stays LOW)
 volatile uint16_t pulseCount = 15; //initalise the pulse counter (time the pulse stays HIGH)
 uint16_t Tpulse = 0; //temporary pulse counter
 uint16_t Tduty = 0; //temporary delay counter
 byte initalised = 0; //the pulses are OFF until it gets a command from SPI

void setup() {
  pinMode(0,OUTPUT); // set the TTL output as an output (pin 0)
  digitalWriteFast(0,LOW); //set the TTL low
  pinMode(1,OUTPUT); // used for measuring the SPI ISR time
  digitalWriteFast(1,LOW); //turn it off for now.

  //Begin the SPI in SLAVE:
  SPI_SLAVE.begin_SLAVE(SCK, MOSI,MISO,CS0); //pin 13,11,12,10 respectively
  SPI_SLAVE.setCTAR_SLAVE(16, SPI_MODE0); //16 bit frame size, SPI mode 0
  NVIC_ENABLE_IRQ(IRQ_SPI0); //Enable the interrupt for SPI0
}

void loop() {
  while(initalised){
    //this loop modifies pin 0 (laser TTL) as fast as possible
    Tpulse = pulseCount; //set the temporary pulse couter
    Tduty = delayCount; //set the temporary duty counter
     
    digitalWriteFast(0,HIGH); //ON
    
    while (Tpulse != 0){ //wait until the pulse counter reaches zero
      Tpulse--; //decrement the pulse counter
    }
    
    digitalWriteFast(0,LOW); //OFF
  
    while (Tduty != 0){ //wait until the delay counter reaches zero
      Tduty--; //decrement the delay counter
    }
  }
}

// ISR for the SPI0 interface
void spi0_isr(void){
  digitalWriteFast(0,LOW); //make sure the laser is off
  digitalWriteFast(1,HIGH); //Set this pin high to measure the computation time for this ISR
  initalised = 1;
  //Handles the data transfer:
  // get the data from the buffer:
  SPI_SLAVE.rx16(data, dataLength);
  //split up the data into the two types:
  delayCount = data[1] & 0x03FF; //delay count is the lower 10 bytes
  pulseCount = (data[1] >> 12) & 0x000F; //pulse count is the upper 4 bytes
  digitalWriteFast(1,LOW); //end of the ISR, reset the debug pin to zero.
}
 
This sounds familiar...

I know there is a SYSTICK interrupt that's used to keep track of time elapsed with Micros(). I don't know where it's enabled though

Thanks for that. The jitter does go away when I call noInterrupts() in the setup routine. But then I cannot use the SPI interrupt.

Looking at the documentation for the ARMv7-M I tried to turn off the SYSTICK timer by writing 0 to the SYST_CSR register during the setup function (setting the LSB to zero stops the counter). This had no effect on the jitter.

Code:
  SYST_CSR = 0; //disable SYSTICK timer

I think searching for a single running interrupt is like looking for a needle in a haystack (so to speak). It would be better to just clear all interrupt registers (is there a global interrupt register?), and then only re-enable the SPI interrupt. I think I need to study the reference manual some more, but the ARM manuals are no where near as clear or concise as the AVR ones.
 
Your setting of delayCount and pulseCount isn't atomic -- you could get a SPI interrupt between setting Tpulse and Tduty in loop() -- best to wrap those 2 statements in noInterrupts().

You can change priorities for interrupts in the NVIC.

Instead of using code to pulse on and off -- can you use analogWrite -- https://www.pjrc.com/teensy/td_pulse.html ? This uses the hardware FTM module to generate signals with zero jitter.
 
Last edited:
Most any MCU system will have some jitter as you have seen. At the least, systick interrupts, often 1mSec per, introduces a few microseconds of CPU usage. Systicks need to be left enabled as a rule.
To get really precise microsecond level pulse timing, you need to use a hardware timer set to toggle a GPIO bit on each overflow. Or PWM.
Or there is a way to use DMA and a circular list of bit conditions.
 
Status
Not open for further replies.
Back
Top