AVR. Учебный Курс. Асинхронный режим таймера

Иногда полезно иметь в системе часы отсчитывающие время в секундах, да еще с высокой точностью. Часто для этих целей применяют специальные микросехмы RTC (Real Time Clock) вроде PCF8583. Вот только это дополнительный корпус, да и стоит она порой как сам МК, хотя можно обойтись и без нее. Тем более, что многие МК имеют встроенный блок RTC. В AVR его правда нет, но там есть асинхронный таймер, служащий полуфабрикатом для изготовления часиков.

Первым делом нам нужен часовой кварц на 32768Герц.

Почему кварц именно 32768Гц и почему его зовут часовым? Да все очень просто — 32768 является степенью двойки. Два в пятнадцатой степени. Поэтому пятнадцати разрядный счетчик, тикающий с частотой 32768 Гц, будет переполняться раз в секунду. Это дает возможность строить часы на обычной логической рассыпухе без каких либо заморочек. А в микроконтроллере AVR организовать часы с секундами можно почти без использования мозга, на рефлексах периферии.

Асинхронный режим таймера
Помните как работают таймеры? Тактовая частота с основного тактового генератора (RC внешняя или внутренняя, внешний кварц или внешний генератор) поступает на предделители, а с выхода предделителей уже щелкает значениями регистра TCNT. Либо сигнал на вход идет с счетного входа Тn и также щелкает регистром TCNT

Структура же Timer/Counter2 немного отличается от остальных — у него нет счетного входа, зато есть возможность задействовать собственный тактовый генератор.

Для этого на выводы TOSC2 и TOSC1 вешается кварцевый резонатор. Низкочастотный, обычно это часовой кварц на 32768Гц. На Pinboard он смонтирован справа от контроллера и подключается перемычками. Причем тактовая частота процессора должна быть выше как минимум в четыре раза. У нас тактовая от внутреннего генератора 8Мгц, так что нас это условие вообще не парит :)

Часовой кварц вешается просто на выводы. Без конденсаторов и каких либо заморочек.

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

Также немаловажным является тот факт, что асинхронный таймер может тикать сам по себе, от часового кварца, ведь тактовая частота процессора ему не нужна, а это значит тактирование ядра контроллера (самое жручее, что у него есть) можно отключить, загнав процессор в спячку, существенно снизив потребление энергии и просыпаясь только по переполнению таймера (1-2 раза в секунду), чтобы записать новые показания времени.

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

Т.е., например, предустановил ты значение TCNT2, таймер на своей 32кгц молотилке его еще даже прожевать не успел, а твой алгоритм уже пробежал и снова туда что то записал — в результате в TCNT2 наверняка попадет мусор. Чтобы этого не случилось запись буфферизируется. Т.е. это ты думаешь, что записал данные в TCNT2, но на самом деле они попадают во временный регистр и в счетный попадут только через три такта медленного генератора.

Также буфферизируется регистры сравнения OCR2 и регистр конфигурации TCCR2

Как узнать данные уже внеслись в таймер или висят в промежуточных ячейках? Да очень просто — по флагам в регистре ASSR. Это биты TCN2UB, OCR2UB и TCR2UB — каждый отвечает за свой регистр. Когда мы, например, записываем значение в TCNT2 то TCNUB становится 1, а как только наше число из промежуточного регистра таки перешло в реальный счетный регистр TCNT2 и начало уже тикать, то этот флаг автоматом сбрасывается.

Таким образом, в асинхронном режиме, при записи в регистры TCNT2, OCR2 и TCCR2 сначала нужно проверять флаги TCN2UB, OCR2UB и TCR2UB и запись проводить только если они равны нулю. Иначе результат может быть непредсказуемым.

Да, еще один важный момент — при переключениях между синхронным и асинхронным режимом значение в счетном регистре TCNT может побиться. Так что для надежности переключаемся так:

  • Запрещаем прерывания от этого таймера
  • Переключаемся в нужный режим (синхронный или асинхронный)
  • Заново настраиваем таймер как нам нужно. Т.е. выставляем предустановку TCNT2 если надо, заново настраиваем TCCR2
  • Если переключаемся в асинхронный режим, то ждем пока все флаги TCN2UB, OCR2UB и TCR2UB будут сброшены. Т.е. настройки применились и готовы к работе.
  • Сбрасываем флаги прерываний таймера/счетчика. Т.к. при всех этих пертурбациях они могут случайно установиться
  • Разрешаем прерывания от этого таймера

