AVR. Учебный курс. Таймеры

Распечатать

С счетчиком итераций главного цикла мы разобрались и выяснили, что для точных временных отсчетов он не годится совершенно — выдержка плавает, да и считать ее сложно. Что делать?

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

И такой счетчик есть, даже не один — это периферийные таймеры. В AVR их может быть несколько штук да еще с разной разрядностью. В ATmega16 три, в ATmega128 четыре. А в новых МК серии AVR может даже еще больше, не узнавал.

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

Что умееют таймеры

  • Тикать с разной скоростью, подсчитывая время
  • Считать входящие извне импульсы (режим счетчика)
  • Тикать от внешнего кварца на 32768гц
  • Генерировать несколько видов ШИМ сигнала
  • Выдавать прерывания (по полудесятку разных событий) и устанавливать флаги

Разные таймеры имеют разную функциональность и разную разрядность. Это подробней смотреть в даташите.

Источник тиков таймера
Таймер/Счетчик (далее буду звать его Т/С) считает либо тактовые импульсы от встроенного тактового генератора, либо со счетного входа.

Погляди внимательно на распиновку ног ATmega16, видишь там ножки T1 и T0?

Так вот это и есть счетные входы Timer 0 и Timer 1. При соответствующей настройке Т/С будет считать либо передний (перепад с 0-1), либо задний (перепад 1-0) фронт импульсов, пришедших на эти входы.

Главное, чтобы частота входящих импульсов не превышала тактовую частоту процессора, иначе он не успеет обработать импульсы.

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

Зачем это вообще надо? Да хотя бы организовать часы реального времени. Повесил на них часовой кварц на 32768 Гц да считай время — за секунду произойдет 128 переполнений (т.к. Т/С2 восьми разрядный). Так что одно переполнение это 1/128 секунды. Причем на время обработки прерывания по переполнению таймер не останавливается, он также продолжает считать. Так что часы сделать плевое дело!

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

То есть еще до попадания в счетный регистр частота импульсов будет делиться. Делить можно на 8, 32, 64, 128, 256, 1024. Так что если повесишь на Т/С2 часовой кварц, да пропустишь через предделитель на 128, то таймер у тебя будет тикать со скоростью один тик в секунду.

Удобно! Также удобно юзать предделитель когда надо просто получить большой интервал, а единственный источник тиков это тактовый генератор процессора на 8Мгц, считать эти мегагерцы задолбаешься, а вот если пропустить через предделитель, на 1024 то все уже куда радужней.

Но тут есть одна особенность, дело в том, что если мы запустим Т/С с каким нибудь зверским предделителем, например на 1024, то первый тик на счетный регистр придет не обязательно через 1024 импульса.

Это зависит от того в каком состоянии находился предделитель, а вдруг он к моменту нашего включения уже досчитал почти до 1024? Значит тик будет сразу же. Предделитель работает все время, вне зависимости от того включен таймер или нет.

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

Например первый таймер работает на выводе 1:64, а второй на выводе 1:1024 предделителя. У второго почти дотикало в предделителе до 1024 и вот вот должен быть тик таймера, но тут ты взял и сбросил предделитель, чтобы запустить первый таймер точно с нуля. Что произойдет? Правильно, у второго делилка тут же скинется в 0 (предделитель то единый, регистр у него один) и второму таймеру придется ждать еще 1024 такта, чтобы получить таки вожделенный импульс!

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

Для сброса предделителей достаточно записать бит PSR10 в регистре SFIOR. Бит PSR10 будет сброшен автоматически на следующем такте.

Счетный регистр
Весь результат мучений, описанных выше, накапливается в счетном регистре TCNTх, где вместо х номер таймера. он может быть как восьмиразрядным, так и шестнадцати разрядным, в таком случае он состоит из двух регистров TCNTxH и TCNTxL — старший и младший байты соответственно.

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

А дело все в чем — таймер считает независимо от процессора, поэтому мы можем положить вначале один байт, он начнет считаться, потом второй, и начнется пересчет уже с учетом второго байта.

Чувствуете лажу? Вот! Таймер точное устройство, поэтому грузить его счетные регистры надо одновременно! Но как? А инженеры из Atmel решили проблему просто:
Запись в старший регистр (TCNTxH) ведется вначале в регистр TEMP. Этот регистр чисто служебный, и нам никак недоступен.

Что в итоге получается: Записываем старший байт в регистр TEMP (для нас это один хрен TCNTxH), а затем записываем младший байт. В этот момент, в реальный TCNTxH, заносится ранее записанное нами значение. То есть два байта, старший и младший, записываются одновременно! Менять порядок нельзя! Только так

Выглядит это так:

1
2
3
4
	CLI 			; Запрещаем прерывания, в обязательном порядке!
	OUT	TCNT1H,R16	; Старшей байт записался вначале в TEMP
	OUT	TCNT1L,R17	; А теперь записалось и в старший и младший!
	SEI 			; Разрешаем прерывания

Зачем запрещать прерывания? Да чтобы после записи первого байта, прога случайно не умчалась не прерывание, а там кто нибудь наш таймер не изнасиловал. Тогда в его регистрах будет не то что мы послали тут (или в прерывании), а черти что. Вот и попробуй потом такую багу отловить! А ведь она может вылезти в самый неподходящий момент, да хрен поймаешь, ведь прерывание это почти случайная величина. Так что такие моменты надо просекать сразу же.

Читается все также, только в обратном порядке. Сначала младший байт (при этом старший пихается в TEMP), потом старший. Это гарантирует то, что мы считаем именно тот байт который был на данный момент в счетном регистре, а не тот который у нас натикал пока мы выковыривали его побайтно из счетного регистра.

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

Итак, главным регистром является TCCRx
Для Т/С0 и Т/С2 это TCCR0 и TCCR2 соответственно, а для Т/С1 это TCCR1B

Нас пока интересуют только первые три бита этого регистра:
CSx2.. CSx0, вместо х подставляется номер таймера.
Они отвечают за установку предделителя и источник тактового сигнала.

У разных таймеров немного по разному, поэтому опишу биты CS02..CS00 только для таймера 0

  • 000 — таймер остановлен
  • 001 — предделитель равен 1, то есть выключен. таймер считает тактовые импульсы
  • 010 — предделитель равен 8, тактовая частота делится на 8
  • 011 — предделитель равен 64, тактовая частота делится на 64
  • 100 — предделитель равен 256, тактовая частота делится на 256
  • 101 — предделитель равен 1024, тактовая частота делится на 1024
  • 110 — тактовые импульсы идут от ножки Т0 на переходе с 1 на 0
  • 111 — тактовые импульсы идут от ножки Т0 на переходе с 0 на 1

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

За прерывания от таймеров отвечают регистры TIMSК, TIFR. А у более крутых AVR, таких как ATMega128, есть еще ETIFR и ETIMSK — своего рода продолжение, так как таймеров там поболее будет.

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

На данный момент нас тут интересуют только прерывания по переполнению. За них отвечают биты

  • TOIE0 — разрешение на прерывание по переполнению таймера 0
  • TOIE1 — разрешение на прерывание по переполнению таймера 1
  • TOIE2 — разрешение на прерывание по переполнению таймера 2

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

Регистр TIFR это непосредственно флаговый регистр. Когда какое то прерывание срабатывает, то выскакивает там флаг, что у нас есть прерывание. Этот флаг сбрасывается аппаратно когда программа уходит по вектору. Если прерывания запрещены, то флаг так и будет стоять до тех пор пока прерывания не разрешат и программа не уйдет на прерывание.

Чтобы этого не произошло флаг можно сбросить вручную. Для этого в регистре TIFR в него нужно записать 1!

А теперь похимичим
Ну перекроим программу на работу с таймером. Введем программный таймер. Шарманка так и останется, пускай тикает. А мы добавим вторую переменную, тоже на четыре байта:

1
2
3
4
; RAM ========================================================
		.DSEG
CCNT:	.byte	4
TCNT:	.byte	4

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

Делаем RJMP на обработчик с вектора.

1
2
3
4
5
6
         .ORG $010
         RETI			; (TIMER1 OVF) Timer/Counter1 Overflow
         .ORG $012
         RJMP	Timer0_OV	; (TIMER0 OVF) Timer/Counter0 Overflow
         .ORG $014
         RETI			; (SPI,STC) Serial Transfer Complete

Добавим обработчик прерывания по переполнению таймера 0, в секцию Interrupt. Так как наш тикающий макрос активно работает с регистрами и портит флаги, то надо это дело все сохранить в стеке сначала:

Кстати, давайте создадим еще один макрос, пихающий в стек флаговый регистр SREG и второй — достающий его оттуда.

1
2
3
4
5
6
7
8
9
10
11
12
		.MACRO PUSHF
		PUSH	R16
		IN	R16,SREG
		PUSH	R16
		.ENDM
 
 
		.MACRO POPF
		POP	R16
		OUT	SREG,R16
		POP	R16
		.ENDM

Как побочный эффект он еще сохраняет и R16, помним об этом :)

1
2
3
4
5
6
7
8
9
10
11
12
13
Timer0_OV:	PUSHF
		PUSH	R17
		PUSH	R18
		PUSH	R19
 
		INCM	TCNT
 
		POP	R19
		POP	R18
		POP	R17
		POPF
 
		RETI

Теперь инициализация таймера. Добавь ее в секцию инита локальной периферии (Internal Hardware Init).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
; Internal Hardware Init  ======================================
	SETB	DDRD,4,R16		; DDRD.4 = 1
	SETB	DDRD,5,R16		; DDRD.5 = 1
	SETB	DDRD,7,R16		; DDRD.7 = 1
 
	SETB	PORTD,6,R16		; Вывод PD6 на вход с подтягом
	CLRB	DDRD,6,R16		; Чтобы считать кнопку
 
	SETB	TIMSK,TOIE0,R16 	; Разрешаем прерывание таймера
 
	OUTI	TCCR0,1<<CS00		; Запускаем таймер. Предделитель=1
					; Т.е. тикаем с тактовой частотой.
 
	SEI				; Разрешаем глобальные прерывания
; End Internal Hardware Init ===================================

Осталось переписать наш блок сравнения и пересчитать число. Теперь все просто, один тик один такт. Без всяких заморочек с разной длиной кода. Для одной секунды на 8Мгц должно быть сделано 8 миллионов тиков. В хексах это 7A 12 00 с учетом, что младший байт у нас TCNT0, то на наш счетчик остается 7А 12 ну и еще старшие два байта 00 00, их можно не проверять. Маскировать не нужно, таймер мы потом переустановим все равно.

Одна только проблема — младший байт, тот что в таймере. Он тикает каждый такт и проверить его на соответствие будет почти невозможно. Т.к. малейшее несовпадение и условие сравнение выпадет в NoMatch, а подгадать так, чтобы проверка его значения совпала именно с этим тактом… Проще иголку из стога сена вытащить с первой попытки наугад.

Так что точность и в этом случае ограничена — надо успеть проверить значение до того как оно уйдет из диапазона. В данном случае диапазон будет, для простоты, 255 — величина младшего байта, того, что в таймере.

Тогда наша секунда обеспечивается с точностью 8000 000 плюс минус 256 тактов. Не велика погрешность, всего 0,003%.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
; Main =========================================================
Main:		SBIS	PIND,6		; Если кнопка нажата - переход
		RJMP	BT_Push
 
		SETB	PORTD,5	; Зажгем LED2
		CLRB	PORTD,4	; Погасим LED1
 
Next:		LDS	R16,TCNT	; Грузим числа в регистры
		LDS	R17,TCNT+1
 
		CPI	R16,0x12	; Сравниванем побайтно. Первый байт
		BRCS	NoMatch	; Если меньше -- значит не натикало.
		CPI	R17,0x7A	; Второй байт
		BRCS	NoMatch	; Если меньше -- значит не натикало.
 
