Ассемблер (ASM) для AVR в вопросах и ответах

Обсуждаем контроллеры компании Atmel.
Ответить
afz
Опытный кот
Аватара пользователя
Сообщения: 744
Зарегистрирован: Сб дек 22, 2012 08:17:42
Откуда: Караганда, Казахстан

Сообщение afz »

Programmer86 писал(а): Код под спойлером. Также приложил архив с проектом в AtmelStudio, т.к. код под спойлером получается не структурированным.
Он и в проекте ни разу не структурированный...
Programmer86 писал(а): Вы меня подтолкните, что может быть, я сам подправлю код.
Первая грубая ошибка - из прерываний выходят по RETI, а не по RET'у. В результате код, который выполнялся при открытых прерываниях после выполнения программы прерывания продолжит исполнение при закрытых прерываниях и следующего прерывания может не произойти. То есть здесь, скорее всего, до этого успеет выполниться SEI в другом месте и до зависания дело не дойдет, но, тем не менее, в общем случае так делать нельзя.

Вторая, и еще более грубая ошибка - это то, что прерывающая программа обязана сохранять и восстанавливать регистры, которыми она пользуется. Здесь ничего подобного нет. В частности, здесь в любой момент исполнения основной программы (от SEI до CLI) могут внезапно измениться регистры Data1, Data2, Servo1 и Servo2 и, самое главное, регистр статуса SREG. Не вникал, может быть изменение регистров данных и предусмотрено, но забытый SREG приведет к тому, что, допустим, цикл Loop4_1 досчитал до конца, выполнилась команда dec TEMP2, результат получился ноль, тут прерывание, которое испортит признак Z, и вперед, цикл будет исполняться еще 256 раз. Совпадение, конечно, достаточно редкое, но рано или поздно оно произойдет и, поскольку оно редкое, будет похоже на случайный сбой.

Третья ошибка - это как раз, в тему того, о чем я вел дискуссию страницей раньше. Если полностью заполнить таблицу векторов командами (r)jmp, то нужна и вторая таблица с объявлением меток, на которые ссылаются эти RJMP. Та самая:

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

 EXT_INT0:
 EXT_INT1:
 EXT_TIMER2_COMP:
 EXT_TIMER2_OVF:
...
Только она должна быть сплошной, в нее должны входить все метки, на которые ссылаются RJMP из таблицы векторов, они указывают на один и тот же адрес, а по нему должна находиться ловушка для незапланированных прерываний, которую следует написать после этой таблицы, типа rjmp .-1 или, на худой конец, RETI. А, собравшись использовать какое-либо прерывание, надо выдернуть его строчку из таблицы и вставить в нужное место программы. Ну, или скопипастить, а в таблице закомментировать. А так, как сделано у тебя, если вдруг произойдет незапланированное прерывание от INT0 до SPI_STC, запустится программа приема с UART, а от USART_UDRE до SPM_RDY - программа USART_INIT.

Это то, что бросилось в глаза. А про структурирование... На асме принято метки писать с первой позиции, а коды операций от меток через один или несколько tab'ов, чтобы они шли в колонку. Ну и комментариев в программе на асме надо писать раз в 10 больше, чем у тебя, причем структурировать в C-стиле следует именно комментарии...
Кто мешает тебе выдумать порох непромокаемый? (К. Прутков, мысль № 133)
Реклама
Родился
Сообщения: 3
Зарегистрирован: Вс дек 23, 2012 12:36:11

Сообщение Programmer86 »

Спасибо, afz, что откликнулись. Я понял, что все работает. Это все ошибка в коде программы на ПК, она перестает посылать байты к МК. Но я все же хочу исправить ошибки про которые вы писали.
Заменил RET на RETI.
По второй ошибки я вас не понял. Мне надо сохранить регистр SREG? Не могли бы вы написать примерчик?
На счет третьей ошибки, вы имели сделать нечто подобное?
EXT_INT0: RETI
EXT_INT1: RETI
EXT_TIMER2_COMP: RETI
EXT_TIMER2_OVF: RETI
EXT_TIMER1_CAPT: RETI
Реклама
afz
Опытный кот
Аватара пользователя
Сообщения: 744
Зарегистрирован: Сб дек 22, 2012 08:17:42
Откуда: Караганда, Казахстан

