реклама на сайте
подробности

 
 
 
Reply to this topicStart new topic
> обработка повторных нажатий клавиатуры, atmega32+4x4 kbd
GoodNews
сообщение Feb 21 2010, 18:50
Сообщение #1


Частый гость
**

Группа: Участник
Сообщений: 88
Регистрация: 12-01-09
Из: Minsk (BY)
Пользователь №: 43 259



Драйвер клавиатуры построен на базе атмеловского апноута AVR245. Есть одно но - повторные нажатия клавиш не могу реализовать. Т.е. теоретически представляю как это должно выглядеть, но на практике не получается. Я думал вначале использовать сравнение состояний порта клавиатуры. Т.е. при инициализации драйвера берём объявляем некоторую статическую переменную, в которую загружаем значение порта (все клавиши отпущены), затем в функции обработки клавиш считываем текущее состояние порта и сравниваем со значением по-умолчанию. Если не совпадает, то выводим значение, если совпадает - пропускаем. Также по идее должно проверяться была ли физически отпущена клавиша. Посоветуйте плиз как можно выйти из данного положения.
Go to the top of the page
 
+Quote Post
Алекс1981
сообщение Feb 21 2010, 18:55
Сообщение #2


Участник
*

Группа: Участник
Сообщений: 33
Регистрация: 16-03-09
Из: Волгоградская обл
Пользователь №: 46 144



Насколько я понимаю: требуется отсеять повторные нажатия клавиш? Если да, то есть вариант такой: после обработчика нажатия клавиши ставить задержку 100-200 мс, тем самым не нужно будет думать об отпускании клавиш. Если вариантов нажатий клавиш немного - можно ввести переменную состояния девайса и менять ее при нажатии определенной клавиши, если много, то запоминать нажатие последней клавиши и действительно как вы указали - проверять на соответствие и отсеивать
Go to the top of the page
 
+Quote Post
GoodNews
сообщение Feb 21 2010, 19:28
Сообщение #3


Частый гость
**

Группа: Участник
Сообщений: 88
Регистрация: 12-01-09
Из: Minsk (BY)
Пользователь №: 43 259



Цитата(Алекс1981 @ Feb 21 2010, 20:55) *
после обработчика нажатия клавиши ставить задержку 100-200 мс, тем самым не нужно будет думать об отпускании клавиш

Хочется без лишних задержек, так как кроме этого ещё реализовано динамическое обновление дисплея, так что эта задержка не будет удачным решением. В драйвере используются флаги для проверки состояний клавиш, однако физически они не проверяются на момент фактического "поднятия" клавиши, только програмно, т.е. выполнил кусок кода, выставил флаг в 1 или 0.
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Feb 21 2010, 19:57
Сообщение #4


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Сам я не видел никогда апноута AVR245, так что мой совет может оказаться совсем не в тему.
Как насчёт сделать так, чтобы нажатия и отпускания клавиш приводили к возникновению прерывания?
Ежели это будет весьма накладно и неудобно - нужно сделать чтобы клавиатура опрашивалась по таймеру(т.е. опять таки прерывание).
Сохраняя каждый раз last state и сравнивая его с только что считанным значением можно всегда понять было ли что-то отпущено или нажато....

Вопрос задан как-то не совсем ясно...приходится заниматься телепатией sad.gif
Цитата
Есть одно но - повторные нажатия клавиш не могу реализовать.
это что значит? Как в компе, нажал кнопку и держишь, сначала печатеается одна буква, потом некоторая задержка, а потом буковка начинает повторятся, пока кнопочку не отпустишь? Это имеется ввиду?

Или же нужно что-то типа подавления дребезга контактов?
Нужно чтобы топикстартер всё это прояснил.

Клавиатура с точки зрения железа как построена?

Лень мне искать аппноут smile.gif


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
rezident
сообщение Feb 21 2010, 21:08
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882