; Если совпало то делаем экшн
Match:		INVB	PORTD,7,R16,R17	; Инвертировали LED3	
 
; Теперь надо обнулить счетчик, иначе за эту же итерацию главного цикла
; мы сюда попадем еще не один раз -- таймер то не успеет натикать 255 значений,
; чтобы число в первых двух байтах счетчика изменилось и условие сработает.
; Конечно, можно обойти это доп флажком, но проще сбросить счетчик :) 
 
		CLR	R16			; Нам нужен ноль
 
		CLI 				; Доступ к многобайтной переменной
						; одновременно из прерывания и фона
						; нужен атомарный доступ.  Запрет прерываний
 
		OUTU	TCNT0,R16		; Ноль в счетный регистр таймера
		STS	TCNT,R16		; Ноль в первый байт счетчика в RAM
		STS	TCNT+1,R16		; Ноль в второй байт счетчика в RAM
		STS	TCNT+2,R16		; Ноль в третий байт счетчика в RAM
		STS	TCNT+3,R16		; Ноль в первый байт счетчика в RAM
		SEI 				; Разрешаем прерывания снова.
 
; Не совпало - не делаем :) 
NoMatch:	NOP
 
		INCM	CCNT		; Счетчик циклов по тикает
					; Пускай, хоть и не используется.
		JMP	Main
 
 
BT_Push:	SETB	PORTD,4	; Зажгем LED1
		CLRB	PORTD,5	; Погасим LED2
 
		RJMP	Next
; End Main =====================================================

Скачать проект с этим примером

Вот как это выглядит в работе

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

Можно еще немного оптимизировать процесс проверки. Сделать его более быстрым.

Надо только сделать счет не на повышение, а на понижение. Т.е. загружаем в переменную число и начинаем его декрементировать в прерывании. И там же, в обработчике, проверяем его на ноль. Если ноль, то выставляем в памяти флажок. А наша фоновая программа этот флажок ловит и запускает экшн, попутно переустанавливая выдержку.

А что если надо точней? Ну тут вариант только один — заюзать обработку события прям в обработчике прерывания, а значение в TCNT:TCNT0 каждый раз подстраивать так, чтобы прерывание происходило точно в нужное время.

Запись опубликована в рубрике AVR. Учебный курс с метками , , , . Добавьте в закладки постоянную ссылку.

