Итак, вот что получилось.
kbd.cCODE
#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.hCODE
#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);