Суббота, 24.06.2017, 23:49
Вы вошли как Гость | Группа "Гости"Приветствую Вас Гость | RSS
Главная | Каталог статей | Мой профиль | Регистрация | Выход | Вход
Обитель RC-инженера
Форма входа
Меню сайта

Категории раздела
Программирование микроконтроллеров AVR [8]
Программирование микроконтроллеров STM32 [6]
Программирование ПЛИС Altera [3]
Разное [2]
Статьи на различную тематику
Самодельные системы управления моделями [5]

Наш опрос
Есть ли у вас модели самолетов с убирающимися шасси ?
Всего ответов: 22

Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz

  • Главная » Статьи » Программирование микроконтроллеров AVR

    Драйвер кнопок устройства
    Некоторое время назад я делал проект кодера для радиоуправляемых моделей Vcoder и мне понадобилось использовать кнопки для управления работой прошивки.

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

    Фактически алгоритм выглядел так

    1. Проверяем нажата ли хоть одна кнопка:

    -прочитали байт из порта к которому подключены кнопки
    -проверим по маске нажата ли хоть одна кнопка
    --если не нажата - то счетчик удержания нажатия кнопки =0, и идем на выход из подпрограммы



    2. если хоть одна кнопка нажата то:

    - запоминаем состояние порта (битовую конфигурацию нажатых и не нажатых кнопок)
    - увеличиваем счетчик проверки удержания кнопки.


    3. Если счетчик удержания кнопки достиг некоторого порогового значения - то выставляем флаг того что кнопка нажата и сбрасываем счетчик удержания.


    В принципе работал этот принцип примерно так же как работает клавиатура на компьютере: при нажатии на кнопку происходит замер длительности нажатия для устранения дребезга, и если кнопка какое то время не отпускалась то генерируется нажатие, далее по истечении еще какого то промежутка времени, если кнопка не отпускалась то нажатие генерируется снова...


    удобно? - тогда показалось что вполне !

    драйвер кнопок с таким алгоритмом и был реализован в прошивке VCoder

    Но прошло некоторое время и это решение показало себя не очень удобным.

    Например, нажатие на кнопку @MENU@ приводит к переходу в меню настроек аппаратуры, а в самом меню к переходу по выбранному пункту меню.. и пользователь аппаратуры зачастую чуть передержав кнопку @MENU@ входил не только в меню, но и в первый его пункт (обычно это был пункт RESET MODEL - который сбрасывал все настройки модели !)


    Сначала проблему решали полумерами в виде внедрения диалога проверки в пункт RESET MODEL (требовалось ответить YES для сброса), пункт YES конечно же располагался у кнопки @MENU@...

    Потом этот пункт переместили на кнопку @EXIT@, а пункт отказа от сброса (NO) разместили у кнопки @MENU@...

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

    постепенно пользователи привыкли, однако думаю что моя икота иногда вызывается гневом именно пользователей прошивки Vcoder при работе их с кнопкой @MENU@

    Через какое то время я начал писать еще одну прошивку (A-Coder) и тогда решил что с этим нужно что то делать - негоже иметь такие ляпы в такой казалось бы простой задачке как опрос кнопок управления.

    И придумался мне следующий функционал:
    Должны быть кнопки которые при нажатии генерируют автоповтор (после нажатия и удержания кнопки генерируется повторяющееся коды нажатой кнопки), а должны быть те кто не генерируют (то есть при нажатии и удержании кнопки генерируется только одно нажатие, и повторного, сколько бы мы кнопку не удерживали - не будет)

    Первый режим (автоповтор при удержании) я назвал push\hold, второй режим, при котором нажатие генерируется только один раз, независимо от времени удержания - push\up

    Этот функционал реализовывался несколько раз и алгоритм каждый раз мне нравился все больше и больше :)
    предлагаю вам текст этой подпрограммы для использования в ваших проектах

    константы:

    конфигурация подключения кнопок:

    • PIN_MN_KEY - порт PINx к которому
      подключены кнопки, все кнопки управления должны быть подключены к одному
      порту, но не обязательно к пинам порта идущим подряд


    • MN_KEY_MASK - битовая маска кнопок порта (соответствующие биты кнопок установлены в "1")


    • MN_KEY_UP, MN_KEY_DN, MN_KEY_LF, MN_KEY_RG - номера бит к которым подключены кнопки, внимание в константах именно номера бит!!

    внешние переменные (размещаются в DSEG, изменяются другой подпрограммой)

    • COUNTER - переменная в DSEG: счетчик
      интервалов, от 20 до 100 гц (в принципе любой частоты, изменяется в
      пределах 0..255 циклически)

    переменные драйвера (размещаются в DSEG):
    • KR1_KEY_MASK - входной параметр: маска кнопок которые опрашиваются в режиме push\hold
    • KR1_KEY_READ - выходной параметр: зарегистрированный код нажатой кнопки (выходная переменная модуля !)
    • KR1_OLD_KEY - временно хранимый код нажатой кнопки
    • KR1_PH_ONTIME - количество циклов вызова для регистрации кнопки в режиме push\hold
    • KR1_PU_ONTIME - количество циклов вызова для регистрации кнопки в режиме push\up
    • KR1_PU_WAIT - вспомогательная переменная, флаг того что кнопка в режиме push\hold еще не отпускалась


    ; key_reader.asm - модуль чтения нажатых кнопок меню

    .equ            COUNTER = T1_COUNTER        ; имя переменной-счетчика в DSEG
    KEY_READER:            ; прочитаем состояние кнопок
                        IN        R16        , PIN_MN_KEY
                        ANDI    R16        , MN_KEY_MASK   ; маска кнопок меню

                        ; проверим нажата ли хоть одна кнопка
                        CPI        R16        , (1<<MN_KEY_UP)|(1<<MN_KEY_DN)|(1<<MN_KEY_LF)|(1<<MN_KEY_RG)
                        BREQ    KR1_EXIT                ; ни одна из кнопок не нажата, идем на выход
                       
                        ; зарегистрировано нажатие какой-то кнопки
                        LDS        R17        , KR1_OLD_KEY    ; нажатые кнопки в прошлый период
                        CP        R16        , R17            ; сравним текущие и старые нажатые кнопки
                        BREQ    KR1_TIMER_LIST            ; кнопки удерживаются - проверим время

                        ; нажатые кнопки сейчас и нажатые ранее кнопки - отличаются
                        STS        KR1_OLD_KEY    , R16        ; сохраним нажатые кнопки
                        LDS        R16        , COUNTER
                        STS        KR1_KEY_COUNTER    , R16    ; сохраним момент нажатия
                        RJMP    KR1_END                    ; идем на выход

    KR1_TIMER_LIST:        ; кнопка нажата и удерживается, проверим достаточное ли времени прошло для
                        ; регистрации нажатия в обоих режимах сканирования клавиатуры

    KR1_TIMER_PH_LIST:    ; режим push\hold
                        ; проверка достаточности нажатия в режиме push\hold
                        MOV        R15        , R16            ; сохраним нажатые кнопки меню

                        LDS        R17        , KR1_KEY_MASK    ; прочитаем маску чтения push\hold
                        AND        R16        , R17            ; наложим маску
                        CP        R16        , R17            ; есть кнопки нажатые для этого режима?
                        BREQ    KR1_TIMER_PU_LIST        ; перейдем к проверке нажатых кнопок в режиме
                                                        ; push\up
                        ; у нас нажаты кнопки в режиме push\hold
                        LDS        R16        , COUNTER        ; загрузим текущий счетчик
                        LDS        R17        , KR1_KEY_COUNTER    ; загрузим счетчик когда было зарегистрировано
                                                            ; нажатие кнопок
                        SUB        R16        , R17            ; определим время нажатия
                        LDS        R17        , KR1_PH_ONTIME    ; берем необходимую длительность нажатия
                        CP        R16        , R17
                        BRLO    KR1_TIMER_PH_LIST_NO    ; кнопка удерживается недостаточное время, выходим
                       
    KR1_REG_KEY_PRESS:    ; кнопка удерживается достаточное время, зарегистрируем нажатие
                        STS        KR1_KEY_READ    , R15    ; сохраним нажатые кнопки
                        LDI        R16        , (1<<MN_KEY_UP)|(1<<MN_KEY_DN)|(1<<MN_KEY_LF)|(1<<MN_KEY_RG)
                        STS        KR1_OLD_KEY    , R16        ; сбросим временный код нажатых кнопок
                        RJMP    KR1_END                    ; идем на выход   

    KR1_TIMER_PU_LIST:    ; режим push\up                   
                        LDS        R16        , KR1_PU_WAIT    ; проверим не ожидаем ли мы отпускания кнопок
                        CPI        R16        , 1                ; меню после регистрации нажатия
                        BREQ    KR1_PROC_WAIT            ; выходим ожидая отпускания кнопок
                                           
                        ; проверим нужное время ли удерживается кнопка
                        LDS        R16        , COUNTER        ; загрузим текущий счетчик
                        LDS        R17        , KR1_KEY_COUNTER    ; загрузим счетчик когда было зарегистрировано
                                                            ; нажатие кнопок
                        SUB        R16        , R17            ; определим время нажатия
                        LDS        R17        , KR1_PU_ONTIME    ; берем необходимую длительность нажатия
                        CP          R16        , R17
                        BRLO      KR1_TIMER_PH_LIST_NO    ; кнопка удерживается недостаточное время, выходим
                       
                        ; кнопка удерживается достаточное время установим флаг ожидания отпускания
                        LDI        R16        , 1
                        STS        KR1_PU_WAIT    , R16
                        RJMP      KR1_REG_KEY_PRESS        ; и зарегистрируем нажатие кнопки

    KR1_EXIT:            ; выход, по отпусканию кнопок
                        STS       KR1_OLD_KEY    , R16    ; сбросим временные нажатые кнопки
                        LDI        R16        , 0
                        STS       KR1_PU_WAIT    , R16        ; сбросим флаг ожидания отпускания (уже отпустили)
                                                        ; для режима push\up

    KR1_END:            ; выход
    KR1_TIMER_PH_LIST_NO: ; выходим по недостаточному времени удержания кнопок
    KR1_PROC_WAIT:        ; ожидаем отпускание нажатых кнопок в режиме push\up

                        RET


    И следом идет процедура которая считывает код кнопок нажатие которых было зарегистрировано

    GET_KEY:            ; функция чтения считанной кнопки
                        PUSH      R17
                        LDS        R16        , KR1_KEY_READ
                        LDI         R17        , (1<<MN_KEY_UP)|(1<<MN_KEY_DN)|(1<<MN_KEY_LF)|(1<<MN_KEY_RG)
                        STS        KR1_KEY_READ    , R17
                        POP        R17
                        RET

    Файл драйвера на языке ассемблера можно скачать здесь -> http://vg.ucoz.ru/load/iskhodnye_teksty_programm_na_assemblere_avr/drajver_knopok_ustrojstva/4-1-0-8
    Категория: Программирование микроконтроллеров AVR | Добавил: ВитГо (15.08.2012)
    Просмотров: 2039 | Теги: avr, кнопки | Рейтинг: 0.0/0
    Всего комментариев: 0
    Добавлять комментарии могут только зарегистрированные пользователи.
    [ Регистрация | Вход ]

    Copyright MyCorp © 2017
    Сделать бесплатный сайт с uCoz