Проблемы при измерение периода сигнала таймером

Обсуждаем контроллеры компании Atmel.
Ответить
Родился
Сообщения: 17
Зарегистрирован: Ср ноя 29, 2017 00:14:49

Сообщение Orel007127 »

Здравствуйте. МК ATmega8A. Имеется датчик (TCS3200). На выход датчик выдает меандр с разной частотой в зависимости от показаний. Изначально решил измерять один период с помощью режима захвата. Сначала устанавливаю свой флаг в единицу, разрешаю прерывания и в бесконечном цикле жду пока флаг не станет нулем. В прерывание по захвату при первом переходе от лог.0 до лог.1 я сохраняю значение регистра ICR1, а при втором переходе уже считаю разность между сигналами.

Код: Выделить всё

ISR(TIMER1_OVF_vect)
{
	Ovr++;
}

ISR(TIMER1_CAPT_vect)
{
	if (Count==0)
	{
		Time1L = ICR1L; // Сохраняю регистр захвата
		Time1H = ICR1H;
		Count++; // Увеличиваю число прерываний по захвату
	} else
	{
		Time2L = ICR1L; // Сохраняю регистр захвата
		Time2H = ICR1H;
		TIMSK &= ~(1<<TICIE1); // Запрещаю прерывание по захвату
		TIMSK &= ~(1<<TOIE1); // Запрещаю по переполнению
		Rez = ((Time2H<<8)+Time2L)-((Time1H<<8)+Time1L)+(Ovr*0xFFFF); // Подсчет длительности сигнала в тактах таймера
		CaptFlag = 0;
	}
}

long int TCS3200Start(char color)
{
	Count = 0; // Обнуление счетчика прерываний
	Ovr = 0; // Обнуление счетчика переполнений
	CaptFlag = 1; // Ставлю флаг, что начался захват сигнала
	TIMSK |= 1<<TICIE1; // Разрешаю прерывание по захвату
	TIMSK |= 1<<TOIE1; // Прерывание по переполнению
	while (CaptFlag==1) {};
	return Rez; // Возвращаю значение, которое должно было обновится до снятия флага захвата
}

int main(void)
{
	// =======T1======
	TCCR1B |= 1<<ICNC1 | 1<<ICES1 | 0b1<<CS10;
	// ===============
	sei();
	while(1)
	{
		_delay_ms(2000);
		long int buff = TCS3200Start('R');
	}
}
Тут появилось первые проблемы.
Вопрос 1: Если цикл ожидания пустой, то МК из него не выходит. Но после того, как я туда добавил задержку в 1мкс (while (CaptFlag==1) {_delay_us(1);};) все стало нормально работать. Почему так?
Вопрос 2: Изначально я хотел вместе с разрешение на прерывания отключать предделитель, что бы таймер полностью останавливался и заметил, что из-за этого появился разброс в показаниях датчика. Вообще датчик сам шумит немного, но когда я заменяю прошивку МК на ту, что с отключением предделителя, шум этот очень сильно увеличивается. Почему отключение предделителя на это влияет? Можно ли предделители таймера вообще отключать?

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

Код: Выделить всё

ISR(TIMER1_COMPA_vect)
{
	HzFlag = 0;
        // Тут потом добавил отправку регистра TCNT1 по ЮАРТ для отладки
	TIMSK &= ~(1<<TICIE1); // Запрещаю прерывание по захвату
	TIMSK &= ~(1<<OCIE1A); // Запрещаю прерывание по совпадению
}

ISR(TIMER1_CAPT_vect)
{
	Count++;
}

 long int TCS3200Start(char color)
{
	Count = 0; // Обнуление счетчика прерываний
	HzFlag = 1;
	TCNT1 = 0; // Очищаю то, что насчитал таймер
	TIMSK |= 1<<OCIE1A; // Разрешаю прерывание по совпадению
	TIMSK |= 1<<TICIE1; // Разрешаю прерывание по захвату
	while (1) {if (HzFlag==0) {break;} } // Жду когда флаг сбросится
	return Count; // Возвращаю колличество прерываний по захвату
}