Сообщение afz »

Programmer86 писал(а): По второй ошибки я вас не понял. Мне надо сохранить регистр SREG? Не могли бы вы написать примерчик?
Регистры общего назначения (R0 - R31) положено сохранять в стеке. Сохраняем командой PUSH. восстанавливаем командой POP. Естественно, необязательно сохранять и восстанавливать все 32 РОНа, если какие-то регистры в этом конкретном прерывании не используются, или используются для передачи параметров из прерывания в основную программу, их сохранять и потом восстанавливать не надо. А так, все сохранения и восстановления должны быть согласованы, какие регистры сохранялись, те же надо и восстанавливать. Восстанавливаем в порядке обратном тому, в котором сохраняли. Это о РОНах.

А вот SREG - это регистр, целиком доступный только по командам IN/OUT. для его сохранения надо сначала освободить какой-нибудь РОН, сохранив в стеке его значение, затем прочитать в него SREG командой IN и сохранить это значение в стеке. То есть в начале программы обслуживания прерывания ставим:

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

   PUSH   Rx
   IN     Rx,SREG
   PUSH   Rx
Далее добавляем сохранение тех регистров, которые используются в этом прерывании в качестве рабочих (чтобы их изменение в прерывании не привело к их изменению в основной задаче) и только потом пишем собственно программу обслуживания прерывания. В ее конце восстанавливаем все сохраненные при входе регистры, завершаем это восстановление командами

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

   POP     Rx
   OUT     SREG,Rx
   POP     Rx
   RETI
которые восстановят SREG и Rx.

Rx здесь - любой РОН, поскольку он сохраняется, его тоже можно использовать в качестве рабочего.

Programmer86 писал(а): На счет третьей ошибки, вы имели сделать нечто подобное?
EXT_INT0: RETI
EXT_INT1: RETI
EXT_TIMER2_COMP: RETI
EXT_TIMER2_OVF: RETI
EXT_TIMER1_CAPT: RETI
Так следует делать только в том случае, если незапланированное прерывание таки обнаружится и начинаешь его поиск под отладчиком, только тогда надо писать не RETI, а, допустим, зацикленный сам на себя RJMP. тогда смотрим в отладчике, где она повисла, и находим, кто это нас прервал и по какой причине. А так вполне достаточно перечислить все эти метки по одной в строку, а после них написать RETI или зацикленный на себя RJMP. Все метки этой таблицы будут ссылаться на один и тот же адрес - адрес этой команды RETI/RJMP, в отлаженной программе это вполне допустимо. Если же оно таки улетит на эту команду, тогда уже предпринимаем действия по выявлению причины путем прописывания зацикленных RJMP по каждой метке.

А программы обслуживания используемых прерываний располагаем где-нибудь в другом месте, из расчета, чтобы на нее не "наехало" управление в естественном порядке.
Кто мешает тебе выдумать порох непромокаемый? (К. Прутков, мысль № 133)
Родился
Сообщения: 3
Зарегистрирован: Вс дек 23, 2012 12:36:11

Сообщение Programmer86 »

Спасибо, afz. Разжевали доступно. Спасибо! Буду пробовать.
Реклама
Эиком - электронные компоненты и радиодетали
Друг Кота
Аватара пользователя
Сообщения: 20117
Зарегистрирован: Чт сен 18, 2008 12:27:21
Откуда: Столица Мира Санкт-Петербург

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

У кого-нибудь есть код перевода 16-битного числа в формате DS18B20 во что-нибудь удобоваримое для вывода со знаком с точностью 0,5 градуса?
[ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ]
Реклама
afz
Опытный кот
Аватара пользователя
Сообщения: 744
Зарегистрирован: Сб дек 22, 2012 08:17:42
Откуда: Караганда, Казахстан

Сообщение afz »

Gudd-Head писал(а):У кого-нибудь есть код перевода 16-битного числа в формате DS18B20 во что-нибудь удобоваримое для вывода со знаком с точностью 0,5 градуса?
Есть только такое.
Спойлер

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

; **************************************************************

.macro	ZLDA
	ldi	@0l,low(@1)
	ldi	@0h,High(@1)
.endm


	; Перевод 32-разрядного двоичного числа в десятичное (BCD).
