Научите писать под AVR

Обсуждаем контроллеры компании Atmel.
Ответить
Идёт направо - песнь заводит, Налево - сказку говорит.
Аватара пользователя
Сообщения: 5000
Зарегистрирован: Чт апр 21, 2011 17:55:50
Откуда: Иркутск

Сообщение СКАЗОЧНИК »

Уф... видимо, не умею я вопросы задавать... :cry:

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

Для чайника....

Можно где почитать с картинками? Где разряды нарисованы???? А то я уже всякую арифметику двоичную смотрю...

Вот пример... есть temp, в нем младший байт числа и есть temp1, в котором старший байт.

Дальше

subi temp, 120 ; Здесь из младшего байта вычли 120. А если в младшем байте всего было предположим число 35... Значит надо искать остатки в старшем байте...

Откуда берется флаг переноса???
Станислав
Реклама
Друг Кота
Аватара пользователя
Сообщения: 6339
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Сообщение Jack_A »

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

div120:	ldi	r24,0
	ldi	r25,0
d5:	subi	r16,120
	sbci	r17,0
	brcs	d_fin
	adiw	r24,1
	rjmp	d5
d_fin: ret
Как-то так . В R16,R17 - делимое, в R24,R25 - результат (мл. - ст. ) . Не самый быстрый способ. Например, для 65535 занимает свыше 3800 машинных тактов.
Бит переноса С устанавливается, если при вычитании 7-й бит числа, из которого вычитают, меняется из 0 в 1.

C: Rd7 ·K7+ K7 · R7 +R7 ·Rd7
Set if the absolute value of the constant plus previous carry is larger than the absolute value of Rd; cleared otherwise.
Реклама
Идёт направо - песнь заводит, Налево - сказку говорит.
Аватара пользователя
Сообщения: 5000
Зарегистрирован: Чт апр 21, 2011 17:55:50
Откуда: Иркутск

Сообщение СКАЗОЧНИК »

Тааааак... Вот что до меня начало доходить...

оператор subi вычитает из регистра константу и если регистр был меньше константы в любом случае появляется флаг переноса (С).
и оператор sbci вычитает из старшего регистра 0 и флаг переноса. Т.е., если флага не было, то ничего и не вычитает. А если флаг был от предыдущего оператора, то вычитает его.

Правильно?

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

В чем смысл следующих двух операторов?
Станислав
Друг Кота
Аватара пользователя
Сообщения: 6339
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Сообщение Jack_A »

СКАЗОЧНИК писал(а): оператор sbci вычитает из старшего регистра 0 и флаг переноса.
Не совсем. Вычитает не обязательно 0, а столько, сколько ему заказано. SBCI R16,7 например. В нашем примере 0, потому что делитель однобайтовый. При следующем вычитании из младшего байта ничего страшного не случится, т.к. там не SBCI , а SUBI, и он предыдущий флаг игнорирует.
А дальше : если С установлен при SBCI старшего байта, то хана, делимое исчерпано и бегом на выход, а если нет - длинное (2-байтовое) прибавление 1 и на повтор цикла. Потому и выбраны R24,R25, что только эта пара ( не считая X,Y и Z ) допускает операцию с переносом в старший байт.
Рекомендую освоить какую-нить Астудию, в ней при пошаговой симуляции очень четко видно, что кто у кого забирает и что где устанавливает. Студия - лучший учитель, I think .
Реклама
Эиком - электронные компоненты и радиодетали
Идёт направо - песнь заводит, Налево - сказку говорит.
Аватара пользователя
Сообщения: 5000
Зарегистрирован: Чт апр 21, 2011 17:55:50
Откуда: Иркутск

Сообщение СКАЗОЧНИК »

Jack_A писал(а):В нашем примере 0, потому что делитель однобайтовый.
Вот эта фраза меня ввела в ступор... ( Я понял так, что из старшего байта надо вычитать только флаг переноса, поэтому и в команде написан 0... т.к. ничего больше не требуется.
Jack_A писал(а):При следующем вычитании из младшего байта ничего страшного не случится
Как же не случится? А где же занимаемая у старшего единица будет? Она же должна добавится в младший????
Jack_A писал(а):то хана, делимое исчерпано и бегом на выход,
Jack_A писал(а):прибавление 1 и на повтор цикла.
А вот это дошло. ))) Когда невозможно больше отнимать, тогда выход, а двухбайтовое прибавление можно заменить однобайтовым - это и будет результат без остатка.
Jack_A писал(а):Рекомендую освоить какую-нить Астудию
А вот чтобы в студии проверить, думаю, надо понять сначала что туда написать, чтобы это проверить. )))) Не будешь же абракадабру всякую эмулировать. ))))

А за помощь в любом случае спасибо!!! :beer:

З.З.Ы. Я короче, сделал так:

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

	INT_0:
		
		PUSHF

			clr temp3

			cli
				in temp, TCNT1L
				in temp1, TCNT1H

			T:	subi temp, 120
				sbci temp1, 0
				brcs FIN

					inc temp3
					rjmp T

			FIN:
			
			sei

		POPF

		ldi temp, 0x00          ; Обнуляем счетчик
      	        out TCNT1H, temp
		out TCNT1L, temp

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

