linux 10ms 中断,AT9260 GPIO中断

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#i nclude

#define DEVICE_NAME "button"

#define KEY_TIMER_DELAY1    (HZ/50)             //按键按下去抖延时20毫秒

#define KEY_TIMER_DELAY2    (HZ/10)             //按键抬起去抖延时100毫秒

#define KEYSTATUS_DOWN              0           //按键按下

#define KEYSTATUS_UP                1           //按键抬起

#define KEYSTATUS_DOWNX             2           //按键不确定

#define KEY_NUM                     4           // 4个按键

#define DP_MAJOR 0//主设备号

#define DP_MINOR 0  //次设备号

unsigned int button_major = DP_MAJOR;

// 按键驱动的设备结构体、定时器

#define MAX_KEY_BUF     16   // 按键缓存区大小

typedef unsigned char KEY_RET;

// 设备结构体

typedef struct

{

unsigned int keyStatus[KEY_NUM];    // 4个按键的按键状态

KEY_RET buf[MAX_KEY_BUF];

unsigned int head,tail;             // 按键缓存区头和尾

wait_queue_head_t wq;               // 等待队列

struct cdev cdev;                   // cdev 结构体

}KEY_DEV;

KEY_DEV key_dev,*key_devp;

#define BUF_HEAD (key_dev.buf[key_dev.head]) //缓冲头

#define BUF_TAIL (key_dev.buf[key_dev.tail]) //缓冲尾

#define INCBUF(x,mod) ((++(x))&((mod)-1))

static struct timer_list key_timer[KEY_NUM]; // 4个按键去抖定时器

// 按键硬件资源、键值信息结构体

static struct key_info

{

int irq_no;                         // 中断号

int irq_type;                       // 中断类型

unsigned int gpio_port;             // GPIO端口

int key_no;                         // 键值

}key_info_tab[4]=

// 按键所使用的CPU资源

{

{

AT91_PIN_PB0,AT91_AIC_SRCTYPE_FALLING,AT91_PIN_PB0,1

},

{

AT91_PIN_PB1,AT91_AIC_SRCTYPE_FALLING,AT91_PIN_PB1,2

},

{

AT91_PIN_PB2,AT91_AIC_SRCTYPE_FALLING,AT91_PIN_PB2,3

},

{

AT91_PIN_PB3,AT91_AIC_SRCTYPE_FALLING,AT91_PIN_PB3,4

},

};

// 初始化

static void at91sam9260_key_io_init(void)

{

at91_set_gpio_input(AT91_PIN_PB0, 1);

at91_set_deglitch(AT91_PIN_PB0, 1);

at91_set_gpio_input(AT91_PIN_PB1, 1);

at91_set_deglitch(AT91_PIN_PB1, 1);

at91_set_gpio_input(AT91_PIN_PB2, 1);

at91_set_deglitch(AT91_PIN_PB2, 1);

at91_set_gpio_input(AT91_PIN_PB3, 1);

at91_set_deglitch(AT91_PIN_PB3, 1);

}

/* 记录键值并唤醒等待队列 */

static void keyEvent(unsigned int key)

{

BUF_HEAD = key_info_tab[key].key_no;                  // 记录键值

key_dev.head = INCBUF(key_dev.head, MAX_KEY_BUF); // 调整缓冲区头指针

wake_up_interruptible(&(key_dev.wq));                // 唤醒等待队列

}

// 按键设备的中断处理

// 键被按下后,将发生中断,在中断处理程序中,应该关闭中断进入查询模式,延迟20ms以实现去抖动

// 这个中断处理过程只包含顶半部,无底半部

static irqreturn_t at91sam9260_enit_key(int irq,void *dev_id,struct pt_regs *reg)

{

int key = (int)dev_id;

int found = 0;

int i;

for (i = 0; i < ARRAY_SIZE(key_info_tab); i++)   // 查找产生中断的按键

{

if (key_info_tab[i].irq_no == irq)

{

found = 1;

break;

}

}

if (!found)                                     // 没找到

{

printk(KERN_NOTICE"bad irq %d in button\n", irq);

return IRQ_NONE;   //错误的中断

}

disable_irq(key_info_tab[key].irq_no);          // 找到,关闭对应中断

key_dev.keyStatus[key] = KEYSTATUS_DOWNX;  // 不确定是否为按下

key_timer[key].expires = jiffies + KEY_TIMER_DELAY1;// 延迟

add_timer(&key_timer[key]);  // 启动定时器

return IRQ_HANDLED;   //正确的中断

}

