Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Проблема опроса матрицы кнопок
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > Linux
fademike
Помогите, пожалуйста с проблемой очень начинающему "программисту"!

К отладочной плате SK-AT91SAM9G45-XC6SLX_V1B с установленным на нее Linux kernel, подключена матрица кнопок с подтяжкой к питанию. Написал модуль линукса, чтобы он мне в устройстве "\dev\Keys" показывал какая кнопка нажата. Линии матрицы я подвязал к прерываниям, а столбцы к нулям. При нажатии на кнопку, модуль по отрицательному фронту запускает прерывание и начинает сканировать все кнопки, создавая еще прерывания и так, пока не выдаст ошибку или пока не отпустить кнопку.
Как сделать так, чтобы прерывания не вызывались во время выполнения функции прерывания??

Флаги все перебрал, disable_irq вообще не вариант((

Ошибка в командной строке появляется после нажатия на кнопку более секунды!

#irq 88: nobody cared (try booting with the "irqpoll" option)
[<c002f584>] (unwind_backtrace+0x0/0xf4) from [<c0066dc8>] (__report_bad_irq+0x74/0xa4)
[<c0066dc8>] (__report_bad_irq+0x74/0xa4) from [<c0066f7c>] (note_interrupt+0x184/0x1f4)
[<c0066f7c>] (note_interrupt+0x184/0x1f4) from [<c00678a0>] (handle_simple_irq+0x7c/0x94)
[<c00678a0>] (handle_simple_irq+0x7c/0x94) from [<c0033448>] (gpio_irq_handler+0xb8/0xdc)
[<c0033448>] (gpio_irq_handler+0xb8/0xdc) from [<c0029044>] (asm_do_IRQ+0x44/0xa4)
[<c0029044>] (asm_do_IRQ+0x44/0xa4) from [<c0029ad4>] (__irq_svc+0x34/0x60)
Exception stack(0xc042df70 to 0xc042dfb8)
df60: 00000000 0005317f 0005217f 60000013
df80: c042c000 c04526e8 c042fb88 c042fb80 70022f5c 41069265 70022f28 00000000
dfa0: 600000d3 c042dfb8 c002b52c c002b538 60000013 ffffffff
[<c0029ad4>] (__irq_svc+0x34/0x60) from [<c002b538>] (default_idle+0x2c/0x34)
[<c002b538>] (default_idle+0x2c/0x34) from [<c002b3c0>] (cpu_idle+0x6c/0xa4)
[<c002b3c0>] (cpu_idle+0x6c/0xa4) from [<c03150e0>] (rest_init+0xc8/0x120)
[<c03150e0>] (rest_init+0xc8/0x120) from [<c0008e60>] (start_kernel+0x414/0x5ac)
[<c0008e60>] (start_kernel+0x414/0x5ac) from [<70008034>] (0x70008034)
handlers:
[<c01fef60>] (my_interrupt+0x0/0x22c)
Disabling IRQ #88

После одной ошибки на линии прерывания он больше не выполняет прерывание по фронтам, а делает прерывание самостоятельно через какое-то время. А в столбце "CPU0" в таблице "#cat /dev/interrupts" ничего не меняется!

Пробовал даже переназначать порты input на output (а output на input) в цикле опроса, чтобы прерываний не происходило, но и это не помогло((



Код
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>

#include <mach/gpio.h>

#include <asm-generic/gpio.h>
#include <mach/at91_pio.h>
#include <asm/gpio.h>

static int my_dev_id;
int bline[6];
unsigned long save_key = 0;


const char button_name[30][15] = {
{"Button Clear\n"},{"Left line 1\n"},{"Left line 2\n"},{"Left line 3\n"},{"Left line 4\n"},{"Return\n"},\
{"Escape\n"},{"Right line 1\n"},{"Right line 2\n"},{"Right line 3\n"},{"Right line 4\n"},{"Down\n"},\
{"Up\n"},{"Bk Sp\n"},{"Enter\n"},{"7\n"},{"4\n"},{"1\n"},{"0\n"},{"F1\n"},{"8\n"},\
{"5\n"},{"2\n"},{".\n"},{"F2\n"},{"9\n"},{"6\n"},{"3\n"},{"+/-\n"},{"F3\n"}};


char * str_word (void)
{
unsigned int count;
for (count = 29; count>=1; count--) if (save_key&(0x1<<count)) break;
return &button_name[count];
}


static ssize_t hello_read(struct file * file, char * buf,
              size_t count, loff_t *ppos)
{
    char *hello_str = str_word ();
    int len;

    len = strlen(hello_str);
    if (count < len)    return -EINVAL;
    if (*ppos != 0)    return 0;
    if (copy_to_user(buf, hello_str, len))    return -EINVAL;
    *ppos = len;
    return len;
}

static irqreturn_t my_interrupt (int irq, void *dev_id) {

unsigned char line;
unsigned long key = 0;


at91_set_gpio_output(AT91_PIN_PB6, 1);    
at91_set_gpio_output(AT91_PIN_PB7, 1);    
at91_set_gpio_output(AT91_PIN_PB8, 1);    
at91_set_gpio_output(AT91_PIN_PB9, 1);    
at91_set_gpio_output(AT91_PIN_PB10, 1);    

key = 0;
for (line =1; line<=5; line++) {

if (line == 5) at91_set_gpio_output(AT91_PIN_PB6, 0);
if (line == 4) at91_set_gpio_output(AT91_PIN_PB7, 0);
if (line == 3) at91_set_gpio_output(AT91_PIN_PB8, 0);
if (line == 2) at91_set_gpio_output(AT91_PIN_PB9, 0);
if (line == 1) at91_set_gpio_output(AT91_PIN_PB10, 0);

key <<= 6;

if (!at91_get_gpio_value(AT91_PIN_PB26)) key |= (0x1<<6);
if (!at91_get_gpio_value(AT91_PIN_PB25)) key |= (0x1<<5);
if (!at91_get_gpio_value(AT91_PIN_PB24)) key |= (0x1<<4);
if (!at91_get_gpio_value(AT91_PIN_PB23)) key |= (0x1<<3);
if (!at91_get_gpio_value(AT91_PIN_PB22)) key |= (0x1<<2);
if (!at91_get_gpio_value(AT91_PIN_PB28)) key |= (0x1<<1);

if (line == 5) at91_set_gpio_output(AT91_PIN_PB6, 1);
if (line == 4) at91_set_gpio_output(AT91_PIN_PB7, 1);
if (line == 3) at91_set_gpio_output(AT91_PIN_PB8, 1);
if (line == 2) at91_set_gpio_output(AT91_PIN_PB9, 1);
if (line == 1) at91_set_gpio_output(AT91_PIN_PB10, 1);

}

if (key==0) save_key |= 0x1;
else save_key = key;



at91_set_gpio_output(AT91_PIN_PB6, 0);    
at91_set_gpio_output(AT91_PIN_PB7, 0);    
at91_set_gpio_output(AT91_PIN_PB8, 0);    
at91_set_gpio_output(AT91_PIN_PB9, 0);    
at91_set_gpio_output(AT91_PIN_PB10, 0);    


return IRQ_NONE;
}


static const struct file_operations hello_fops = {
    .owner        = THIS_MODULE,
    .read        = hello_read,
};

static struct miscdevice hello_dev = {
    MISC_DYNAMIC_MINOR,
    "Key",
    &hello_fops
};


static int __init my_init ( void){

unsigned long irqflags;
printk(KERN_INFO "My_init\n");
at91_set_gpio_output(AT91_PIN_PB6, 0);    
at91_set_gpio_output(AT91_PIN_PB7, 0);    
at91_set_gpio_output(AT91_PIN_PB8, 0);    
at91_set_gpio_output(AT91_PIN_PB9, 0);    
at91_set_gpio_output(AT91_PIN_PB10, 0);    

at91_set_gpio_input(AT91_PIN_PB22, 1);
at91_set_gpio_input(AT91_PIN_PB23, 1);
at91_set_gpio_input(AT91_PIN_PB24, 1);
at91_set_gpio_input(AT91_PIN_PB25, 1);
at91_set_gpio_input(AT91_PIN_PB26, 1);
at91_set_gpio_input(AT91_PIN_PB28, 1);


bline[0] = gpio_to_irq(AT91_PIN_PB22);
    if (bline[0] < 0) {printk(KERN_INFO "\n\nError_button_22_irq!!\n\n");}
bline[1] = gpio_to_irq(AT91_PIN_PB23);
    if (bline[1] < 0) {printk(KERN_INFO "\n\nError_button_23_irq!!\n\n");}
bline[2] = gpio_to_irq(AT91_PIN_PB24);
    if (bline[2] < 0) {printk(KERN_INFO "\n\nError_button_24_irq!!\n\n");}
bline[3] = gpio_to_irq(AT91_PIN_PB25);
    if (bline[3] < 0) {printk(KERN_INFO "\n\nError_button_25_irq!!\n\n");}
bline[4] = gpio_to_irq(AT91_PIN_PB26);
    if (bline[4] < 0) {printk(KERN_INFO "\n\nError_button_26_irq!!\n\n");}
bline[5] = gpio_to_irq(AT91_PIN_PB28);
    if (bline[5] < 0) {printk(KERN_INFO "\n\nError_button_28_irq!!\n\n");}

irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED;

if (request_irq( bline[0], my_interrupt, irqflags, "my_interrupt", &my_dev_id) ) return -1;
if (request_irq( bline[1], my_interrupt, irqflags, "my_interrupt", &my_dev_id) ) return -1;
if (request_irq( bline[2], my_interrupt, irqflags, "my_interrupt", &my_dev_id) ) return -1;
if (request_irq( bline[3], my_interrupt, irqflags, "my_interrupt", &my_dev_id) ) return -1;
if (request_irq( bline[4], my_interrupt, irqflags, "my_interrupt", &my_dev_id) ) return -1;
if (request_irq( bline[5], my_interrupt, irqflags, "my_interrupt", &my_dev_id) ) return -1;

    if (misc_register(&hello_dev)) printk(KERN_ERR   "Error\n");

printk( KERN_INFO "Siccessfully loading ISR handler on IRQ %d\n", irqq );
return 0;
}

static void __exit my_exit(void) {
int x;
for (x=0; x<=5; x++) synchronize_irq (bline[x]);
for (x=0; x<=5; x++) free_irq(bline[x], &my_dev_id);

printk(KERN_INFO "Successfully unloading, irq_counter = %d\n", irq_counter);
}
module_init(my_init);
module_exit(my_exit);
MODULE_AUTHOR("My_copy_internet");
MODULE_DESCRIPTION("LDD:1.0 s_08/lab1_interrupt.c");
MODULE_LICENSE( "GPL v2");

sasamy
Цитата(fademike @ Oct 14 2013, 10:56) *
Помогите, пожалуйста с проблемой очень начинающему "программисту"!


http://lxr.free-electrons.com/source/drive...rix_keypad.c#L2
xor.kruger
Доброго времени суток.
Сразу вопрос: почему возникла надобность писать свой модуль? Можно ведь воспользоватся готовым - для этого необходимо установить опцию в ядре CONFIG_KEYBOARD_MATRIX и описать в платформе или DTS как подключены кнопки.

update:
sasamy опередил sm.gif
sasamy
Цитата(xor.kruger @ Oct 14 2013, 12:00) *
Сразу вопрос: почему возникла надобность писать свой модуль? Можно ведь воспользоватся готовым - для этого необходимо установить опцию в ядре CONFIG_KEYBOARD_MATRIX и описать в платформе или DTS как подключены кнопки.


У меня случайно даже пример есть как подключить на неиспользуемом ISI матрицу 4х4 sm.gif
arch/arm/mach-at91/board-sam9m10g45ek.c
CODE

...
#include <linux/input/matrix_keypad.h>
...

/*
* GPIO keyboard
*/
#if defined(CONFIG_KEYBOARD_MATRIX) || defined(CONFIG_KEYBOARD_MATRIX_MODULE)
static const uint32_t ek_keymap[] = {
/* KEY(row, col, keycode) */
KEY(0, 0, KEY_0),
KEY(0, 1, KEY_1),
KEY(0, 2, KEY_2),
KEY(0, 3, KEY_3),

KEY(1, 0, KEY_4),
KEY(1, 1, KEY_5),
KEY(1, 2, KEY_6),
KEY(1, 3, KEY_7),

KEY(2, 0, KEY_8),
KEY(2, 1, KEY_9),
KEY(2, 2, KEY_COMMA),
KEY(2, 3, KEY_ESC),
KEY(3, 0, KEY_E),
KEY(3, 1, KEY_UP),
KEY(3, 2, KEY_ENTER),
KEY(3, 3, KEY_DOWN),
};

static const struct matrix_keymap_data ek_keymap_data = {
.keymap = ek_keymap,
.keymap_size = ARRAY_SIZE(ek_keymap),
};

static const uint32_t ek_row_gpios[] =
{
AT91_PIN_PB21,
AT91_PIN_PB22,
AT91_PIN_PB24,
AT91_PIN_PB26
};

static const uint32_t ek_col_gpios[] =
{
AT91_PIN_PB28,
AT91_PIN_PB23,
AT91_PIN_PB25,
AT91_PIN_PB27
};

static struct matrix_keypad_platform_data ek_mkp_pdata = {
.keymap_data = &ek_keymap_data,
.row_gpios = ek_row_gpios,
.col_gpios = ek_col_gpios,
.num_row_gpios = ARRAY_SIZE(ek_row_gpios),
.num_col_gpios = ARRAY_SIZE(ek_col_gpios),
.col_scan_delay_us = 10,
.debounce_ms = 10,
.wakeup = 1,
.active_low = 1,
};

static struct platform_device ek_mkp_device = {
.name = "matrix-keypad",
.id = -1,
.dev = {
.platform_data = &ek_mkp_pdata,
},
};

static void __init ek_add_device_mkp(void)
{
int i;

for (i = 0; i < ARRAY_SIZE(ek_row_gpios); i++) {
at91_set_GPIO_periph(ek_row_gpios[i], 1);
at91_set_deglitch(ek_row_gpios[i], 1);
}

for (i = 0; i < ARRAY_SIZE(ek_col_gpios); i++)
at91_set_gpio_output(ek_col_gpios[i], 0);

platform_device_register(&ek_mkp_device);

}
#else
static void __init ek_add_device_mkp(void) {}
#endif
.....
static void __init ek_board_init(void)
{
....
/* GPIO keyboard */
ek_add_device_mkp();
....
fademike
Цитата
Сразу вопрос: почему возникла надобность писать свой модуль? Можно ведь воспользоватся готовым - для этого необходимо установить опцию в ядре CONFIG_KEYBOARD_MATRIX и описать в платформе или DTS как подключены кнопки.


Просто, для изучения ОС писал простой модуль опроса матрицы и наткнулся на ошибку в прерываниях, как избавиться от нее я не понял.. Ну, буду тогда использовать Linux/drivers/input/keyboard/matrix_keypad.c

sasamy, спасибо за пример!
fademike
А кто-нибудь может подсказать, как вывести консоль на дисплей (/dev/tty1), используя клавиатуру матрицы кнопок с драйвером "Linux/drivers/input/keyboard/matrix_keypad.c"???
Tarbal
Цитата(fademike @ Oct 14 2013, 17:57) *
А кто-нибудь может подсказать, как вывести консоль на дисплей (/dev/tty1), используя клавиатуру матрицы кнопок с драйвером "Linux/drivers/input/keyboard/matrix_keypad.c"???


Я помню только принцип, т.к. в последний раз с год назад возился.
Все мыши и клавиатуры определены как класс input:
/dev/input/
by-id event0 event10 event12 event3 event5 event7 event9 mouse0
by-path event1 event11 event2 event4 event6 event8 mice

Точнее вам надо, чтобы устройство возникло здесь:
/sys/class/input/
а в /dev/input/ оно попадет автоматически.

Для каждого eventX можно посмотреть к какому устройству он принадлежит
cat /sys/class/input/event0/device/name

Надо написать еще один драйвер, взаимодействующий с тем, что читает кнопки, либо модифицировать ваш, чтобы он попал в input. В документации ядра есть информация, а также куча примеров в самом ядре. Остальное делается в пользовательском пространстве.
Я делал только для Х11, но уверен, что и для остального это совсем не сложно.
sasamy
Цитата(fademike @ Oct 14 2013, 17:57) *
А кто-нибудь может подсказать, как вывести консоль на дисплей (/dev/tty1), используя клавиатуру матрицы кнопок с драйвером "Linux/drivers/input/keyboard/matrix_keypad.c"???


Если корневая собрана в buildroot или вообще стандартный init, то добавить в /etc/inittab строку (если ее нет)
tty1::respawn:/sbin/getty 38400 tty1 linux

матричная клавиатура - стандартное устройство ввода, ничего дополнительно для нее не нужно, разве что включить в ядре интерфейс, если не включен
http://lxr.free-electrons.com/source/drive...ut/Kconfig#L142

Tarbal
Точно. В кернеле ничего делать не надо.
http://lxr.free-electrons.com/source/drive...rix_keypad.c#L2
драйвер оперирует с устройством input_dev.


Значит вы сможете увидеть, что устройство появилось в системе прочитав имя в одном из
/sys/class/input/event0/device/name
она совпадет с заданным в строке 567 файла:
http://lxr.free-electrons.com/source/drive...rix_keypad.c#L2
.name = "matrix-keypad",
fademike
Все заработало!)) Огромное Вам спасибо!!
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.