Цитата(GoodNews @ Feb 21 2010, 23:50) *
Я думал вначале использовать сравнение состояний порта клавиатуры. Т.е. при инициализации драйвера берём объявляем некоторую статическую переменную,
Все правильно вы мыслите. Только статических переменных нужно две. В одной переменной хранится предыдущее состояние матрицы кнопок, а в другой - устойчивое (после устранения дребезга) состояние матрицы кнопок. Для формирования кодов повтора используйте вторую переменную. XORите новое устойчивое состояние и старое. Если результат XORа равен нулю, но состояние отличается от того, где ни одна кнопка не нажата, то используете ветвление программы для формирования автоповтора. Автоповтор требует отсчета двух временных интервалов. Первый интервал задает задержку начала автоповтора. Второй интервал формирует период автоповтора. Все просто как суп с лапшой smile.gif
Go to the top of the page
 
+Quote Post
GoodNews
сообщение Feb 21 2010, 21:40
Сообщение #6


Частый гость
**

Группа: Участник
Сообщений: 88
Регистрация: 12-01-09
Из: Minsk (BY)
Пользователь №: 43 259



Цитата(sigmaN @ Feb 21 2010, 21:57) *
Клавиатура с точки зрения железа как построена?

Лень мне искать аппноут smile.gif

AVR245: Code Lock with 4x4 Keypad and I2C™ LCD
Сорри. Неправильно написал. Имел ввиду, что не могу понять, как реализовать обработку повторных нажатий (чтобы их избежать).
Вот как код выглядит.
kbd.c
CODE
#include <avr/io.h>
#include <avr/interrupt.h>
#include "kbd.h"

unsigned char KBD_KeyPressed;
unsigned char KBD_LastKey;
unsigned char KBD_ScanRow;
unsigned char KBD_KeyDown;
//Потом убрать
unsigned char KBD_LastRow;
volatile unsigned char column;

//Обработка переполнения Timer1
ISR(TIMER1_OVF_vect)
{
if (!KBD_KeyDown)
{
KBD_PORT |= (0x10<<KBD_ScanRow);
if (++KBD_ScanRow > 3)
KBD_ScanRow = 0;
KBD_PORT &= ~(0x10<<KBD_ScanRow);
asm("nop");
column = (~KBD_PIN) & 0x0f;
}
}

//Считываем значение Timer1
uint16_t read_TCNT1(void)
{
uint16_t i;
unsigned char sreg;

sreg = SREG; //запоминаем состояние бита I (остальные биты нам в общем то побоку)
asm("cli"); //убедимся что флаг I сброшен
i = TCNT1; //читаем TCNT1
SREG = sreg; //востанавливаем состояние бита I
//(если он был сброшен до входа в процедуру то эта операция не даст никакого эффекта)
return i;
}

unsigned char KBD_GetKey(void)
{
unsigned char sreg;
sreg = SREG;
asm("cli");
switch (column)
{
case 0x00:
KBD_KeyDown = 0;
break;
case 0x01:
KBD_LastKey = KBD_Array[KBD_ScanRow][0];
KBD_LastRow = KBD_ScanRow;
if (!KBD_KeyDown)
KBD_KeyPressed = 1;
KBD_KeyDown = 1;
break;
case 0x02:
KBD_LastKey = KBD_Array[KBD_ScanRow][1];
KBD_LastRow = KBD_ScanRow;
if (!KBD_KeyDown)
KBD_KeyPressed = 1;
KBD_KeyDown = 1;
break;
case 0x04:
KBD_LastKey = KBD_Array[KBD_ScanRow][2];
KBD_LastRow = KBD_ScanRow;
if (!KBD_KeyDown)
KBD_KeyPressed = 1;
KBD_KeyDown = 1;
break;
case 0x08:
KBD_LastKey = KBD_Array[KBD_ScanRow][3];
KBD_LastRow = KBD_ScanRow;
if (!KBD_KeyDown)
KBD_KeyPressed = 1;
KBD_KeyDown = 1;
break;
default:
KBD_LastKey = 0;
KBD_KeyPressed = 0;
}

SREG = sreg;
if (KBD_KeyPressed)
{
KBD_KeyDown = 0;
KBD_KeyPressed = 0;
return (KBD_LastKey);
} else return (NO_KEY);
}