ZF210:
	; Регистры

	; R3, R4, R5, R6 - исходное число, R3 - младший байт, R6 - старший.

	; Сначала очистим ZFRX
	ZLDA	x,zfrx
	clr	r2
	ldi	r16,zfex-zfrx
L10:	st	x+,r2
	dec	r16
	brne	L10
	nop	

	ldi	R17,32 ; Количество битов в преобразуемом числе
	
L11:	
	; Возьмем очередной бит исходного числа и в соответствии с его значением 
	; установим бит переноса для ZFAdd10 (младший бит R2)

	clr	r2
	lsl	r3
	rol	r4
	rol	r5
	rol	r6
	brcc	L12
	inc	R2
L12:	ldi	R18,zfex-zfrx

	; Сложим содержимое ZFRX само с собой (* 2) 
	; и добавим из переноса (R2) значение очередного бита.

	ZLDA	x,zfex
L13:	ld	r0,-X
	mov	r1,r0
	rcall	ZFAdd10
	st	X,r0
	dec	r18	; Повторим для всех байтов ZFRX
	brne	L13
	dec	r17	; и для всех битов исходного числа
	brne	L11
	ret




; ******************************************************************

ZFAdd10:		; Сложение двух однобайтовых десятичных чисео в BCD.

			; R0 - первое слагаемое, результат
			; R1 - второе слагаемое
			; R2 - входной перенос - выходной перенос
			; R16 - тоже рабочий
	mov	r16,r2
	clr	r2
	sbrc	r16,0
	sec
	adc	r0,r1
	brhs	L4
	brcs	L6
	ldi	r16,0x66
	add	r0,r16
	brhs	L1
	brcs	L2
	sub	r0,r16
	
	ret
L1:	brcs	L3
	ldi	R16,0x60
	sub	r0,r16
	ret
L2:	ldi	r16,6
	sub	r0,r16
L3:	inc	r2
	ret
L4:	brcs	l7
	ldi	r16,0x66
	add	r0,r16
	brcs	L5
	ldi	r16,0x60
	sub	r0,r16
	ret
L5:	inc	r2
	ret
L6:	ldi	r16,0x66
	add	r0,r16
	brhs	l8
	ldi	r16,6
	sub	r0,r16
L8:	inc	r2
	ret
L7:	ldi	r16,0x66
	add	r0,r16
	inc	r2
	ret
Нужно, во-первых, уменьшить число битов до 16 (ИМХО - легко), а с числом из 1820 разобраться так: во-первых, запоминаем знак, если минус - взять дополнение до 2 от числа, затем к нему прибавить 0x04 и сдвинуть на 3 разряда вправо (это округление), затем умножить его на 10 (оно уже сдвинутое на 1 разряд влево, т.е. умноженное на 2, его надо скопировать в другую пару паре регистров, сдвинуть еще на 2 разряда, т.е. итого умножить на 8 и сложить эти две пары оегистров), после чего отдать моей программе, модифицированной до 16 бит.

Я тут поначалу ошибся, сейчас поправил
Кто мешает тебе выдумать порох непромокаемый? (К. Прутков, мысль № 133)
Реклама
Держит паяльник хвостом
Аватара пользователя
Сообщения: 906
Зарегистрирован: Ср мар 28, 2012 21:45:24
Откуда: ВО

Сообщение ILYAUL »

Вот

Там есть

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

brne	X1
		swap	SHIFT_REG					// Значения собираются в старшей тетраде , готовим их к дальнейшему расчёту
;/    ***********************
		out		GPIOR0,SHIFT_REG			;= а пока запоминаем их
..................
ldwi	Z,Accuracy					// В Accuracy значение с которой вычисляется температура после запятой
		in		count,GPIOR0				// Вот и потребовалось значение которое мы запомнили
		cpse	count,zero					// Если оно вдруг 0х00 то пропускаем вычисление
		rjmp	Add_Thousandths				// Иначе вычисляем
		st		Y+,zero						// Но и записываем 0х00 для вывода на дисплей
NEW_FIND_or_OUT:
Так вот , значения температуры ( самой) укладываются в подпрограмму преобразования в BCD от 1-100 и такая как у меня тебе не нужна, у меня в проекте преобразования больше чем 1000.

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