Однако вопрос про то, как перенос преобразует младший байт остается...
Станислав
Реклама
Друг Кота
Аватара пользователя
Сообщения: 6339
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Сообщение Jack_A »

СКАЗОЧНИК писал(а):
Как же не случится? А где же занимаемая у старшего единица будет? Она же должна добавится в младший????
Мы занимаем единицу не у старшего байта, а из флага С. В результате если вычитаемое будет больше уменьшаемого, взводится бит С, и фактически тогда будем вычитать не А - В , а ( А + 256 ) - В. Взведенный бит С в последующей команде SBCI Rx,0 как раз и вычитается из старшего байта.
З.З.Ы. Я короче, сделал так:
А вот так делать нельзя ! Длинные вычисления в прерывании - никогда не используйте ! В прерывании устанавливается флаг, в главном цикле посматриваем на этот флаг, и когда он установлен, производим какие-то телодвижения и сбрасывается флаг. Приблизно так :

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

   ;  в  прерывании  :
        inc    FLAG

    ;  в  главной :
        tst     FLAG
        breq   bypass
        call     convers
        sub     FLAG,FLAG
bypass:

Само сабой разумеется, время выполнения преобразующей подпрограммы должно быть меньше, чем интервал срабатывания прерывания таймера, иначе получится каша : не успели обработать одно, а уже следующий самосвал разгружается :)
Похоже, конструируются часы :) Так зачем каждый раз проводить преобразование ? Только при изменении показаний, выводимых на индикатор .
Реклама
Идёт направо - песнь заводит, Налево - сказку говорит.
Аватара пользователя
Сообщения: 5000
Зарегистрирован: Чт апр 21, 2011 17:55:50
Откуда: Иркутск

Сообщение СКАЗОЧНИК »

Jack_A писал(а):Похоже, конструируются часы
Ага, типа того. 8)
Jack_A писал(а):Длинные вычисления в прерывании
Я просто посчитал, что такое вычисление у меня успеет уложится в необходимый (возможный) интервал времени. Т.к. частота 20 МГц у МК, а частота вращения планки со светодиодами около 2000 оборотов/мин. То получается по моим вычислениям примерно от 20 до 40 миллисекунд на оборот (от скорости вращения зависит), замеряем постоянно, от прерывания до прерывания. Делим на 120 секторов, получается по 250 микросекунд на сектор (тоже примерно). За это время прохождения одного сектора в 3 градуса МК успевает выполнить около 5000 команд при такой частоте. Т.е. в прерывании должен успеть посчитать деление.
А дальше этот результат используем, во втором таймере, который формирует интервалы зажигания светодиодов на секторах... Как-то так.
Станислав
Друг Кота
Аватара пользователя
Сообщения: 6339
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Сообщение Jack_A »

В данном конкретном случае может быть, но лучше приучить себя так не делать. Кстати, не все команды выполняются за один так, так что может быть впритык.
Идёт направо - песнь заводит, Налево - сказку говорит.
Аватара пользователя
Сообщения: 5000
Зарегистрирован: Чт апр 21, 2011 17:55:50
Откуда: Иркутск

Сообщение СКАЗОЧНИК »

Прошу прощения. Возник еще один вопрос...

А можно ли так написать?

ldi temp, (0<<5)

Задвинет ли эта команда ноль на пять позиций влево?

И можно ли писать так?

ldi temp, (1<<1)
ldi temp, (1<<5)

Не затрет ли мне первую единицу???
Станислав
Друг Кота
Аватара пользователя
Сообщения: 20104
Зарегистрирован: Чт сен 18, 2008 12:27:21
Откуда: Столица Мира Санкт-Петербург

Сообщение Gudd-Head »

СКАЗОЧНИК писал(а):Не затрет ли мне первую единицу???
А скопировать код в студию и запустить пошаговую отладку чтобы посмотреть?
[ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ]
Идёт направо - песнь заводит, Налево - сказку говорит.
Аватара пользователя
Сообщения: 5000
Зарегистрирован: Чт апр 21, 2011 17:55:50
Откуда: Иркутск

Сообщение СКАЗОЧНИК »

