Как добавить дополнительную миллисекундную задержку к генератору микросекундной задержки

Я пытаюсь сделать генератор задержки. Я уже нашел отличный пример в Генерация короткого импульса после задержки, который работает в диапазоне от ~5 до 32767 микросекунд (он ограничен 16-битным счетчиком микросекунд, разделенным на 2).

Как бы мне расширить этот диапазон до миллисекундного диапазона (скажем, до 1 с или около того)? Поскольку этот пример основан на прерываниях, добавление операторов delay(), похоже, не работает.

Я также пробовал менять предварительный делитель таймера, но тогда джиттер также увеличивается до неприемлемого уровня.

Вот какой код у меня сейчас. Отправляйте команды через последовательную консоль arduino, D500 для задержки 500 мкс, L100 для импульса длиной 100 мкс. Буквы M и N изменяют предделитель.

uint16_t pulse_delay = 10; //половина микросекунды
uint16_t pulse_length = 10;
String inputString = "";         // строка для хранения входящих данных
boolean stringComplete = false;  // является ли строка полной
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)  //подавление шума при захвате входного сигнала
      | _BV(ICES1) //положительный фронт
      | _BV(CS11); // /8 предделитель
  TIMSK1 = _BV(ICIE1); //включить прерывание захвата входа
  Serial.begin(9600);
  inputString.reserve(200);
  Serial.println(F("RDY"));
  printvals();
}
void loop(){
  if (stringComplete) {
      switch(inputString.charAt(0)) {
        case 10: //\n (Новая строка; Перевод строки)
          // предыдущее окончание строки было CRLF, поэтому теперь LF перенесено на следующую строку
          break;
        case 63: //?
          //вывести текущие значения
          printvals();
          break;
        case 68: //D (Задержка)
          read_value = getcommandvalue(inputString);
          if (read_value >= 1) {
            pulse_delay = 2 * read_value;
            printvals();
          }
          break;
        case 76: //L (длина)
          read_value = getcommandvalue(inputString);
          if (read_value >= 1) {
            pulse_length = 2 * read_value;
            printvals();
          }
          break;
        case 77: //M (дополнительное предварительное масштабирование)
          TCCR1B = _BV(ICNC1)  //подавление шума при захвате входного сигнала
            | _BV(ICES1) //положительный фронт
            | _BV(CS10) // 64 предделитель
            | _BV(CS11); // /8 предделитель
          break;
        case 78: //N (8-кратное предварительное масштабирование)
          TCCR1B = _BV(ICNC1)  //подавление шума при захвате входного сигнала
            | _BV(ICES1) //положительный фронт
              | _BV(CS11); // /8 предделитель
          break;
        case 79: //О
          ms_delay = getcommandvalue(inputString);
          // Я не знаю, где выполнить эту ms_delay
          break;
      }
      inputString = "";
      stringComplete = false;
  }
}



void serialEvent() {
  while (Serial.available()) {
    // получаем новый байт:
    char inChar = (char)Serial.read();
    // добавляем его к inputString:Z
    inputString += inChar;
    // если входящий символ — символ новой строки, установить флаг
    // поэтому основной цикл может что-то с этим сделать:
    if ((inChar == '\n') or (inChar == '\r')) {
      stringComplete = true;
    }
  }
}

ISR(TIMER1_CAPT_vect){
  TCCR1A = _BV(COM1A0) | _BV(COM1A1); //установить OC1A при совпадении
  TIFR1 = _BV(OCF1A);  // очистить флаг прерывания
  TIMSK1 |= _BV(OCIE1A); //включить прерывание сопоставления
  OCR1A = pulse_delay; //время начала импульса

  TCNT1 = TCNT1 - ICR1; //TCNT1 теперь содержит время с момента входного импульса, даже если
                        //прерывание не запускается немедленно
}
ISR(TIMER1_COMPA_vect){
  TIMSK1 &=~ _BV(OCIE1A); //отключить прерывание сопоставления
  TCCR1A = _BV(COM1A1); //очистить OC1A при совпадении
  OCR1A = pulse_delay + pulse_length;
}

, 👍1


1 ответ


1

Вы можете подсчитать совпадения сравнения таймера. Ниже приведена иллюстрация принципа, возможно, не точная, как написано, но я уверен, что цикл счетчика будет достаточно точным для ваших нужд, учитывая, что вы хотите использовать Arduino. В зависимости от нужного вам разрешения по времени вы можете запустить другой код, пока активен программный счетчик.

Следующее может отсчитываться до нескольких секунд в зависимости от значений OCR1A и counter_target.

unsigned int state, pulse_delay;
byte counter[3]={0,0,0}; // 24-битный счетчик, в принципе можно сделать его длиннее

byte counter_target[3]={0,0,0}; // определяет нужную вам длину задержки

void setup(){
state=0;
TCCR1A=0;
TCCR1B=0;
}

void loop(){
switch (state){
case 0: // машина состояний простоя // здесь есть другой код
counter_target[0]=0; // Например
counter_target[1]=0;
counter_target[2]=1; // соответствует (1+65536)*pulse_delay/clock_scaler
// когда вы будете готовы начать считать
state=1;
break;

case 1: // настройка таймера
counter[0]=0; // Обнуляем 24-битный счетчик
counter[1]=0;
counter[2]=0;
OCR1A = pulse_delay; // количество импульсов для подсчета = 1+OCR1A
OCR1B = OCR1A+1;
ICR1  = OCR1A+1;    // Просто чтобы убедиться, что это не вызовет совпадение при сравнении
TIFR1 |= (_BV(ICF1) | _BV(OCF1A) | _BV(OCF1B)); // Очистить флаги совпадений
state=2;
TCCR1A = B00000011; // Быстрый режим ШИМ 15 на UNO, OCR1A=TOP
TCCR1B = B00011001; // Прескалер часов 1
break;

case 2: // Подсчет
loop_until_bit_is_set(TIFR1,OCF1A); 
TIFR1 |= (_BV(ICF1) | _BV(OCF1A) | _BV(OCF1B)); // Очистить флаги совпадений
counter[0]+=1; // Увеличить счетчик на 1
if(counter[0]==0x00){counter[1]+=1;
if(counter[1]==0x00){counter[2]+=1;}} // и т.д.

if(counter[0]==counter_target[0] && counter[1]==counter_target[1] && counter[2]==counter_target[2]){state=0;}  // Проверяем, достигли ли мы целевой задержки
break;

}
}
,