Как добавить дополнительную миллисекундную задержку к генератору микросекундной задержки
Я пытаюсь сделать генератор задержки. Я уже нашел отличный пример в Генерация короткого импульса после задержки, который работает в диапазоне от ~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;
}
@Victor Claessen, 👍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;
}
}
- Arudino получает команду прерывания ДО перехода в спящий режим, из-за чего он не получает никаких команд прерывания для пробуждения.
- Использование millis() и micros() внутри процедуры прерывания
- Как сгенерировать аппаратное прерывание в mpu6050 для пробуждения Arduino из режима SLEEP_MODE_PWR_DOWN?
- Arduino непрерывно считывает значение АЦП с помощью прерывания
- Использование TIMER0_COMPB_vect
- 4-битный счетчик вверх и вниз
- Включить и отключить отдельные прерывания
- Как настроить векторный таймер прерываний сторожевого таймера на Arduino Redboard/Uno?