I am trying to make a delay generator. I already found an excellent example in Generating a short pulse after a delay that works in the range from ~5-32767 microseconds (it is limited by a 16 bit microsecond counter divided by 2).
How would I go about extending that range into the millisecond range (say up to 1s or so)? Since this example is interrupt-based, adding delay() statements do not really seem to work.
I have also tried changing the timer prescaler, but then the jitter is also scaled up in an unacceptable way.
This is the code I have now. Send commands via the arduino serial console, D500 for 500µs delay, L100 for a 100µs long pulse. Letters M and N change the prescaler.
uint16_t pulse_delay = 10; //half-microseconds
uint16_t pulse_length = 10;
String inputString = ""; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
long read_value = 0;
long ms_delay = 0;
void printvals() {
Serial.print(F("Delay: "));
Serial.print(pulse_delay / 2);
Serial.println(F(" µs"));
Serial.print(F("Length: "));
Serial.print(pulse_length / 2);
Serial.println(F(" µs"));
}
long getcommandvalue(String inputstring) {
inputString.remove(0,1);
return inputString.toInt();
}
void setup(){
pinMode(8, INPUT);
pinMode(9, OUTPUT);
TCCR1A = 0;
TCCR1B = _BV(ICNC1) //input capture noise cancel
| _BV(ICES1) //positive edge
| _BV(CS11); // /8 prescaler
TIMSK1 = _BV(ICIE1); //enable input capture interrupt
Serial.begin(9600);
inputString.reserve(200);
Serial.println(F("RDY"));
printvals();
}
void loop(){
if (stringComplete) {
switch(inputString.charAt(0)) {
case 10: //\n (Newline; Line Feed)
// previous line ending was CRLF, so now LF was carried to the next string
break;
case 63: //?
//print current vales
printvals();
break;
case 68: //D (Delay)
read_value = getcommandvalue(inputString);
if (read_value >= 1) {
pulse_delay = 2 * read_value;
printvals();
}
break;
case 76: //L (Length)
read_value = getcommandvalue(inputString);
if (read_value >= 1) {
pulse_length = 2 * read_value;
printvals();
}
break;
case 77: //M (additional prescaling)
TCCR1B = _BV(ICNC1) //input capture noise cancel
| _BV(ICES1) //positive edge
| _BV(CS10) // 64 prescaler
| _BV(CS11); // /8 prescaler
break;
case 78: //N (8x prescaling)
TCCR1B = _BV(ICNC1) //input capture noise cancel
| _BV(ICES1) //positive edge
| _BV(CS11); // /8 prescaler
break;
case 79: //O
ms_delay = getcommandvalue(inputString);
// I don't know where to execute this ms_delay
break;
}
inputString = "";
stringComplete = false;
}
}
void serialEvent() {
while (Serial.available()) {
// get the new byte:
char inChar = (char)Serial.read();
// add it to the inputString:Z
inputString += inChar;
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
if ((inChar == '\n') or (inChar == '\r')) {
stringComplete = true;
}
}
}
ISR(TIMER1_CAPT_vect){
TCCR1A = _BV(COM1A0) | _BV(COM1A1); //set OC1A on match
TIFR1 = _BV(OCF1A); // clear interrutp flag
TIMSK1 |= _BV(OCIE1A); //enable match interrupt
OCR1A = pulse_delay; //pulse begin time
TCNT1 = TCNT1 - ICR1; //TCNT1 now contains time since input pulse, even if
//the interrupt isn't run immediately
}
ISR(TIMER1_COMPA_vect){
TIMSK1 &=~ _BV(OCIE1A); //disable match interrupt
TCCR1A = _BV(COM1A1); //clear OC1A on match
OCR1A = pulse_delay + pulse_length;
}