in		count,GPIOR0				// Вот и потребовалось значение которое мы запомнили
В этом регистре собственно само значение после запятой - т.е смещение от начала таблицы до нужного тебе значения . Только надо просто переделать таблицу - сразу занести в нее значения в BCD и все. Будет типа

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

ldi ZH,high(XXXXX*2)
Ldi ZL, low(XXXXX+(например) 4)*2)
add ZL,count
adc ZH,zero
lpm.....
где 4 это добавка к смещению т.к. при точности 0,0625 в таблице будет h30,h36,h32,h35
Вложения
ALARM_SEARCH.rar
(2.32 КБ) 148 скачиваний
ALARM_SEARCH.asm
(5.97 КБ) 471 скачивание
akl
Друг Кота
Сообщения: 4470
Зарегистрирован: Пт мар 07, 2008 06:54:43
Откуда: Ижевск

Сообщение akl »

Это не хвост, это антенна
Сообщения: 1495
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

ILYAUL писал(а):...

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

//------------------------------------------------------------------------
#define tab_h(x) HIGH(x), LOW(x)
#define tab_l(x) LOW(x), HIGH(x)
//------------------------------------------------------------------------

/************************************************************************/
#if (BCD==YES)

.dseg

HEX_DEC_BUFFER:
.equ	HEX_DEC_BUFFER_LENGHT = 5
.byte	HEX_DEC_BUFFER_LENGHT

.cseg

Hex_Dec:
	ldx		HEX_DEC_BUFFER
	ldz		Tab_10_16*2

	ldi		r19, 4

Hex_Dec_Cycle:
	lpm		r17, Z+
	lpm		r16, Z+

	rcall	Hex_Dec_Digit
	st		X+, r18

	dec		r19
	brne	Hex_Dec_Cycle

	mov		r16, r14
//	ori		r16, 0x30
	st		X+, r16
Hex_Dec_End:
	ret

Hex_Dec_Digit:
	ldi		r18,-1
Hex_Dec_Digit_Cycle:
	inc		r18
	sub		r14,r16
	sbc		r15,r17
	brsh	Hex_Dec_Digit_Cycle
	add		r14,r16
	adc		r15,r17
//	ori		r18,0x30
	ret


Tab_10_16:
.db tab_h (10000)
.db tab_h (1000)
.db tab_h (100)
.db tab_h (10)

#endif
//==================
Контактная информация:
Держит паяльник хвостом
Аватара пользователя
Сообщения: 906
Зарегистрирован: Ср мар 28, 2012 21:45:24
Откуда: ВО

Сообщение ILYAUL »

Вообщем- то для него этого достаточно

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

;**************************************************
;*    ;;+Подпрограмма преобразования в BCD;;*     *
;**************************************************
CONVBCD:
		clr		temp				;+ Преобразуем в BCD
BCD1:
		subi	temp1,10
		brcs	EndBCD
		inc		temp
		rjmp	BCD1
EndBCD:		
		subi	temp1,-10
		ori		temp1,'0'			; перевод в ASCII
		cp		temp,zero
		breq	XXXX
		ori		temp,'0'			;/ в temp1 младшее значение в temp - старшее
		ret
XXXX:
		ldi		temp,0x20			;| Убираем первый , незначащий ноль
		ret
Друг Кота
Аватара пользователя
Сообщения: 20117
Зарегистрирован: Чт сен 18, 2008 12:27:21
Откуда: Столица Мира Санкт-Петербург

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

Я тут подумал и упрощаю задачу:
надо получить 3 байта:
1ый — знак (скажем, $A = "+", $B = "-");
2ой — целая часть температуры в двоичном формате (процедура перевода в BCD в проге уже есть);
3ий — дробная часть (0 или 5).
[ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ]
Держит паяльник хвостом
Аватара пользователя
Сообщения: 906
Зарегистрирован: Ср мар 28, 2012 21:45:24
Откуда: ВО

Сообщение ILYAUL »

Ну и чего, та же самая таблица , только с двумя значениями 0x30 и 0х35 т.к дробь будет только 1 или 0 -другого не дано
Знак проверяется по старшему биту - 1 - значит минус . для подготовки для BCD лучше инвертировать в + , там выше в коде это есть
Друг Кота
Аватара пользователя
Сообщения: 20117
Зарегистрирован: Чт сен 18, 2008 12:27:21
Откуда: Столица Мира Санкт-Петербург

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

