Hello,
My project requires the generation of trains of pulses of varying pulse widths, ranging from 500ns to 50us. I have a genuino uno. I am using a Tektronix TDS2014C to validate pulses.
My first attempt is to use a code that I found here in the forum that creates the fastWrite() function - FastWrite - Programming Questions - Arduino Forum
My code looks like this:
int w = 1; //in microseconds
int on;
int off;
#define fastWrite(_pin_, _state_) ( _pin_ < 8 ? (_state_ ? PORTD |= 1 << _pin_ : PORTD &= ~(1 << _pin_ )) : (_state_ ? PORTB |= 1 << (_pin_ -8) : PORTB &= ~(1 << (_pin_ -8) )))
// the macro sets or clears the appropriate bit in port D if the pin is less than 8 or port B if between 8 and 13
void setup() {
pinMode(12, OUTPUT);
pinMode(13, OUTPUT);
Serial.begin(57600);
}
void loop() {
noInterrupts(); //disables interrupts for crucial parts of code
while(1) {
on = w;
off = w;
fastWrite(12, HIGH); // set Pin high
fastWrite(13, HIGH); // set Pin high
delayMicroseconds(on); // waits "on" microseconds
fastWrite(12, LOW); // set pin low
fastWrite(13, LOW); // set Pin high
delayMicroseconds(off); //wait "off" microseconds
}
interrupts(); //allows interrups to happen again
}
This approach gives me the following 2 problems:
-
I cannot go below 1us
-
I cannot have pulse widths of lengths other than integers. Eg, it is not possible to get a 5.5us pulse (since delayMicroseconds only takes integers)
To solve the problems I tried to dig a bit deeper and I read a bit about assembly commands (Arduino Playground - AVR - tbh this goes beyond my knowledge).
I understand that the 'asm("nop");' command should pause the execution for 62.5 ns (1/16GHz).
So to get 500ns pulse width, in principle I would need to use 8 times the command when pin is HIGH and 8 times when the pin is LOW. However, when I do this I get different pulse widths that the timing doesn't match.
Just by trial and error, I managed to get 500ns pulse width with this code:
double w = 0.5;
int on;
int off;
int whichpin=10;
int var = 0;
int x = 0;
int pulseNumber = 1000;
#define fastWrite(_pin_, _state_) ( _pin_ < 8 ? (_state_ ? PORTD |= 1 << _pin_ : PORTD &= ~(1 << _pin_ )) : (_state_ ? PORTB |= 1 << (_pin_ -8) : PORTB &= ~(1 << (_pin_ -8) )))
// the macro sets or clears the appropriate bit in port D if the pin is less than 8 or port B if between 8 and 13
void setup() {
pinMode(10, OUTPUT);
Serial.begin(9600);
Serial.println("start...");
}
void loop() {
noInterrupts(); //disables interrupts for crucial parts of code
while(1) {
fastWrite(whichpin, HIGH); // set Pin high
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
fastWrite(whichpin, LOW); // set pin low
asm("nop");
asm("nop");
asm("nop");
asm("nop");
}
}
Now here I have some problems and questions again:
-
Why do 6x nop in the HIGH give 500ns while it takes 4x nop in the LOW to give 500ns?
-
The amount of nop needed is not linear with the actual pause. In specific, for 1us pulse, I need 14x nop in the HIGH and 12x nop in the LOW. For 2us, I need 30x nop in the HIGH and 28x nop in the LOW.
-
If I try to make a loop that runs for specific times executing the nop, I get completely different delay times. So it seems the only way is to actually type nop; again and again, which is not practical.
-
I wonder how reproducible will those delays be in different computers etc?
I've also read about PWM, but I wasn't able to change the frequency of my board. I calculate that if I would set the PWM frequency at 20,000 Hz and then change the duty cycle from 1% to 100%, I could, in theory get all the values I need (eg 1% is 500ns).
With all the above in mind, I would appreciate any solutions or a better direction for my project. I've also read about
Thanks a lot in advance for your help