Библиотека 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);
}
}
@Lithimlin, 👍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;
}
}
Как всегда, пожалуйста, поставьте лайк, если вы найдете это полезным.
- Arduino непрерывно считывает значение АЦП с помощью прерывания
- 4-битный счетчик вверх и вниз
- Включить и отключить отдельные прерывания
- Как настроить векторный таймер прерываний сторожевого таймера на Arduino Redboard/Uno?
- Измерить количество циклов и время цифрового входа
- Прерывание сравнения Timer2 не работает должным образом
- Проблема прерывания библиотеки MPU6050 Arduino Jeff Rowberg
- генерировать два сдвинутых по фазе ШИМ-импульса, запускаемых внешним сигналом с частотным разделением, с помощью Arduino uno?