Чего? Какая таблица? :)
[ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ]
Держит паяльник хвостом
Аватара пользователя
Сообщения: 906
Зарегистрирован: Ср мар 28, 2012 21:45:24
Откуда: ВО

Сообщение ILYAUL »

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

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

Ммм... сложновато.
Я вижу примерно так:
Если > 0, загружаем в первый байт "+", сдвигаем целую часть во 2-й байт и смотрим дробную часть: 0 или 5;
Если < 0, загружаем в первый байт "-", сдвигаем целую часть во 2-й байт, берём дополнене и смотрим дробную часть: 0 или 5.
[ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ]
Держит паяльник хвостом
Аватара пользователя
Сообщения: 906
Зарегистрирован: Ср мар 28, 2012 21:45:24
Откуда: ВО

Сообщение ILYAUL »

У DS-ки одна замечательная вещь , она устанавливает старший бит в 1 , если температура отрицательная. Проверяем и выводим +/-
Если темпю. отрицательная , переводим в положит и

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

		lsr		SHIFT_REG					// и значение текущей температуры т.е делим на 10
X1:		ror		TERMO_REG					// В TERMO_REG собираются значения температуры, в SHIFT_REG - значения после запятой
		ror		SHIFT_REG
		dec		count
		brne	X1
		swap	SHIFT_REG
Так вот у Вас SHIFT_REG будет или = 1 или 0 Проверяете и выводите сразу после запятой 0 или 5
Вымогатель припоя
Сообщения: 630
Зарегистрирован: Пн июн 14, 2010 13:07:29
Откуда: Жуковский

Сообщение a_skr »

r17:r16 - вход
выход:
r20: '+'/'-'
r21: градусы
r22: 0/5

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

	ldi r20,'+'
	and  r17,r17 ; проверка на отрицательность
	brpl plus

	com  r17  ; отрицательное
	neg  r16
	sbci r17,0xFF
	ldi r20,'-'

plus: ; (уже) положительное
	ldi  r22,5   
	sbrs r16,3 ; проверка на 0.5
	clr  r22

	lsr r17   ; сдвигаем вправо на 4 бита
	ror r16
	lsr r17
	ror r16
	lsr r17
	ror r16
	lsr r17
	ror r16
	
	mov r21,r16 ; и получаем целые градусы
Друг Кота
Аватара пользователя
Сообщения: 20117
Зарегистрирован: Чт сен 18, 2008 12:27:21
Откуда: Столица Мира Санкт-Петербург

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

a_skr писал(а): and r17,r17 ; проверка на отрицательность
Почему не Хотя в машинных кодах это одно и то же :)
Спасибо за код, лови плюсы.

Кто-нибудь сможет компактней? :))
[ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ]
Вымогатель припоя
Сообщения: 630
Зарегистрирован: Пн июн 14, 2010 13:07:29
Откуда: Жуковский

Сообщение a_skr »

Почему не tst r17
да, нагляднее так. просто ассемблер лет 15 назад изучал ;)
Спасибо за код, лови плюсы.
Спасибо! :)
Держит паяльник хвостом
Аватара пользователя
Сообщения: 906
Зарегистрирован: Ср мар 28, 2012 21:45:24
Откуда: ВО

Сообщение ILYAUL »

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

ldi r20,'+'
   and  r17,r17 ; проверка на отрицательность
   brpl plus

   com  r17  ; отрицательное
   neg  r16
   sbci r17,0xFF
   ldi r20,'-'
=

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

sbrs R17,MSB
ldi R20,'+'
Sbrc R17,MSB
ldi R20,'-'

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

lsr r17   ; сдвигаем вправо на 4 бита
   ror r16
   ror r17
   ror r16
   ror17
   ror r16
   ror r17
   ror r16
=

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

      lsr      SHIFT_REG               // и значение текущей температуры т.е делим на 10
X1:      ror      TERMO_REG               // В TERMO_REG собираются значения температуры, в SHIFT_REG - значения после запятой
      ror      SHIFT_REG
      dec      count
      brne   X1
      swap   SHIFT_REG

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

Sbrs    SHIFT_REG,LSB
ldi   R20,'0'
sbrc SHIFT_REG,LSB
Ldi R20,'5'
Ответить

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