|
Микроконтролеры AVR. Джон Мортон. Ошибки в книге? |
|
|
|
Oct 27 2009, 12:40
|

Участник

Группа: Участник
Сообщений: 43
Регистрация: 24-08-07
Из: Санкт-Петербург
Пользователь №: 30 043

|
Добрый день! Речь идет не о банальных опечатках, а о более глобальных ошибках. Например, вот фрагмент кода, который формирует секундную задержку для микроконтроллера AT90S1200. Кварц на 2.4576 МГц. Код ldi Count30,30 ldi Mark80,80 TimeLoop: out TCNT0,temp cp temp,Mark80 brne TimeLoop subi Mark80,-80 dec Count30 brne TimeLoop Понимаю, что вместо out TCNT0,temp должно быть in temp,TCNT0. Быстрей всего это косяк переводчика. Но насколько работоспособен сам принцип формирования такой задержки? Насколько я понял, таймер считает до 255 и начинает заново, значит после 3-х циклов, когда Mark80 превысит значение 255 равенства не наступит. Правильно ли я понимаю, или есть какие-то особенности?
|
|
|
|
|
Oct 27 2009, 13:46
|

Участник

Группа: Участник
Сообщений: 43
Регистрация: 24-08-07
Из: Санкт-Петербург
Пользователь №: 30 043

|
Цитата А как байт (Mark80) может превысить 255? Рабочие регистры 8 разрядные, следовательно максимальное шестнадцатеричное число может поместить FF или 255 в десятичном формате. Код subi Mark80, -80 на 4 итерации, следовательно мы превысим 255 Я на это не обратил внимание. Я имел ввиду, что таймер только до 255 досчитает, потом обнулится. Или я что-то не так понимаю? Цитата А какую частоту считает TCNT0? СК/1024
|
|
|
|
|
Oct 27 2009, 14:02
|

Гуру
     
Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954

|
Цитата(Merovey @ Oct 27 2009, 15:40)  Но насколько работоспособен сам принцип формирования такой задержки? Вы не привели начало программы... Но, если инициализация проведена "правильно", и пределитель = 1024, то 30 * 80 * 1024 = 2457600. Вроде всё правильно. Насколько это жизнеспособно? Имхо, такие (по продолжительности) задержки в программе - самоубийство (разве, что что-то совсем простое проектируется, но таких устройств в жизни не бывает). Ставить задержки в программу можно (нужно) только если требуемая задержка сопоставима по продолжительности со временем работы процедуры обработки прерывания. Учебный пример может быть далёк от жизни, поскольку он иллюстрирует некий приём, метод работы. Цитата(Merovey @ Oct 27 2009, 16:46)  Код subi Mark80, -80 на 4 итерации, следовательно мы превысим 255 Регистр таймера - 8 бит, и переменная Mark80 - тоже. В переменной Mark80 хранится значение регистра таймера, уменьшенное на 80. Т.е. через 80*1024 тактов генератора счетный регистр таймера примет значение, такое же как и Mark80, значение в Mark80 "отодвинем" ещё на 80 и счетчик Count30 уменьшим на единицу. И так 30 раз (первоначальное значение счетчика). Итого: через 30*80*1024 тактов покинем TimeLoop (если строго говорить, то плюс ещё сколько-то тактов).
|
|
|
|
|
Oct 27 2009, 14:34
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Как переводчик может перепутать: "out TCNT0,temp" и "in temp,TCNT0"??? Да и вообще зачем переводчику править текст проги? Как говорится "если в слове МИР перепутать 3 буквы, то получится нецензурщина". Это явный косяк автора. Причём похоже на всю голову. Исходя из приведенного вами текста (с учётом замены out на in) мы видим следующее: 1) TCNT не устанавливается в цикле. Таким образом после сравнения он продолжает считать дальше. 2) Mark80 вычитается по 80, что вообще необъяснимо. Первое сравнение получим при значении таймера 80 Последующие сравнения будут происходить ч/з 176 Таким образом округлённо получим задержку 176*30+80=5380. При делителе 1024 это составит чуть больше 2 сек. Даже если предположить, что я ошибся в расчётах и задержка высчитана правильно, то это всё равно из ряда вон выходящий пример с дурным стилем. Стандартным видом задержки является что-то в виде следующего. Код wait: ldi w1,high(FCnt) ; Загрузить счётчик задержки ldi w2,low(FCnt); Загрузить счётчик задержки wt01: ; rjmp pc+1 ; rjmp pc+1 ; rjmp pc+1 ; rjmp pc+1 ; rjmp pc+1 ; rjmp pc+1 subi w2,1 sbci w1,0 brcc wt01 Где длительность приблизительно равна (1+1+2)*FCnt -1+2 такта (без учёта прерываний). Если к примеру мы разрэмим 2 rjmp, то результат будет (2+2+1+1+2)*FCnt -1+2 такта и так далее. В качестве ядра (там где стоят rjmp) мы можем считать события. В том числе переполнения таймера или элементарные отсчёты таймера. Например при частоте 2.4576 при делителе 1024 один такт таймера составит 0.416666 мс соответственно секунда составит 2400 тактов. Если мы возьмём по 80 (как в примере), то получим 30*80=2400. Соответственно прога быдет выглядеть так (без инициализации таймера и сохраняя стилистику автора) Код wait_1s: ldi w1,30 ; Загрузить счётчик задержки ldi temp,0 out TCNT0,w2 ldi w2,80 wt01: in temp,TCNT cp temp,w2 brne wt01 ldi temp,0 out TCNT0,w2 dec w1 brne wt01 Реально обычно так не делают. Обычно исходят из того, что 2400 / 256 = 9.... Итого получается 9 полных циклов + 96 тактов. Задержка выглядит так: Код wait_1s: ldi temp,-96 ; Загрузить счётчик задержки out TCNT0,temp sbi TIFR,TOV0 ldi temp,9 wt01: sbis TIFR,TOV0 rjmp wt01 sbi TIFR,TOV0 dec temp brne wt01
|
|
|
|
|
Oct 27 2009, 15:03
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(SasaVitebsk @ Oct 27 2009, 16:34)  2) Mark80 вычитается по 80, что вообще необъяснимо. Вычитается по "минус 80". Итого сравнения через 80 30*80*1024 = 2457600
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Oct 28 2009, 07:32
|