Несоблюдение этой последовательности ведет к непредсказуемым и трудно обнаруживаемым глюкам.

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

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

Примеры:
Контроллер использует режим энергосбережения и отключения ядра, а пробуждается по прерываниям от асинхронного таймера. Тут надо учитывать тот факт, что если мы будем изменять значения регистров TCNT2, OCR2 и TCCR2, то уход в спячку нужно делат ТОЛЬКО после того, как флаги TCN2UB, OCR2UB и TCR2UB упадут. Иначе получится такая лажа — асинхронный таймер еще не успел забрать данные из промежуточных регистров (он же медленный, в сотни раз медленней ядра), а ядро уже отрубилось. И ладно бы конфигурация новая не применилась, это ерунда.

Хуже то, что на время модификаций регистров TCNT или OCR блокируется работа блока сравнения, а значит если ядро уснет раньше, то блок сравнения так и не запустится — некому его включить будет. И у нас пропадет прерывание по сравнению. Что черевато тем, что событие мы прошляпим и будем их терять до следующего пробуждения из спячки.
А если контроллер будится прерыванием по сравнению? То он уснет окончательно. Опаньки!
Вот и лови такой глюк потом.

Так что перед уходом в режимы энергосбережения надо обязательно дать асинхронному таймеру прожевать введенные значения (если они были введены) и дождаться обнуления флагов.

Еще один прикол с асинхронным режимом и энергосбережением заключается в том, что подсистема прерываний при выходе из спячки стартует за 1 такт медленного генератора. Так что даже если мы ничего не меняли, то обратно в спячку сваливаться нельзя — не проснемся, т.к. прерывания не успеют запуститься.

Так что выход из спячки и засыпание по прерыванию асинхронного таймера должно быть в таком виде:

  • Проснулись
  • Что то сделали нужное
  • Заснули

И длительность операции между Проснулись и Заснули НЕ ДОЛЖНА БЫТЬ МЕНЬШЕ чем один тик асинхронного таймера. Иначе анабиоз будет вечным. Можешь delay поставить, а можешь сделать как даташит советует:

  • Проснулись
  • Что то сделали нужное
  • Ради прикола записали что то в любой из буфферизиуемых регистров. Например, в TCNT было 1, а мы еще раз 1 записали. Ничего не изменилось, но произошла запись, поднялся флаг TCN2UB который продержится гарантированно три такта медленного генератора.
  • Подождали пока флаг упадет
  • Уснули.

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

Ну и последний, но важный, момент — после подачи питания, или выхода из глубокой спячки, с отключением не только ядра, а вообще всей периферии, пользоваться медленным генератором настоятельно рекомендуется не раньше чем через 1 секунду (не миллисекунду, а целая секунда!). Иначе генератор может еще быть нестабильным и в регистрах будет еще каша и мусор.

И, в завершение статьи, небольшой примерчик. Запуск асинхронного таймера на Atmega16 (Как полигон используется плата Pinboard)

Проект типовой, на базе диспетчера, одно лишь отличие — диспечтер переброшен на таймер0, чтобы освободить таймер2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main(void)
{
InitAll();			// Инициализируем периферию
InitRTOS();			// Инициализируем ядро
RunRTOS();			// Старт ядра. 
 
UDR = 'R';			// Маркер старта, для отладки
 
SetTimerTask(InitASS_Timer,1000);	// Так как таймер в асинхронном режиме 				// запускается медленно, то делаем
					// Выдержку для запуска инициализации таймера. 
 
while(1) 			// Главный цикл диспетчера
{
wdt_reset();			// Сброс собачьего таймера
TaskManager();			// Вызов диспетчера
}
return 0;
}

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

