Бегущий огонь с подпрограммной задержкой

Обсуждаем контроллеры компании Atmel.
Ответить
Родился
Сообщения: 5
Зарегистрирован: Вт апр 08, 2014 18:53:06

Сообщение plollol »

Здравствуйте. Только учусь программировать на assembler. Хочу реализовать простую программу на ATMega 8535. Суть программы состоит в том, что при не нажатой кнопке выполняется одна программа ( я её назвал _1), а при нажатой кнопке - вторая программа ( _2 ). Но так как практического программирования на assembler практически не имею, то имеются ошибки. Укажите что я делаю не так:

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

.include "m8535def.inc"
;Назначим новые имена для регистров
.def D0=r20				;присвоение новых имен разрядам задержки
.def D1=r21				;
.def D2=r22				;
.cseg					;начало сегмента кодов
.org 0					;установка адреса начала программы
reset:					;Устанавливаем указатель стека на конец RAM
		ldi r16,low(RAMEND)
		ldi r17,high(RAMEND) ;выыделение регистров 16 и 17
		out spl,r16          ;под начало и конец стека
		out sph,r17          ;соответственно
;Предустановка портов 1/0
		clr r16					;настраиваем порт B на ввод данных
		out DDRB,r16
;Основной цикл
loop:		in 16,PINB		;считываем из порта В
			mov r17,r16		;копируем результат в r17
			lsr r17			;сдвигаем копию вправо,чтобы последующей командой
			eor r16,r17		;исключающее ИЛИ проверить , не равны ли  PINB,0 и OINB,1
			rjmp _1
			rjmp _2
			clr r16
			rjmp loop
_1:
;Конфигурация входов/выходов
		ldi r16,0xFF  ;запишем в регистр 16 значение 255 (любое кроме 0) 
		out DDRA,r16	;Устанавливаем порт А на вывод информации
;Зажжём первый светодиод на всех портах
		ldi r16, 0x01 ; запишем единицу в регистр 16	
		out PORTA,r16
;Основной цикл (бесконечный)
main:	rol r16			;Циклически сдвинем единичку влево
		in r19,SREG		;Сохраним флаги
		out PORTA,r16	;Выведем сдвинутый r16 в порт А	
		ldi D2,$18		;*загрузим параметры выдержки времени
		ldi D1,$6A	;*изменяя значения этих регистров
		ldi D0,$00   ;*меняем выдержку времени
		rcall delay		
		out SREG,r19		;Восстановим флаги
		rjmp main		;Вернёмся к началу цикла
;Подпрограмма задержки
delay:	 subi D0,1		;вычитаем 1 из D0
		sbci D1,0		;вычитаем с учетом знака переноса
		sbci D2,0		;вычитаем с учетом знака переноса
		brcc delay		;на метку delay если флаг переноса очищен
		ret			;выход из подпрограммы задержки


_2:
;Конфигурация входов/выходов
		ldi r16,0xFF  ;запишем в регистр 16 значение 255 (любое кроме 0) 
		out DDRA,r16	;Устанавливаем порт А на вывод информации
;Зажжём первый светодиод на всех портах
		ldi r16, 0x01 ; запишем единицу в регистр 16	
		out PORTA,r16
;Основной цикл (бесконечный)
main1:	rol r16			;Циклически сдвинем единичку влево
		in r19,SREG		;Сохраним флаги
		out PORTA,r16	;Выведем сдвинутый r16 в порт А	
		ldi D2,$18		;*загрузим параметры выдержки времени
		ldi D1,$6A	;*изменяя значения этих регистров
		ldi D0,$00   ;*меняем выдержку времени
		rcall delay1		
		out SREG,r19		;Восстановим флаги
		rjmp main1		;Вернёмся к началу цикла
;Подпрограмма задержки
delay1:	 subi D0,1		;вычитаем 1 из D0
		sbci D1,0		;вычитаем с учетом знака переноса
		sbci D2,0		;вычитаем с учетом знака переноса
		brcc delay1		;на метку delay если флаг переноса очищен
		ret			;выход из подпрограммы задержки
Реклама
Открыл глаза
Аватара пользователя
Сообщения: 59
Зарегистрирован: Вт фев 18, 2014 17:48:40
Откуда: Владимирская область

Сообщение ufofly »

Так а что не получается?
Помощь - понятие растяжимое, как и за неё благодарность.
Реклама
Вымогатель припоя
Аватара пользователя
Сообщения: 577
Зарегистрирован: Ср июн 19, 2013 08:10:48
Откуда: Москва, СПб, Липецк, Рязань

Сообщение ИС-пытатель »

А ни хрена у него не получается!

Начало хорошее.
А вот с этого места и пошли сплошные косяки:

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