Участник

Группа: Участник
Сообщений: 43
Регистрация: 24-08-07
Из: Санкт-Петербург
Пользователь №: 30 043

|
Цитата(Палыч @ Oct 27 2009, 17:02)  Ну один из примеров, где используется этот принцип, вполне, мне кажется, применим. Как система управления на светофоре. А насчет отодвинем 30 раз? после 4 раз мы же переполним регистр Mark80? Да и таймер, получается, столько не отсчитает.. Цитата(SasaVitebsk @ Oct 27 2009, 17:34)  Стандартным видом задержки является что-то в виде следующего. Что-то похожее есть у автора, например так же секундная задержка: Код ldi Delay1,0x00 ldi Delay2,0x35 ldi Delay3,0x0C
Loop: subi Delay1,1 sbci Delay2,0 sbci Delay3,0 brcc Loop Предыдущий способ предлагается использовать когда нужно во время работы счетчика выполнять другие действия. Но, мне кажется, Вы правы. Без инициализации таймера не работоспособен пример.. Во всяком случае я не понимаю, как он может работать
Причина редактирования: Нарушение п.3.4 правил форума.
|
|
|
|
|
Oct 28 2009, 07:57
|

Гуру
     
Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954

|
Цитата(Merovey @ Oct 28 2009, 10:32)  А насчет отодвинем 30 раз? после 4 раз мы же переполним регистр Mark80? Да и таймер, получается, столько не отсчитает.. Переполним... Но, и счетный регистр таймера работает с переполнением. Поэтому я и употребил - "отодвинем". Цитата(Merovey @ Oct 28 2009, 10:32)  Без инициализации таймера не работоспособен пример.. Во всяком случае я не понимаю, как он может работать Сама книга - мне недоступна, и Вы привели из неё толко выдержку. Конечно, инициализация таймера должна быть (как минимум установить прескалер). В книгах бывает объясняют работу с устройствами "по-частям": отдельно - инициализация, отдельно - использование... Куски кода предполагается - объединит читатель. Может и в этой книге так?
|
|
|
|
|
Oct 28 2009, 12:31
|

Участник

Группа: Участник
Сообщений: 43
Регистрация: 24-08-07
Из: Санкт-Петербург
Пользователь №: 30 043