//Конфигурирования контроллера для работы с клавиатурой
void KBD_init(void)
{
//Конфигурируем порт клаваиатуры
KBD_REG |= (X1|X2|X3|X4);
KBD_REG &= ~(Y1|Y2|Y3|Y4);

//Конфигурирем выводы клавиатуры
//X1-X4 - выходы, высокий уровень
//Y1-Y4 - входы, высокий уровень, pull-up активен
KBD_PORT |= (X1|X2|X3|X4|Y1|Y2|Y3|Y4);

//Конфигурация таймеров
TCNT0 = 0x00;
TCNT1 = 0x1FF;
TIMSK = (1<<TOIE1); //Включить прерывание по переполнению для Timer1
TCCR1A = 0x00;
TCCR1B = (1<<CS11); //Включаем делитель /8

//Включаем обработку прерываний
asm("sei");
}

kbd.h
CODE
#ifndef _KBD_H
#define _KBD_H
#endif
#include <avr/io.h>

//Столбцы клавиатуры
#define Y1 (1<<0)
#define Y2 (1<<1)
#define Y3 (1<<2)
#define Y4 (1<<3)

//Строки клавиатуры
#define X1 (1<<4)
#define X2 (1<<5)
#define X3 (1<<6)
#define X4 (1<<7)

//Порт клавиатуры
#define KBD_PORT PORTB
#define KBD_PIN PINB

//Конфигурация порта клавиатуры
#define KBD_REG DDRB

void KBD_init(void);
unsigned char KBD_GetKey(void);
uint16_t read_TCNT1(void);

//Если ни одна клавиша не была нажата, то возвращаем значение -1
static uint8_t NO_KEY = -1;

//Массив определяющий значения клавиш
static uint8_t KBD_Array[4][4]=
{
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
extern volatile unsigned char column;
extern unsigned char KBD_LastRow;

Как я уже сказал, идея в том, что надо нажать кнопку, напечатается один символ. Второй символ напечатается после того, как кнопка будет отпущена. А вот уже этим вторым символом может быть эта же кнопка ну и т.д. Т.е. нужно пройти через факт "одно нажатие - один символ".

Цитата(rezident @ Feb 21 2010, 23:08) *
Все правильно вы мыслите.

smile.gif верной дорогой значит! Автоповтор в принципе я не планирую использовать. Клавиатура используется для прямого ввода значений частоты и управления меню (меню пока ещё на стадии планирования, поэтому пока даже и не рассматривается). Надо переварить супчик smile.gif Спасибо за наводку на мысль smile.gif

Сообщение отредактировал rezident - Feb 21 2010, 22:17
Причина редактирования: Нарушение п.3.4 Правил форума.
Go to the top of the page
 
+Quote Post
rezident
сообщение Feb 21 2010, 22:26
Сообщение #7


Гуру
******

Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882



Цитата(GoodNews @ Feb 22 2010, 02:40) *
Т.е. нужно пройти через факт "одно нажатие - один символ".
Дык стандартный путь примените. Реализуйте клавиатуру так, как все делают. Ваш UI (User Interface - пользовательский интерфейс) должен работать не с состоянием матрицы кнопок напрямую, а с буфером клавиатуры. В буфер клавиатуры должны помещаться скан-коды клавиш по факту нажатия или отпускания или автоповтора. Реализуйте три функции: а) putkey, б) checkkey, в) getkey с помощью которых вы будете работать с буфером клавиатуры.
putkey - сканирует матрицу клавиатуры; устраняет дребезг; определяет соответствие нажатой (и/или отжатой) кнопки и скан-кода; помещает скан-код в буфер клавиатуры, увеличивая счетчик буфера; формирует автоповтор последней нажатой клавиши
getkey - извлекает текущий скан-код из буфера клавиатуры, уменьшая счетчик буфера
checkkey - извлекает текущий скан-код из буфера клавиатуры, но без изменения счетчика буфера.
getkey и checkkey возращают скан-код 'NUL', если буфер клавиатуры пуст.
Go to the top of the page
 
+Quote Post
DpInRock
сообщение Feb 22 2010, 02:15
Сообщение #8


Гуру
******

Группа: Участник
Сообщений: 2 254
Регистрация: 4-05-07
Из: Moscow
Пользователь №: 27 515



