Hello. I am new to the forum.
I need to generate two synchronous pulse trains (pulse 2 at falling edge of pulse 1) with variable ON time for both. The ON time can vary from 5us to 10ms. I wish to set the ON time using Serial Monitor terminal.
So far using my trivial knowledge and posts from this awesome forum, I have successfully generated two pulse train sequences. But I am able to vary ON time of only first pulse using serial monitor and getting confused while trying for the other.
The code I have written is as below:
// This code generates two sequential pulses
char myData[20]; // array to store input from serial terminal
int z = 1000; // z is the variable ON time set using serial terminal. It is initialized to 1 ms
void setup()
{
Serial.begin(9600);
// pinMode(9, OUTPUT);//delay pulse pin// PB1
//pinMode(10, OUTPUT);//exposure pulse pin// PB2
DDRB = 0B00000110; // set the Arduino Pin 9, 10 as OUTPUT
cli();
TIMSK0 &= ~(1 << TOIE0);
sei();
}
void exposure() // this function generates pulse 2. Its ON time is kept fixed. I want this also variable
{
//digitalWrite(10, HIGH);
PORTB = 0b00000100;
delayMicroseconds(5); // Pulse 2 ON time at 5us. It should be variable.
//digitalWrite(10, LOW);
PORTB = 0b00000000;
delayMicroseconds(1995); // this is OFF time to set sequence period close to 2ms. It needs correction based on frequency input.//
}
void loop()
{
byte y = Serial.available(); // check if there is serial input from user.//
if (y != 0)
{
byte m = Serial.readBytesUntil('\n', myData, 20); // read serial input till newline and store into myData array//
myData[m] = '\0';
Serial.println(myData);
memset(myData, 0x30, 8);
z = atoi(myData); // extract Pulse 1 ON time from user ASCII input to integer format//
Serial.println(z, DEC); // print ON time//
}
PORTB = 0b00000010; //equivalent to digitalWrite(9, HIGH);
delayMicroseconds(z); // This is Pulse 1 ON time
//PORTB = 0b00000000; //equivalnet to digitalWrite(9, LOW);
exposure(); // jump to exposure subroutine
}
Also, I see timing jitter in both the pulse. Is it possible to get jitter free pulse using UNO?
Please use comments telling what the lines in the code are supposed to do. Nobody knows what's coming on serial, as an example.
Hello,
I have added comments. I hope they make the code clear.
Yes just disable the interrupts coming from timer0.
So you are repeating the pulses as quickly as possible? Does that mean you don't have any requirement for the off times?
If so, I would use Timer1 and set the prescale to 8 to get the timing in half-microsecond intervals. Use a FastPWM mode where you can set TOP in ICR1. Set OCR1A for the end of Pulse 1 and set OCR1B for the start of Pulse 2 (both the same time). At TOP, Pulse 2 will end and Pulse 1 will start again.
1 Like
Ah. Sorry I forgot to mention the frequency of pulse train. It will also be variable between 1 Hz to 100 Hz.
So I will have two pulse trains; pulse1 will have width 9+/-1 ms and Pulse 2 will have width 4us to 20us. Pulse 2 will go HIGH at falling edge of Pulse 1. The sequence will repeat at 1 Hz to 100 Hz.
The above code gives me two synchronized pulses. I have just set random pulse frequency in the code. Now I am facing two bottlenecks:
(1) There is jitter in the pulses. My scope shows 2us for smaller pulse i.e. Pulse2 at 20us width.
(2) I want to set the pulse width and pulse frequency using serial monitor. So far I could set Pulse 1 width remotely.
[Grumpy_Mike] disable means? Shall I just not enable it or disable at the end of loop?
[johnwasser] using timer won't allow me to play with pulse frequency, right?
Turn off
No because it needs to be disabled to do accurate timing.
Note when you do disable it then the millis, micros and delay will no longer work.
You should not be going through the loop because this in itself adds a delay every time round the loop.
In which case you can't use pure software to generate your signal because of the variable time it takes to read in and process external data.
Without any extra programmable hardware chips or a dual core processor you have painted yourself into a corner.
I agree with Mike - you can't do what you want purely in software on the Arduino. Consider using a programmable waveform generator chip.
1. Select Newline option in the Line ending tab of serial monitor and send the following code to set ON-time at 200 us (for example) for Pulse1.
200A //A indicates Pulse1.
2. Select Newline option in the Line ending tab of serial monitor and send the following code to set ON-time at 50 us (for example) for Pulse2.
50B //B indicates Pulse2
Sketch
// This code generates two sequential pulses
char myData[20]; // array to store input from serial terminal
int z1, z2;// = 1000; // z1, z2 are the variables for ON time set using serial terminal.
void setup()
{
Serial.begin(9600);
// pinMode(9, OUTPUT);//delay pulse pin// PB1
//pinMode(10, OUTPUT);//exposure pulse pin// PB2
DDRB = 0B00000110; // set the Arduino Pin 9, 10 as OUTPUT
cli();
TIMSK0 &= ~(1 << TOIE0);
sei();
}
void exposure() // this function generates pulse 2. Its ON time is kept fixed. I want this also variable
{
//digitalWrite(10, HIGH);
PORTB = 0b00000100;
delayMicroseconds(z2); // Pulse 2 ON time at 5us. It should be variable.
//digitalWrite(10, LOW);
PORTB = 0b00000000;
delayMicroseconds(1995); // this is OFF time to set sequence period close to 2ms. It needs correction based on frequency input.//
}
void loop()
{
byte y = Serial.available(); // check if there is serial input from user.//
if (y != 0)
{
byte m = Serial.readBytesUntil('\n', myData, 20); // read serial input till newline and store into myData array//
//myData[m] = '\0';
//Serial.println(myData);
if (myData[m - 1] == 'A')
{
myData[m - 1] = '\0';
z1 = atoi(myData); // extract Pulse 1 ON time from user ASCII input to integer format//
Serial.println(z1, DEC); // print ON time for Pulse 1
}
else
{
myData[m - 1] = '\0';
z2 = atoi(myData); // extract Pulse 2 ON time from ASCII input to integer format//
Serial.println(z2, DEC); // print ON time for Pulse 2
}
memset(myData, 0, 8);
}
PORTB = 0b00000010; //equivalent to digitalWrite(9, HIGH);
delayMicroseconds(z1); // This is Pulse 1 ON time
//PORTB = 0b00000000; //equivalnet to digitalWrite(9, LOW);
exposure(); // jump to exposure subroutine
}
@Grumpy_Mike "you can't use pure software to generate your signal because of the variable time it takes to read in and process external data"- Oh! Thats sad.
I have a pure hardware version of this generator built using 74HC123 IC. It is fairly accurate and solve the ultimate purpose. But it lacks option to set ON time for both pulses remotely using PC. I have to manually vary the width using rotary potentiometers and have to spare an oscilloscope just to check the pulses.
@GolamMostafa I must admit that I copied the serial communication part from one of your post. As always they are crisp and clear.
I will still complete the project using arduino ignoring the jitter and let you know of the progress.
Thanks for the assistance.
Thanks for ack.
In post #9, I have shown how to change ON-time for both Pulse1 and Pulse2 form the InputBox of the Serial Monitor. It may be helpful to you.
@TomGeorge The application is to trigger a camera to capture a pulsed light spot in exact synronization.
@Grumpy_Mike I have customized the code as per my requirement. Its all good, till I set the frequency close to 65Hz. Frequency lower than that (say 50 Hz), the output sets at somewhere 108 Hz. I though it may be due to range of "delaymicroseonds()" which is 64k and made changes in code. But the issue remains. Can you suggest why is it so?
// This code generates two sequential pulses
char myData[20]; // array to store input from serial terminal
int t1, t2;// = 1000; // z1, z2 are the variables for ON time set using serial terminal.
int f; // frequency
float Tu, Tm;
void setup()
{
Serial.begin(19200);
// pinMode(9, OUTPUT);//delay pulse pin// PB1
//pinMode(10, OUTPUT);//exposure pulse pin// PB2
DDRB = 0B00000110; // set the Arduino Pin 9, 10 as OUTPUT
cli();
TIMSK0 &= ~(1 << TOIE0);
sei();
}
void exposure() // this function generates pulse 2. Its ON time is kept fixed. I want this also variable
{
//digitalWrite(10, HIGH);
PORTB = 0b00000100;
delayMicroseconds(t2); // Pulse 2 ON time at 5us. It should be variable.
//digitalWrite(10, LOW);
PORTB = 0b00000000;
// delayMicroseconds(T - t1); //frequency adjust
}
void loop()
{
byte y = Serial.available(); // check if there is serial input from user.//
if (y != 0)
{
byte m = Serial.readBytesUntil('\n', myData, 20); // read serial input till newline and store into myData array//
if (myData[m - 1] == 'D')
{
myData[m - 1] = '\0';
t1 = atoi(myData); // extract Pulse 1 ON time from user ASCII input to integer format//
Serial.print("Delay ");
Serial.println(t1, DEC); // print ON time for Pulse 1
}
else if (myData[m - 1] == 'E')
{
myData[m - 1] = '\0';
t2 = atoi(myData); // extract Pulse 2 ON time from ASCII input to integer format//
Serial.print("Exposure ");
Serial.println(t2, DEC); // print ON time for Pulse 2
}
else if (myData[m - 1] == 'F')
{
myData[m - 1] = '\0';
f = atoi(myData); // extract frequency from ASCII input to integer format//
Serial.print("Freq ");
Serial.println(f, DEC); // frequency in Hz
}
memset(myData, 0, 8);
}
Tu = 1000000 / f; // Period in us
Tm = 1000 / f; // Period in ms
if (Tu >= 15000)
{
delay(Tm - 0.001 * (t1 + t2));
}
else {
delayMicroseconds(Tu - t1 - t2); //frequency adjust
}
PORTB = 0b00000010; //equivalent to digitalWrite(9, HIGH);
delayMicroseconds(t1); // This is Pulse 1 ON time
//PORTB = 0b00000000; //equivalnet to digitalWrite(9, LOW);
exposure(); // jump to exposure subroutine
}
int t1, t2;// = 1000;
So t1 and t2 are not set to anything so they default to zero.
Note the delayMicroseconds only has a resolution of 4 so any value that is not a multiple of 4 will be treated as if it was a multiple of 4.
What is the jitter like?
Quote from Arduino Reference Manual:
This function works very accurately in the range 3 microseconds and up to 16383.
What reference manual? It is still true the micro seconds counter has only a resolution of four.
Try it yourself, get out your oscilloscope and measure the pulse you get with a 4, 5, 6, 7 & 8 delay.
Or print out values of micros and see if you can find one that is not a multiple of four. Write a program to do this.
I think you misunderstood what the term accurate means here.
But that is not necessarily the same as using delayMicroseconds().
Could be quite interesting. You may find that it does not step where you expected.
Does this 4 stand for "4 us" or for "4 CPU clock cycles"?
Accuracy and precision are two terms that are clearly taught in the "Electrical Measurements" course?