中断子系统练习记录
前言:
简单记录下理解不深刻且老是遗忘的一些点
后面也可以直接在此博客中抄写修改成对应的中断驱动
博客状态:
持续更新中
1.API
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
功能:解析并映射成软中断号
参数:
@dev:节点的指针
@index:下标 interrupts的下标
返回值:成功返回软中断号,失败返回0
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
功能:注册中断
参数:
@irq:软中断号
@handler:中断处理函数的函数指针
irqreturn_t (*irq_handler_t)(int, void *);
irqreturn_t key_irq_handle(int irqno, void *dev)
{
//return IRQ_NONE;
return IRQ_HANDLED; //中断被正常执行了
}
@flags:中断触发方式
IRQF_TRIGGER_RISING //上升沿
IRQF_TRIGGER_FALLING //下降沿
IRQF_TRIGGER_HIGH //高电平
IRQF_TRIGGER_LOW //低电平
IRQF_SHARED //共享中断
@name:中断的名字 cat /proc/interrupts
@dev:向中断处理函数传递的参数
返回值:成功返回0,失败返回错误码
const void *free_irq(unsigned int irq, void *dev_id)
功能:释放中断
参数:
@irq:软中断号
@dev_id:向中断处理函数传递的参数
返回值:是注册中断时候传递的name
2.概念
首先,一个大前提,数据的传输分三段,cpu、主控板内部对应子系统的控制器、以及设备端;
cpu和控制器我们不用管,linux里面已经很完善了,因此,我们要做的就是调用接口和控制器对接上,让设备的数据和控制器进行流通。
3.调试过程
3.1设备树调通
原理图:
结论:
KEY1 连接gpio5_1
KEY1 连接gpio4_14
全部低电平触发
设备树:
/{
......
psd_key_irqs {
compatible = "psd,gpio-keys";
user1 {
interrupt-parent = <&gpio5>;
interrupts = <5 0>;
};
user2 {
interrupt-parent = <&gpio4>;
interrupts = <14 0>;
};
};
......
};
系统运行中设备树查看:
cd /proc/device-tree
镜像文件和dtb文件位置:
按键中断例程:
.h
#ifndef __KEY_CTRL_H__
#define __KEY_CTRL_H__
#include <linux/timer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#define KEY_NAME1 "KEY_USER1"
#define KEY_NAME2 "KEY_USER2"
#define CONSUMER_LABEL1 "user1"
#define CONSUMER_LABEL2 "user2"
#define PATH_DTS_KEY_USR1 "/psd_key_irqs/user1"
#define PATH_DTS_KEY_USR2 "/psd_key_irqs/user2"
#define KMD_ERR(str) \
printk("%s %s line: %d %s \n", __FILE__, __FUNCTION__, __LINE__, str);
typedef struct my_key {
char *dev_name;
struct device_node *key_node;
unsigned int key_irq_no;
int key_num;
int key_status;//led开关状态
int Level_state;//电平状态
struct timer_list mytimer; // 分配定时器
} key_ctrl_t;
/*key1设备树控制的初始化*/
int key1_ctrl_init(key_ctrl_t * key);
/*key2设备树控制的初始化*/
int key2_ctrl_init(key_ctrl_t * key);
/*led设备树控制的卸载处理函数*/
void key_ctrl_exit(key_ctrl_t * key);
#endif
.c
#include"key_ctrl.h"
#include <linux/timer.h>
void my_timer_callback1(unsigned long para)
{
printk("key1 down...\n");
}
void my_timer_callback2(unsigned long para)
{
printk("key2 down...\n");
}
irqreturn_t key1_irq_handle(int irq, void* dev)
{
key_ctrl_t *key1 = (key_ctrl_t *)dev;
// 启动定时器
mod_timer(&key1->mytimer, jiffies + HZ/50);
return IRQ_HANDLED;
}
irqreturn_t key2_irq_handle(int irq, void* dev)
{
key_ctrl_t * key2 = (key_ctrl_t *)dev;
// 启动定时器
mod_timer(&key2->mytimer, jiffies + HZ/50);
return IRQ_HANDLED;
}
int key1_ctrl_init(key_ctrl_t * key)
{
int ret;
key->key_node = of_find_node_by_path(PATH_DTS_KEY_USR1);
if(IS_ERR(key->key_node))
{
KMD_ERR("of_find_node_by_path ERR");
ret = -ENODATA;
goto exit;
}
// 2.解析得到软中断号
key->key_irq_no = irq_of_parse_and_map(key->key_node, 0);
if (key->key_irq_no == 0) {
printk("irq_of_parse_and_map error\n");
ret = -EAGAIN;
goto exit_node;
// 资源暂时不可用
}
init_timer(&key->mytimer);
key->mytimer.expires = jiffies + HZ/50;
// timer_setup_on_stack(&key->mytimer, my_timer_callback1, 0);
key->mytimer.function = my_timer_callback1; // 设置定时器到期时调用的回调函数
add_timer(&key->mytimer); //将定时器加入到系统定时器链表中
ret = request_irq(key->key_irq_no, key1_irq_handle,
IRQF_TRIGGER_LOW, KEY_NAME1, key);
if (ret) {
printk("request_irq key1 error\n");
goto exit_irq;
}
return 0;
exit_irq:
free_irq(key->key_irq_no, NULL);
exit_node:
of_node_put(key->key_node);
exit:
return ret; // 出错返回
}
int key2_ctrl_init(key_ctrl_t * key)
{
int ret;
key->key_node = of_find_node_by_path(PATH_DTS_KEY_USR2);
if(IS_ERR(key->key_node))
{
KMD_ERR("of_find_node_by_path ERR");
ret = -ENODATA;
goto exit;
}
// 2.解析得到软中断号
key->key_irq_no = irq_of_parse_and_map(key->key_node, 0);
if (key->key_irq_no == 0) {
printk("irq_of_parse_and_map error\n");
of_node_put(key->key_node); // 清理已获取的节点
ret = -EAGAIN;
goto exit_node;
// 资源暂时不可用
}
init_timer(&key->mytimer);
key->mytimer.expires = jiffies + HZ/50;
// timer_setup(&key->mytimer, my_timer_callback2, 0);
key->mytimer.function = my_timer_callback2; // 设置定时器到期时调用的回调函数
add_timer(&key->mytimer); //将定时器加入到系统定时器链表中
ret = request_irq(key->key_irq_no, key2_irq_handle,
IRQF_TRIGGER_LOW, KEY_NAME2, key);
if (ret) {
printk("request_irq key2 error\n");
goto exit_irq;
}
return 0;
exit_irq:
free_irq(key->key_irq_no, NULL);
exit_node:
of_node_put(key->key_node);
exit:
return ret; // 出错返回
}
void key_ctrl_exit(key_ctrl_t * key)
{
free_irq(key->key_irq_no,key);
if (timer_pending(&key->mytimer)) {
del_timer_sync(&key->mytimer);
}
}
MODULE_DESCRIPTION("key_ctrl_driver");
MODULE_LICENSE("GPL");