Блин... :cry:
Первая вообще байт в ноль сбрасывает...
А вторая затирает... (((( Я расстроен.. :facepalm:

З.Ы. посмотрел в отладке в АВРстудио...
Станислав
Друг Кота
Аватара пользователя
Сообщения: 20104
Зарегистрирован: Чт сен 18, 2008 12:27:21
Откуда: Столица Мира Санкт-Петербург

Сообщение Gudd-Head »

Не расстраивайтесь :)
ldi temp, (1<<1)|(1<<5)
[ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ]
Идёт направо - песнь заводит, Налево - сказку говорит.
Аватара пользователя
Сообщения: 5000
Зарегистрирован: Чт апр 21, 2011 17:55:50
Откуда: Иркутск

Сообщение СКАЗОЧНИК »

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

З.Ы. есть одно НО...
Мне не нужно за один раз установить единицы... ((( Мне нужно в одной команде поставить одну единицу в нужный разряд, а в следующей, если условие выполнилось, добавить еще одну единицу в другой разряд, и так. далее.. Тоже самое с нулем... При этом байт который был уже исправлен, должен остаться таким, просто к нему добавляются нули или единицы...
Станислав
Друг Кота
Аватара пользователя
Сообщения: 20104
Зарегистрирован: Чт сен 18, 2008 12:27:21
Откуда: Столица Мира Санкт-Петербург

Сообщение Gudd-Head »

Установить нули можно предварительно загрузив единицы и проинвертировав: ldi temp, ~(1<<1)|(1<<5)
Если нужно установить или сбросить какой-то конкретный бит, пользуйтесь логическими сложением и умножением соответственно.
В приложении с 13-й страницы почитайте.
Вложения
Asm_AVR_rus.pdf
(930.92 КБ) 623 скачивания
[ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ]
Друг Кота
Аватара пользователя
Сообщения: 3872
Зарегистрирован: Пт янв 29, 2010 10:27:40
Откуда: Москва

Сообщение Engineer_Keen »

Есть еще удобные команды SBR/CBR, которые на самом деле замаскированные ORI/ANDI, т.е. это как раз и есть наложение масок.
Сбросить например младшие 2 бита можно так

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

CBR temp,(1<<0)|(1<<1)
поставить в 1 так

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

SBR temp,(1<<0)|(1<<1)
Остальные биты естественно сохраняются как были...
Неправильно собранная из неисправных деталей схема нуждается в отладке и сразу не работает... (С)
Идёт направо - песнь заводит, Налево - сказку говорит.
Аватара пользователя
Сообщения: 5000
Зарегистрирован: Чт апр 21, 2011 17:55:50
Откуда: Иркутск

Сообщение СКАЗОЧНИК »

Спасибо, друзья! ))) То, ЧТО надо!!!
Станислав
Идёт направо - песнь заводит, Налево - сказку говорит.
Аватара пользователя
Сообщения: 5000
Зарегистрирован: Чт апр 21, 2011 17:55:50
Откуда: Иркутск

Сообщение СКАЗОЧНИК »

Прошу прощения, ребят... у меня очередной вопрос.

Хочу организовать большую переменную 32 бита, т.е. 4 байта.. Желательно в оперативке, потому что буду ее заполнять с одного конца нулями и единицами методом сдвига. Надо чтобы автоматом сдвигалось дальше в следующий байт. Как это сделать?
Но это еще не все. Потом эту переменную надо сравнить с ранее записанной во Флеш (кстати, во флеш можно сразу записать одну переменную в 32 бита?). Так вот, как сравнить две большие переменные?

З.Ы. либо разбить все это на 4 байта и сравнивать по отдельности, но тоже как? Как заполнить 4 отдельных байта нулями и единицами последовательно сдвигая в следующий байт?

Все, спасибо, не надо. Я вроде, сам понял.... Извините...
Станислав
Друг Кота
Аватара пользователя
Сообщения: 20104
Зарегистрирован: Чт сен 18, 2008 12:27:21
Откуда: Столица Мира Санкт-Петербург

Сообщение Gudd-Head »

Во флэш можно залить что угодно.
Сравнивать большие переменные побайтно.
Сдвиг сквозь байты проще всего через перенос.
[ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ]
Встал на лапы
Аватара пользователя
Сообщения: 112
Зарегистрирован: Сб янв 05, 2013 07:03:09
Откуда: в #опе изумруды?

Сообщение mail_robot »

блин, как здорово что для нормальных программистов придумали С++ :facepalm:
Нужно делать то, что нужно. А то что не нужно, делать не нужно (с) Винни Пух
Идёт направо - песнь заводит, Налево - сказку говорит.
Аватара пользователя
Сообщения: 5000
Зарегистрирован: Чт апр 21, 2011 17:55:50
Откуда: Иркутск

Сообщение СКАЗОЧНИК »

mail_robot писал(а):как здорово что для нормальных программистов придумали С++
Для Тини13 писать на С++, все равно, что на самолете парковаться около дома... Да и не дает он понимания работ контроллера. Реально на ассемблере фундамент лучше закладывается. Для будущих проектов на больших МК, можно и на С переехать, однако уже понимая, что и где он как сделает...
Gudd-Head писал(а):Сдвиг сквозь байты проще всего через перенос.
Спасибо, вот это уже и сообразил. Зря вопрос задал. Все оказывается проще, чем можно себе представить. Просто голова иногда совсем отключается. :)

Зато есть другой вопрос. )

Флаг регистра SREG Z - устанавливается, в команде cpi, когда точно равно? Или когда равно или больше? Просто мне надо одним условием определить точно ли переменная равна заданному числу... Подразумеваю, что синонимы команд должны быть точны - (breq метка) и (brbs SREG_Z, метка).
Станислав
Ответить

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