При последующих входах проверяются флаговые биты готовности регистров таймера. Если они все по нулям, то мы на всякий случай зануляем флаги прерывания таймера, чтобы не было глюков и ложных срабатываний, а потом разрешаем нужное нам прерывание. И выходим.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void InitASS_Timer(void)
{
if(ASSR & (1<<AS2))					//Если это второй вход то
	{	
	if (ASSR & (1<<TCN2UB | 1<<OCR2UB | TCR2UB) ) 	// проверяем есть ли хоть один бит флаговый
		{
		SetTask(InitASS_Timer);			// Если есть, то отправляем на повторный цикл ожидания
		}
	else						// Если все чисто, то можно запускать прерывания
		{
		TIFR  |= 1<<OCF2 | 1<< TOV2;		// Сбрасываем флаги прерываний, на всякий случай.
		TIMSK |= 1<< TOIE2;			// Разрешаем прерывание по переполнению
		return;
		}
	}
 
TIMSK &=~(1<<OCIE2 | 1<< TOIE2);	// Запрещаем прерывания таймера 2
ASSR  = 1<<AS2;				// Включаем асинхронный режим
TCNT2 = 0;
TCCR2 = 5<<CS20; 			// Предделитель на 128 на 32768 даст 256 тиков в секунду
					// Что даст 1 прерывание по переполнению в секунду.		
SetTask(InitASS_Timer);			// Прогоняем через диспетчер, чтобы зайти снова. 
}

А дальше движуха идет уже по прерыванию. Раз в секунду у нас переполняется регистр асинхронного таймера и генерит прерывание. Тут уже делаем что хотим. Я просто переменную увеличивал и выводил в UART порт.

1
2
3
4
5
ISR(TIMER2_OVF_vect)			// Прерырвание по переполнению таймера 2
{
UDR = i;
i++;
}

Можно было сделать переменные содержащие часы:минуты:секунды и щелкать этими переменными со всей их логикой переполнения часов/минут, но мне было лень. И так все понятно.

