Может ли Arduino uno регулировать входную квадратную волну, фазу и частоту только с помощью таймера-счетчика?

Требования:

  • У меня есть входной импульсный вход 4 кГц
  • Разделите его частоту пополам
  • Произвольно отрегулируйте фазу
  • Произвольно регулировать пошлину
  • Выведите наружу модифицированный импульс

Мое решение (CPU):

uint8_t cnt = 0;
uint16_t t1 = 235;
uint16_t t2 = 5;
String cmd;

void setup() {
  pinMode(3, OUTPUT);
  pinMode(2, INPUT);
     
  Serial.begin(9600);
  delay(500);
  attachInterrupt(digitalPinToInterrupt(2), rising_edge, RISING);
  Serial.println("PWM init");
}

void loop() {
  if (Serial.available() > 0) {
    cmd = Serial.readString();

    if (cmd.substring(0,8) == "setdelay"){
      t1 = cmd.substring(9).toInt();
      Serial.print("setting delay to ");
      Serial.print(t1);
      Serial.println(" us");
    }
    else if (cmd.substring(0,7) == "sethigh"){
      t2 = cmd.substring(8).toInt();
      Serial.print("setting high time to ");
      Serial.print(t2);
      Serial.println(" us");
    }
  Serial.flush();
  }
}
void rising_edge() {
  cnt = cnt + 1;
  if (cnt >= 2) {                           //разделить на 2
    delayMicroseconds(t1);
    digitalWrite(3,HIGH);
    delayMicroseconds(t2);
    digitalWrite(3,LOW);
    cnt = 0;
  }
}

Однако это сильно использует процессор, и мне нужно использовать процессор для чего-то другого.Можно ли реализовать все это с помощью ТК ?

Заранее спасибо !

, 👍2

Обсуждение

Вы можете использовать входящие тактовые импульсы в качестве внешнего (асинхронного) тактового источника для timer2. Для начала посмотрите регистр АССР в таблице данных. Вы не можете изменить рабочий цикл только с помощью этого метода. Хотя вы используете дополнительный таймер в "режиме одного выстрела", но это все равно потребует прерываний., @Gerben


1 ответ


Лучший ответ:

1

это сильно использует процессор, и мне нужно использовать процессор для чего-то еще

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

  • Serial.ReadString(): см. Сообщение в блоге Majenko Reading Serial на Arduino

  • delayMicroseconds(): для этого вы действительно можете использовать таймер.

Для генерации импульсов вы можете сохранить примерно ту же логику, что и ваш существующий код: выводить один импульс каждый раз , когда на входе ощущается восходящее ребро. Этот импульс может быть удобно сгенерирован Таймер 1 на выводе 9 с помощью ШИМ - режима и остановкой таймера после одного цикла. Я бы использовал инвертирующий режим ШИМ с таймером , начинающимся с нуля на входном импульсе:

  • в микросекундах t1 сопоставление устанавливает высокое значение выходного сигнала

  • при t1+t2 микросекундах переполнение таймера устанавливает низкий уровень выходного сигнала, а прерывание переполнения останавливает и сбрасывает таймер

Вот предварительная, непроверенная реализация:

// Установите тайминги импульсов. Вызывается из loop() по запросу пользователя.
void set_timings(uint16_t t1, uint16_t t2)
{
    uint16_t ocr1a = t1 * 16 - 1;        // начальное время в НИЗКОМ состоянии
    uint16_t icr1 = (t1 + t2) * 16 - 1;  // период таймера
    noInterrupts();  // защита критической секции
    OCR1A = ocr1a;
    ICR1 = icr1;
    interrupts();
}

// Настройка таймера 1. Вызывается из setup().
void setup_timer() {
    DDRB  |= _BV(PB1);    // установить вывод digital 9 = PB1 = OC1A в качестве выхода
    TCCR1A = _BV(COM1A0)  // ШИМ на OC1A, режим инвертирования
           | _BV(COM1A1)  // то же самое
           | _BV(WGM11);  // режим 14: быстрый ШИМ, top = ICR1
    TCCR1B = _BV(WGM12)   // то же самое
           | _BV(WGM13)   // то же самое
           | 0;           // остановлено
    TCNT1  = 0;           // сброс
    TIFR1  = _BV(TOV1);   // очистить флаг переполнения
    TIMSK1 = _BV(TOIE1);  // включить прерывание переполнения
    set_timings(235, 5);  // начальные тайминги
}

// Прерывание, вызванное восходящим фронтом входа.
void on_rising_edge() {
    static uint8_t count;
    if (++count % 2) return;   // разделить на 2
    TCCR1B |= _BV(CS10);       // таймер запуска, часы @ F_CPU
}

// Остановите и сбросьте таймер, когда он переполнится.
ISR(TIMER1_OVF_vect) {
    TCCR1B = 0;  // стоп
    TCNT1  = 0;  // сброс
}
,