Библиотека TimerOne — время отклика

Я пытаюсь написать библиотеку для (довольно медленной) связи с видимым светом и недавно попытался включить библиотеку TimerOne в качестве таймера. Я хочу отправлять биты с частотой 2 кГц, то есть с периодом 500 мкс. Я инициализирую Timer1 с 500(мкс) вот так, а затем присоединяю к нему прерывание для вызова моей функции передачи:

Timer1.initialize(500);
Timer1.attachInterrupt(transmitBit);

Однако, когда я пытаюсь измерить период с помощью логического анализатора, он сообщает мне, что на самом деле период составляет 2000 мкс. Может ли это быть из-за каких-то накладных расходов, создаваемых библиотекой? Где еще может быть проблема?

Если вам нужна какая-либо дополнительная информация, просто дайте мне знать, и я постараюсь ее предоставить.

Заранее спасибо за ваш отзыв.

ПРАВКА: Я только что попробовал добавить точку в качестве параметра к attachInterrupt следующим образом:

Timer1.attachInterrupt(transmitBit, 500);

И теперь я измеряю период 30 мкс по какой-то причине? Может кто-нибудь объяснить мне, что происходит?

РЕДАКТИРОВАНИЕ: Это остальная часть моего (соответствующего) кода:

Transmitter::Transmitter()
{
  [...]
  Timer1.initialize(500);
}

Transmitter* Transmitter::_instance;

void Transmitter::start() {
  _instance = this;
  Timer1.attachInterrupt(_transmitBit, 500);
  _active = true;
}

static void Transmitter::_transmitBit() {
  _instance->transmitBit();
}

void Transmitter::transmitBit() {
  if(_active) {
    //Подготовка
    if(!_manHalf) { //изменять состояние только после полной передачи бита Манчестера.
      if(_busy) {
        //Serial.println("Передача!");
        /** if there is currently a transmission running
          * read the next bit from the frame
          */
        _state = *(_frame + _pos);
        if(_pos >= _frameSize) {
          _busy = false;
          _state = 0;
          _pos = 0;
        }
        _pos++;
      } else { //иначе передаем шаблон ожидания
        _state = 0;
      }
    }
    //Передача инфекции
    if (!_manHalf) {
      digitalWrite(_pin, !_state);
    } else {
      digitalWrite(_pin, _state);
    }
    _manHalf = !_manHalf;
  } else {
    digitalWrite(_pin, HIGH);
  }
}

, 👍1


1 ответ


1

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

В следующем примере я предполагаю, что вы используете чип ATmega328. Код может немного отличаться для других микроконтроллеров. В этом случае вы можете прочитать технические описания для обоих микроконтроллеров и решить, что вам нужно изменить.

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

например: byte my_data[10] = {0,0,0,0,0,0,0,0,0,0};

Далее вам нужно инициализировать timer1 в основном цикле

TCCR1A=0;
TCCR1B=0;

Для отправки данных со скоростью 2000 бит в секунду можно использовать следующие настройки

ICR1=124; // Счетчик таймера сбрасывается на 0 после достижения 124
OCR1A=125;
OCR1B=125; // другие регистры совпадения счетчика должны быть выше ICR1
TCCR1A=B00000010; // режим генерации сигнала 14, быстрая ШИМ, ICR1=top
TCCR1B=B00011011; // масштабирование часов 64, 1 отсчет = 4 мкс. 125*4 мкс = 500 мкс
TIFR1 |= (_BV(ICF1) | _BV(OCF1A) | _BV(OCF1B)); // Очистить флаги совпадений

Это установит бит регистра совпадения таймера 1 ICF1 каждые 500 мкс. Есть несколько способов использования таймера для отправки данных. Я бы предпочел опрос флага совпадения. Точность синхронизации примерно такая же, как вы можете получить — пара тактов:

loop_until_bit_is_set(TIFR1,ICF1); // ждем, пока TCNT1=ICR1
TIFR1 |= (_BV(ICF1) | _BV(OCF1A) | _BV(OCF1B)); // очистить флаги совпадений
// переключить цифровой выход на высокий или низкий уровень в зависимости от бита данных, который необходимо отправить

Один из способов — запустить цикл конечного автомата следующим образом:

unsigned int state;

void setup(){
state=0;
}

void loop(){
switch (state){
case 0: // бездействие или получение данных для отправки
TCCR1A=0;
TCCR1B=0;
// вставьте здесь другой код
// условная функция, при получении данных сообщения установить состояние=1;
break;

case 1: // настройка таймера
ICR1=124; // Счетчик таймера сбрасывается на 0 после достижения 124
OCR1A=125;
OCR1B=125; // другие регистры совпадения счетчика должны быть выше ICR1
TCCR1A=B00000010; // режим генерации сигнала 14, быстрая ШИМ, ICR1=top
TCCR1B=B00011011; // масштабирование часов 64, 1 отсчет = 4 мкс. 125*4 мкс = 500 мкс
TIFR1 |= (_BV(ICF1) | _BV(OCF1A) | _BV(OCF1B)); // Очистить флаги совпадений
state=2;
break;

case 2:
loop_until_bit_is_set(TIFR1,ICF1); // ждем, пока TCNT1==ICR1
TIFR1 |= (_BV(ICF1) | _BV(OCF1A) | _BV(OCF1B)); // очистить флаги совпадений
// переключить цифровой выход на высокий или низкий уровень в зависимости от бита данных, который необходимо отправить

// добавляем условную функцию, чтобы после завершения отправки данных выполнить сброс до состояния=0;
break;

}
}

Как всегда, пожалуйста, поставьте лайк, если вы найдете это полезным.

,