;Основной цикл
loop:      in 16,PINB      ;считываем из порта В
         mov r17,r16      ;копируем результат в r17
         lsr r17         ;сдвигаем копию вправо,чтобы последующей командой
         eor r16,r17      ;исключающее ИЛИ проверить , не равны ли  PINB,0 и OINB,1
         rjmp _1
         rjmp _2
         clr r16
         rjmp loop
Первое. Командой

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

in 16, pinB 
Вы считываете в регистр значение ВСЕХ ножек порта (это нормально, ошибка дальше).
Если Вы хотите проверить только конкретный бит - Вы должны "исключить" все остальные. И сделать Вы это должны для каждого проверяемого бита. Т.е. следующий кусок кода должен быть таким:

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

mov r17, r16 ; копируем считанное значение. Это нужно сделать до того, как мы его изменим следующими командами
andi r16, (1<<0) ; Здесь выражение расшифровывается, как единица сдвинутая влево 0 раз. Т.е. число 1 (установленный в 1  0-й бит)
; при побитном И все остальные ненужные нам биты просто сбрасываются. А 0-й бит остается без изменений.
Дальше можно так:

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

andi r17, (1<<1) ; 1-й бит остается без изменений. биты 7-2, 0 будут сброшены.
lsr r17    ; сдвигаем вправо.
Или можно так:

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

lsr r17 ; сначала сдвигаем
andi r17, (1<<0) ; а теперь сбрасываем не нужные биты. Заметь, теперь мы не трогаем 0-й бит (сдвинули же)
Привели мы все это дело к единообразию. Если не сбросить остальные биты - то сравнение будет некорректным, т.к они могут влиять на результат сравнения.
Теперь само сравнение. Вы используете команду

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

eor r16, r17
Что ж.. оригинально, можно и так. Но вообще для этого в процессоре предусмотрены специальные команды. Такие как CP (от слова ComPare - сравнить) и CPI (сравнить с числовой константой). Первая используется для сравнения регистров между собой (эквивалента вычитанию одного из другого, только результат вычитания не модифицирует первый регистр), а вторая используется для сравнения регистра с числовой константой (регистры r16-r31, констната до 255 (8бит)). Хотя, в Вашем случае можно и так, как Вы это сделали. При этом стоит помнить о том, что логические команды (and, or, eor и т.д.) не изменяют флаг C (Carry - перенос). Это может однажды сыграть злую шутку. Поэтому лучше все же написать так:

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

cp r16, r17 ; или cp r17, r16 - не принципиально
разбираем дальше. Регистры Вы между собой сравнили - сравнение вызвало установку или сброс определенных флагов. Теперь Вам надо сделать условный переход (он опирается как раз на те самые флаги):

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

breq M1 ; если регистры равны (установлен в 1 флаг Z), то перейти на метку M1. В противном случае выполнить следующую команду.
; следующей командой может быть Ваш блок обработки, если регистры не равны друг другу.
;конец Вашего блока, команды безусловного (jmp, rjmp) перехода на начало обработки и т.д. (прыжок на метку loop)
После этого можно разместить другой нужный нам код:

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

M1:    ; метка блока, если регистры равны
; блок команд обработки случая равенства
; прыжок на начало (jmp loop)
Типы условных переходов смотрите в даташите. Чаще всего используемые это:

BREQ - равны (Z = 1)
BRNE - не равны (Z = 0)
BRCS - второй операнд больше первого (C = 1)
BRCC - второй операнд <= первому

Что же делаете Вы? Вы ставите БЕЗУСЛОВНЫЙ переход.

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

rjmp _1
Т. е. у Вас программа не зависимо ни от чего будет прыгать на метку _1.
Спрашивается, зачем тогда было формировать условие, если потом Вы его не используете?

Далее команды, которые у Вас стоят после

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

rjmp _1
и до метки просто не выполнятся, думаю, сами уже догадались почему. Дальше Ваш код не разбирал. Действуйте по аналогии. Определитесь, что хотите получить и смотрите, какие команды максимально соответствуют Вашим желаниям. Не изобретайте велосипед.

Да, заметил еще, что Вы написали две процедуры с совершенно одинаковым кодом (речь идет о задержке). Это вовсе не обязательно. Такая забота чрезмерна и код подобного рода называется избыточным. Оставьте только одну любую и в обоих случаях обращайтесь к ней одной.
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 1900
Зарегистрирован: Сб фев 21, 2009 13:11:40
Откуда: Москва

Сообщение ibiza11 »

....сколько критики по поводу EOR)....


plollol, советую вам сначала нарисовать блок-схему алгоритма вашей программы. на листочке. если в процессе выполнения одной подпрограммы "мигания" вам необходимо переключиться на другую подпрограмму мигания по нажатию кнопки, то следует в этой самой подпрограмме в цикле проверять состояние кнопки, и при обнаружении нажатия - переходить к другой подпрограмме мигания.
Ставим плюсы: )
Реклама
Эиком - электронные компоненты и радиодетали
Ответить

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