Алгоритм устранения дребезга выглядит так.
Язык описания - около Си.

CODE
unsigned char time;
void Some_timer_interrupt(void) // every 1 ms
{
time++;
}

unsigned char KEY_EVENT=0;

unsigned char key_state=0,Prev_key;
void Keyboard_task(void)
{
unsigned char key;
switch (key_state)
{
//state 0 -- wait for key press
case 0:
if (PIN()==xFF) break;
key_state=1; Prev_key=PIN();time=0;
break;
//state 1 - Убираем дребезг
case 1:
key=PIN();if (key<>Prev_key) {time=0;Prev_key=key;break;}//значение клавиатуры изменилось....
if (time>40) {if (key==0xFF) key_state=0; // ни фига - помеха проскочила....
else
{KEY_EVENT=key|0x80;key_state=2;} // ага, 40 миллисекунд клава стоит как вкопанная
};
break;
//ждем отжатия
case 2:
if (PIN()!=0xFF) {time=0;break;}// клава все еще не отжата
if (time>40) {key_state=0;KEY_EVENT=0x40};//40 миллисекунд как стабильно отжата. Можем послать сообщение //
//отжатии, а можем и не посылать. Обычно этого не требуется.
break;

}

}


Ну эта процедура вызывается время от времени. В фоновом цикле. Времени она не отъедает нисколько.

Сообщение отредактировал Omen_13 - Feb 22 2010, 05:13
Причина редактирования: Оформление кодбокса


--------------------
On the road again (Canned Heat)
Go to the top of the page
 
+Quote Post
GoodNews
сообщение Feb 23 2010, 11:21
Сообщение #9


Частый гость
**

Группа: Участник
Сообщений: 88
Регистрация: 12-01-09
Из: Minsk (BY)
Пользователь №: 43 259



Как переварю, реализую и отпишусь. Всем большое спасибо за ответы! С 23-м Февраля всех!
Go to the top of the page
 
+Quote Post
GoodNews
сообщение Feb 24 2010, 09:03
Сообщение #10


Частый гость
**

Группа: Участник
Сообщений: 88
Регистрация: 12-01-09
Из: Minsk (BY)
Пользователь №: 43 259



2DpInRock:
Как я понял, KEY_EVENT - что-то типа флага статуса и он в общем-то не обязателен. Что-то вроде feedback'а.
Go to the top of the page
 
+Quote Post
GoodNews
сообщение Feb 24 2010, 19:55
Сообщение #11


Частый гость
**

Группа: Участник
Сообщений: 88
Регистрация: 12-01-09
Из: Minsk (BY)
Пользователь №: 43 259



Итак, вот что получилось.
kbd.c
CODE

#include <avr/io.h>
#include <avr/interrupt.h>
#include "kbd.h"
#define READ_KEY_COL() ((~KBD_PIN) & 0x0f)
enum {
STATE_WAIT,
STATE_DEBOUNCE,
STATE_WAITKEYUP
};
static uint8_t KBD_PrevKey;
static uint8_t KBD_LastKey;
uint8_t KBD_ScanRow;
uint8_t KBD_KeyDown = 0;
uint8_t KEY_STATE = STATE_WAIT;
uint8_t COLUMN_DEFAULT_STATE = 0x00;
volatile uint16_t time = 0;
volatile unsigned char column;

volatile uint8_t isKeyInBuffer = 0;
volatile uint8_t columnInBuffer;
volatile uint8_t rowInBuffer;

//Массив определяющий значения клавиш
static uint8_t KBD_Array[4][4]=
{
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};

