0

I have a Teensy 3.2 and I am using the Arduino IDE to program it. I am trying to generate 8 pulses with the frequency of 40 kHz and this needs to be repeated every second. I have written 3 different codes and none of them is working quite the way I wanted. The duty cycle of the pulses are not always constant (i.e. the falling edge often shifts in the time axis), indicating an inaccuracy with the delay between the analogWrite commands. There seems to be an issue with delays not being consistent and changing within the 8 pulses and between the 8 pulses. This inaccuracy is different between the 3 different codes, but exists in all of them. Changing the processor speed between 16 MHz and 120 MHz does not appear to change the problem (except in code 3 where NOP is dependent on it). Is there a way to make the cycles more consistent at 40 kHz? Do I need to use an external timer or oscillator? The two images below display the two problems. The first image shows the differences in time delays and output frequency. The red dashed lines are provided to show the discrepancy in the same pulse at successive iterations of the code. The second image shows a recurring issue where the square wave is not in fact a square, and doesn't seem to be caused by software problems.

Image 1:

enter image description here

Image 2:

enter image description here

The codes I have written can be found below:

Code 1:

const uint8_t pin1 = 5;
const uint8_t pin2 = 6;

void setup() {
  analogWriteFrequency(pin1, 40000);
  analogWriteFrequency(pin2, 40000);
}

void loop() {
  analogWrite(pin1,128);   //for 50% duty cycle
  delayMicroseconds(188);  //It takes 187.5us to generate 8 pulses at the given frequency
  analogWrite(pin1,0);
  delay(1000);
}

Code 2:

const uint8_t pin1 = 5;
const uint8_t pin2 = 6;

void setup() {
  pinMode(pin1, OUTPUT);
  pinMode(pin2, OUTPUT);
}

void loop() {
  PWMSignal();
  delay(1000);
}

void PWMSignal(){
  for(int i =0; i<=8; i++){
      analogWrite(pin1, 255);
      analogWrite(pin2, 0);
      delayMicroseconds(10);
      analogWrite(pin1, 0);
      analogWrite(pin2, 255);
      delayMicroseconds(10);
      analogWrite(pin2, 0);
  }
}

Code 3:

const uint8_t pin1 = 5;
const uint8_t pin2 = 6;

// Variable N determines the length of the delay. 
// Execution of NOP corresponds to 62.5ns delay 
// when the processor speed is 16 MHz.
const int N = 50;
#define NOP __asm__ __volatile__ ("nop\n\t")

void setup() {
  pinMode(pin1, OUTPUT);
  pinMode(pin2, OUTPUT);
}

void loop() {
  PWMSignal();
  delay(1000);
}

void PWMSignal(){
  for(int i =0; i<=8; i++){
      analogWrite(pin1, 255);
      analogWrite(pin2, 0);
      delayNOP();
      analogWrite(pin1, 0);
      analogWrite(pin2, 255);
      delayNOP();
      analogWrite(pin2, 0);
  }
}

void delayNOP(){
  for(int j =0; j<=N; j++){
    NOP;
  }
}
2
  • 1
    Try disabling interrupts around your bursts of pulses.
    – Majenko
    Commented Jul 3, 2018 at 20:17
  • Maybe using the IntervalTimer of the Teensy 3.x to manually toggle a pin (with direct port writing, not digitalWrite()) would give more reliable results. Hint: GPIOx_PTOR will toggle a pin in one single operation. "x" is the port number, and write (1<<p) to it where p is the pin number.
    – Majenko
    Commented Jul 3, 2018 at 20:23

2 Answers 2

1

it seems in the first picture that the two pulses have different duty cycles which is why it appears shifted. It looks like the 'on' part stays on for less time than the 'off' so your duty cycle is probably <50 but your frequency might still be 40kHz.

I think the most accurate way of getting 40kHz just with a teensy is to use timer interrupts as Majenko suggested. Check out the documentation for it here, its pretty straight forward,you set the timer to go off every 12.5us and in the ISR simply toggle a digital pin and keep count of how many times you've toggled it. If your teensy needs to run other interrupts you should change the priority using myTimer.priority(number); to possibly give the 40kHz interrupt a higher priority. One thing, don't use digitalWrite in the ISR as its slow af, either try digitalWriteFast or use assembly language to toggle it as pointed by Majenko.

Alternatively you could use a 555 timer which is easy to setup and I'm pretty sure it's been used to gerate 40kHz at 50% duty cycle, although it might be a bit tricky finding the correct capacitor/resistor values if they're too small/non-standard. Then you would hook up the output of the 555 timer to whatever you're hooking it up to and also to a digital input pin on the micro. Then you would have to set an external interrupt on that pin so whenever the 555 goes high the interrupt on the pin would fire and you would keep track of how many pulses have been generated.

Alternatively, you could use a digital signal synthesizer. I used the ad9837 for one of my projects. It can generate something like 0-3Mhz with a 5Mhz external clock at a resolution of 0.02 which is pretty good. I've got an altium design for it if you don't want to buy the module and an adapted version of the adafruit miniGen library which works with teensy3.5 link. Then you'd need to do the same feedback loop as the 555 to keep track of how many pulses have been produced (or your way of waiting X microseconds). The module is actually pretty low power so you could power it from a digital pin and turn that pin on/off every second.

I think you should try the interrupt method first, as it's the easiest. Also, I get that you want 8 pulses evey one second but maybe for debugging purposes have the 40kHz pulse go constantly and change your scope to measure frequency so you can see how far off you are from 40khz.Then once you know you're actually getting 40khz have it run every 1second.

0

One option is to use the IntervalTimer of the Teensy:

// Set up the variables and objects:
IntervalTimer myTimer;
volatile int count = 0;   

// ... later, start it:
count = 0;
myTimer.begin(togglePin, 12);

// ... and:
void togglePin() {
    GPIOB_PTOR = 1<<8; // Note: find the right register and bit for your chosen pin.
    count++;
    // 16  toggles is 8 pulses.
    if (count == 16) {
        myTimer.end();
    }
}

Note: this hasn't been tested, and you will need to do some research to get the right register and bit number, and also configure the pin for output and to your default level.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.