Например TDA7294

РадиоКот >Обучалка >Микроконтроллеры и ПЛИС >Микроконтроллеры AVR - пишем, компилируем, прошиваем... >

Теги статьи: Добавить тег

Переключение между массивами. Б.О. v2.2

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

Для начала уясним такую вещь: при сбросе (то есть, обработке прерывания Reset), содержимое регистров не стирается. Это позволяет нам использовать внешний вывод микросхемы "Reset" как переключатель между какими-то режимами программы. То есть, мы можем повесить на этот вывод кнопочку.

Кнопочка сброса

Для того чтобы эта кнопочка стала переключателем, мы должны выделить какой-то регистр под счетчик сбросов. То есть, при каждом нажатии на кнопочку "ресет", содержимое регистра будет увеличиваться на 1. Вообще говоря, это будет происходить не только по нажатию на кнопочку, но и по другим причинам, вызывающим сброс (кроме отключения питания). Но остальные причины можно отнести скорей к аварийным, и их учитывать не будем.

Итак, в самом самом начале выделим регистр. Пусть его зовут ResCnt:

.def     ResCnt=R21

Теперь можно прямо на метку ресет поставить инкремент этого регистра:

Reset:    inc ResCnt

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

Reset:
          ldi Temp1,0
          ldi Temp2,0

l_res:
          dec Temp1
          brne l_res

          dec Temp2
          brne l_res

          inc ResCnt

Ну вот что-то типа того.

Теперь надо соорудить несколько массивов. Желательно, чтоб их количество было кратно какой-нибудь степени двойки. Пусть их будет четыре, и их зовут: Array0, Array1, Array2, и Array3. В каждый из массивов будет записана своя программа работы светодиодов. Допустим так:

0 - справа-налево
1 - слева-направо
2 - крест-накрест
3 - туда-сюда

Пишем:

Array0:
.db  0b10000000,0b01000000
.db  0b00100000,0b00010000
.db  0b00001000,0b00000100
.db  0b00000010,0b00000001
.db  0,0

Array1:
.db  0b00000001,0b00000010
.db  0b00000100,0b00001000
.db  0b00010000,0b00100000
.db  0b01000000,0b10000000
.db  0,0

Array2:
.db  0b10000001,0b01000010
.db  0b00100100,0b00011000
.db  0b00011000,0b00100100
.db  0b01000010,0b10000001
.db  0,0

Array3:
.db  0b10000000,0b01000000
.db  0b00100000,0b00010000
.db  0b00001000,0b00000100
.db  0b00000010,0b00000001
.db  0b00000001,0b00000010
.db  0b00000100,0b00001000
.db  0b00010000,0b00100000
.db  0b01000000,0b10000000
.db  0,0

Ну, примерно так. Объясняю насчет нулей, которые мы ставим в конце каждого массива: ноль - это флажок окончания массива. Когда программа читает этот элемент - она не выводит его на индикацию, а возвращается к началу массива.

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

SelectArray:                    ;выбор массива

         andi ResCnt,0b00000011 ;отсечка лишних разрядов сч. сбросов

         cpi ResCnt,0           ;сравнение счетчика сбросов с конст.
         brne Tst1              ;если не равно - след. проверка
         ldi ZH,High(Array0*2)  ;загрузка начального адреса массива
         ldi ZL,Low(Array0*2)
         rjmp ReadArray

Tst1:    cpi ResCnt,1
         brne Tst2
         ldi ZH,High(Array1*2)
         ldi ZL,Low(Array1*2)
         rjmp ReadArray

Tst2:    cpi ResCnt,2
         brne Tst3
         ldi ZH,High(Array2*2)
         ldi ZL,Low(Array2*2)
         rjmp ReadArray

Tst3:    ldi ZH,High(Array3*2)
         ldi ZL,Low(Array3*2)
         rjmp ReadArray


ReadArray:

         ldi Temp,0            ;прибавление относит. адреса
         add ZL,Temp1
         adc ZH,Temp

         lpm                   ;загрузка из ПЗУ

         mov Temp,R0           ;копирование в РОН
         cpi Temp,0            ;пороверка на нулевой элемент
         breq Init             ;если нулевой - в начало
         inc Temp1             ;увеличение относит адреса на 1

         rjmp Output           ;перейти на вывод в порт

Init:    ldi Temp1,0           ;загрузить нач. значение
         rjmp SelectArray

Output:  out PortB,Temp        ;вывод в порт
         reti                  ;выход из обработчика

Коротко о том, что здесь происходит.

Поскольку мы переключаемся между четырьмя массивами - нам нужно только 2 двоичных разряда (2^2 = 4). Поэтому, все разряды старше 2-го мы хладнокровно вырезаем командой

andi ResCnt,0b00000011

Далее происходит следующее: мы последовательно сравниваем счетчик сбросов ResCnt с числами от 0 до 3. От того, какому числу равен счетчик, зависит начальный адрес какого массива мы загрузим в регистровую пару Z. Как только Z загружена - можно приступать к чтению массива в порт. Этим занимается кусок программы начиная с метки ReadArray.

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

Ну вот и все!
Полный текст программы лежит здесь: runfire_v2_2.asm (кодировка Windows-1251)

<<--Вспомним пройденное----Поехали дальше-->>




Как вам эта статья?

Заработало ли это устройство у вас?

22 0 0
6 3 1