224 комментария: AVR. Учебный курс. Таймеры

  1. Arhangelsk говорит:

    Головоломка: Не сброшен предделитель? OUT SFIOR,PSR10

  2. kalobyte говорит:

    LDI R16,5 ; 0000 0101 предделитель на 1024
    OUT TCCR1B,R16 ; Запускаем таймер!

    почему нельзя OUT TCCR1B,5 ?
    заче лишняя операция? со времен з80 помню еше
    мой вариант — это реализация прямого доступа к памяти aka dma?

  3. Cluster говорит:

    А я так и не понял почему у меня МК не просыпается из power-save спящего режима, когда таймер работает асинхронно… Из idle отлично просыпается по прерыванию.

  4. Привет. Вопрос по таймерам. Микроконтроллер ATtiny25. По идее следующий код должен запускать счетчик (Timer/Counter1):

    ;разрешаем прерывание по переполнению
    ldi temp, 0b00000100
    out TIMSK, temp
    ;запускаем счетчик без делителя
    ldi temp, 0b00000001
    out TCCR1, temp

    Но ничего не происходит. Счетчик не считает, прерывание не происходит. Смотрю в симуляторе AVR Studio 4.
    Где моя ошибка? Спасибо.

  5. StarXXX говорит:

    Посмотри описание битов CS02..CS00: В даташите написано:
    110 External clock source on T0 pin. Clock on falling edge.
    111 External clock source on T0 pin. Clock on rising edge.
    т.е. 110 — это спад, с 1 до 0, а 111 — это фронт, с 0 до 1
    А у тебя наоборот

  6. Stebanoid говорит:

    что-то я в даташите не нашёл каким образом нужно выставлять CSn2:0 что бы получить деление частоты на 64 в attiny2313. Там написано, что эти биты шевелить надо, а каким именно образом — не написано. :(

    • DI HALT говорит:

      Там табличка должна быть, смотри внимательней шит. Если мне не изменяет память, на делитель 64 значение 3 (11 т.е.) Для 1024 это 5.

      • Stebanoid говорит:

        В даташите на 2313 таблички нет, но по даташиту на другой контроллер увидел, что 000 это остановка, 001 — систем клок, а потом все СУЩЕСТВУЮЩИЕ в контроллере предделители идут подряд. В общем, так всё оно и было.

  7. Xenom0rph говорит:

    А можно ли, как-нибудь вызывать прерывание, по прохождению одной минуты, если да то как? Не могу высчитать нужные значения регистров ))

  8. Xenom0rph говорит:

    все вопрос решен, нельзя )

    • DI HALT говорит:

      Можно можно! Присобачить схемку, которая раз в минуту будет дергать какой либо из INT =)

      или повесить на TOSC1 TOSC2 низкочастотный кварц, который будет дрыгаться со скоростью 17.0666666666666 Гц

  9. Dan говорит:

    Насчёт первого комментария и OUT SFIOR,PSR10 — AVR Studio говорит «invalid register».
    Может быть, правильнее так:

    LDI r16,1<<PSR10
    OUT SFIOR,r16

    ?

  10. Stebanoid говорит:

    Сделал я свои часы (давно уже), но они на секунду в сутки спешат. Для того, что бы не спешили нужно раз в секнду останавливать их на 47 системных тактов, а т.к. у меня предделитель таймера на 64, то это получается меньше одного тика таймера.
    сначала хотел тключить таймер от предделителя, а потом понял, что отключённый предделитель будет продолжать тикать…
    Как мне это сделать красиво?

    PS Есть смутная идея — сразу после прерывания счётчик предделителя должен быть в нуле, поэтому можно отсчитать 47 тактов и сбросить предделитель… Но уж больно это на грязный хак похоже…

  11. Xenom0rph говорит:

    А можно как нибудь сделать, что бы МК не выходил из режима Idle при преполнении таймера?

  12. testicq говорит:

    Юзаю дефайн для меги 8
    Вот никак не пойму, что за инструкция такая: ‘SETB PORTB,6′
    студия упорно твердит что не знает ее!
    Сделал так:
    PUSH R16
    LDI R16,1<<6
    OUT PINB,R16
    POP R16

    Вроде 6й пин выставляет, но не восстанавливает регистр R16 (че за лажа?)

    И есть странность, как только добавляю
    .ORG OVF1addr
    RJMP Timer1OV
    и сам обработчик, состоящий только из RETI происходит срыв стека, МК постоянно генерит прерывание таймера, причем при отладке — прогу ни разу не начинает выполнять, а сразу рвется запускать обработчик прерывания таймера, там RETI взводит флаг I и понеслось — лавина запусков этого обработчика — ну и естественно срыв крышы у стека

    • DI HALT говорит:

      Хы, ошибся чуток. Не SETB (это вообщет команда процессора архитектуры С51), а SBI

    • DI HALT говорит:

      А ты стек инициализировал?

      Переход с нулевого адреса на тело программы (перепрыг через таблицу векторов) сделал?

      • testicq говорит:

        да, я об этом тоже подумал, глядя на дизассеблер в студии… теперь все ясно чего это он вдруг сразу в обработчик заперся, а я грешным делом с чего-то взял, что он сам пропускает таблицу векторов прерываний… Спасибо! Бум пробовать.

        P.S. А стек не инициализировал. Я только начинаю осваиваться, пока не знаю как — поищу сначала, может у тебя на сайте уже описано.

        • DI HALT говорит:

          Открой любой из моих примеров программ. (первую, вторую или третью) Там в самом начале, в исходниках.

          • testicq говорит:

            Угу, тока что разобрал одну твою прогу, состоящую из нескольких файлов и понял про инициализацию стека и таблицу векторов — все просто :-)
            Порадовала забивка RETI в таблицу векторов прерываний — красиво, хотя можно в критических случаях и эту память заюзать, если несколько векторов не использованы.

          • DI HALT говорит:

            Это практически невозможный вариант. Когда размер прошивки такой, что не влазит в ПЗУ, то это наверняка ОЧЕНЬ навороченная программа и прерывания там юзаются все какие только можно.

          • testicq говорит:

            Похоже на то, хотя в x86 asm’e как только я не извращался… :-) Ну, не буду флудить — читаю дальше твои полезнючие статьи. Надеюсь смогу разобраться во всем и сделать свой задуманный мегапроект.

  13. testicq говорит:

    если в восьмиразрядный регистр надо положить число, то нет проблем OUT TCNT0,Rx и никаких гвоздей

    Не — команда OUT только для РВВ с номерами от 0 до 31, а TCNT0 имеет номер 0×32

  14. Andrew говорит:

    А нельзя как-нибудь прочитать текущее значение предделителя? Т.е. задача примерно такая: AtMega8, типа частотомер, 16-битный таймер задает интервал, 8-битный — считает клоку. 1-го бита — на весь диапазон мало, вот если поставить предделитель 256 и по прерыванию получить все 16 бит(старший байт — в счетчике, младший — в предделителе).
    Другие варианты есть, но хочется как попроще.

  15. Cfyzzz говорит:

    Подскажите, пожалуйста, почему светодиоды, подключенные к РВ0 и РВ2, не мигают. Мигает тока РВ1, он же ОС1 (почему он мигает догадался). МК работет без внешнего кварца (надеюсь на внутренний). Вот листинг для меги8л:

    .include «c:avrdefm8def.inc»

    .def Temp=R16
    .def Temp1=R17
    .def Temp2=R18
    .def Temp3=R19
    .def Temp4=R20
    .def Temp5=R26
    .def Temp6=R21
    .def Temp7=R29
    .def Tempo=R22
    .equ k_div=0

    .cseg
    .org 0

    jmp RESET ; Reset Handler
    jmp EXT_INT0 ; IRQ0 Handler
    jmp EXT_INT1 ; IRQ1 Handler
    jmp TIM2_COMP ; Timer2 Compare Handler
    jmp TIM2_OVF ; Timer2 Overflow Handler
    jmp TIM1_CAPT ; Timer1 Capture Handler
    jmp TIM1_COMPA ; Timer1 CompareA Handler
    jmp TIM1_COMPB ; Timer1 CompareB Handler
    jmp TIM1_OVF ; Timer1 Overflow Handler
    jmp TIM0_OVF ; Timer0 Overflow Handler
    jmp SPI_STC ; SPI Transfer Complete Handler
    jmp USART_RXC ; USART RX Complete Handler
    jmp USART_UDRE ; UDR Empty Handler
    jmp USART_TXC ; USART TX Complete Handler
    jmp ADC_ ; ADC Conversion Complete Handler
    jmp EE_RDY ; EEPROM Ready Handler
    jmp ANA_COMP ; Analog Comparator Handler
    jmp TWSI ; Two-wire Serial Interface Handler
    jmp SPM_RDY ; Store Program Memory Ready Handler

    RESET:
    ldi Temp,high(RAMEND) ; Main program start
    out SPH,Temp ; Set stack pointer to top of RAM
    ldi Temp,low(RAMEND)
    out SPL,Temp
    ldi Temp7,0b00000001
    ldi Temp,0b11111111
    out DDRB,Temp

    ldi Temp5,high(15625)
    out OCR1AH,temp5
    ldi Temp5,low(15625)
    out OCR1AL,Temp5
    ldi Temp5,(1<<COM1A0)
    out TCCR1A,Temp5
    ldi temp5,0b00001011
    out TCCR1B,temp5
    ldi Temp5,(1<<OCIE1A ) +(1<<TOIE1)
    out TIMSK,Temp5

    ldi Temp5,$00
    out TCNT1H,Temp5
    ldi Temp5,$00
    out TCNT1L,Temp5
    sei ; Enable interrupts

    Cycle:

    rjmp cycle;

    TIM1_OVF:
    TIM1_COMPA:
    cli
    ldi Temp5,$00
    out TCNT1H,Temp5
    ldi Temp5,$00
    out TCNT1L,Temp5
    sei

    sbrc Temp7,0
    rjmp Init

    ; lsl Temp7
    ldi Temp7,0b00000001
    rjmp Output

    Init:
    ldi Temp7,0b00000100

    Output:
    out PortB,Temp7

    reti

    ;RESET:
    EXT_INT0: ; IRQ0 Handler
    EXT_INT1: ; IRQ1 Handler
    TIM2_COMP: ; Timer2 Compare Handler
    TIM2_OVF: ;Timer2 Overflow Handler
    TIM1_CAPT: ; Timer1 Capture Handler
    ;TIM1_COMPA: ; Timer1 CompareA Handler
    TIM1_COMPB: ; Timer1 CompareB Handler
    ;TIM1_OVF: ; Timer1 Overflow Handler
    TIM0_OVF: ; Timer0 Overflow Handler
    SPI_STC: ; SPI Transfer Complete Handler
    USART_RXC: ; USART RX Complete Handler
    USART_UDRE: ; UDR Empty Handler
    USART_TXC: ; USART TX Complete Handler
    ADC_: ; ADC Conversion Complete Handler
    EE_RDY: ; EEPROM Ready Handler
    ANA_COMP: ; Analog Comparator Handler
    TWSI: ; Two-wire Serial Interface Handler
    SPM_RDY: ; Store Program Memory Ready Handler

    reti;

    • Cfyzzz говорит:

      В общем ответ сам нашёл. Вместо команд jmp МЕТКА нужно писать rjmp МЕТКА. Т.к. первая команда работает с абсолютным адресом, а вторая с относительным. Соответсвенно адреса перехода в первом случае 2 байта занимают, во втором 1 байт, что и привело к ошибке.

  16. Cycle:
    rjmp cycle;
    - а этот код правильно работает с учетом разного регистра первой буквы метки?

  17. Xpyct говорит:

    В моей проге работают все три таймера
    вот инициализация:
    OUTI TCNT0,0
    OUTI TCCR0,4

    OUTI TIMSK,1<<TOIE0
    OUTI TCCR1A,(0<<COM1A1)|(1<<COM1B1)|(1<<WGM10)
    OUTI TCCR1B,(1<<WGM12)|(1<<CS10)

    OUTI TCNT2,189
    OUTI TCCR2,4
    OUTI TIMSK,1<<TOIE2

    почемуто не входит в прерывание от нулевого таймера, а от второго работает. В чем может быть причина? Причем пока был только нулевой таймер он работал, а как добавил второй то нулевой работать перестал =(
    Вектор прерывания стоит
    .ORG OVF0addr ; Timer/Counter0 Overflow
    RJMP TIM0OVF

  18. Ridik911 говорит:

    есть вопросик… если я выбераю внутренний такт частоту по таймеру стороживой собачки -
    это 128к = 131072 или = 128000?
    что меня ждет(чтото включится чтото выключится..) если я выберу пониженую тактовку от собачки?

    заметил только что на этой частоте при програмировании через унипрог необходимо вкл. тормоз.

    • DI HALT говорит:

      Тебя ждет медленный проц только и всего. А частота будет 128000гц. Т.е. 128кгц.

      • Ridik911 говорит:

        спасибо.. пониженая частота и нужна.. вычисления просты и важна энергосбережение

        а ниже никак не сделать.. ниже только 32к от кварца?

        • DI HALT говорит:

          Есть еще программно доступный прескалер. Забыл имя регистра. Им можно еще поделить в дофига раз.

          • Ridik911 говорит:

            прескалер CLK — делитель тактовой частоты? круто.. тротлинг организовать или режим сбережения энергии (как в ноутбуке) (автоматическому понижению частоты при переключении работы от сети-блокаПитания на батарейку)

            128 kHz Internal Oscillator
            Clock Prescale Register CLKPR:
            Bit 7 – CLKPCE: Clock Prescaler Change Enable
            Bits 3..0 – CLKPS3..0: Clock Prescaler Select Bits 3 — 0 — оно?

          • DI HALT говорит:

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

  19. dema говорит:

    «А для того чтобы получить секунду нам надо тикнуть 3906 раз. Ок, значит в счетный регистр надо загрузить число 65535-3906″

    тут ошибка — надо отнимать от 65536 т.к. прерывание не при достижении 65535, а при переполнении..

  20. Security говорит:

    Ответте плиз на такой вопрос. Использую прерывание от таймера, которое вызывается каждую 0,1мсек (таймер в режиме СТС). А теперь вопрос. Что будет если мой обработчик прерывания будет занимать болше времени, чем 0,1мсек. Будет прервано выполнение ранее вызванного прерывания и оно же будет запцщено с начала, или как?

  21. Hunter_kr говорит:

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

  22. Flagman говорит:

    Господа, посмотрите прогу, реализующую часы. МК — ATiny2313. Всё собирается и даже вроде работает. Скажите есть ли какие-нибудь ошибки или подводные камни в данной реализации.

    .include «tn2313def.inc»

    .MACRO exit
    reti
    .ENDMACRO

    .CSEG
    .org 0
    start: rjmp main
    reti
    reti
    reti
    reti
    reti
    reti
    reti
    reti
    reti
    reti
    reti
    reti
    reti
    reti
    reti
    reti
    main:
    CLI
    LDI R16, High(65535-15535)
    OUT TCNT1H, R16
    LDI R16, Low(65535-15535)
    OUT TCNT1L, R16
    SEI
    ldi R16,128
    out TIMSK,R16
    LDI R16,2
    OUT TCCR1B,R16
    time:
    CPI R17,20
    BRNE tick
    CBR R17,0
    CPI R18,60
    BRNE secund
    CBR R18,0
    CPI R19,60
    BRNE minute
    CBR R19,0
    CPI R20,24
    BRNE hour
    CBR R20,0
    tick: inc R17
    exit
    secund: inc r18
    exit
    minute: inc r19
    exit
    hour: inc r20
    exit

  23. panov говорит:

    Здравствуйте!

    Подскажите пожалуйста начинающему «программисту».
    Возникла такая проблема:
    При работе программы — последовательно зажигается следующий светодиод, а предыдущий тухнет, задержка 0,5с. Светодиоды подключенны к порту D. При симуляции в AVR Studio 4 всё нормально, но при заргузке в микроконтроллер цикл останавливается на состоянии — все светодиоды потухли. Если использовать порт B — всё нормально, цикл повторяется.
    В чём причина и как можно её обойти, чтобы использовать порт D?

    ; «Бегущий огонёк»
    ; Для AVR: ATTINY2313-20PU
    ; Тактовая частота: 20 МГц

    .device ATtiny2313
    .include «tn2313def.inc»

    ;Переменные
    .def temp=r16 ;Рабочая переменная
    .def Delay1=r17
    .def Delay2=r18
    .def Delay3=r19

    ;Определение выходов
    ldi temp,0b1111111 ;Определяем выходы порта D
    out DDRD,temp
    ldi temp,0b0000001 ;Начальное состояние для выходов порта D
    out PortD,temp

    START:
    ;Задержка — 0,5с
    ldi Delay1,0×80 ;Присвоение значений переменным
    ldi Delay2,0×84
    ldi Delay3,0x1E
    Loop:
    subi Delay1,1 ;Вычитание 1 из Delay1
    sbci Delay2,0 ;Вычитание 1 из Delay2, если флаг C=1
    sbci Delay3,0 ;Вычитание 1 из Delay3, если флаг C=1
    brcc Loop ;Возвращение в начало, если флаг С сброшен

    ;Заданное время прошло, зажигаем следующий светодиод
    in temp,PortD ;Считывание текущего состояния индикатора
    lsl temp ;Сдвиг влево
    brcc PC+2 ;Если переноса не было пропускаем команду
    ldi temp,0b0000001 ;Сброс в начальное состояние
    out PortD,temp ; Вывод в порт D
    rjmp START

    • DI HALT говорит:

      Вроде ничего крамольного не вижу. Должно работать и неважно на каком порту.

      Все диоды погашены значит бит ушел в ПД7 которого нет… Так. А PD7 у нас нету, а что из него читается? По даташиту ты из него можешь ТОЛЬКО читать. Т.к. физически его нет. А начальное значение у него 0. Соответственно твоя единичка уходит в PD7 и там пропадает навсегда. И в С-Flag уже не попадет никогда. Вот твой цикл и клинит.

      • panov говорит:

        Спасибо большое за подробный ответ.
        Всё исправил, на скорую руку(может конечно коряво), но работает.
        ;********************************************************
        ;Заданное время прошло, зажигаем следующий светодиод
        in temp,PortD ;Считывание текущего состояния индикатора
        lsl temp ;Сдвиг влево
        cpi temp,0×80 ;temp=128
        brne PC+2 ;Если нет, пропускаем команду
        ldi temp,0b0000001 ;Сброс в начальное состояние
        out PortD,temp ; Вывод в порт D
        rjmp START
        *********************************************************

  24. STEM говорит:

    у Т/С2 есть входы TOSC1 и TOSC2, на которые можно повесить кварцевый резонатор.

    А подскажи пожалуста, оба эти входа настраиваются на ввод?

    • DI HALT говорит:

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

  25. Khroft говорит:

    LDI R16,1<<TOIE1
    OUT TIMSK, R16

    Господа, понятно, что эти команды должны сделать, но не понятно как именно это происходит. ПОдскажите, плз, это не ясно: 1<<TOIE1

    • DI HALT говорит:

      Это означает поставить 1 на место TOIE1
      1<<4 означает поставить 1 на 4е место в бите. Т.е. будет 00010000

      Это средство макроязыка, чтобы не помнить каждый раз какой бит на каком месте.

  26. Jyraf говорит:

    Доброго времени суток!
    Подскажите что у меня не так.
    Работаю в AVR Studio, использую ATtiny2313, работают оба таймера.
    Вот инициализация:

    outi OCR1AH,High(15280) ; загружаем старший байт в ТС1
    outi OCR1AL,Low(15280) ; загружаем младший байт в ТС1
    outi OCR0A,124 ; загружаем байт в ТС0

    ; сбрасываем таймер 1 при совпадении, делитель на 1024

    outi TCCR1B,(1<<WGM12)|(5<<CS10)

    outi TCCR0A,(2<<WGM00) ; сброс таймера 0 при совпадении
    outi TCCR0B,(5<<CS00) ; делитель — 1024

    Вот кусок файла макросов:

    ; ==============================================
    .MACRO seim ; разрешение прерывания от таймера 0

    clri TCNT0 ; сброс счетчика таймера 0
    clri TIFR ; сброс флагов прерывания от таймера 0
    ; при совпадении (канал А)
    outi TIMSK,1<<OCIE0A ; разрешение прерывания при совпадении
    ; счетчика 0 (канал А)
    sei ; разрешили прерывания глобально

    .ENDMACRO
    ; ==============================================
    .MACRO clim ; запрет прерывания от таймера 0

    outi TIMSK,0<<OCIE0A ; запрещение прерывания при совпадении
    ; счетчика 0 (канал А)
    .ENDMACRO
    ; ==============================================
    .MACRO seit ; разрешение прерывания от таймера 1

    clri TCNT1H ; сброс счетчика таймера 1
    clri TCNT1L ;
    clri TIFR ; сброс флага прерывания от таймера 1
    outi TIMSK,1<<OCIE1A ; разрешение прерывания при совпадении
    ; счетчика 1 (канал А)
    sei ; разрешили прерывания глобально

    .ENDMACRO
    ; ==============================================
    .MACRO clit ; запрещение прерывания от таймера 1

    outi TIMSK,0<<OCIE1A ; запрещение прерывания при совпадении
    ; счетчика 1 (канал А)

    .ENDMACRO
    ; ==============================================

    Естественно выше определены макросы для
    outi — вывод данных в порт;
    clri — очистка порта…

    Проблема следующая:
    ТС1 работает нормально, если разрешить прерывание — сначала обнуляет счетчик, сбрасывает TIFR, потом происходит прерывание, обрабатывается и т.д… При запрете прерываний OCIE1A падает в 0. Тут как бы проблем нет.
    ТС0 выделывается… Если разрешить прерывания, а флаг OCF0A установлен (прерывание произошло раннее), то счетчик сбрасывается, и программа сразу уходит на обработку прерываний от ТС0. Получается, что в макросе не работает строка

    clri TIFR ; сброс флагов прерывания от таймера 0
    ; при совпадении (канал А)
    Пробовал напрямую через

    clr temp
    out TIFR,temp

    Пробовал маску (чистим первые биты TIFR)

    andi temp,0b11111000
    out TIFR,temp

    Не идет, проблема остается…

  27. Jyraf говорит:

    Вопрос снят. Извините за беспокойство, английский не знаю, с переводом туго…
    Правильно работает если в обоих таймерах писать

    seri TIFR

    то есть TIFR сбрасывается записью в него 1!!!

  28. RunningWolf говорит:

    Созрел вопрос:
    Нарисовал программку мигания диода. Для проверки текущего статуса использовал CPI. Если в регистре 0 — включаем и пишем 1. Если же 1 — выключаем и пишем в регистр 0. Такое впечатление, что не работает ни на эмуляторе, ни в контроллере. А вот CPSE (если константу пихать в другой регистр и сравнивать с ним) заработало.
    Почему же не работало CPI? Заранее спасибо.

    • DI HALT говорит:

      Дай кусок кода скажу

      • RunningWolf говорит:

        Спасибо, прочитал «Ветвления» и понял. Я рассчитывал, что оно будет работать так же, как SBIC/SBIS. Т.е. пропускать следующую команду (у меня это был RJMP).
        В реальности же дальше должна идти именно команда условного перехода (для такой логики — BRNE).
        Черт, хочется всего и сразу, а без знания теории граблей много =)

        Вопрос по таймеру:
        Я его запустил, он у меня досчитал до переполнения, пришло прерывание. Все замечательно. Если же я хочу, чтобы у меня что-то происходило раз в секунду, мне в обработке прерывания каждый раз переинициализировать таймер? Или это можно как-то изящнее сделать?

        • DI HALT говорит:

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

          Есть вариант (не для всех таймеров) настроить на прерывание по сравнению. Т.е. таймер считает до OCR и дает прерывание, ну и сам сбрасывается. Тебе лишь остается выставить делитель таймера и OCR таким, чтобы интервал был удобным каким нибудь, скажем 100мс в этом случае в прерывании тебе надо только щелкать счетчиком и как нащелкает 10шт то выставлять флаг какой нибудь в памяти и это будет означать что наступила секунда

  29. portvein говорит:

    DI HALT, подскажи плиз, как подключать осциллятор ко второму таймеру? Не могу въехать, у тебя написано, просто подключить к ногам TOSC1 и TOSC2, а в даташите про TOSC2 ни слова =(

    When AS2 is written to one, Timer/Counter2 is clocked from a Crystal Oscillator connected to the Timer Oscillator 1 (TOSC1) pin.

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

  30. panov говорит:

    Здравствуйте!!!

    Есть 2 вопроса:

    1 Можно ли программировать микроконтроллер ATMEGA644-20PU в Uniprof если именно эту модель он не поддерживает? А если можно, то какую модель выбирать из списка вручную:
    1200, 2313, 2323, 2333, 2343, 4414, 4433, 8515, 8535, mega103, mega128, mega1280, mega1281, mega16, mega161, mega162, mega163, mega165, mega168, mega169, mega2560, mega2561, mega32, mega325, mega3250, mega48, mega603, mega64, mega640, mega645, mega6450, mega8, mega8515, mega8535, mega88, pwm23, tiny12, tiny15, tiny13, tiny2313, tiny24, tiny25, tiny26, tiny45, tiny44, tiny85, tiny84.

    2 Помогите пожалуйста найти ошибку в программе.
    Микроконтроллер ATtiny2313-20PU (стоит кварц на 20МГц) считает количество импульсов с датчика частоты вращения. При выводе значения на динамический светодиодный 7-ми сегментный 3х разрядный дисплей должна гореть точка целой части (на среднем индикаторе). Точка горит, но появляется бледный её дубликат на соседнем старшем индикаторе (разряд десятков). В схеме ошибок нет, так как когда не был очищен регистр разрядов индикатора, разряды при включении микроконтроллера имели случайное место на индикаторе и всё равно этот бледный дубликат точки присутствовал, всегда также на разряде десятков.
    Вопрос: откуда берётся этот бледный дубликат точки?
    ;********************************************************************************
    ;Вывод на динамический дисплей
    DISPLAY:

    ;Обновление дисплея при каждом N-ым вызове
    dec Counter ;Уменьшение счётчика на 1
    breq PC+2 ;Если не 0 пропускаем следующую команду
    reti ;Переход назад в цикл
    ldi Counter, 255 ; Установка счётчика в исходное состояние

    ;Сброс предыдущего значения разряда порта B
    ldi temp, 255 ;Устанавливаем все единицы
    out PortB, temp ;Выводим в порт B

    ;Включение соответствующего индикатора
    in temp, PinD ;Считываем текущее значение порта D
    lsl temp ;Указываем на следующий индикатор
    sbrc temp, 3 ;Если не вышли за пределы пропускаем следующую команду
    ldi temp, 0b00000001 ;Переход к первому разряду

    out PortD,temp ;Выводим разряд в порт D

    ;Вывод значения индикатора
    inc Discharging ;Выбираем следующий индикатор
    cpi Discharging, 3 ;Если разряд равен 3
    brne PC+2 ;Пропускаем следующую команду
    clr Discharging ;Выбираем первый индикатор

    ldi ZL, 23 ;Устанавливаем ZL на R23
    add ZL, Discharging ;Прибавляем номер индикатора
    ld temp, Z ;Копируем конвертируемое число в temp
    clr ZL ;Устанавливаем ZL на R0
    add ZL, temp ;Прибавляем temp к ZL
    ld temp, Z ;Считываем RX в temp

    cpi Discharging, 1 ;Если не средний индикатор
    brne PC+2 ;Пропускаем следующую команду
    cbr temp, 0b10000000 ;Сброс 7 bit десятков, отвечающего за точку целой части

    out PortB,temp ;Выводим значение разряда в порт В

    reti ;Переход назад в цикл
    ;********************************************************************************

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

    • DI HALT говорит:

      1) А он ее даже не определил? Если не определил, то унипроф тут тебе уже не поможет :( Переходи на AVRDUDE или более часто обновляющиеся программаторы.

      2)А точно в схеме нет утечек? И уверен в правильности схемы? т.к. если у тебя динамическая индикация работает нормально, то и все остальные сегменты должны быть в норме. Ты же байт переключаешь целиком, включая точку.

      Попробуй это отладить в замедленном режиме. Сделай интервал между переключениями сегментов в 1-2секунды. Тупым могучим циклом и посмотри какие у тебя сигналы выводятся для правого и левого сегмента. Так и поймаешь багу.

      Вообще заведи себе макрос затык такого вида:

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

      и пихай его в сомнительные места.

  31. milvus говорит:

    Спасибо DI HALT, классные статьи, но у меня такой вопрос, с момента нажатия кнопки до включения таймера проходит 9 тактов контроллера, значит реальное время еще зависит и от этого, и при больших частотах надо кроме обнуления предделителя учитывать и это? А еще в обработчике прерываняи неизвестно сколько команд! Я правильно понимаю?

    • DI HALT говорит:

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

  32. panov говорит:

    Доброе утро!

    Подскажите пожалуйста, можно ли к ATtiny2313 подключить отдельную микросхему памяти примерно на 8кбайт(лучше больше) для сохранения данных в автономной установке, если да то посоветуйте какую и как считать с неё данные в компьютер, чтобы получить график?

  33. RunningWolf говорит:

    Приветствую! Что-то никак не могу пофиксить проблему с таймерами на Mega8.
    Штука следующая: Для движения робота пользую ШИМ от OC1B и выводы PB1, PB3 для выбора направления вращения. Также пользую UART для обмена информацией с компом. По UART приходят к МК команды на вкл/выкл двигателя и должны уходить события датчиков. Отдельно управление двигателем и опрос датчиков работают отлично, а вот вместе…
    В Proteus все живет, а в железе — не хочет.
    Подозреваю, что как-то вмешивается таймер Timer0, который я завожу для опроса датчиков.
    При этом когда приходит команда на двигатель, он слегка дергается и все.
    Пробовал ставить опрос датчиков в основной цикл — двигатель вообще ни на что не реагирует.
    Создается впечатление, что при наличии второго таймера, он сбивает сигнал на PB1 или PB3.
    Буду признателен за подсказку — где копать и, особенно, за пример кода, как же все-таки одновременно сделать:
    - и ШИМ на OC1B
    - и управление направлением вращения на PB и PB3
    - и опрос датчиков по таймеру
    Заранее спасибо!

  34. Name4Me говорит:

    Подскажите плиз как сбросить предделитель в 48 меге?

  35. Name4Me говорит:

    ММ сам задалсам отвечу :) возможно так: OUTI GTCCR,1<<PSRSYNC

  36. milvus говорит:

    написал свою первую пробную программу — часы
    только вот проблема, в основном ровно в 1 мин. и 47 сек. происходит сброс на 0:0
    сторожевой таймер не включал, стек вроде проследил до 40 секунд, держится на 1 уровне, все в цикле, должно быть нормально,
    в общем не могу понять что делать??
    студия пишет
    AVR Simulator: Invalid opcode 0x00c0 at address 0x00003f
    и таких предупреждений полно, с разными адресами

  37. stein говорит:

    А счетчики таймеров тикают все одновременно или по одному за такт?

    • stein говорит:

      Это глобальный вопрос был, а не уточнение предыдущего

    • DI HALT говорит:

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

      • stein говорит:

        Тогда получается, что можно организовать выполнение двух прерываний по таймерам одновременно? И если в них одновременно происходят взаимоисключающие операции, то произойдет конфликт прерываний (ёмть, сто раз слышал это выражение и только сейчас понял его смысл))? И что происходит с микроконтроллером тогда?
        Или они, даже если происходят одновременно, по очереди выполняются (типа сначала прерывание от таймера Т/С0, потом от Т/С1)?

  38. rutic говорит:

    Привет DI
    У меня проблема со сторожевым таймером Mega16. Как сделать так, что бы он сбрасывал контроллер при зависании которое возникает вовремя выполнения программы обработки прерывания? Я пробовал после вызова программы обработки делать так:
    sei
    wdr
    ldi temp,0b00001000
    out WDTCR,temp
    r: rjmp r ; это типа зависание
    Но это не помогает(
    При зависании в других участках программы всё срабатывает отлично.

    • rutic говорит:

      Естественно
      r: rjmp r
      в реальной программе нет.
      Устройство реально собрано и работает но изредко зависает при выполнении одного и тогоже действия…

      • DI HALT говорит:

        Ну я бы рекомендовал не таймер мучать, а искать причину зависания. А вачдог настраивается всего один раз — при старте программы.

        • rutic говорит:

          Причина в сильных электромагнитных помехах. Зависание возникает очень редко, но это всёравно неприятно.
          Как я понимаю, собачий таймер бесполезен при зависаниях, если они возникают при обработке прерывания(, покрайней мере в меге16.

          • DI HALT говорит:

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

  39. Tiratron говорит:

    Доброе время суток всем форумчанам. Пожалуйста помогите в решении такого вопроса: пишу программку для частотомера, в принципе все уже готово и работает, НО сама программа задумана таким образом что перед самим началом активации счетчика внешних импульсов и таймера (временных ворот) должен произойти сброс предделителей счетчиков Т/С0 и Т/С1. (Кристалл — )

  40. Tiratron говорит:

    Доброе время суток всем форумчанам. Пожалуйста помогите в решении такого вопроса: пишу программку для частотомера, в принципе все уже готово и работает, НО сама программа задумана таким образом что перед самим началом активации счетчика внешних импульсов и таймера (временных ворот) должен произойти сброс предделителей счетчиков Т/С0 и Т/С1. (Кристалл — tiny 2313). Для этого в регистр GTCCR в перевый разряд PSR10 надо записать 1. Так вот сам вопрос — какой командой это можно сделать? Пробовал:
    ldi temp, 0×01;
    out GTCCR, temp;
    Такие строчки AVR Studio переваривает, но сами предделители НЕ сбрасывает. А на команду:
    out GTCCR, PSR10;
    вобще ругается.
    На этой страничке форума вычитал что можно задать:
    outi GTCCR, 1;
    но кристал tiny 2313 не поддерживает команду outi.

    Заранее благодарен всем за ответы на вопрос.

    • DI HALT говорит:

      Ну во первых outi это не команда а макрос.

      А во вторых запись
      out GTCCR, PSR10 не корректна. Нельзя напрямую записать в порт ИО что нибудь. Только через регистр.

      Первый случай

      1
      2
      
      ldi temp, 0×01
      out GTCCR, temp

      Корректен и должен работать. Откуда уверенность что прескалер не сбарсыватеся?

      • Tiratron говорит:

        Благадарю за ответ! Уверенность о несбрасывании прескалера исходит из следующего:
        1) в AVR Studio при прогоне программы через встроенный эмулятор при команде

        ldi temp, 0×01;
        out GTCCR, temp;

        визуально не отображается установка 1 в разряд PSR10 при выполнении строки out GTCCR, temp;, а при выпоснении следующей строчки эта единичка должна сбрасываться. Но это вполне возможено и простая недоработка разработчиков AVR Studio.
        2) предварительно установив таймер Т/С1 в режим счетчика с частотой работы
        Ф/64 (при установленной по умолчанию в AVR Studio частоте тактового генератора 4 МГц, период составит 16 мс). В программе строка пуска этого таймера стоит сразу же после строки сброса прскалеров. Тоесть первый тик таймера должен произойти через 16 мс после его пуска, а он происходи гараздо раньше, а вот второй тик и все последующие — через каждые 16 мс.
        3) при прогоне программы (частотомера) в реальной схеме визуально видно очень частое дергание двух младших разрядов числа. Опять же, при прогоне нескольких измерительных циклов на эмуляторе в AVR Studio хорошо видно что каждый раз подсчитанное число тиков отличается на несколько единиц (иногда превышает десяток).

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

        • Tiratron говорит:

          Отвечаю сам на поставленный вопрос — вся трабла началась со следующего:

          ldi rab, timezero;
          ………………..
          ldi time, 0×01;
          out GTCCR, rab;

          И после этого я думал что записал в регистр GTCCR содержимое РОН temp. А всего-то нужно было внимательно проверить.

          Прошу прощения за лишнюю тревогу по «глупому вопросу». Теперь сам с уверенностью могу сказать что команда

          ldi temp, 0×01;
          out GTCCR, temp;

          работает (счетчики сбрасываются в ноль).

  41. Virtuoso говорит:

    Всем привет.
    У меня тут возникла необходимость создания частотомера на ATmega88.
    Я прикинул и решил сделать это дела на счетчике считая количество тактов между 2-мя фронтами на каком-то пине (я использовал прерывание INT0), но когда стал переводить такты в Герцы, то возникла проблемы точности моего частотомера:
    - Если время между 2-мя фронтами равно 1 такту контроллера, то получается что частоты источника равна частоте контроллера.
    - Если … 2 такта контроллера, то — 1/2 частоты контроллера.
    И так далее, чем больше тактов, тем меньше диапазон и выше точность, но это никуда не годится, так как при таком раскладе нельзя замерить какую-то промежуточную частоту (поближе к частоте контроллера).
    Получается, если контроллер — 4Mhz, то 1 такт счетчика = 4MHz, а 2 такта уже 2MHz, таким макаром высчитать 3MHz нереально.
    Подскажите как реализовать частотомер, в принципе?
    Спасибо.

    • DI HALT говорит:

      Ну по моему это очевидно. Частототомер просто обязан работать на частоте намного больше чем измеряемая частота. Либо иметь делитель частоты на входе.

      AVR не имеет делителя частоты, поэтому частотомеры любят делать на PIC т.к. у них у многих МК есть аппаратный делитель входной частоты.

      Ну либо делать его на дискретке, например, на счетчике.

  42. Blacky говорит:

    Попытался сделать програмку, чтобы освоить таймеры. По прерыванию меняю уровень на PortA0. На PortD4 висит кнопка, по нажатию на которую должна повысится частота импульса. К PortA0 еще прицепил динамик через сопротивление, чтобы не только померять прибором, но и услышать. Вот и все. Работать это дело отказывается (на PortA0 постоянно низкий уровень). Может кто поможет. Код вдогонку:

    .INCLUDE «m16def.inc»

    .MACRO outi
    LDI R16,@1
    OUT @0,R16
    .ENDMACRO

    .CSEG
    .ORG 0×000
    rjmp RESET ; Reset Handler
    .ORG OVF1addr ; Timer/Counter1 Overflow
    rjmp T1_OVF

    .org 0×0030
    RESET: ldi r16,high(RAMEND)
    out SPH,r16
    ldi r16,low(RAMEND)
    out SPL,r16
    OUTI DDRA,0xFF
    OUTI DDRD,0×00
    OUTI PORTD,0xFF
    OUTI TIMSK, 1<<TOIE1
    cbi PORTA,0
    cli
    OUTI TCNT1H, High(65535-32)
    OUTI TCNT1L, Low(65535-32)
    sei
    OUTI TCCR1B, 5
    main: in R18, PIND
    sbrc R18, 4
    rjmp main
    rcall wait1
    OUTI TCCR1B, 0
    cli
    OUTI TCNT1H, High(65535-10)
    OUTI TCNT1L, Low(65535-10)
    sei
    OUTI TCCR1B, 5
    rjmp main

    wait1: push R16
    ldi R16,250
    wt: dec R16
    brne wt
    pop R16
    ret

    T1_OVF: in R17, PINA
    sbrc R17,0
    cbi PORTA,0
    sbrs R17,0
    sbi PORTA,0
    reti

    • DI HALT говорит:

      Прогони в отладчике авр студии да все увидишь.

      • Blacky говорит:

        Я так понял, счетный регистр не изменяется. Но я же запускаю таймер OUTI TCCR1B, 5 и прерывания разрешены. В чем же косяк?..

        • Blacky говорит:

          Ой-ой-ой. А он после переполнения сбрасывается на 0 оказывается. Хотя по сути почему меня это удивляет… Пошел думать дальше.

        • DI HALT говорит:

          Он изменяется. но предделитель стоит в 1024 так что в студии наблюдать это будет проблематично (надо будет сделать 1024 шага чтобы увидеть изменение). Сделай пока запуск как
          OUTI TCCR1B, 1 и прогони в студии до переполнения.

          • Blacky говорит:

            Я просто думал, что после переполнения Т1 сбросится на записанное мной значение, а не на ноль). Если ждать весь пересчет Т1 да еще и с предделителем, получится слишком маленькая частота, и я ее не услышу. Решил теперь использовать Т0. Все переделал, изменил таблицу прерываний, коэффициент деления, добавил цикл ожидания отпускания кнопки. В студии вижу: таймер несколько раз переполняется — программа все равно не уходит в прерывание.

            .INCLUDE «m16def.inc»
            ;freq = 16.000MHz
            .MACRO outi
            LDI R20,@1
            OUT @0,R20
            .ENDMACRO

            .CSEG
            ;Start of Interrupts table
            .ORG 0×000
            rjmp RESET ; Reset Handler
            reti ; IRQ0 Handler
            reti ; IRQ1 Handler
            reti ; Timer2 Compare Handler
            reti ; Timer2 Overflow Handler
            reti ; Timer1 Capture Handler
            reti ; Timer1 CompareA Handler
            reti ; Timer1 CompareB Handler
            reti ; Timer1 Overflow Handler
            rjmp T0_OVF ; Timer0 Overflow Handler
            reti ; SPI Transfer Complete Handler
            reti ; USART RX Complete Handler
            reti ; UDR Empty Handler
            reti ; USART TX Complete Handler
            reti ; ADC Conversion Complete Handle
            reti ; EEPROM Ready Handler
            reti ; Analog Comparator Handler
            reti ; Two-wire Serial Interface Hand
            reti ; IRQ2 Handler
            reti ; Timer0 Compare Handler
            reti ; Store Program Memory Ready Han
            ;End of Interrupts table
            .org 0×0030
            RESET: OUTI SPH, high(RAMEND) ; Main program start
            OUTI SPL, low(RAMEND) ; Set stack pointer to top of RA
            OUTI DDRA, 0xFF
            OUTI DDRD, 0×00
            OUTI PORTD, 0xFF
            OUTI TIMSK, 1<<TOIE0
            cli
            OUTI TCNT0, 0×00
            sei
            OUTI TCCR0, 4
            cbi PORTA, 0
            main: in R18, PIND
            sbrc R18, 4
            rjmp main
            rcall wait1
            main1: in R18, PIND
            sbrs R18, 4
            rjmp main1
            rcall wait1
            cli
            OUTI TCCR1B, 3
            sei
            rjmp main

            wait1: push R16
            ldi R16,250
            wt: dec R16
            brne wt
            pop R16
            ret

            T0_OVF: in R17, PINA
            sbrc R17,0
            cbi PORTA,0
            sbrs R17,0
            sbi PORTA,0
            reti

          • DI HALT говорит:

            А на кой ты в главном цикле при каждой итерации дергаешь TCCR1B?

          • Blacky говорит:

            А. Я это исправил уже) Частота просто бы не менялась. (Как я понимаю) Но ситуацию это не меняет. Программа все равно не заходит в прерывание.

          • Blacky говорит:

            Исправил на:
            OUTI TCCR0, 3

          • Blacky говорит:

            Все заработало, как я и задумал. Закометил таблицу прерываний, добавив:
            .ORG OVF0addr
            rjmp T0_OVF
            А также переставил sei.
            Так что где был косяк, я так и не понял. Sei, кстати, имеет ограниченную область действия?(Или я сейчас чушь сказал?)
            Конечный вариант такой:
            .INCLUDE «m16def.inc»
            ;freq = 16.000MHz
            .MACRO outi
            LDI R20,@1
            OUT @0,R20
            .ENDMACRO

            .CSEG
            ;Start of Interrupts table
            .ORG 0×000
            rjmp RESET ; Reset Handler
            .ORG OVF0addr
            rjmp T0_OVF
            /*reti ; IRQ0 Handler
            reti ; IRQ1 Handler
            reti ; Timer2 Compare Handler
            reti ; Timer2 Overflow Handler
            reti ; Timer1 Capture Handler
            reti ; Timer1 CompareA Handler
            reti ; Timer1 CompareB Handler
            reti ; Timer1 Overflow Handler
            rjmp T0_OVF ; Timer0 Overflow Handler
            reti ; SPI Transfer Complete Handler
            reti ; USART RX Complete Handler
            reti ; UDR Empty Handler
            reti ; USART TX Complete Handler
            reti ; ADC Conversion Complete Handle
            reti ; EEPROM Ready Handler
            reti ; Analog Comparator Handler
            reti ; Two-wire Serial Interface Hand
            reti ; IRQ2 Handler
            reti ; Timer0 Compare Handler
            reti ; Store Program Memory Ready Han
            */
            ;End of Interrupts table
            .org 0×0030
            RESET: OUTI SPH, high(RAMEND) ; Main program start
            OUTI SPL, low(RAMEND) ; Set stack pointer to top of RA
            OUTI DDRA, 0xFF
            OUTI DDRD, 0×00
            OUTI PORTD, 0xFF
            sei
            OUTI TCNT0, 0
            OUTI TCCR0, 4
            OUTI TIMSK, 1<<TOIE0
            cbi PORTA, 0

            main: in R18, PIND
            sbrc R18, 4
            rjmp main
            rcall wait1

            main1: in R18, PIND
            sbrs R18, 4
            rjmp main1
            rcall wait1
            OUTI TCCR0, 3
            rjmp main

            ;Çàäåðæêà
            wait1: push R16
            ldi R16,250
            wt: dec R16
            brne wt
            pop R16
            ret

            ; Îáðàáîò÷èê ïðåðûâàíèÿ
            T0_OVF: in R17, PINA
            sbrc R17,0 ; Åñëè íîëü, òî ïðîïóñêàåì óñòàíîâêó â íîëü
            cbi PORTA,0 ; Óñòàíîâêà â íîëü
            sbrs R17,0 ; Åñëè 1, òî ïðîïóñêàåì óñòàíîâêó â 1
            sbi PORTA,0 ; Âûäàåì â PA «1″
            reti ; Âîçâðàò èç ïðåðûâàíèÿ

          • DI HALT говорит:

            Я понял в чем ошибка. В таблице векторов. Оказывается у разных МК она имеет разную плотность.

            У тини2313 ее вектора имеют длину одну команду (т.к. памяти мало и не нужна дальнобойна разрядность), поэтому байда вида

            RETI
            RETI
            RETI

            прокатывает. У меги16 изза большей длинны памяти переход может быть и в два слова, поэтому
            Вектора там идут с шагом два байта

            RETI
            NOP
            RETI
            NOP

            Либо прописывать ORG для каждого вектора.

          • Blacky говорит:

            Вон оно что. Спасибо за пояснение. Я таблицу копипастил из даташита. Там адреса слева стояли, но я как всегда был невнимателен. А шаг там и правда двухбайтовый.

          • DI HALT говорит:

            В студии трассировал? Явно что нет. Я с первого же прогона увидел, что прерывание выполняется, но переход идет не на тот вектор. =)

  43. Savenkot говорит:

    Имеем: tn2313 , внутр.генератор 8 МГц, CKDIV8=0. Вопрос: что будет считать таймер — 8 или 1 МГц ?

  44. Savenkot говорит:

    Дошло наконец-то: частоту с генератора можно поделить регистром CLKPR и это будет основная тактовая частота контроллера (про это кстати мало где упоминается), а для подсчета таймером эту частоту еще раз делим битами CSx2:CSx0. Как бы мне у себя в голове тактовую частоту поднять…

  45. neon-f говорит:

    после картинки, третий абзац, опечатка — «своего собственного собственного генератора»

  46. Roland_Deschain говорит:

    Все комменты не читал — больно много. Есть замечание по поводу инвертирования состояния выходной ножки. Пока есть маааааленький опыт работы только с Тиней2313, так у неё для инвертирования состояния выходной ножки (или режима входной) надо просто выставить соответствующий бит в PINx. То есть:

    sbi DDRB, DDRB0 ;PORTB0 — выход
    sbi PORTB, PORTB0 ;поставили 1 в PORTB0
    nop ;чтобы в PINB0 1 записалась

    sbi PINB, PINB0 ;теперь в PORTB0 и PINB0 вылез 0
    sbi PINB, PINB0 ;теперь в PORTB0 опять 1
    nop ;и только теперь в PINB0 — 1

    sbi PINB, PINB0 ;теперь в PORTB0 и PINB0 вылез 0
    ;…

    Как прочитал в даташите — не поверил. Но симулятор AVRStudio развеял сомнения :)

  47. Roland_Deschain говорит:

    Дословно из даташита:

    Writing a logic one to PINxn toggles the value of PORTxn, independent on the value of
    DDRxn. Note that the SBI instruction can be used to toggle one single bit in a port.

  48. evgen говорит:

    Привет. Работаю с 90S2313. в ходе выполнения программы используются оба таймера поочереди — сначала 8-битный с предделителем 1024, а потом 16-битный с предделителем 64. вопрос такой: нужно ли сбрасывать предделитель перед использованием 16-битного таймера в моем случае и как это делается в s2313? чот в даташите не нашел про это

  49. evgen говорит:

    по какой команде запускается таймер? по записи значения предделителя OUT TCCR1B,Rх?

  50. dima_m говорит:

    Попробовал поиграть с регистром SFIOR в Atmega8L. Поставил на все таймеры предделитель 64 и такой код чтоб постоянно сбрасывать предделитель.
    Start:
    nop
    nop
    ldi r16,0b00000011
    nop
    out sfior,r16
    nop
    nop
    rjmp Start
    запустил прогу, все равно все счетчики тикают, Т0 и Т1 одинаково, Т2 как то с запозданием.
    Это что баг в AVR studio? Надеюсь в самом мк все пучком будет сбрасываться?

    • DI HALT говорит:

      Вполне может быть баг студии. У ней есть ряд косяков работы с таймерами. Зашей в железо и позырь осциллографом.

      • dima_m говорит:

        Ладнек позырю. А интересно сами разработчики хоть знают об этих косяках? Или ждут себе пока пользователи сами не сообщат им? Недавно закачал sp2 на версию 4.18 с их сайта. Все равно видать не исправили еще. А еще только что на макетной плате тестировал кое что с atmega8 и чисто случайно перемычку воткнул не туда. Потом радостно включил питание и у меня 20 вольт очень радостно прямо на мк даванули. Ясно что мк просто щелкнул и дым повалил. Сейчас вот сижу и радуюсь. Повезло что в загашнике еще штук 8 валяется. Это мой первый мк который спалил за пол года работы с ними. Как я так лоханулс, непонятно. Вообщето я все очень аккуратно всегда делаю. Сто раз перепроверяю. Поэтому по жизни у меня с электричеством мало аварий было. Особо ничего не сжигал.

        • DI HALT говорит:

          А хз. может попробовать им багрепорт написать. У аврстудии с мегой8 и таймером другой тоже баг уже давно есть — в режиме фаст шим он считает как фаз коррект шим. Т.е. 0..1-255..254-0 а не 0-255-0. А в железе все ок

          • dima_m говорит:

            Я написал бы, да в ангельском языке не соображаю. Можно попробовать конечно на русском. Но думаю если ты напишешь, то у тебя лучше получится. Кстати SFIOR в студии все таки нормально сбрасывает Т2, а вот Т0 и Т1 никак. А в железе все пучком работает. Тиристор открывается как миленький на Т1. Угол открытия не плавает.

  51. ftp27host говорит:

    Подскажите пожалуйста, что не так:

    .include «m8def.inc»
    .DSEG
    .CSEG

    .ORG OVF0addr ;0×0009
    RJMP Timer_over
    .ORG INT_VECTORS_SIZE ;19

    Reset:
    LDI R16,1
    OUT TIMSK,R16
    Main:
    LDI R16,200
    Delay1:
    LDI R17,172
    OUT TCNT0, R17
    CLT
    LDI R17,5
    OUT TCCR0,R17
    Delay2:
    BRTC Delay2
    DEC R16
    BRNE Delay1
    RJMP Main

    Timer_over:
    SET
    LDI R17,0
    OUT TCCR0,R17
    RETI

    .ESEG

    Уже довольно долго бьюсь над тем чтобы при переполнении попадать на свою функцию, но никак не выходит >.<

    • ftp27host говорит:

      Проверил прошить МК. Эффект тот же. Уж точно не баг АВР Студии..

    • DI HALT говорит:

      Как минимум две очень грубых ошибки. Если бы ты протрассировал свой код в отладчике, покомандно, то ты бы сразу увидел что у тебя неправильно работать и как оно себя ведет. Но ты видимо не догадался. Так сделай это! :)

      • ftp27host говорит:

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

        • ftp27host говорит:

          ах да. у себя в коде я еще сделал RJMP Reset в самом начале

        • DI HALT говорит:

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

          Второе — где инициализация стека? У тебя без нее не будет возврата из прерывания.

          Третье — а прерывания глобально кто разрешать будет?

          Четвертое, не ошибка но тоже важно. Чо бля за магические числа в иницйиализации таймера?

          • ftp27host говорит:

            Скопипастил инициализацию стека и разрешение прерываний. Все заработало. Прерывание не срабатывало из за того, что я его не разрешил :))
            Магические числа инициализации таймера из доков. было как то неуютно пользоваться библиотекой m8def.inc для таких целей. К тому же в начале изучения МК первых нормальных результатов я добился только из за этой документации ))
            Спасибо!

    • DI HALT говорит:

      Тут не над чем биться, тут надо пошагово выполнить и будет тебе счастье.

  52. rfgfq говорит:

    Подскажите пожалуйста, как настроить прерывания при равенстве таймера и регистра сравнеия, но чтобы таймер продолжал тикать дальше

    • DI HALT говорит:

      В обычный режим. Записать в TCCRxx только делитель (биты CS), оставив все как есть (вроде бы по нулям по дефолту) Ну и в TIMSK разрешить прерывания.

      • rfgfq говорит:

        для мега8 уходит на прерывание по совпадению раньше. Подскажите пожалуйста, что не так:

        .ORG $003
        RJMP Metka1
        Metka:LDI R16,1 ; 0000 0001 предделитель на 1
        OUT TCCR2,R16 ; Запускаем таймер

        LDI R16,64
        OUT OCR2,R16; заношу в регистр сравнения число

        SEI ;разрешение прерывания

        ldi r16,0b10000000
        OUT TIMSK,R16; разрешение прерывания по совпадению

        • DI HALT говорит:

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

  53. dima_m говорит:

    DIHALT ты выше писал по тексту
    CLI ; Доступ к многобайтной переменной
    ; одновременно из прерывания и фона
    ; нужен атомарный доступ. Запрет прерываний

    OUTU TCNT0,R16 ; Ноль в счетный регистр таймера
    STS TCNT,R16 ; Ноль в первый байт счетчика в RAM
    STS TCNT+1,R16 ; Ноль в второй байт счетчика в RAM
    STS TCNT+2,R16 ; Ноль в третий байт счетчика в RAM
    STS TCNT+3,R16 ; Ноль в первый байт счетчика в RAM
    SEI ; Разрешаем прерывания снова.
    Если регистр R16 и так сохраняется в стеке во входе в прерывание, обязательно ли запрещать прерывания в данной ситуации? Выйдя из прерывания он снова станет 0 и дальше запишется это значение в нужные нам регистры.

    • DI HALT говорит:

      В принципе можно и не запрещать, но если при этом у нас в прерывании будет доступ к TCNT то будет работать некорректно. Ведь мы можем начать ТСНТ менять, а в прерывании считать или записать значение наполовину измененное.

      • dima_m говорит:

        Да понял, рубишь в точку. А вот еще мысля, допустим я хочу на таймере T0 замутить 10 таких счетчиков. Регистр то счетный у Т0, всего 225. Пока он у себя в прерывании прощелкает все эти 10 счетчиков, то как раз натикает свои 255 тактов.И выйдя из прерывания опять нужно нырять обратно. На исполнение основного кода не останется лишних тактов. Как бы ты это решил? Т1 и Т2 уже заняты. Я бы наверное прескал поставил 8. Тогда прерывание возникало бы каждые 256×8=2048 тактов.Погрешность слегка увеличиться ну и ладно не проблема. Зато больше времени останется на исполнение основного кода. Кстати в последнем своем проекте я уже применил прием описанный в этой статье. Два таких программных таймера замутил на Т0. Сделал зарядное устройство на 10 ампер для гелевых батарей в поломоечную машину.

        А еще попутно, просто мне интересно.Ты все время в интернете сидишь? Как контролируешь появление новых вопросов? Сколько примерно в день ты получаешь вопросов на твоем сайте?

        • DI HALT говорит:

          Я бы сделал по другому. У меня был бы тик. Скажем 1мс (или еще минимальная достаточная дискретность для самого быстрого счетчика) и я бы в прерывании таймера этим тиком увеличивал 10 переменных. А главная программа сравнивала бы эти 10 переменных на совпадение (в каждом своем обработчике сравнивается своя переменная).

          А если у меня есть диспетчер ядра (RTOS) то таймер бы наоборот обнулял 10 переменных по тикам, а как занулится какая либо из переменных — пихал в диспетчер задач нужный обработчик.

          Почти всегда, ага. Бывает отхожу — прихожу. У меня все комменты в админке в виде плоского списка. Там же я могу не отходя от кассы на них отвечать. В день отвечаю примерно на 20-50 вопросов.

          • dima_m говорит:

            Так, так, хочу вникнуть в твои рассуждения. Не совсем понял. Я ж потом все это буду на практике применять. Значит есть один счетчик №1. Когда возникает прерывание по таймеру, проверяется что он натикал 1 мс. Если натикал, тогда 10 переменных увеличились на 1. И так каждую милисекунду все 10 пернеменных будут увеличены на 1. получается все эти 10 переменных имеют максимально достижимое время это 256 мс. Получается у меня будет 10 счетчиков и каждый может дотикать до 256 мс и потом снова с нуля. А если мне нужно скажем 1сек? Тогда счетчик №1 может иметь дискретность 4 мс. Тогда все 10 счетчиков смогут достичь 1 с. Так что ли?

          • DI HALT говорит:

            Нет. Прерывание этого таймера возникает не чаще чем раз в 1мс. В этот момент в его обработчике щелкают переменных-таймеров. У каждого ожидающего обработчика свой.

          • dima_m говорит:

            ПОНЯЛ.

  54. Gordienko Victor говорит:

    Подскажите пожалуйста почему когда симулирую в протеусе частота не получаеться равной 16 МГц ?

    #include

    interrupt [20] void Tim (void)
    {
    PORTD.1=~PORTD.1; // меняю состояние
    }

    void main (void)
    {
    DDRD=0xFF;
    TCCR0=(1<<WGM01)|(0<<WGM00)|(0<<CS02)|(0<<CS01)|(1<<CS00); // сброс при совпад, тактора частота таймера 16 МГц
    TCNT0=0;
    /*
    Тактовая частота микроконтроллера 16 МГц
    Тактовый сигнал таймера равен 16000000 Гц/1 = 160000000 Гц.
    Время одного такта таймера 1/16000000 = 0.65*10^-8 с
    Время одного такта нужной нам частоты 1/16000000 = 0.65*10^-8 с
    Сколько тактов таймера укладывается в 0.65*10^-8 с ? 0.65*10^-8/0.65*10^-8 = 1 тактов
    1 в двоич. системе = 1
    */
    OCR0=0b1;

    #asm(«sei»)

    while (1)
    {
    TIMSK = (1<<OCIE0);
    }

    }

  55. Gordienko Victor говорит:

    что именно ?
    смтрю на ножке D.1 и частота меньше 16 МГц

  56. Gordienko Victor говорит:

    Тактовая частота микроконтроллера 16 МГц, в микроконтроллере clock frequency и фьзы на 16 МГц поставил.

    • DI HALT говорит:

      И хочешь чтобы у тебя нога дергалась со скоростью 16мгц??? Не будет такого никогда!

      У тебя передерг ноги на прерывании. А теперь давай посчитаем:

      Переход к прерыванию. Если писать на асме — два такта минимум. Если на Си — не менее десятка (сохранение регистров)

      Передергивание вывода — четыре такта, минимум. На Си может и гораздо больше быть.

      Выход из прерывания — два такта в лучшем случае, и тот же десяток в худшем.

      Итого, на обработку передерга ножки уходит в среднем не меньше чем 25 тактов. И о каких 16мгц на ноге может идти речь? В лучшем случае 16/25мгц и то я сомневаюсь.

  57. Gordienko Victor говорит:

    Все понял ))) Примерно так и получалось немножко меньше… Большое спасибо!

  58. ZMIY говорит:

    [b]Предделитель[/b]
    [quote]Делить можно на 8, 32, 64, 128, 256, 1024.[/quote]
    Это опечатка или на 16 делить нельзя ?

  59. Seaman говорит:

    Ди, приветствую! Необходимо твоё компетентное мнение. Есть задача: на ногу Mega168 циклически поступают 6 импульсов и два пропуска (11111100). Подскажи, пожалуйста, с какой стороны подойти к методу детектирования первого импульса в посылке? Частота импульсов внутри одной посылки постоянная, но может меняться от посылки к посылке. Задача сводится к отлавливанию двух пропусков, за которыми пойдёт первый импульс. Но как это сделать, пока ума не приложу. В среднем, частота импульсов в посылке от 0,6 до 5 кГц. Частота кварца МК — 20 МГц.

    Идея у меня пока, упрощённо, такая:
    1. Ловим на входе импульс (какой он по счёту, мы не знаем пока), запускаем таймер и ждём следующего импульса
    2. Получаем следующий и измеряем его период.
    3. Через измеренное время ждём импульс или его отсутствие.
    4. Если импульса не было, считаем, что это был первый пропуск, и ждём второй через измеренное время.
    5. Если снова не было импульса, отмеряем период и, получив импульс, считаем его первым…

    Но всё это будет жутко «плавать», как мне кажется. И не будет работать, если первым придёт 6-ой по счёту импульс. В общем, если есть идея, комрад, предложи. Заранее благодарен!

    • DI HALT говорит:

      Хм… А если мы поймаем между двумя пропусками? Не вариант! Т.к. у нас включение произвольное, то что нам мешает потупить какое то время, пожрать импульсов поболее, чтобы разобраться в ситуации лучше? Тогда можно будет точно сказать, что это у нас и где.

  60. Ruslan-maniak говорит:

    Озадачен вот каким вопросом: в этой процедуре
    Timer0_OV: PUSHF
    PUSH R17
    PUSH R18
    PUSH R19

    INCM TCNT

    POP R19
    POP R18
    POP R17
    POPF

    RETI

    Я не понимаю что такое «INCM TCNT». Макрос мы такой не добавляли. Оператора у нас такого нет. Вообще до этого ни где INCM не упоминалось. Будьте добры, объясните откуда это и для чего.

  61. Алекс говорит:

    Доброго времени суток уважаемые форумчане!
    Помогите пожалуйста разобраться с таймером Tiny25.

    Выше в коментах уже встречал подобную проблему у devmind.livejournal.com, но человек пишет, что программа стала криво. У меня все хуже. Пробовал на трех разных машинах — результат один и тот-же…

    Итак суть проблемы.
    Использую Attiny25, пишу в AVR Studio (последняя версия с сервис паками)…
    ТС0 использую под протокол 1-Wire, с ним все в порядке, работает как нужно. А вот ТС1 не запускается. На зарубежных форумах вычитал рекомендации использовать AVR Simulator2. Попробовал, да, в нем таймер запускается, но возникают проблемы с прерываниями, INT0 работает не правильно (срабатывает по любому изменению на входе, а не по спаду, как нужно), биты в порте В как то не так становятся, в общем полный геморой. Кроме того, в самом счетчике ТС1 не очищается счетный регистр TCNT1, непонятки при попытке сбросить предделитель бит PSR1 в регистре GTCCR, и т.д.
    В железе пока не воплощал. Грешу на косяки AVR Studio. Может кто что еще подскажет полезного.
    Вот кусок кода.
    Всем зареннее спасибо.

    ; === Инициализация таймер 1 ==========================================================

    outi OCR1A,TI ; в канал А время импульса запуска симистора
    out OCR1B,calc_brigh ; в канал В время до включения симистора

    * * *

    ; === Запуск ТС1 ======================================================================

    sbrp TIMSK,(1<<OCIE1A) ; разрешаем прерывание по совпадению канал А
    sbrp TCCR1,(1<<CS12) ; запускаем ТС1 с предделителем 8
    VS_On ; включить оптопару

    * * *

    ; === Перезапуск ТС1 ==================================================================

    clri TCCR1 ; останавливаем ТС1
    clri TCNT1 ; сбрасываем ТС1

    cbrp TIMSK,(1<<OCIE1А) ; запрещаем прерывание по совпадению канал А
    sbrp TIFR,(1<<OCF1В) ; сбрасываем флаг совпадения канал В
    sbrp TIMSK,(1<<OCIE1В) ; разрешаем прерывание по совпадению канал В
    sbrp TCCR1,(1<<CS12) ; запускаем ТС1 с предделителем 8

    * * *

    ; === Остановка ТС1 ===================================================================

    clri TCCR1 ; останавливаем ТС1
    clri TCNT1 ; сбрасываем ТС1
    VS_Off ; выключаем оптопару

  62. dima_m говорит:

    DI привет, не могу понять, почему в студии не запускаются таймеры у Attiny25,45,85? Может сталкивался? Глюк студии чтоли? Пять часов убил а они не запускаются. Надо бы попробовать в железо прогу зашить и там проверить.

    • DI HALT говорит:

      Прескалеры выставил, а они не тикают? У этих тинек вроде бы PLL еще есть на таймеры. Там не все так просто. МОжет ты его забыл включить?

      • dima_m говорит:

        Да прескалы выставил а они не тикают. У attiny25,45,85 у таймеров нет PLL регистров. Сдается мне что это глюк студии, наверное. Прикольно, как теперь программу отладить?

        • Алекс говорит:

          ATtiny25 адреса 0х27(0х47) регистр PLLCSR… Все там есть, только по умолчанию PLL выключен. У меня Т/С0 работает нормально, а вот с Т/С1 глюки. И скорее глюки в программе AVRStudio… Все с кем общался рекомендуют пробовать воплощать проект в железе.
          Как вариант, попробуй симулировать работу таймеров в AVR Simulator2. Но учти, что при этом могут не корректно работать прерывания…
          ;-)

          • dima_m говорит:

            Ну да точно есть. ШОК … не узрел. Просто этот PLL регистр в attiny26 прямо в таймере виден а в аттину 25 закопали его в другое место. Да надо внимательнее даташиты смотреть. Обычно это я делаю в последнюю очередь. А надобы наоборот сперва даташит потом все остальное. Просто английский не знаю поэтому косяк. Начать учить чтоли?

            1.А ты в английском шариш? Как переводиш? Какой то прогой?
            2.А вчем прикол между AVR Simulator2 и AVR Simulator?

          • Алекс говорит:

            Английский не знаю, перевожу Socrat personal (качество перевода, на мой взгляд, лучше чем у Промта)…

          • dima_m говорит:

            О! смотри че нарыл в коментах одной из статей:

            simulavr2 поддерживает больше функций и заточен под новые модели процов.
            Например обычный simulavr не поддерживает запуск и моделирование таймера1 в attiny25.

            Для тактового сигнала таймера1 примененен хитрый pll генератор на 64 мгц (PLL — означает генератор с фазовой автоподстройкой частоты). Через некоторое время после включения он синхронизируется с основным тактовым генератором процессора (чтобы их частоты отличались в целое число раз) и при этом выставляет в младший бит регистра pllcsr единичку.
            Только после появления этой единички можно настраивать коэффициент деления и выставлять задержку таймера 1.

            Ну попробуй допетри что поддерживает и что не поддерживает это AVR Simulator? ШОК!

          • Алекс говорит:

            О! Полезная инфа. Я этого раньше нигде не читал. Попробую…

            С AVR Simulator2 есть свои глюки. Я нарывался на то, что не правильно срабатывают внешние прерывания. То есть настроил срабатывание INT0 по спаду импульса, а он срабатывает по любому изменению уровня (как PCI)…
            При этом в AVR Simulator прерывания отрабатываются правильно, а Т/С1 не запускается…
            __________
            В стране бардак, а вы хотите чтобы симуляторы нормально работали :-)

          • dima_m говорит:

            Слушай Алекс, а ты каким программатором пользуешся? Случайно не аппаратным FT232R который у DIHALTA есть на сайте.

          • Алекс говорит:

            Не… Использую USBasp.

            Кстати, нашел у себя кусок переведенного даташита на таймер счетчик 1 ATtiny25… Перевод не полный и кривой, но для понимания достаточный… Давай почту, перешлю…
            Да, еще, PLL используется если Т/С работает в асинхронном режиме (частота задается собственным кварцем или внешним генератором)… Если использовать генератор центрального процессора, в PLL, по идее лезть не нужно…

          • dima_m говорит:

            давай почитаю dmalash@gmail.com

          • dima_m говорит:

            Хотел в ATMEL написать отчет об ошибках в студии, да чета не нашел как. На сайте все вечно у них меняется.

          • dima_m говорит:

            Слушай Алекс, в общем фишка такая. В attiny 26 как равно и attiny 25_45_85 таймеры Т0 запускаются. А вот Т1 не запускаются как мы выше и говорили. НО! Оказываются запускаются все же и Т1. Просто студия не отображает что регистр TCNT1 тикает. А он все же тикает где то в глубинах студии. Так как прерывания по переполнению и по сравнению работают. А однажды при очередной эмуляции вдруг регистр TCNT1 у меня затикал циферки побежали, но это было только один раз. Думаю эта инфа и DI полезной будет. Но это я говорю о attiny 26, думаю что и в attiny 25_45_85 та же песня.
            А все таки земля вертится!

          • dima_m говорит:

            Так то ж в нашей стране, а программа то американьская.

  63. v.m.s. говорит:

    «Поэтому предделители можно и нужно сбрасывать. Также надо учитывать и то, что предделитель един для всех счетчиков, поэтому сбрасывая его надо учитывать то, что у другого таймера собьется выдержка до следующего тика, причем может сбиться конкретно так.»
    А как выкручиваться — сохранять содержимое TCNTх другого таймера в стеке или где-нибудь в ОЗУ перед сбросом предделителя?

    • DI HALT говорит:

      ТСNT не сбивается, он у каждого свой. Не путай. Просто в ТСНТ другого таймера +1 придет позже если там юзается предделитель и его сбросили.

  64. v.m.s. говорит:

    Насторожила такая возможная ситуация:
    »
    А если ты будешь сбрасывать предделитель в цикле, во благо первого таймера (у которого предделитель, например, 1:64), чаще чем раз в 1024 такта, то второй таймер так никогда и не тикнет,»

    Как разруливать — правильным построением алгоритма? Или?

  65. МК ATTiny26. Почему в симуляторе не работает такой код?
    ldi r16, (1<<PLLE) ; Enable PLL
    out PLLCSR, r16

    waitPLL:
    in r16, PLLCSR ; Wait for PLL to lock (approx. 100ms)
    sbrs r16, PLOCK
    rjmp waitPLL

    in r16, PLLCSR ; Set PLL as PWM clock source
    ldi r17, (1<<PCKE)
    or r16, r17
    out PLLCSR, r16

    PLOCK на нуле все время. Ждал довольно долго. На железе не пробовал. Сколько примерно по времени занимает синхронизация? Может что-то еще может влияет на этот PLOCK?

  66. CS говорит:

    А подскажет кто-нибудь формулу для расчёта таймера 0 в ATMega8? Который 8 битный.
    Только не программы-калькуляторы, а формулу! Чтобы знать что надо записывать в таймер, чтобы получать переполнения каждые t мксек, при частоте f и предделителе n?
    Я бьюсь, бьюсь — но никак не догоню. Калькулятор-то мне посчитал всё красиво. Но я хочу нормальную прогу себе через define а не с магическими числами.

    • DI HALT говорит:

      Эмм… а чо там считать то?

      скорость тиков
      Ftim = Fcpu/Prescaler

      Длительность тика в секундах:
      Tick_leng = 1/Ftim

      На задержку Time надо
      N=Time/Tick тиков.

      Задержка это число до переполнения, т.е. в регистр вносим 255-N

      • CS говорит:

        Угу, спасибо! У меня дело в расстановке скобок было, получается. Вышла такая хрень:
        n = (RTHiTimerTick * 0,000001) / (RTHiTimerPrescaler / RTCoreClock);
        Потому что по действиям на бумажке оно верно выходило, а вот в формулу собрать не хватало ума…

  67. alpan говорит:

    Подскажите можно ли как-нибудь таймер сделать инвертированным? Имеется ввиду, что таймер тикает спадами в ноль на выходе T1.

    • DI HALT говорит:

      Таймерт тикает всегда вверх, направление счета тут задать нельзя.

      А что значит на выходе Т1? Нужна другая полярность выходных импульсов? Это можно сделать изменив биты COMxx

      • alpan говорит:

        В ночи видимо голова уже не соображала, и я не совсем правильно выразился =)
        Да, нужно именно изменить полярность выходных импульсов. У меня как бы было два варианта: дополнительно использовать внешний инвертор, либо инвертировать средствами МК. Сейчас попробую разобраться с битами COMxx. Спасибо.

      • alpan говорит:

        Ди, насколько я понял, этими битами задается состояние выхода OC1x, но не T1. Как получить на выходе T1 инвертированные пульсации по переполнению таймера я так и не воткнул (

        • DI HALT говорит:

          А блин, тебе надо считать импульсы по другому фронту чтоль?

          • alpan говорит:

            Ситуация простая: я по таймеру хочу запускать АЦП, но там вход запуска расчета инвертированный. То есть он запускается, когда на этом входе «0″, в остальное время надо подавать «1″.

          • alpan говорит:

            И да, АЦП у меня внешний.

          • alpan говорит:

            Таков сигнал на выходе T1 по умолчанию:

            _____|`|__________|`|__________|`|____

            А мне надо NOT(T1), т.е.:

            ««`|_|«««««|_|«««««|_|««`

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

          • DI HALT говорит:

            На каком, нахрен, ВЫХОДЕ Т1? Т1 это СЧЕТНЫЙ ВХОД. На нем не может быть никакого сигнала. Какой контроллер?

          • alpan говорит:

            Блин, я просто думал что когда таймер тикает он выдает на соответствующую лапу (T1 или T0) импульсы во время тика. Туплю серьезно. Спасибо за попытку вникнуть в весь этот бред =)

          • DI HALT говорит:

            Сделай проще — по прерыванию переполнения выдавай на любой нужной ноге любой нужный уровень. Думаю комбинации
            0
            NOP
            NOP
            NOP
            1

            Будет достаточно для того, чтобы АЦПшка запустилась

          • alpan говорит:

            Да, я как раз об этом сейчас подумал. Просто изначально природу таймера как захотел, так и понял, а потом мучался =)

  68. DIMA040891 говорит:

    Здраствуйте! Возник вопрос могут ли таймеры контроллеров tiny работать в асинхронном режиме?

  69. DIMA040891 говорит:

    А когда там эти пины называются???? в 2313 чё то я не нашёл входов TOSC1 и TOSC2.
    И вот тут ппутный вопрос возник, в некоторых моделях МК(вроде в новых) в альтернативных функциях порта есть надпись PCINT8 например. Что это такое? вроде как некое прерывание но не могу понять как с ним работать.

    • DI HALT говорит:

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

      А PCINT это прерывания по изменению на ноге. Позволяет отследить прерывание на любой из этих ног и бросить по вектору. Где мы уже определяем на какой ноге было вызвано прерывание и реагируем.

  70. taimag говорит:

    Скажи, а как ты определил что если на часовом кварце делать, то будет 128 переполнений в секунду?

  71. nikos говорит:

    Всем привет! Не могу побороть программу-частотомер, прошу помощи. Код перепроверил вдоль и поперёк, видимо, что-то упустил.

    Листинг по ссылке, описал максимально подробно, в коде всё с комментами.

    http://easyelectronics.ru/repository.php?act=view&id=73

    Спасибо всем, кто откликнется.

    • nikos говорит:

      Нашёл проблемную строку:

      LDI freq,TCNT0 //получаем значение счётного регистра

      Почему-то во «freq» всегда попадает значение «50″. При этом пробовал другие регистры напрямую, без дефайна, результат такой же….
      Разве из TCNT0 нельзя получать значение при помощи LDI??? Щас буду пробовать LD….

      А значение константы «tic» всегда равно значению счётного регистра «TCNT0″. Пробовал другое имя для константы — то же самое…..
      Бред какой-то…. Либо я чего-то не понимаю, либо студия выносит мне мозг…..

      • DI HALT говорит:

        Налицо полное непонимание языка ассемблер. Это тебе не Си.

        Команда LDI грузит ТОЛЬКО константу и только в старшие регистры (R16-R17). И ничего более. Для других действий нужны другие команды. см. даташит.

        Что же ты делаешь свой командой

        LDI freq,TCNT0

        ты во FREQ грузишь значение которому рана метка TCNT0 она равна 50. Т.к. регистр таймера TCNT лежит в 50й ячейке памяти. Это всего лишь переопределенная константа. Где то в inc файлах есть нечто вида .equ TCNT0 = 0×50 чтобы тебе не приходилось смотреть в даташит на адреса ячеек.

        Как же взять собственно значение из регистра перферии? Для этого есть команды IN и OUT ин берет ,аут кладет.

        Так что

        IN freq, TCNT0

        При этом freq должен быть регистром и только регистром от R0 до R16. Если же у тебя freq в памяти, как константа. То:

        IN R16,TCNT0
        STS freq, R16

      • DI HALT говорит:

        Внимательно смотри систему команд, она в даташите в разделе Instruction Set Summary там все команды есть, и для каждой укзано:

        С каким операндами она может работать, какие флаги она изменяет и изменяет ли, какое время в тактах нужно на команду.

    • nikos говорит:

      Перечитал вот эту статью:
      http://easyelectronics.ru/avr-uchebnyj- … h-rom.html

      Правильно было так:
      IN freq,TCNT0 //получаем значение счётного регистра

      Но всё равно не понятно, почему константа, объявленная через .equ всегда принимает значение счётного регистра «TCNT0″??? Или это всего-лишь ошибка трассировщика???

      • DI HALT говорит:

        Она не принимает значение счетного регистра. И это не ошибка. Просто компилятор подставляет вместо константы нужный адрес в команду и все. Чтобы тебе не держать ее в голове. Вот только компилятору то все равно что это за константа? Она может быть равна некому жестко прибитому физическому адресу (регистр периферии), а может быть назначена тобой от балды. Для него это некая абстрактная цифра которую он если может куда то занести, то занесет. Если не сможет (например команда не позволяет грузить константы или она вылазит за диапазон) то ругнется ошибкой.

  72. nikos говорит:

    Спасибо ) Уже вычитал из твоей же статьи )))

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