Перейти к содержанию
    

Помогите с матричной клавиатурой!

Помогите сделать матричную клаву. Моя не пашет(((беру готовую рабочую схему, пишу програмулину, всё вроде делаю по примеру, а она всё равно не пашет, прерывание не срабатывает. Кто нить может поделиться соображениями/кодом реализации матричной клавиатуры через прерывание. Поиском пользовался, кое что нашел, но это только подтвердило что всё должно работать, но...(((

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А что за схема? Как соединены выводы? Можно поподробнее?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

и зачем именно через прерывание?

клавиатура - весьма медленное устройство ввода, для её обслуживания прекрасно подходит периодический опрос её состояния.

или планируется, что устройство будет просыпаться по прерыванию от клавиатуры?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

И как вы вообще прерывание от клавы делаете? Завели все опросные линии на отдельные Int или объединили их каким нибудь способом?

 

И к чему всё это поясните? Я в библиотеки выкладывал рабочую библиотеку 3х4 с возможностью динамического ввода и даже с вводом букв по принципу сотового телефона. Возьмите - пользуйтесь.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

по прерываниям :)

 

#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>

#define byte 			uint8_t
#define word 			uint16_t

#define KEY_SHORT		10
#define KEY_LONG		6000

volatile byte			scan = 0;
volatile byte			scan_stop = 0;
volatile byte			scan_code = 0;
volatile word			key_age = 0;
volatile byte			key_pressed = 0;
volatile byte			key_long = 0;

ISR(TIMER1_COMPA_vect)
{
if (!scan_stop) PORTL = ~_BV(++scan & 7);
else {
	key_age += (key_age == 0xFFFF) ? 0 : 1;
	if (key_age == KEY_SHORT) key_pressed = 1;
	else if (key_age == KEY_LONG) key_long = 1;
}
PCICR |= _BV(PCIE2);
}

ISR(PCINT2_vect)
{
register byte i, p;

if ((p = PINK) != 0xFF) {					// key pressed
	for (i = 0; i != 8; i++) if(!(p & _BV(i))) break;
	scan_code = (scan << 3) + i;
	PCMSK2 = _BV(i);
	scan_stop = 1;
	key_age = 0;
} else {									// key released
	PCMSK2 = 0xFF;
	scan_stop = 0;
}
PCICR &= ~_BV(PCIE2);
}

word
getkey()
{
word key;

for (;;) {
	if (key_pressed) {
		key_pressed = 0;
//
// do something
//
		break;
	}
	else if (key_long) {
		key_long = 0;
//
// do something
//
		break;
	}
}
return (key);
}

void
init(void)
{
// timer initialization
PRR0 &= ~_BV(PRTIM1);			// enable timer 1

TCCR1A = 0;
TCCR1B = _BV(WGM12) | _BV(CS11);
TIMSK1 = _BV(OCIE1A);
TCNT1 = 0;
OCR1A = 2000;

// setup button interrupts
PCMSK2 = 0xFF;

sei();
}

int
main(void)
{
word i;

init();

for (;;) {
	i = getkey();
//
// do something
//
}
exit(0);
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не вдаваясь в разбор вашего исходника, попробую пояснить на "пальцах".

Для обслуживания матричной клавиатуры нужно реализовать как минимум три функции.

Функция №1. Сканирование матрицы, устранение дребезга, определение нажатых/отпущенных клавиш, формирование соответствующих скан-кодов, помещение скан-кодов в буфер клавиатуры.

Функция №2. Выдача текущего скан-кода без извлечения его из буфера.

Функция №3. Выдача текущего скан-кода с извлечением его из буфера.

Функция №1 обычно вызывается по прерыванию от таймера с равномерным интервалом времени (от 5мс до 100мс). Чаще, чем 200 раз в секунду вызывать ее нет смысла: а) никакая супер-пупер-машинистка не сможет с такой частотой колотить по клавишам; б) дребезг многих кнопок как раз порядка 1мс...10мс. Реже 10 раз в секунду тоже вызывать не желательно, т.к. будет значительная задержка генерации скан-кодов, а пользователю придется довольно долго удерживать клавиши для устойчивого определения нажатия их в программе. Лично я использую период 10...20мс.

Буфер нужен в любом случае. Хотя бы даже из одного байта буфер. Потому, что процесс опроса матрицы клавиш и процесс использования полученного скан-кода в общем случае асинхронные. Асинхронные процессы синхронизируются с помощью буферов. Буфер для кодов клавиатуры может быть линейным или циклическим. В первом случае используются две переменных: счетчик количества скан-кодов, находящихся в буфере и указатель на текущий скан-код. Во-втором случае нужны три переменных: тоже счетчик скан-кодов, указатель на позицию скан-кода, предназначенного для извлечения из буфера и указатель на позицию для записи следующего скан-кода. В первом случае необходимо строго следить за атомарностью (одновременностью) обнуления счетчика и указателя при извлечении всех скан-кодов. Потому, что Функции №2, 3 (чтение/извлечение скан-кода из буфера) могут быть прерваны Функцией №1.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

На ATmega16, на асме, код коцаный перекоцаный:

.include "m16def.inc"

.equ	LCD_RS	= 5
.equ	LCD_E	= 7

.def	temp	= r16
.def	argument= r17		;argument for calling subroutines
.def	return	= r18		;return value from subroutines

.CSEG
.org 0
       jmp RESET            ; Reset 

	jmp	EXT_INT1
	reti
	jmp	EXT_INT1
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop



RESET:
	clr	r16
       out	GICR,	r16          	;Disable external interrupt
       out	DDRA,	r16           	;All in
       out	DDRB,	r16
       out	DDRC,	r16
       out	DDRD,	r16

       out	PORTA,	r16          	; All Tri-state
       out	PORTB,	r16
       out	PORTC,	r16
       out	PORTD,	r16

 		ldi	r16, 0b00100000
       out	DDRA, r16          ; ????? out A
       ldi	r16, 0xFF 
       out	DDRB, r16          ; ????? out B
       ldi	r16, 0xFF 
       out	DDRC, r16          ; ????? out C
	ldi	r16, 0b10100000  
       out	DDRD, r16          ; ????? out D


       ldi	r16, 	0x00		;PORTA5 is RS
       out	PORTA, 	r16          
       ldi	r16, 	0b00110000 
       out	PORTB, 	r16                 	
       ldi	r16, 	0x00 
       out	PORTC, 	r16          
	ldi	r16, 	0x00  	;PORTD7 is E
       out	PORTD, 	r16   

ldi	r16,	LOW(RAMEND)    ;Stack Pointer
       out	SPL,	r16
       ldi	r16,	HIGH(RAMEND)   
       out	SPH,	r16

	ldi	r16,	0x00
       out	SFIOR,	r16
       ldi	r16,	0x0B 
       out	MCUCR,	r16
       ldi	r16,	0x10	
       out	WDTCR,	r16

rcall	LCD_init
rcall	LCD_delay



ldi		r16,	0x80
   	out		GICR,	r16 


;rcall	text_out


LOOP:	
	sei
	rjmp	LOOP

EXT_INT1:
rcall	text_out		;MUST WORK!(((((((((((((((
reti


lcd_command8:	;used for init (we need some 8-bit commands to switch to 4-bit mode!)
;in	temp, DDRD		;we need to set the high nibble of DDRD while leaving
				;the other bits untouched. Using temp for that.
;sbr	temp, 0b11110000	;set high nibble in temp
;out	DDRD, temp		;write value to DDRD again
in		temp, 		PortC		;then get the port value
andi	temp, 		0x0F
andi	argument, 	0xF0	;then clear the low nibble of the argument
				;so that no control line bits are overwritten
or		temp,		argument		;then set the data bits (from the argument) in the	
				;Port value
out		PortC, 		temp		;and write the port value.
cbi		PortA,		LCD_RS
sbi		PortD,		LCD_E		;now strobe E
nop
nop
nop
nop
nop
nop
nop
nop
cbi		PortD, 		LCD_E
;cbi		PortA,		LCD_RS

ret

lcd_putchar:
push	argument		;save the argmuent (it's destroyed in between)

in		temp,		PortC		;then get the data from PortD
;push	temp
andi	temp,		0x0F	;clear ALL LCD lines (data and control!)
andi	argument,	0xF0	;we have to write the high nibble of our argument first
				;so mask off the low nibble
or		temp, 		argument		;now set the argument bits in the Port value
out		PortC, 		temp		;and write the port value
sbi		PortA, 		LCD_RS		;now take RS high for LCD char data register access
sbi		PortD, 		LCD_E		;strobe Enable
nop
nop
nop
nop
nop
nop
nop
nop
cbi		PortD, LCD_E

pop		argument		;restore the argument, we need the low nibble now...
andi	temp, 	0x0F	;clear the data bits of our port value
swap	argument		;we want to write the LOW nibble of the argument to
				;the LCD data lines, which are the HIGH port nibble!
andi	argument, 0xF0	;clear unused bits in argument
or		temp, 		argument		;and set the required argument bits in the port value
out		PortC, temp		;and write the port value
sbi		PortA, LCD_RS		;now take RS high for LCD char data register access
sbi		PortD, LCD_E		;strobe Enable
nop
nop
nop
nop
nop
nop
nop
nop
cbi		PortD, LCD_E
;pop		temp
;out		PortC,	temp	
ret

lcd_command:	;same as LCD_putchar, but with RS low!
push	argument		;save the argmuent (it's destroyed in between)

in		temp,		PortC		;then get the data from PortD
andi	temp,		0x0F	;clear ALL LCD lines (data and control!)
andi	argument,	0xF0	;we have to write the high nibble of our argument first
				;so mask off the low nibble
or		temp, 	argument		;now set the argument bits in the Port value
out		PortC, temp		;and write the port value
cbi		PortA, LCD_RS		;now take RS high for LCD char data register access
sbi		PortD, LCD_E		;strobe Enable
nop
nop
nop
nop
nop
nop
nop
nop
cbi		PortD, LCD_E

pop		argument		;restore the argument, we need the low nibble now...
andi	temp, 	0x0F	;clear the data bits of our port value
swap	argument		;we want to write the LOW nibble of the argument to
				;the LCD data lines, which are the HIGH port nibble!
andi	argument, 0xF0	;clear unused bits in argument
or		temp, 		argument		;and set the required argument bits in the port value
	out		PortC, temp		;and write the port value
cbi		PortA, LCD_RS		;now take RS high for LCD char data register access
sbi		PortD, LCD_E		;strobe Enable
nop
nop
nop
nop
nop
nop
nop
nop
cbi		PortD, LCD_E
ret

LCD_wait:
ldi		r18,	0x03
LCD_wait_l1:
clr		r19
LCD_wait_l2:
dec		r19
brne	LCD_wait_l2		
dec		r18
brne	LCD_wait_l1

ret


LCD_delay:
clr	r2
LCD_delay_outer:
clr	r3
	LCD_delay_inner:
	dec	r3
	brne	LCD_delay_inner
dec	r2
brne	LCD_delay_outer
ret

LCD_init:	
rcall	LCD_delay		;first, we'll tell the LCD that we want to use it
ldi	argument, 0x20		;in 4-bit mode.
rcall	LCD_command8		;LCD is still in 8-BIT MODE while writing this command!!!

rcall	LCD_delay
ldi	argument, 0x28		;NOW: 2 lines, 5*7 font, 4-BIT MODE!
rcall	LCD_command		;

rcall	LCD_delay
ldi	argument, 0x0F		;now proceed as usual: Display on, cursor on, blinking
rcall	LCD_command

rcall	LCD_delay
ldi	argument, 0x01		;clear display, cursor -> home
rcall	LCD_command

rcall	LCD_delay
ldi	argument, 0x06		;auto-inc cursor
rcall	LCD_command
ret

text_out:
ldi	Zl, LOW(2*line1)
ldi	Zh, HIGH(2*line1)
text_out_l1:
lpm
adiw	Zl, 1
tst		r0
breq	text_out_l2

mov		argument, r0
rcall	LCD_putchar
rcall	LCD_wait
rjmp	text_out_l1
text_out_l2:

ldi		argument, 0x80
ori		argument, 0x40
rcall	LCD_command
rcall	LCD_wait

ldi	Zl, LOW(2*line2)
ldi	Zh, HIGH(2*line2)
text_out_l3:
lpm
adiw	Zl, 1
tst		r0
breq	text_out_l4
mov		argument, r0
rcall	LCD_putchar
rcall	LCD_wait
rjmp	text_out_l3
text_out_l4:
ret




line1:	.db		' ',' ',' ','G','R','E','A','T','I','N','G','S','!',' ',' ',' ',0
line2:	.db		' ',' ',' ','V','I','C','E',' ','C','I','T','Y','!',' ',' ',' ',0

...как тэг кода ставить? не подскажете?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

coolibin,

как приведённый код соотносится с обсуждаемой темой?

...как тэг кода ставить? не подскажете?
давим кнопку "Ответить"; обращаем внимание на спецкнопочки над полем ввода сообщения.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

как приведённый код соотносится с обсуждаемой темой?

Это собственно и есть моя программа которая не работает, прерывание не происходит((

Изменено пользователем coolibin

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...как тэг кода ставить? не подскажете?

можно и в окне быстрого ответа просто написать (code)тут ваш код(/code), только скобочки квадратные поставить [].

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Это собственно и есть моя программа которая не работает, прерывание не происходит((

так про матричную клавиатуру в коде ни единого намека :)

а прерывание не происходит, потому что вектор прописан неправильно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

а прерывание не происходит, потому что вектор прописан неправильно

Поправлю, для INT0 - правильно, а для INT1 - неправильно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

так про матричную клавиатуру в коде ни единого намека :)

а прерывание не происходит, потому что вектор прописан неправильно.

Ну, задумывалась она как матричная клава. Дальше прерывания не дошёл. А что в векторе прерываний не правильно? Дело в том что это моя первая прога раньше никогда не писал под микропроцы

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А что в векторе прерываний не правильно?
не на месте.

разрешено INT1, а инициализирован вектор INT0.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

не на месте.

разрешено INT1, а инициализирован вектор INT0.

 

Вы имеете ввиду нужно так:

jmp RESET; Reset 
nop
jmp EXT_INT1

?

Я так сначала и делал, но результат не изменился(((

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...