63 thoughts on “AVR. Учебный Курс. Асинхронный режим таймера”

  1. На часовой кварц лучше тоже кондеры вешать. Лично у меня без них часы не шли, даже с заземленным корпусом кварца. A RTOS на сях тоже ваша самописная? Поделитесь?

    1. Может ты слишком рано пытался его врубить? Ему секунда на раскачку нужна.

      Да, на Сях, самописная. Правда до ртос я его так и не допилил — диспетчер да служба таймеров. Но в целом функциональная вполне себе штука получилась. Конешн поделюсь — качай проект, там все сорцы лежат.

      1. Кондеры внутри скорей всего уже есть. Без кондеров даже обычный тактовый кварц может запуститься, только нестабильно будет работать.

        1. Если не лень и под рукой имеются лучше поставить.
          У меня в качестве тактового генератора часовой кварц упорно не хотел нормально запускать. Либо 2я гармоника либо вообще не запускается.
          После напайки кондеров проблема решилась.

          1. Позволю себе процитировать кусок даташита на 8ую Мегу:
            Timer/Counter Oscillator
            For AVR microcontrollers with Timer/Counter Oscillator pins (TOSC1 and TOSC2), the crystal is connected directly between the pins. By programming the CKOPT Fuse, the user can enable internal capacitors on XTAL1 and XTAL2, thereby removing the need for external capacitors. The Oscillator is optimized for use with a 32.768 kHz watch crystal. Applying an external clock source to TOSC1 is not recommended.
            Note: The Timer/Counter Oscillator uses the same type of crystal oscillator as Low-Frequency Oscillator and the internal capacitors have the same nominal value of 36 pF.
            Короче, кондёры по 36 пик подключаются внутри.

    1. Последнее время больше на Си. Т.к. в основном использую мегу16 и в перспективе это все будет перетаскиваться на арм. Правда вот недавно начал еще один коммерческий проектик — там буду писать на асме, т.к. в маленький проц надо будет запихать ОЧЕНЬ много всего.

      1. Ну и как общие ощущения между языками? Я как 1.5 года пишу на асме. Чета долго на асме пишутся проги. Надоело уже. Зато благодаря асму хорошо понял как устроен проц уже легко с флеш памятью работать там и все такое. Думаю вот си начать изучать, что бы проги быстрее писать. Хочеться да вот только колеться, это ж надо время сперва на это убить с пол годика не меньше. Хотя асм за месяц понял.

        1. Язык как язык. Есть ряд удобностей, есть ряд недостатков (например мне не хватает удобной работы с индексами или индексных переходов). На си получается писать быстрей конечно, но во время отладки я в асмовый код все равно постоянно лазаю :)

    1. Напишу свою историю работы с асинхроннім таймером. Короче написал код как DI HALT рассказал, прошил, подождал секунду и балалайка. Я и так и сяк, и с бубном и без бубна ну не тикает таймер и всё.
      Всё решил случай. Дал прошивку другу он прошил в свою мегу 8 звонит, говорит работает.
      Я в шоке. Говорю как? Друг говорит работает и всё. Ну как в последствии выяснилось что у него питане было 3,3В а у меня 5В. Понизил я питание до 3,3В и вот чудо светодиод замигал. При напряжении питания +5В надо кондёры внешние подключать как для и обычного кварца. Емкость не помню. Ещё особенность которая у меня была таймер спешит на пару десятков секунд в сутки. Ну как по мне это не в какие ворота не лезит.
      Вот у меня дома стоят китийские часы. Просто часы без всяких наворотов только часы и будильник в них есть. Но как замечательно они идут. За 2 года начали спешить на 2 минуты. А есть китайское барахло за пол года на 7 минут а то и больше спешат. Ну это тоже не в какие ворота не влезает. Так вот по этому поводу вопрос а почему одни часы идут нормально а другие спешат или отстают?(Хотя отставания не встречал)

      1. Может голимый кварц? Среди них полно всякого говна. Я на пинборду фирменные гейеровские ставлю, с ними у меня никогда проблем не было. У меня, кстати, питание 5 вольт было включено — тикает как миленький!

        1. DI я честно говоря не знаю. Я перепробовал их штуки 3. Причём каждый раз покупал новые и разные. А по поводу точности хода что скажешь?

          1. Да я как то глобально не засекал время. Вот на этих блестяшках за несколько дней не заметил сколь нибудь заметного ухода. А больше не тестил.

  2. Главное приемущество такой реализации — возможность программной корректировки. На внешних RTC такая функция тоже есть, но не на всех (например в Ds1307 нету :().

    А главный недостаток — если пропадет питание МК, то и часы собьются. Хотя можно организовать резервное питание, а МК будет просыпаться из Power down mode по прерыванию от RTC, чтобы инкрементировать время.

    У меня таким образом были реализованы настенные светодиодные часики на меге8. Автоматическую коррекцию проводил каждые сутки на несколько секунд.

    1. Эту тему на форуме обсасывали.
      Что на RTC, что на контроллере — всё зависит от кварца и кондёров. Без последних будут спешить. Можно поставить подстроечник, можно — программную коррекцию, тут уже у контроллера бОльшие преимущества.

      Мне удалось найти кварц в барахле, убегающий не более 1 секунды за трое суток (без кондёров). Для домашних часов нормальный результат.

        1. Лучше бы этот вопрос раскрыть по подробней, кондеры есть да не у всех! В меге 8 надо включать принудительно «programming the CKOPT Fuse», а в мега48/88 их нет вообще, с м16/32 об этом действительно можно не думать

  3. Давно интересовал вопрос, правда он не относится к данной статье. Например атмел выпустила новый какой нибудь мк, потом оказывается у него есть аппаратные баги типа как у атмеги 8 закорочены между собой ножки vcc и avcc. Почему чип не исправляют? так и штампуют с этим багом миллионами. Им что легче новый чип выпустить чем старый слегка переделать?

    1. Исправляют. Выходят новые ревизии чипов. Скачай свежий даташит на какой нибудь старый контроллер (Ту же мегу16). Там будет в конце раздел Errata, где описаны все баги и приколы. И в какой ревизии они есть.

      А вообще исправить топологию не так просто. Это надо всю производственную линию на новый фотошаблон переводить. Заново все тестировать и проверять.

  4. О! Чета подумал, а слабо к часам прикрутить еще и календарь? А где то я видел на просторах интернета суперский проект на avr реализующий эту идею. Там все было расписано по полочкам.

  5. Уважаемый DI HALT
    как всегда хорошая статья..но..
    Всё же (и не только я об этом упоминаю один) Ваш конёк был всегда в отличие от остальных это ASM (это было круто прежде)
    не так уж много подобного уровня ресурсов с описанием на ассемблере, при чём так грамотно и правильно (литературы и конкретных примеров ,как было у Вас на самом деле не так уж много,как кажется на первый взгляд)
    не такая уж и большая эта прога,если привести параллели кусок того же на Асме
    честно говоря чем больше у вас тут на СИ,которого и так полно везде тем меньше ваш ресурс стал отличается от множества других,теряя свою неповторимость,чем и привлекателен был лично для меня.
    но как говорится,
    -в чужой монастырь со своим уставом не ходят.

    1. Асм нужен исключительно ради понимания процесса работы контроллера. С него надо начать, его надо прочувствовать, научиться в нем думать. Чуять слабые места, тормоза, уметь ловить глюки. Писать критичные к скорости и обьему места.

      Но засиживаться на асме ни в коем случае нельзя.

      Ну и при работе с периферией совершенно нет разницы на каком языке написан пример.

  6. «Ну и при работе с периферией совершенно нет разницы на каком языке написан пример.»
    — А раз нет разницы, зачем платить больше?(с) )))
    Тем более,что Асм, это вопрос «религии», соглашусь что засиживаться не стоит,
    но это ведь раздел — учебный курс.
    Лично я знаю людей,которые зарабатывают этим на жизнь,как и Вы, но пишут всё же на Асме, хотя знакомы и с Бейсиком, и Си. Вопрос предпочтений,
    а потом,когда были не желательны такие вещи,как сэкономить память и не тратить лишнее время на оптимизацию кода? Вы абсолютно правы в том ,что написали выше,и спорить не собираюсь,НО всё же,я предпочитаю читать подобные статьи где есть куски не только на Си и на Асме,одно другому не мешает,ведь так? (а Вам влом?)))
    Скажу больше,не все обладают талантом умения рассказать просто о сложных вещах доступным для понимания языком,как это получается у Вас.(Учился я как раз на Ваших статьях,можно сказать с ноля, хотелось бы и дальше)

    1. Си рулит даже не скоростью написания, а кроссплатформенностью. Т.е. сегодня ты делаешь на AVR, а завтра появился STM8 который втрое дешевле при тех же возможностях. В грамотно написанной на Си проге (с слоем HAL) для смены процессора на принципиально иной тебе надо только один файлик подправить. Это работы на день. На асме придется все переписывать с нуля. Попутно изучая новую систему команд. Поэтому те кто пишет ТОЛЬКО на асме да еще коммерческие проекты они самоубийцы. Исключение — что то мелкое, на мелких контроллерах вроде AVR Tiny. Где надо ужаться в мизерный размер и минимальную цену.

      Курс учебный да, поэтому я даю и Си и асм. Переписывать одно и то же на разных языках считаю бессмысленным. Кто знает Си (хотя бы на уровне прочтения первых глав K&R) и знает асм — легко адаптирует эти примеры на асм. По крайней мере включит мозги и попытается подумать самостоятельно, а не задействует магические клавиши копипаста.

    1. Увеличивает помехозащищенность кварца. Уровни там слабенькие, какая нибудь наводка может его дернуть некстати и будет тебе ложное срабатывание или вообще повиснет все.

  7. Есть у асинхронного таймера такой прикол. Если включено прерывание Т2 по переполнению, и по совпадению, то при наступлению прерывания по совпадению когда ТCNT2=255, прерывание по переполнению как бы «отваливается», т.е. больше не срабатывает вообще. Можно ли это как то побороть?

    1. У него обычно очень жидкие ножки, отломится влет. Ну и полезно его заземлять, колебания на нем слабенькие и лишние помехи ему ни к чему.

  8. Однако…
    Никогда не думал, что таймер2 так может глючить…
    Максимум, что я замечал — так это при касании пальцем выводов кварца он начинает тикать в 2 раза быстрее.

  9. По ходу дела кварц кварцу рознь…
    Было у меня 3 кварца, так один из них реально медленно стартовал, у другого вообще один вывод был закорочен на корпус.
    2 из них при касании выводов пальцами ускорялись в 2 раза, зато третий — останавливался.

    1. Нигде нету. Генерация звука элементарная — гонишь на выход частоту требуемой тональности. Ну, а уж мелодии составлять это не ко мне.

      1. Я так понимаю, нужно оба таймера задействовать? Один на частоту, второй — на длительность?
        Тогда могу поразбираться и в курс для начинающих написать — кусок-то полезен будет во многих прикладухах, от замка до робота…

  10. в прерывании таймера для таймерной службы диспечера написано так:
    sei();
    TimerService();

    для чего разрешаются прерывания в прерывании?

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

      Так можно делать, но надо смотреть в оба.

  11. Немного не пойму, если без использования RTOS, как обрабатывать продолжительные операции? Допустим, по прерыванию асинхронного таймера тикает счётчик часов. И каждые 10 секунд нужно запросить данные с термодатчика. Как сделать так, чтобы секунды отсчитывались непрерывно и независимо от остальной деятельности МК?

      1. Т.е., например, по прерыванию от асинхронного таймера — только щёлкаем секундами. А по прерыванию от обычного таймера, где-нибудь раз в секунду тоже, обновляем экран. И раз в десяток секунд опрашиваем датчик. Получается, delay что в функциях экрана и датчика не станут тормозить работу асинхронного таймера. Я правильно думаю?

        1. Да, у тебя получается распараллеливание на прерываниях. Хороший метод, но не везде прокатывает, т.к. рано или поздно прерывания закончатся, а события которые надо куда то деть и развесить еще нет :)

            1. как вариант использовать метод конечных автоматов. http://www.kit-e.ru/articles/circuit/2006_11_164.php вот тут он просто замечательно расписан. Так же в этих статьях расписано как очень просто реализовать таймерную службу и сообщения.

              [code]switch (Status.AssTimerState)
              {
              case ASS_INIT0:
              if (Status.AssTimerChanged)
              {
              Status.AssTimerChanged =0;
              ResetTimer(T_ASS_INIT);
              }
              if (GetTimer(T_ASS_INIT) >= _time_ass_init0)
              {
              Status.AssTimerState = ASS_INIT1;
              Status.AssTimerChanged = 1;
              }
              break;

              case ASS_INIT1:
              TIMSK &=~(1<<OCIE2 | 1<< TOIE2); // Запрещаем прерывания таймера 2
              ASSR = 1<<AS2; // Включаем асинхронный режим
              TCNT2 = 0;
              TCCR2 = 5<= _time_ass_init)
              {
              Status.AssTimerState = ASS_INIT3;
              Status.AssTimerChanged = 1;
              }
              break;

              case ASS_INIT3:
              if (ASSR & (1<<TCN2UB | 1<<OCR2UB | TCR2UB) ) // проверяем есть ли хоть один бит флаговый
              break; // Если есть, то отправляем на повторный цикл ожидания
              else // Если все чисто, то можно запускать прерывания
              {
              TIFR |= 1<<OCF2 | 1<< TOV2; // Сбрасываем флаги прерываний, на всякий случай.
              TIMSK |= 1<< TOIE2; // Разрешаем прерывание по переполнению
              Status.AssTimerState = ASS_WAIT;
              break;
              } [/code]

              1. упс, нечайно отправил раньше времени. Тут инициализация асинхронного таймера для атмеги16, код взять из этой статьи, тока переделан для конечных автоматов.

  12. Здравствуйте пишу код для 8-битного Timer-а Код выложу, Fast pwm mode. Но дело в том что oco/T0 не реагирует не на совпадение и не на сбросе таймера, а также OCR0 не меняется.

    Код

    .include «m8515def.INC»
    .DEF TEMP = R16

    .DSEG ;=============================

    .CSEG ;==============================
    .ORG 0

    RJMP init
    RETI
    RETI
    RETI
    RETI
    RETI
    RETI
    RETI
    RETI
    RETI
    RETI
    RETI
    RETI
    RETI
    RJMP TIM_PRER ; TIMER0
    RETI
    RETI

    TIM_PRER:

    IN TEMP, OCR0
    CPI TEMP, $FF ; если равно OCR0 = $FF сброс
    BREQ GAAN
    INC TEMP
    RJMP BOLO
    GAAN: LDI TEMP, $F5
    BOLO:
    OUT OCR0, TEMP

    RETI ; —— END Timer Interrupt———————

    init: LDI TEMP, HIGH(RAMEND)
    ;OUT SPH, TEMP
    ;LDI TEMP, LOW(RAMEND)
    ;OUT SPL, TEMP
    ; END STECK

    LDI TEMP, 0b00000001
    OUT DDRB, TEMP

    LDI TEMP, 0x00
    OUT PORTB, TEMP
    ; ENT PORTS

    LDI TEMP, $80
    OUT ACSR, TEMP
    ; END COMPARATOR

    LDI TEMP, $F5
    OUT OCR0, TEMP

    LDI TEMP, 0b01101100
    OUT TCCR0, TEMP

    LDI TEMP, $01
    OUT TIMSK, TEMP

    ; END INITIALIZATION TIMER

    SEI
    ; MAIN PROCEDURE

    MAIN:
    RJMP MAIN

    .ESEG ;=================================
    Можете объяснить почему не работает PINB как PWM? спасибо.

  13. можете подсказать, почему может «не тикать» Timer 2 + кварц 32768

    происходит одно прерывание по переполнению, потом Timer 2 «встает». повторно, прерывание по переполнению Timer 2 можно вызвать, если пальцем коснуться…

    codevision 3.12, Atmega 16, DIP корпус, системный кварц 16 МГц, на выводах TOSC1, TOSC2 подключен часовой кварц ( с материнской платы компа). Часовые кварцы менял. Расстояние до выводов кварца около 5 мм.

    TIMSK=(0<<OCIE2) | (1<<TOIE2) ;
    Иннициализация таймера 2
    void tmr2init32k ()
    {
    TIMSK=(0<<OCIE2) | (0<<TOIE2) ; // Запрещаем прерывания таймера 2
    // Переводим Таймер 2 в асинхронный режим (тактирование от часового кварцевого резонатора).
    ASSR = 1<<AS2;
    TCNT2 = 0x00;
    TCCR2 = 0x05; //Устанавливаем коэффициент деления равным 128.
    OCR2 = 0x00;
    // Ждем готовности таймера.
    while (ASSR & (1<<TCN2UB | 1<<OCR2UB | TCR2UB) );
    // Разрешаем прерывание от Таймера 2.
    TIFR |= 1<<OCF2 | 1<< TOV2; // Сбрасываем флаги прерываний, на всякий случай.
    TIMSK=(1<<TOIE2) ; // Разрешаем прерывание по переполнению
    }

    пробовал иннициализировать Timer 2 по вашей процедуре, результат аналогичный : один тик и встает

    void InitASS_Timer(void)
    {
    povtor2:
    if(ASSR & (1<<AS2)) //Если это второй+ вход то
    {
    if (ASSR & (1<<TCN2UB | 1<<OCR2UB | TCR2UB) ) // проверяем есть ли хоть один бит флаговый
    {
    goto povtor2; // Если есть, то отправляем на повторный цикл ожидания
    }
    else // Если все чисто, то можно запускать прерывания
    {
    TIFR |= 1<<OCF2 | 1<< TOV2; // Сбрасываем флаги прерываний, на всякий случай.
    TIMSK |= 1<< TOIE2; // Разрешаем прерывание по переполнению
    return; //выход из процедуры ?
    }
    }
    TIMSK &=~(1<<OCIE2 | 1<< TOIE2); // Запрещаем прерывания таймера 2
    ASSR = 1<<AS2;
    TCNT2 = 0;
    TCCR2 = 5<<CS20; /
    goto povtor2;
    }

    1. > | TCR2UB) ) // проверяем есть ли хоть один бит флаговый
      это не ошибка у вас?
      может быть | 1<<TCR2UB))

Добавить комментарий

Ваш e-mail не будет опубликован.

Перед отправкой формы:
Human test by Not Captcha