|
Ну вот пример программы из книги, где это используется. Поочередно включаются светодиоды: CODE .device at90s1200 .nolist .include "C:\Program Files\Atmel\AVR Studio\1200def.inc" .list
.def temp=r16 .def Mark240=r17 .def Counter=r18 .def Speed=r19
rjmp Init
Init: ser temp out DDRB,temp ldi temp,0b11111100 out DDRD,temp
ldi temp,0b00000001 out PortB,temp
ldi temp,0b00000011 out PortD,temp
ldi temp,0b00000101 out TTCR0,temp
ldi Mark240,240 ldi Counter,5 ldi Speed,5
Start: ; Проверяется нажатие кнопки уменьшения скорости sbic PinD,0 rjmp UpTest inc Speed cpi Speed,11 brne ReleaseDown dec Speed
ReleaseDown: sbis PinD,0 rjmp ReleaseDown
; Проверяется нажатие кнопки увеличения скорости UpTest: sbic PinD,1 rjmp Timer dec Speed brne ReleaseUp inc Speed
ReleaseUp: sbis PinD,0 rjmp ReleaseUp
; Формируем задержку Timer: in temp,TCNT0 cp temp,Mark240 brne Timer
subi Mark240,-240 dec Counter brne Start
;Меняем СИД mov Counter,Speed
in temp,PortB lsl temp brcc PC+2 ldi temp,0b00000001
out PortB,temp rjmp Start Цитата(Палыч @ Oct 28 2009, 10:57)  Переполним... Но, и счетный регистр таймера работает с переполнением. Поэтому я и употребил - "отодвинем". но переполнение происходит же не одинаково? время разве не уедет? я понимаю так, достигли мы в Mark80 значения 240. Прибавляем 80, получаем переполнение. А Timer считает дальше (до 255), и равенство будет не ранее, чем переполнится Timer и дойдет до нужного значения, так? Это время будет соответствовать тому же периоду, что и увеличение со 160 до 240?
Причина редактирования: Нарушение п.3.4 Правил форума.
|
|
|
|
|
Oct 28 2009, 13:11
|

Гуру
     
Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954

|
Цитата(Merovey @ Oct 28 2009, 15:31)  Код .device at90s1200 ldi temp,0b00000101 out TTCR0,temp Вот это - и есть инициализация таймера. Остальные регистры = Initial Value - "значение при включении/сбросе". P.S. Уж не знаю чья ошибка, но регистр называется TCCR0 Цитата(Merovey @ Oct 28 2009, 15:31)  но переполнение происходит же не одинаково? время разве не уедет? Почему же происходит не одинаково? Пусть счетный регистр таймера и Mark80 в текущий момент имеют значение 240. Произошло сравнение (они равны); прибавляем к Mark80 значение 80 (вычитаем минус 80) - получаем Mark80 равным 64 (240+80=320; 320 -не помещается в байт; один разряд с весом 256 вылез за старший разряд байта; 320-256=64). На таймер/счетчик поступают с пределителя импульсы; счетный регистр их считает: 241 242 ... 254 255 0 1 ... 63 64 Значение на счетном регистре достигнет 64 через те же 80 импульсов от пределителя. P.S. Может так понятнее: Увеличить число на 80 - абсолютно то же самое, что прибавить 80 раз по единице.
|
|
|
|
|
Oct 29 2009, 06:02
|

Участник

Группа: Участник
Сообщений: 43
Регистрация: 24-08-07
Из: Санкт-Петербург
Пользователь №: 30 043

|
Цитата(Палыч @ Oct 28 2009, 16:11)  P.S. Уж не знаю чья ошибка, но регистр называется TCCR0 Это моя ошибка, точнее описка. Цитата(Палыч @ Oct 28 2009, 16:11)  Почему же происходит не одинаково? Пусть счетный регистр таймера и Mark80 в текущий момент имеют значение 240. Произошло сравнение (они равны); прибавляем к Mark80 значение 80 (вычитаем минус 80) - получаем Mark80 равным 64 (240+80=320; 320 -не помещается в байт; один разряд с весом 256 вылез за старший разряд байта; 320-256=64). На таймер/счетчик поступают с пределителя импульсы; счетный регистр их считает: 241 242 ... 254 255 0 1 ... 63 64 Значение на счетном регистре достигнет 64 через те же 80 импульсов от пределителя. Теперь понял. Спасибо. Насчет первичной инициализации понятно. Я думал, еще в самом цикле нужно определять ).
|
|
|
|
|
Nov 1 2012, 08:00
|
Участник

Группа: Участник
Сообщений: 22
Регистрация: 19-06-07
Из: Челябинск
Пользователь №: 28 542

|
У меня есть вопрос какраз по этой книге. Когда мы рассматриваем задержки с применением команд subi и sbci, мы расчитываем число которое нам нужно записать в регистры по формуле T*F/x. х у нас приведен в таблице для разных диапазонов времени и тактовой частоты. ВОПРОС, КАК МЫ ПОЛУЧАЕМ ЭТОТ Х?
Сообщение отредактировал Valek - Nov 1 2012, 08:01
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|