int main(void)
{
	// =======T1======
	TCCR1B |= 1<<ICNC1 | 1<<ICES1 | 0b1<<CS10;
	OCR1A = 0x1F40; // При делителе 1 должна получится задержка в 1мс
	// ===============
	sei();
	while(1)
	{
		_delay_ms(1000);
 		long int buff = TCS3200Start('R');
	}
}
В итоге прерываний по совпадению функция возвращала меньше, чем было на самом деле. Стал смотреть в чем дело. Выяснил, что прерывание по совпадению срабатывает по два раза. Первый раз, когда в регистре TCNT1 0x00F2. Второй раз регистр содержит число немного больше, чем нужно, но это видимо из-за задержек при переходе на прерывание и отправку по ЮАРТ.
После того, как я добавил небольшую задержку перед разрешением прерываний по захвату, двойное срабатывание прекратилось. Прерывание наступало на 0x00F2, но данные с МК приходили не каждую секунду, как должны были, а почти через 2 секунды. Из-за чего?
Попробовал убрать эту задержку и закоментировать строку с разрешением прерывание по захвату и МК не смог выйти из цикла при ожидании снятия моего флага. Стоило добавить задержку в 1 мкс прям перед циклом или внутри цикла и МК стал выходить из этого цикла, но прерывание по совпадению так же наступало при 0x00F2.
Вернул изначальны код (второй код в этом сообщении) и вынес запрет прерываний из прерывания по совпадению за цикл ожидания сброса флага. Вместо двойного срабатывания опять просто одно при 0x00F2. Все остальные проблемы осталось такими же.
Вопрос 3: Что не так с кодом? Почему при прерывание в TCNT1 постоянно какое-то 0x00F2?
Реклама
Электрический кот
Аватара пользователя
Сообщения: 1031
Зарегистрирован: Чт июн 20, 2013 00:00:58
Откуда: москва, м.Сходненская

Сообщение Mishany »

таймер захвата используется не правильно!
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

Orel007127, гляньте тут . Там ассемблер, но это неважно.
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Электрический кот
Аватара пользователя
Сообщения: 1031
Зарегистрирован: Чт июн 20, 2013 00:00:58
Откуда: москва, м.Сходненская

Сообщение Mishany »

нашел свой старый проект, там есть исходники и правильная работа с таймером в режиме захвата
ccылка
Реклама
Эиком - электронные компоненты и радиодетали
Родился
Сообщения: 17
Зарегистрирован: Ср ноя 29, 2017 00:14:49

Сообщение Orel007127 »

Первый код вроде хоть как-то справляется. Тяжело ему дается только очень высокая частота сигнала или очень низкая. Ну и может точно немного страдает.
Ладно.
С 0xF2 разобрался. На момент разрешения прерывания по совпадению, флаг этого совпадения (OCF1A ли OCF1B) видимо был установлен и МК сразу же за эти 0xF2 уходил в прерывание. Перед разрешение прерывания добавил TIFR |= 1<<OCF1A; и все стало нормально.
Но вот почему этот флаг установлен был? Когда он успевал? Я же потом сразу запрещал прерывания по совпадению, не может же после этого флаг установиться?
Реклама
akl
Друг Кота
Сообщения: 4450
Зарегистрирован: Пт мар 07, 2008 06:54:43
Откуда: Ижевск

Сообщение akl »

Флаги таймеров устанавливаются независимо от разрешения/запрета прерываний.
Реклама
Родился
Сообщения: 17
Зарегистрирован: Ср ноя 29, 2017 00:14:49

Сообщение Orel007127 »

[uquote="akl",url="/forum/viewtopic.php?p=3449365#p3449365"]Флаги таймеров устанавливаются независимо от разрешения/запрета прерываний.[/uquote]
Т.е. мне всегда перед разрешением какого-либо прерывания у таймера нужно сбросить флаги? Это только к флагам таймеров относится?
akl
Друг Кота
Сообщения: 4450
Зарегистрирован: Пт мар 07, 2008 06:54:43
Откуда: Ижевск

Сообщение akl »

Да, флаги таймеров нужно сбрасывать перед разрешением прерываний. Для другой периферии тоже, чтобы обнулить предисторию.
Типа

Код: Выделить всё

  TIMSK=1<<TICIE1|1<<TOIE1; // Разрешаю прерывание по захвату и переполнению
   TIFR = 1<<ICF1|1<<TOV1; // Сброс флагов
Грызет канифоль
Сообщения: 289
Зарегистрирован: Чт ноя 06, 2014 13:09:06

Сообщение viiv »

[uquote="Orel007127",url="/forum/viewtopic.php?p=3449380#p3449380"]Т.е. мне всегда перед разрешением какого-либо прерывания у таймера нужно сбросить флаги? Это только к флагам таймеров относится?[/uquote]

