Проблема с захватом счетчика

Monster
Offline
Зарегистрирован: 18.06.2012

Помогите разобраться с захватом счетчика и измерением времени с точностью до 1 мкс.

volatile unsigned long time, OVFcount;
volatile byte Flag;

void setup() 
{  
  pinMode(7,OUTPUT);
  pinMode(8,INPUT);
  digitalWrite(7,HIGH);
  Flag = LOW;
  time = 15536;
  OVFcount = 0;
  TCCR1A  = 0;
  TCCR1B  = 0;
  TIMSK1  = 0;
  TCCR1B |= ((1 << WGM13) | (1 << WGM12) | (1 << CS11) | (1 << ICNC1) | (1 << ICES1));   // режим = 12 (CTC+модуль захвата) / делитель = 8 / подавление шума / работа по фронту  
  TIMSK1 |= ((1 << ICIE1) | (1 << TOIE1));                                               // разрешение прерывания захвата и переполнения 
  TCNT1   = 15536; 
  Serial.begin(115200);
}  

// обработчик прерывания переполнения
ISR(TIMER1_OVF_vect) 
{    
  OVFcount++;                                                                            // счетчик переполнений (инкремент раз в 25 мс)
  TCNT1 = 15536;                                                                         // "ноль" счетчика для того, чтобы прерывание по переполнению срабатывало раз в 25 мс
}

// обработчик прерывания захвата
ISR(TIMER1_CAPT_vect) 
{  
  time = ICR1;                                                                           // собственно копипаст значения счетчика из TCNT1 в ICR1, а затем уже в time 
  Flag = HIGH;                                                                           // флаг необходимости расчета времени в мкс и его вывода 
}


void loop() 
{
  if (Flag == HIGH)
  {    
    Flag = LOW;
    time = ((time-15536)/2);                                                             // кол-во микросекунд в счетчике - от 0 до 24999 мкс (деление на 2 из-за периода между тиками = 0.5 мкс)
    time = time + OVFcount*25000;                                                        // общее время с учетом кол-ва переполнений (раз в 25 мс)
    Serial.println(time);    
  }
}

В итоге печатается какая-то лажа, а не то что надо.

Еще хочу понять в чем отличие захвата от простого чтения значения регистра TCNT1?

Спасибо!

 

step962
Offline
Зарегистрирован: 23.05.2011

Monster пишет:

Помогите разобраться с захватом счетчика и измерением времени с точностью до 1 мкс.

[...]

В итоге печатается какая-то лажа, а не то что надо.

Как следует из комментариев, вы выбрали режим 12 работы Т/С1. Согласно даташиту, в этом режиме счетчик изменяется от нуля до значения, записанного в регистре ICR1, после чего сбрасывается ().

Событие захвата приводит к автоматической записи текущего состояния счетчика в регистр ICR1. Стало быть, очередной сброс счетчика произойдет сразу же после события захвата (при следующем инкременте регистр TCNT1 будет содержать значение, большее, чем сохранено в регистре ICR1), а не через 25 мкс (или сколько там осталось до очередного переполнения).

Цитата:

Еще хочу понять в чем отличие захвата от простого чтения значения регистра TCNT1?

Спасибо!

Прочитать значение счетчика TCNT1 вы можете в любой момент. Захват же (и чтение регистра TCNT1 в регистр ICR1) происходит при возникновении события захвата, т.е. в зависимости от выбранного триггера - при изменении логического уровня пина ICP1 (PB0), либо при изменении логического уровня на выходе компаратора AC0 (при изменении соотношения уровней на входах AIN0/AIN1 (PD7/PD6)). Эти события случаются в произвольные моменты времени, соответствующие участки кода (процедуры обработки прерываний) выполняются асинхронно - независимо от вашего (или программы) желания/состояния.

Monster
Offline
Зарегистрирован: 18.06.2012

