![]() |
![]() |
|||||||||||||||
Что такое матрица и с кого все спросить
Автор: Итак, господа хорошие, закончилась череда праздников: Новый Год, Рождество, Старый Новый Год… Вспомнить страшно сколько всего закончилось. Но мы, под чутким руководством Кота Пьющего Но Никогда Не Напивающегося, прошли сквозь все эти ужасы и испытания, и готовы снова трудиться во благо общества… Это была присказка. Сказка. В некотором царстве, в некотором государстве, а именно – у нас на форуме –
неоднократно задавался вопрос: «Как подключить клавиатуру к контроллеру?» Как вы поняли из предыдущей статьи, в борьбе за уменьшение проводов и выводов процессора, прогрессивное человечество придумало динамическую индикацию. Для подключения клавиатуры, хитрое человечество тоже кое-что придумало. Что? А вот чего: динамический опрос! Сегодня, используя это замечательное изобретение, мы подключим к нашему контроллеру клавиатуру от кнопочного телефона и заставим контроллер увидеть эту клавиатуру. Не вдаваясь в скучные подробности, сразу нарисую схему клавиатуры. ![]() То что вы видите, называется «матрица». Этот чтоб у вас в дальнейшем не возникало вопроса «Что такое матрица?»… Так вот, почти все клавиатуры в этом мире имеют матричную структуру. Матрица имеет сколько-то строк и сколько-то столбцов. Количество строк и столбцов матрицы высчитывается исходя из количества кнопок в клавиатуре. Соотношение столбцов и строк при этом должно быть таким, чтобы их сумма была минимальной Например, наш случай: есть 12 кнопок. Число 12 можно разложить на целые множители следующими способами:
12 = 1*12 Замечательно. Теперь посчитаем суммы множителей:
1+12 = 13 То есть, оптимальной для нашего варианта является матрица, у которой 4 строки и 3 столбца (или наоборот). Ну, собственно, что мы и видим на схеме. Теперь осталось подключить сие к контроллеру. Ну что же – подключаем! За основу возьмем схему из предыдущей статьи: ![]() Адресные входы клавиатуры (строки матрицы) подключаем к адресной шине индикаторов. Шина данных (столбцы) – подключаются к свободным выводам контроллера. Каждый провод шины данных подтянут к «плюсу» резистором 300 Ом. Это нужно для того, чтобы в случае, если ни одна кнопка не нажата, на входы контроллера приходила «1». Если этого не сделать – на входе будет так называемое 3-е состояние – не пойми чего. Это не пойми чего, контроллер может понять совершенно произвольно. Может посчитать «нулем», может – «единицей», что вызовет ложные «нажатия» клавиш. Номинал резисторов – произвольный в пределах 200 Ом…2 кОм Как вы поняли, опрос клавиатуры производится параллельно с индикацией. При написании программы, мы это учтем… Итак, священный момент: пишем софт! Для начала – пару слов о том, что и как мы будем писать: lds Temp1,Digit ;загружаем ячейку с цифрой ldi Temp,0b00001110 ;выставляем адрес индикатора out PortD,Temp ;выводим адрес индикатора rcall Decoder ;вызываем 7-сегментный декодер out PortB,Temp1 ;выводим полученный код rcall Delay1 ;вызываем задержку Теперь в этот кусок мы допишем две строки (перед вызовом задержки): in Temp,PinD ;читаем порт D sts Line,Temp Temp ;записываем прочитанное в ОЗУ Как видите, у нас появилась новая переменная Line . В ней хранится то, что мы читаем из порта D . То есть – состояние кнопок в строчках клавиатуры. В Line+0 лежит состояние 0-й (верхней) строчки, в Line+1 – первой, и т.д. Эту переменную надо объявить. Объявляем ее в сегменте данных, после переменной Digit: Digit: .byte 4 Line: .byte 4 Теперь после цикла индикации нужно дописать подпрограмму обработки всего того, что мы прочитали с клавиатуры, за проход индикации. Назовем ее KeyRead: KeyRead: ;блок проверки строки (всего их 4) lds Temp,Line ;читаем состояние строки rcall KeyTest ;вызываем программу проверки ;программа возвращает номер кнопки: 0..2 ;или -1 - если нет нажатых кнопок ldi Temp,0 ;загружаем код текущей строки ;код текущей строки - это номер кнопки, ;с которой начинается эта строка cpi Temp1,255 ;если ничего не нажато - идем дальше brne SetKey ;иначе - переходим на обработку ;конец 0-го блока lds Temp,Line+1 rcall KeyTest ldi Temp,3 cpi Temp1,255 brne SetKey lds Temp,Line+2 rcall KeyTest ldi Temp,6 cpi Temp1,255 brne SetKey lds Temp,Line+3 rcall KeyTest ldi Temp,9 cpi Temp1,255 brne SetKey ldi Temp,0 SetKey: add Temp,Temp1 ;прибавляем номер кнопки ;к коду строчки lds Temp1,Key ;грузим код кнопки, ;прочитанный в прошлый раз andi Temp1,0b00001111 ;"чистим" его по маске cp Temp,Temp1 ;если в прошлый раз brne EndKeyRead ;была нажата не та же кнопка ;выходим ori Temp,0b10000000 ;иначе - пишем флажок "повтор" EndKeyRead: sts Key,Temp ;сохраняем код кнопки в ОЗУ ret ;выходим ;********************************************************* KeyTest: andi Temp,0b01110000 ;обрезаем по маске ldi Temp1,0b01110000 eor Temp,Temp1 ;инвертируем биты по маске breq NoButtons ;если все нули (нет нажатых) ;переходим по метке ldi Temp1,0 ;инициализируем счетчик cpi Temp,0b00010000 ;проверяем 0-ю кнопку breq EndKeyTest ;нажато - выход inc Temp1 ;иначе - инкремент счетчика cpi Temp,0b00100000 ;проверка 2 кнопки breq EndKeyTest inc Temp1 cpi Temp,0b01000000 ;проверка 3 кнопки breq EndKeyTest NoButtons: ldi Temp1,255 ;если ничего не нажато - ;возвращаем 255 EndKeyTest: ret Эта программа будет вызываться из цикла Main сразу же после индикации. Она читает ячейки переменной Line на предмет информации о нажатых кнопках. Если ни одна кнопка не нажата – программа возвращает -1 (или 255 – одно и то же). Если нажата хоть одна кнопка – возвращается ее значение. Если при предыдущей проверке была нажата та же кнопка – ставим флажок «повтор» (старший бит возвращаемого значения). Этот флажок нужен для предотвращения повторного выполнения одной и той же операции (которая закреплена за данной кнопкой). Следующая подпрограмма должна непосредственно запустить выполнение операции, закрепленной за нажатой кнопкой. Она тоже вызывается из главного цикла. Называется Keyboard . Keyboard: ;запускает выполнение операции, закрепленной за кнопкой lds Temp,Key ;загружаем переменную Key mov Temp1,Temp ;проверяем на наличие флажка andi Temp1,0b10000000 ;"повтор" или -1 brne EndKeyboard ;если повтор или -1 - выходим ldi ZH,High(KeysLUT*2);загружаем таблицу ldi ZL,Low (KeysLUT*2);истинности ;(клавиша -> значение) ldi Temp1,0 ;смещаемся на номер клавиши add ZL,Temp adc ZH,Temp1 lpm ;загружаем значение элемента mov Temp,R0 lds Temp1,Digit+2 ;сдвигаем разряды индикатора sts Digit+3,Temp1 ;на один влево lds Temp1,Digit+1 sts Digit+2,Temp1 lds Temp1,Digit sts Digit+1,Temp1 sts Digit,Temp ;пишем в млад. разряд ;индикатора текущее значение EndKeyboard: ret ;********************************************************* KeysLUT: .db 1,2,3,4,5,6,7,8,9,10,0,11 ;массив ;(таблица истинности клавиатуры) Эта программа читает переменную Key, и если она равна 255 или стоит флаг повтора – ничего не делаем и выходим. Иначе – загружаем таблицу истинности (попросту - массив), в которой коду каждой клавиши соотнесено определенное число. Наша программа будет брать значение этого числа, и записывать его в младший разряд индикатора, перед этим сдвинув предыдущее значение индикатора на 1 разряд влево. Вот так хитро, и в то же время – просто. Теперь – вникаем в прогу целиком. Сразу скажу, что я добавил в массив 7-сегментного декодера символы шестнадцатеричной системы: a, b, c, d, e, f. Не удивляйтесь :) Полный текст программы - "indicate+keyboard.asm" Все вопросы, как всегда – на форум.
Эти статьи вам тоже могут пригодиться: |
|
|||||||||||||||
![]() |
![]() |


![]() |
![]() |
|||
|
||||
![]() |
![]() |