// 定时器处理函数

static void key_timer_handler(unsigned long data)

{

int key = data;

//获取当前按键引脚上的电平值来判断按键是按下还是抬起

int up = at91_get_gpio_value(key_info_tab[key].gpio_port);

if (!up)

{

if (key_dev.keyStatus[key] == KEYSTATUS_DOWNX)

//从中断进入

{

key_dev.keyStatus[key] = KEYSTATUS_DOWN;

key_timer[key].expires = jiffies + KEY_TIMER_DELAY2; //延迟

keyEvent(key);  //记录键值,唤醒等待队列

add_timer(&key_timer[key]);

}

else

{

key_timer[key].expires = jiffies + KEY_TIMER_DELAY2; //延迟

add_timer(&key_timer[key]);

}

}

else       //键已抬起

{

key_dev.keyStatus[key] = KEYSTATUS_UP;

enable_irq(key_info_tab[key].irq_no);

}

}

// 按键设备驱动的中断申请函数

// 申请系统中断,中断方式为下降沿触发

static int request_irqs(void)

{

struct key_info *k;

int i;

int ret;

for(i = 0; i < sizeof(key_info_tab)/sizeof(key_info_tab[1]); i++)

//  for(i = 0; i < 1; i++)

{

k = key_info_tab + i;

//设置4个IO口为中断下降沿触发方式

set_irq_type(key_info_tab[i].irq_no, key_info_tab[i].irq_type);      // 设置中断类型

//申请中断(类型为快速中断,中断服务时屏蔽所有外部中断?)将按键序列号作为参数传入中断服务程序

//  if(request_irq(k->irq_no,at91sam9260_enit_key,0,DEVICE_NAME,(void *)i))

ret = request_irq(k->irq_no,at91sam9260_enit_key,0,DEVICE_NAME,(void *)i);

if(ret)

{

printk(KERN_NOTICE "buttons:ret is %d\r\n",ret);

return -1;

}

}

return 0;

}

// 释放中断

static void free_irqs(void)

{

struct key_info *k;

int i;

for(i = 0; i < sizeof(key_info_tab)/sizeof(key_info_tab[1]); i++)

{

k = key_info_tab + i;

free_irq(k->irq_no,(void *)i);

}

}

// 按键设备驱动的打开、释放函数

static int at91sam9260_key_open(struct inode *inode, struct file *filp)

{

key_dev.head = key_dev.tail = 0; //清空按键动作缓冲区

//  keyEvent = keyEvent_raw; //函数指针指向按键处理函数keyEvent_raw

return 0;

}

static int at91sam9260_key_release(struct inode *inode, struct file *filp)

{

//  keyEvent = keyEvent_dummy; //函数指针指向空函数

return 0;

}

// 按键设备驱动读函数

static ssize_t at91sam9260_key_read(struct file *filp, char __user *buf, ssize_t count,

loff_t*ppos)

{

unsigned int key_ret;

unsigned long flags;

retry:

if (key_dev.head != key_dev.tail)                         // 缓冲区有数据?

{

local_irq_save(flags);                                      // 进入临界区 ,关中断

key_ret = BUF_TAIL;                                         // 读出键值

key_dev.tail = INCBUF(key_dev.tail, MAX_KEY_BUF);     // 调整缓冲区尾指针

local_irq_restore(flags);                                   // 退出临界区,开中断

if(copy_to_user(buf, (void *)&key_ret, sizeof(unsigned int)))       // 拷贝到用户空间

{

return -EFAULT;

}

else

{

return sizeof(unsigned int);

}

}

else   // 缓冲区没数据

{

if (filp->f_flags & O_NONBLOCK)                             // 若采用非阻塞方式读取则返回错误

{

return -EAGAIN;

}

interruptible_sleep_on(&(key_dev.wq));                   // 使进程睡眠

if (signal_pending(current))                               //在这里等中断

{   // 如果是信号中断

return -ERESTARTSYS;

}

goto retry;

}

return sizeof(unsigned int);

}

// 按键设备驱动文件操作结构体,主要实现打开、释放和读函数

static struct file_operations at91sam9260_key_fops =

{

.owner = THIS_MODULE,

.open = at91sam9260_key_open,            // 启动设备

.release = at91sam9260_key_release,      // 关闭设备

.read = at91sam9260_key_read,            // 读取按键的键值

};