void KBD_TASK(void)
{
switch (KEY_STATE)
{
//case 0 - ожидание нажатия клавиши
case STATE_WAIT:
if (READ_KEY_COL() == COLUMN_DEFAULT_STATE)
{
KBD_KeyDown = 0;
break;
}
KBD_PrevKey = READ_KEY_COL();
time = 0;
KEY_STATE = STATE_DEBOUNCE;
break;
//Убираем дребезг
case STATE_DEBOUNCE:
KBD_LastKey = READ_KEY_COL();
if (KBD_LastKey != KBD_PrevKey)
{
time = 0;
KBD_KeyDown = 0;
KEY_STATE = STATE_WAIT;
break;
}
if (time > 40)
{
columnInBuffer = KBD_LastKey;
rowInBuffer = KBD_ScanRow;
isKeyInBuffer = 1;
KEY_STATE = STATE_WAITKEYUP; //Всё-таки нажатие не случайное
}
break;
//Ожидание отжатия клавиши
case STATE_WAITKEYUP:
if (READ_KEY_COL() != COLUMN_DEFAULT_STATE)
{
time = 0;
break; //Клавишу не отпустили
}
if (time > 1024)
{
KEY_STATE = STATE_WAIT; //Клавишу отпустили
KBD_KeyDown = 0;
}
}
}
unsigned char KBD_GetKey(void)
{
unsigned char KBD_PressedKey = NO_KEY;
if (isKeyInBuffer)
{
switch (columnInBuffer)
{
case 0x00:
KBD_PressedKey = NO_KEY;
break;
case 0x01:
KBD_PressedKey = KBD_Array[rowInBuffer][0];
break;
case 0x02:
KBD_PressedKey = KBD_Array[rowInBuffer][1];
break;
case 0x04:
KBD_PressedKey = KBD_Array[rowInBuffer][2];
break;
case 0x08:
KBD_PressedKey = KBD_Array[rowInBuffer][3];
break;
default:
KBD_PressedKey = NO_KEY;
}
isKeyInBuffer = 0;
}
return (KBD_PressedKey);
}

//Обработка переполнения Timer1
ISR(TIMER1_OVF_vect)
{
TCNT1 = TMR1_CNT;
time++;

if (!KBD_KeyDown)
{
KBD_PORT |= (0x10<<KBD_ScanRow);
if (++KBD_ScanRow > 3)
KBD_ScanRow = 0;
KBD_PORT &= ~(0x10<<KBD_ScanRow);
asm("nop");
column = READ_KEY_COL();
if (COLUMN_DEFAULT_STATE != READ_KEY_COL())
KBD_KeyDown = 1;
} else
KBD_TASK();
}

//Конфигурирования контроллера для работы с клавиатурой
void KBD_init(void)
{
//Конфигурируем порт клаваиатуры
KBD_REG |= (X1|X2|X3|X4);
KBD_REG &= ~(Y1|Y2|Y3|Y4);

//Конфигурирем выводы клавиатуры
//X1-X4 - выходы, высокий уровень
//Y1-Y4 - входы, высокий уровень, pull-up активен
KBD_PORT |= (X1|X2|X3|X4|Y1|Y2|Y3|Y4);

//Конфигурация таймеров
TCNT0 = 0x00;
TCNT1 = TMR1_CNT;
TIMSK = (1<<TOIE1); //Включить прерывание по переполнению для Timer1
TCCR1A = 0x00;
TCCR1B = (1<<CS12)|(1<<CS10); //Включаем делитель /1024

//Включаем обработку прерываний
asm("sei");
}

kbd.h
CODE

#ifndef _KBD_H
#define _KBD_H
#endif
#include

//Столбцы клавиатуры
#define Y1 (1<<0)
#define Y2 (1<<1)
#define Y3 (1<<2)
#define Y4 (1<<3)

//Строки клавиатуры
#define X1 (1<<4)
#define X2 (1<<5)
#define X3 (1<<6)
#define X4 (1<<7)

//Порт клавиатуры
#define KBD_PORT PORTB
#define KBD_PIN PINB

//Конфигурация порта клавиатуры
#define KBD_REG DDRB

//Если ни одна клавиша не была нажата, то возвращаем значение 0
#define NO_KEY 0

//Состояние выходов порта клавиатуры при отжатых клавишах
#define KBD_DEFAULT_STATE 0xff

//Timer1 стартует с позиции 0xffff (MAX)
#define TMR1_CNT 0xffff

void KBD_init(void);
unsigned char KBD_GetKey(void);
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 9th July 2025 - 05:55
Рейтинг@Mail.ru


Страница сгенерированна за 0.0146 секунд с 7
ELECTRONIX ©2004-2016