step962 пишет:
(при следующем инкременте регистр TCNT1 будет содержать значение, большее, чем сохранено в регистре ICR1)
Почему?  После выполнения ICR1 = TCNT1 по логике идет TCNT = 0, и как после следующего инкремента в TCNT1 может оказаться значение большее, чем в ICR1?

step962 пишет:
Прочитать значение счетчика TCNT1 вы можете в любой момент. Захват же (и чтение регистра TCNT1 в регистр ICR1) происходит при возникновении события захвата, т.е. в зависимости от выбранного триггера - при изменении логического уровня пина ICP1 (PB0)
В чем отличие от способа организации чтения значения счетчика (TCNT1) по прерыванию INT0 на соответствующей ноге по тому же фронту, как это сделано в коде примера?

Верно ли я понимаю, что прерывание захвата после строки

time = ICR1;

надо дополнить строкой

ICR1 = 65535;

???

step962
Offline
Зарегистрирован: 23.05.2011

Monster пишет:

Почему?  После выполнения ICR1 = TCNT1 по логике идет TCNT = 0, и как после следующего инкремента в TCNT1 может оказаться значение большее, чем в ICR1?

Действовать следует не по логике, а по даташиту. В нем для режима(ов) CTC указано два условия очистки счетчика: достижение значения, записанного в регистре OCR1A (режим 4), либо в регистре ICR1 (режим 12 - ваш случай).

Цитата:

В чем отличие от способа организации чтения значения счетчика (TCNT1) по прерыванию INT0 на соответствующей ноге по тому же фронту, как это сделано в коде примера?

при использовании модуля захвата вам, собственно, никуда не надо торопиться. Главное - успеть прочитать захваченное в регистр ICR1 значение до наступления следующего фронта. В случае INT0/INT1 каждый такт промедления со считыванием значения счетчика будет искажать получаемое значение. То есть, при использовании модуля захвата значительную часть работы за вас делает железо. Практически как в UART, только буфер очень короткий - всего на одно значение.

Цитата:

Верно ли я понимаю, что прерывание захвата после строки

time = ICR1;

надо дополнить строкой

ICR1 = 65535;

Можно, конечно, и так изгаляться, но почему бы вместо режима 12 не взять режим 4 и вместо ручной настройки T/C1 на счет в диапазоне от 15536 до 65535 не загрузить в OCR1A значение 50000 и забыть о подбрасывании дров в костер - прерывания будут автоматически щелкать те же самые каждые 25 мс? Единственное - вместо прерывания по переполнению придется использовать прерывание по сравнению канала A (TIMER1_COMPA_vect).

Monster
Offline
Зарегистрирован: 18.06.2012

Да просто хотелось понять в чем преимущества в точности расчета периода используя алгоритм захвата, а не просто чтения счетчика когда это надо. :)

step962
Offline
Зарегистрирован: 23.05.2011

 Ну так, чтобы прочитать счетчик "когда это надо" необходимо решить, "когда это надо", а потом выполнить ряд действий, каждое из которых может потребовать нескольких тактов процессора. При этом в момент чтения может запуститься прерывание более высокого уровня и дополнительно увеличить длительность считывания. Можно успеть все сделать до очередного изменения значения счетчика TCNT1. А можно и не успеть и он щелкнет - может быть, один раз, а может быть и два (три/четыре/пять).
А захват (одновременное изменение/заполнение регистров TCNT1 и ICR1) происходит на аппаратном уровне, без использования ресурсов процессора, умещается в пару тактов (наверное) и на продолжительность обработки ничто не может повлиять - в отличие от чтения счетчика из программы или через механизм прерываний.

Monster
Offline
Зарегистрирован: 18.06.2012

Упростил код до нельзя:

volatile OVFcount;

void setup() 
{  
  pinMode(7,OUTPUT);
  pinMode(8,INPUT);
  digitalWrite(7,HIGH);
  
  OVFcount = 0;
  
  TCNT1  = 0;
  ICR1   = 49999;
  TIMSK1 = B00100001;
  TCCR1A = B00000000;
  TCCR1B = B11011010; 
  
  Serial.begin(9600);
}  