У Атмела (теперь микрочипа) очень хорошая документация, там все описано, что и когда устанавливается и сбрасывается. Например для OCF1B:
Bit 3 – OCF1B: Timer/Counter1, Output Compare B Match Flag
This flag is set in the timer clock cycle after the counter (TCNT1) value matches the Output Compare Register B (OCR1B).
Note that a Forced Output Compare (FOC1B) strobe will not set the OCF1B Flag.
OCF1B is automatically cleared when the Output Compare Match B Interrupt Vector is executed. Alternatively, OCF1B can be cleared by writing a logic one to its bit location.
Вольный перевод:
Данный бит устанавливается на следующем цикле, после того как тайиер TCNT1 детектировал совпадение со значением регистра OCR1B.
Внимание! Запись FOC1B не приводит к установке флага OCF1B.
OCF1B сбрасывается: а) когда влетаем в соответствующее прерывание, б) либо при записи "1" в этот разряд

Отсюда следует, что:
1) Разрешение прерывания таймера (OCIE1B) и глобальное разрешение прерываний никак не влияет на установку флага OCF1B.
2) Если OCF1B установлен И перывания разрешены OCIE1B=1 И стоит бит глобального разрешения прерываний (флажок I в регистре SREG), будет вызван обработчик, при влете в который OCF1B будет сброшен.

Все, больше ничего нету - Вы должны позаботиться чтобы все работало правильно (так как Вам надо). Например, если прерывания глобально разрешены и уже стоит OCF1B (т.е. событие "TC1 compare" уже произошло), то при разрешении перрываний (записи "1" в OCIE1B), сразу же будет вызван обработчик. Если Вам этого не нужно, то необходимо сбросить OCF1B, тогда обработчик будет вызывн при следующем событии "TC1 compare".

К чему это я?
I) В документации все рассписано предельно четко.
II) Флаги прерываний НЕ ВСЕГДА надо сбрасывать перед разрешением прерываний. Если Вы не хотите пропустить событие, которое уже произошло к моменту разрешения прерываний (такое иногда нужно бывает), то сбрасывать не надо. Если Вым нужно "очистить историю" (события в прошлом не интересуют) и "ловить" следующее событие, то да - нужно сбросить.

[uquote="akl",url="/forum/viewtopic.php?p=3449407#p3449407"]Да, флаги таймеров нужно сбрасывать перед разрешением прерываний. Для другой периферии тоже, чтобы обнулить предисторию.
Типа

Код: Выделить всё

  TIMSK=1<<TICIE1|1<<TOIE1; // Разрешаю прерывание по захвату и переполнению
   TIFR = 1<<ICF1|1<<TOV1; // Сброс флагов
[/uquote]

:)) Словави сказали "флаги таймеров нужно сбрасывать перед разрешением прерываний", в примере сбросили ПОСЛЕ :)) .

Код: Выделить всё

   /* Если здесь флаги прерываний установлены, то после разрешения прерываний БУДУТ ВЫЗВАНЫ СООТВЕТСТВУЮЩИЕ ОБРАБОТЧИКИ */
   TIMSK=1<<TICIE1|1<<TOIE1; // Разрешаю прерывание по захвату и переполнению
   /* А СЮДА МЫ ПОПАДЕМ ПОСЛЕ ТОГО КАК ОТРАБОТАЮТ ОБРАБОТЧИКИ */
   TIFR = 1<<ICF1|1<<TOV1; // Сброс флагов
akl
Друг Кота
Сообщения: 4450
Зарегистрирован: Пт мар 07, 2008 06:54:43
Откуда: Ижевск

Сообщение akl »

Не забывайте, что манипуляции с разрешением/запретом прерываний проводятся при CLI (или в обработчике, когда оный сброшен). После смены режима прерываний флаги должны быть сброшены и затем следует команда SEI.
Грызет канифоль
Сообщения: 289
Зарегистрирован: Чт ноя 06, 2014 13:09:06

Сообщение viiv »

[uquote="akl",url="/forum/viewtopic.php?p=3449479#p3449479"]Не забывайте, что манипуляции с разрешением/запретом прерываний проводятся при CLI (или в обработчике, когда оный сброшен). После смены режима прерываний флаги должны быть сброшены и затем следует команда SEI.[/uquote]

Ну я же со смайликами. Но, лучше сделать как Вы описали словами :-)
Ответить

Вернуться в «AVR»