I have wrote a program that is used to implement equivalent time sampling in capturing images for a seemingly faster acquisition rate. I want an acquistion rate of 1uS for my application. So to produce the effect of a 1uS frame rate I need to delay the camera acquistion by increments of 1uS after each rising edge of of the device under test pulse. The problem that I am having is that I need an accurate uS delay that will continue to be accurate for values of greater than 16000 uS. So I do not think I can use delayMicroseconds() or Micros. I was looking into using the AVR library for delay command _delay_us(), but I am unsure on how to implement it, and if it would even work for my application. Any suggestions would be much appreciated. My section of code including delayMicroseconds is included below.
//turns on DUT and camera sync to capture first image
digitalWrite(DUTsig, HIGH);
digitalWrite(syncIn, HIGH);
delayMicroseconds(1);
digitalWrite(syncIn, LOW);
capture = 1;
//prints to lcd screen that the first image has been captured
lcd.clear();
lcd.print("Frames captured");
lcd.setCursor(0,1);
lcd.print(capture);
lcd.setCursor(6,1);
lcd.print("of");
lcd.setCursor(9,1);
lcd.print(frames);
//while loop to implement equivalent time sampling at user defined parameters
while(capture < frames)
{
currentMillis = millis();
//while loop to keep currentMillis synced with millis
while (currentMillis - previousMillis < onTime)
{
currentMillis = millis();
}
//while loop to turn DUTsig off when on time is timed out
while (currentMillis - previousMillis >= onTime)
{
previousMillis = currentMillis; //resets previousMillis to currentMillis
Serial.println(currentMillis); //prints currentMillis to serial monitor for troubleshooting
Serial.println("off"); //prints to serial monitor to indicate that the DUTsig was turned off
digitalWrite(DUTsig, LOW); //writes DUTsig low
}
//while loop to keep currentMillis synced with millis
while (currentMillis - previousMillis < offTime)
{
currentMillis = millis();
}
//while loop that turns on DUTsig and triggers camera with equivalent time delay (sample)
while (currentMillis - previousMillis >= offTime)
{
previousMillis = currentMillis; //resets previousMillis to currentMillis
Serial.println(currentMillis); //prints currentMillis to serial monitor for troubleshooting
Serial.println("on"); //prints to serial monitor to indicate that the DUTsig was turned on
digitalWrite(DUTsig, HIGH); //writes DUTsig high
sample += EST; //increments sample by the equivalent sampling time each cycle
Serial.println(sample); //prints sample to the serial monitor for troubleshooting
delayMicroseconds(sample); //delays syncIn by the sample time to create equivalent time delay
digitalWrite(syncIn, HIGH); //writes syncIn high
delayMicroseconds(1); //holds syncIn high for 1 microsecond
digitalWrite(syncIn, LOW); //writes syncIn low
capture++; //increments capture to show number of frames that have been taken
//prints to lcd screen the number of frames that have been captured
lcd.clear();
lcd.print("Frames captured");
lcd.setCursor(0,1);
lcd.print(capture);
lcd.setCursor(6,1);
lcd.print("of");
lcd.setCursor(9,1);
lcd.print(frames);
}
}
//writes the DUTsig low for the final frame
Serial.println(currentMillis);
Serial.println("off");
digitalWrite(DUTsig, LOW);
I am using an UNO for my project. I wrote some if statements to include _delay_us() for values of 1 and 2 microseconds. I also added in delay() for values in milliseconds. Everything seemed to be working well in my simulations, but whenever I built the circuit and looked at the timing on an oscilloscope all of my uS values seemed to be off. I may just need to do some tweaking to my oscilloscope or delayMicroseconds and _delay_us() could not be accurate enough for my application. I would think they would be with the UNO having a 16MHz crystal. I need a resolution of 1 uS. I am not familiar with working with hardware timers, so I would really like to stick with coding the delays. This is my first arduino project. I thank everyone for the quick responses. Here is my new code with the if statements.
void loop() {
//initializes time at current mS of program and prints to serial monitor for troubleshooting
currentMillis = millis();
previousMillis = currentMillis;
Serial.println(currentMillis);
Serial.println("on");
//turns on DUT and camera sync to capture first image
digitalWrite(DUTsig, HIGH);
digitalWrite(syncIn, HIGH);
_delay_us(1);
digitalWrite(syncIn, LOW);
capture = 1;
//prints to lcd screen that the first image has been captured
lcd.clear();
lcd.print("Frames captured");
lcd.setCursor(0,1);
lcd.print(capture);
lcd.setCursor(6,1);
lcd.print("of");
lcd.setCursor(9,1);
lcd.print(frames);
//while loop to implement equivalent time sampling at user defined parameters
while(capture < frames)
{
currentMillis = millis();
//while loop to keep currentMillis synced with millis
while (currentMillis - previousMillis < onTime)
{
currentMillis = millis();
}
//while loop to turn DUTsig off when on time is timed out
while (currentMillis - previousMillis >= onTime)
{
previousMillis = currentMillis; //resets previousMillis to currentMillis
Serial.println(millis()); //prints millis to serial monitor for troubleshooting
Serial.println("DUT off"); //prints to serial monitor to indicate that the DUTsig was turned off
digitalWrite(DUTsig, LOW); //writes DUTsig low
}
//while loop to keep currentMillis synced with millis
while (currentMillis - previousMillis < offTime)
{
currentMillis = millis();
}
//while loop that turns on DUTsig and triggers camera with equivalent time delay (sample)
while (currentMillis - previousMillis >= offTime)
{
previousMillis = currentMillis; //resets previousMillis to currentMillis
Serial.println(millis()); //prints millis to serial monitor for troubleshooting
Serial.println("DUT on"); //prints to serial monitor to indicate that the DUTsig was turned on
digitalWrite(DUTsig, HIGH); //writes DUTsig high
sample += EST; //increments sample by the equivalent sampling time each cycle
//if statement to increment mSdelay, uSdelay, and accdelay each millisecond
if(sample - uSdelay == 1000)
{
mSdelay++; //increments mSdelay every 1000 uS
uSdelay = mSdelay*1000; //updates uSdelay with mSdelay
accdelay + 1000; //updates accurate delay every 1000 uS
}
//if statement to delay in milliseconds
if(sample - uSdelay == 0)
{
delay(mSdelay); //delays the mS amount for each whole millisecond
Serial.println(sample); //prints value of sample to serial monitor for troubleshooting
}
//if statement for accurate 1 microsecond delays
if(sample - uSdelay == 1)
{
delay(mSdelay); //delays by whole milliseconds
_delay_us(1); //delays syncIn by an additional 1 to create an accurate equivalent time delay
Serial.println(sample); //prints sample to serial monitor for troubleshooting
}
//if statement for accurate 2 microsecond delays
if(sample - uSdelay == 2)
{
delay(mSdelay); //delays by whole milliseconds
_delay_us(2); //delays syncIn by an additional 2 to create an accurate equivalent time delay
Serial.println(sample);
}
//if statement for delays with both milliseconds and microseconds
if(sample >= accdelay)
{
delay(mSdelay); //delays by whole milliseconds
delayMicroseconds(sample-uSdelay); //delays by residual amount of uS after millisecond delay
Serial.println(sample); //prints sample to serial monitor for troubleshooting
}
Serial.println(millis()); //prints millis to serial monitor for troubleshooting
Serial.println("sync on"); //prints sync on to serial monitor to let user know that the above millis is for the sync turning on
digitalWrite(syncIn, HIGH); //writes syncIn high
_delay_us(1); //holds syncIn high for 1 microsecond
digitalWrite(syncIn, LOW); //writes syncIn low
capture++; //increments capture to show number of frames that have been taken
//prints to lcd screen the number of frames that have been captured
lcd.clear();
lcd.print("Frames captured");
lcd.setCursor(0,1);
lcd.print(capture);
lcd.setCursor(6,1);
lcd.print("of");
lcd.setCursor(9,1);
lcd.print(frames);
}
}
//two while loops to finish timing the final cycle's on time
while(currentMillis - previousMillis < onTime)
{
currentMillis = millis();
}
while (currentMillis - previousMillis >= onTime)
{
previousMillis = currentMillis;
Serial.println(currentMillis); //prints currentMillis to serial monitor for troubleshooting
Serial.println("off"); //prints to serial monitor to indicate that the DUTsig was turned off
digitalWrite(DUTsig, LOW); //writes DUTsig low
}
//prints to lcd the number of frames capture and allows the program to restart from main loop
lcd.clear();
lcd.print(frames);
lcd.setCursor(7,0);
lcd.print("captured");
lcd.setCursor(0,1);
lcd.print("* to restart");
char key = kpd.waitForKey();
//resets the sample and capture back to zero for the next cycle of the program
sample = 0;
capture = 0;
}
Would using timers interfere with the millis function? By using the direct port access would I be able to obtain a resolution of 1 uS, or should I just move towards using timers instead? I am in somewhat of a time crunch. I want to take the shortest route of revision, but I need the 1 uS resolution.
If you insist on using the CPU to do the timing, delay by clock cycles using the functions _delay_loop_1() or _delay_loop_2() instead, and do the math yourself. To use that method, tou will have to turn off the interrupts, which disables millis().
That is very helpful information! Thank you so much for replying. I am going to try to work with the timers to create my delays. It seems like the most accurate solution and will probably save me some trouble in the long run!
I replaced all data writes with direct port access and removed all serial prints, and I still can not get the timing to be correct. I was going to try to use timers, but it looks like it take 2.4 uS to enter or exit an ISR. I do not think the timers would be much better because I need an accurate delay of 1 uS. I can not get an accurate millisecond delay of less than 10 mS and microsecond delay of 15 uS for if statements. I guess my series of while loops and if statements are slowing the program down too much. Does anyone have any suggestions on how I can get an accurate 1 uS delay that increments each period. Here is my new code. Thank you to everyone in advance for the help.
void loop() {
//initializes time at current mS of program and prints to serial monitor for troubleshooting
currentMillis = millis();
previousMillis = currentMillis;
//turns on DUT and camera sync to capture first image
PORTB = B00000011;
_delay_us(1);
PORTB &= ~(1 << syncIn);
capture = 1;
//prints to lcd screen that the first image has been captured
lcd.clear();
lcd.print("Frames captured");
lcd.setCursor(0,1);
lcd.print(capture);
lcd.setCursor(6,1);
lcd.print("of");
lcd.setCursor(9,1);
lcd.print(frames);
//while loop to implement equivalent time sampling at user defined parameters
while(capture < frames)
{
//while loop to keep currentMillis synced with millis
if (currentMillis - previousMillis < onTime)
{
currentMillis = millis();
}
//while loop to turn DUTsig off when on time is timed out
if (currentMillis - previousMillis >= onTime)
{
previousMillis = currentMillis; //resets previousMillis to currentMillis
PORTB &= ~(1 << DUTsig);
}
//while loop to keep currentMillis synced with millis
if (currentMillis - previousMillis < offTime)
{
currentMillis = millis();
}
//while loop that turns on DUTsig and triggers camera with equivalent time delay (sample)
if (currentMillis - previousMillis >= offTime)
{
previousMillis = currentMillis; //resets previousMillis to currentMillis
PORTB |= (1 << DUTsig); //writes DUTsig high
sample += EST; //increments sample by the equivalent sampling time each cycle
//if statement to increment mSdelay, uSdelay, and accdelay each millisecond
if(sample - uSdelay == 1000)
{
mSdelay++; //increments mSdelay every 1000 uS
uSdelay = mSdelay*1000; //updates uSdelay with mSdelay
accdelay + 1000; //updates accurate delay every 1000 uS
}
//if statement to delay in milliseconds
if(sample - uSdelay == 0)
{
delay(mSdelay); //delays the mS amount for each whole millisecond
}
//if statement for accurate 1 microsecond delays
if(sample - uSdelay == 1)
{
delay(mSdelay); //delays by whole milliseconds
_delay_us(1); //delays syncIn by an additional 1 to create an accurate equivalent time delay
}
//if statement for accurate 2 microsecond delays
if(sample - uSdelay == 2)
{
delay(mSdelay); //delays by whole milliseconds
_delay_us(2); //delays syncIn by an additional 2 to create an accurate equivalent time delay
}
//if statement for delays with both milliseconds and microseconds
if(sample >= accdelay)
{
delay(mSdelay); //delays by whole milliseconds
delayMicroseconds(sample-uSdelay); //delays by residual amount of uS after millisecond delay
}
PORTB |= (1 << syncIn); //writes syncIn high
_delay_us(1); //holds syncIn high for 1 microsecond
PORTB &= ~(1 << syncIn); //writes syncIn low
capture++; //increments capture to show number of frames that have been taken
//prints to lcd screen the number of frames that have been captured
lcd.clear();
lcd.print("Frames captured");
lcd.setCursor(0,1);
lcd.print(capture);
lcd.setCursor(6,1);
lcd.print("of");
lcd.setCursor(9,1);
lcd.print(frames);
}
}
//two while loops to finish timing the final cycle's on time
while(currentMillis - previousMillis < onTime)
{
currentMillis = millis();
}
while (currentMillis - previousMillis >= onTime)
{
previousMillis = currentMillis;
PORTB &= ~(1 << DUTsig); //writes DUTsig low
}
//prints to lcd the number of frames capture and allows the program to restart from main loop
lcd.clear();
lcd.print(frames);
lcd.setCursor(7,0);
lcd.print("captured");
lcd.setCursor(0,1);
lcd.print("* to restart");
char key = kpd.waitForKey();
//resets the sample and capture back to zero for the next cycle of the program
sample = 0;
capture = 0;
}
The ESP32 chip contains two hardware timer groups. Each group has two general-purpose hardware timers. They are all 64-bit generic timers based on 16-bit prescalers and 64-bit up / down counters which are capable of being auto-reloaded.
I am having a hard time finding the pin toggle independent of CPU in the Gammon post for timers. Under the header Timer Hardware Input/Output there is a table that I posted below. Is this where the toggle without CPU is described? Also if I use timers will I be limited by the number of counts of the timer. For example Timer 1 counts up to 65536. Say I put a prescalar of two on there then it will count up every 500nS. I want a pin to toggle on then off at 1 uS, then 2 uS, then 3 uS, and so on until the camera has captured the whole on time of the device under test. If I had the on time set to 1 second which is 1,000,000 uS then I could only toggle the pin up to 32768 uS, correct?
There are ways to set up the timer where the timer resets when it toggles the pin. So you could set it up to fire at 1uS intervals indefinitely. You could use another timer running in software to turn that on and off at will.
Thanks for the information Delta_G. I have figured out how to use the hardware timers to count up each time it matches a compare register. So I can get the timer to count in 1 uS intervals, but I need to be able to turn the timer on when another pin goes high and let it count the number of microseconds needed for the delay then turn on a second pin, wait another microsecond then turn that second pin back off. Could I use a software timer to start the hardware timer count at the time the first pin goes high, then turn on the second pin after the hardware time has reached a certain count? A major issue is that the count for the delay increments every loop cycle.