// 模块加载函数包括:设备号申请、cdev的添加,另:申请中断、初始化定时器和队列;

// 模块卸载函数恰好相反

static int __init at91sam9260_key_init(void)

{

int i;

int result;

// 申请设备号

dev_t dev = MKDEV(button_major, DP_MINOR);

/* 申请设备号 */

if (button_major)

result = register_chrdev_region(dev, 1, DEVICE_NAME);

else    /* 动态申请设备号 */

{

result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);

button_major = MAJOR(dev);

printk(KERN_INFO "Todo: mknod /dev/%s c %d 0\n", DEVICE_NAME, button_major);

}

// 分配设备结构体的内存

key_devp = kmalloc(sizeof(key_dev),GFP_KERNEL);

if(!key_devp)

{

result = - ENOMEM;

goto fail_malloc;

}

// 添加cdev

memset(key_devp,0,sizeof(key_dev));

cdev_init(&key_dev.cdev, &at91sam9260_key_fops);

if(cdev_add(&key_dev.cdev, dev, 1)){

printk(KERN_ALERT"Add char dev error!\n");

}

key_dev.head = key_dev.tail = 0;             // 初始化结构体

for(i = 0; i < KEY_NUM; i++)

{

key_dev.keyStatus[i] = KEYSTATUS_UP;

}

init_waitqueue_head(&(key_dev.wq));  // 等待队列

// 初始化定时器,实现软件的去抖动

/* 初始化定时器 */

for(i = 0; i < KEY_NUM; i++)

{

key_timer[i]. = key_timer_handler;

key_timer[i].data = i;

init_timer(&key_timer[i]);

}

at91sam9260_key_io_init();   // 初始化

// 注册中断函数

if(-1 == request_irqs())

{

printk(KERN_NOTICE "request_irqs failed!\r\n");

}

else

{

printk(KERN_NOTICE "request_irqs success!\r\n");

}

/*

for(i = 0; i < KEY_NUM; i++)

{

setup_timer(&key_timer[i],key_timer_handler,i);

}

*/

fail_malloc:unregister_chrdev_region(dev,1);

return result;

}

// 按键设备驱动的模块卸载函数

static void __exit at91sam9260_key_exit(void)

{

free_irqs();    // 注销中断

// 释放设备号,删除cdev

cdev_del(&key_dev.cdev);   // 删除字符设备结构体

kfree(key_devp);

unregister_chrdev_region(MKDEV(button_major,DP_MINOR),1);  // 删除字符设备

}

MODULE_AUTHOR("launch");

MODULE_LICENSE("Dual BSD/GPL");

module_init(at91sam9260_key_init);

module_exit(at91sam9260_key_exit);0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux GPIO中断驱动是一种用于在Linux系统中捕捉GPIO引脚中断事件的机制。当GPIO引脚的状态发生变化时,例如从低电平到高电平或从高电平到低电平,系统会自动触发中断,并执行相应的中断处理程序。中断处理程序可以是用户自定义的代码,用于响应GPIO引脚状态的变化并执行相应的操作。这种机制可以用于监测外部设备的状态变化,例如按钮按下、传感器触发等。通过配置中断驱动,可以实现对GPIO引脚中断事件的捕捉和处理。要使用Linux GPIO中断驱动,需要进行以下步骤:首先,通过gpio_to_irq函数将GPIO引脚转换为中断号。然后,在设备树中修改或添加中断节点,并配置中断触发方式和优先级。接下来,编译内核并更新系统。最后,编写用户控件程序来捕捉中断事件并执行相应的操作。通过在/sys/class/gpio目录下操作相应的GPIO引脚目录,可以设置引脚的方向和输出电平值。例如,使用echo命令将引脚暴露为可用接口,然后使用echo命令设置引脚方向为输出,再使用echo命令设置引脚输出电平值为高。通过编写测试代码,可以验证中断驱动的功能和正确性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Linux驱动开发——(使用中断处理)gpio(6)](https://blog.csdn.net/qq_37596943/article/details/103750860)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [linux GPIO中断使用说明 V1.02.pdf](https://download.csdn.net/download/u013497478/12745478)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [zynq操作系统: Linux驱动开发Gpio中断篇](https://blog.csdn.net/qq_42330920/article/details/115615688)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值