// обработчик прерывания переполнения
ISR(TIMER1_OVF_vect) 
{    
  OVFcount++;
}

// обработчик прерывания захвата
ISR(TIMER1_CAPT_vect) 
{  

}


void loop() 
{
  Serial.println(OVFcount);    
}

Счетчик переполнений OVFcount все равно = 0. :(  Что снова не так?

Предложенный 4 режим (СТС) - не подходит в силу необходимости замера периодов от внешнего датчика с высокой точностью.

step962
Offline
Зарегистрирован: 23.05.2011

Monster пишет:

Счетчик переполнений OVFcount все равно = 0. :(  Что снова не так?


Вы выбрали режим 12 - сброс счетчика при достижении значения, сохраненного в регистре ICR1. Прерывание TIMER1_OVF вызывается при переполнении счетчика, т.е. когда он достигает значения 0xFFFF и переходит через него.
Цитата:

Предложенный 4 режим (СТС) - не подходит в силу необходимости замера периодов от внешнего датчика с высокой точностью.


Почему вы думаете что режим 4 (CTC, сброс счетчика при достижении значения в регистре OCR1A) будет менее точным, чем режим 12 (CTC, сброс счетчика при достижении значения в регистре ICR1)?

Monster
Offline
Зарегистрирован: 18.06.2012

step962 пишет:
Вы выбрали режим 12 - сброс счетчика при достижении значения, сохраненного в регистре ICR1. Прерывание TIMER1_OVF вызывается при переполнении счетчика, т.е. когда он достигает значения 0xFFFF и переходит через него.
Почему 0xFFFF если в ICR1 указано 49999 (12 строка)?

В любом случае счетчик переполнений = 0, т.е. вызовов данного прерывания 0!

step962 пишет:
Почему вы думаете что режим 4 (CTC, сброс счетчика при достижении значения в регистре OCR1A) будет менее точным, чем режим 12 (CTC, сброс счетчика при достижении значения в регистре ICR1)?
Потому что захват в 4 режиме невозможен.  Или возможен?

step962
Offline
Зарегистрирован: 23.05.2011

Monster пишет:

Почему 0xFFFF если в ICR1 указано 49999 (12 строка)?

Потому что вы используете режим 12 - CTC со сбросом счетчика при достижении значения, сохраненного в регистре ICR1. При достижении значения 49999 происходит сброс счетчика TCNT1 и установка флага OCF1A. Что приводит к генерации прерывания TIMER1_COMPA (если это прерывание разрешено). И все начинается по новой - счетчик считает до 49999 и сбрасывается, считает и сбрасывается, считает... И ни одного переполнения.

Цитата:

В любом случае счетчик переполнений = 0, т.е. вызовов данного прерывания 0!

А с чего бы данному прерыванию генерироваться? Ведь, как сказано выше, шансов у счетчика добраться до значения 0xFFFF и вызвать срабатывание прерывания по переполнению - 0 целых и 0 в периоде.

Ловить вам надо не событие переполнения счетчика, а событие достижения им заданного в регистре ICR1 значения.

Цитата:
Потому что захват в 4 режиме невозможен.  Или возможен?

 

Ну так взгляните же в даташит! Или - еще лучше/проще - попробуйте режимом 4 побаловаться! Вдруг получится то, что вам необходимо?

Лично у меня - при чтении соответствующего раздела даташита по диагонали - сложилось впечатление, что модуль захвата срабатывает в любом режиме работы таймера/счетчика. Все что необходимо для этого - поступление соответствующего фронта на выводе ICP или переход компаратора в соответствующее положение. Все это настраивается отдельно от режимов работы.

Записывая при этом текущее содержимое регистра TCNT1 в регистр ICR1 и генеря прерывание TIMER1_CAPT, сообщая, так сказать, основной программе: "кушать подано".