网站搜索引擎优化方案论文,网页设计与网站开发方向,西安网站推广,广告公司简介怎么写前一篇已经在设备树的 gpio-led 节点中引入了中断信息#xff0c;接下来将通过API来获取设备树中的中断信息。gpio-led 节点具体内容如下#xff1a;
gpio-key0 {pinctrl-names default;pinctrl-0 pinctrl_gpio_keys; // pinctrl子系…前一篇已经在设备树的 gpio-led 节点中引入了中断信息接下来将通过API来获取设备树中的中断信息。gpio-led 节点具体内容如下
gpio-key0 {pinctrl-names default;pinctrl-0 pinctrl_gpio_keys; // pinctrl子系统配置电气属性key-gpio gpio1 18 GPIO_ACTIVE_HIGH; // gpio子系统进行引脚初始化interrupt-parent gpio1; // 中断类型为 gpio1interrupts 18 IRQ_TYPE_EDGE_FALLING; // 中断引脚为 GPIO1_IO18触发方式为下降沿status okay;
}; 一、中断 API
1、获取中断信息
获取设备树中 interrupts 属性的信息有两种方式一种是针对 gpio 的方式另一种是比较通用的方式。最终获取到的都是中断号这里的中断号和裸机开发时的中断号不一样裸机开发我们是根据参考文档来获取中断号 而下面通过 API 获取到的中断号是经过映射的类似于虚拟内存和物理内存的映射这样做的目的是保护原中断号。这也是为什么后续获取到的中断号会与裸机开发时使用的中断号不一致。 gpio_to_irq gpio_to_irq 是仅用于获取 gpio 中断相关信息要求对应节点的父类中断控制器为 gpio即 interrupt-parent 属性引用的是 gpio 控制器。该接口的声明在 asm/gpio.h接口原型如下:
#define gpio_to_irq __gpio_to_irq
/*** param gpio 表示根据gpio设备树节点获取到的 gpio 编号* return 成功返回中断号失败返回负值*/
int __gpio_to_irq(unsigned gpio); irq_of_parse_and_map irq_of_parse_and_map 是比较通用的中断信息获取方式不仅仅适用于 gpio也适用于其他外设中断。该接口的声明在 linux/of_irq.h接口原型如下
/*** param dev 设备树节点* param index interrupts属性索引* return 成功返回中断号失败返回负值*/
unsigned int irq_of_parse_and_map(struct device_node *dev,int index);
注意interrupts属性中可以包含多个中断信息需要index来获取当前驱动所需的中断信息 2、注册中断
注册中断时主要告诉内核以下内容:
中断号: 映射后的中断号触发方式如何触发中断中断服务函数中断触发后如何处理中断服务函数参数
注册中断使用的 API 为 request_irq函数原型的声明在 linux/interrupt.h
/*** param irq 映射后的中断号* param handler 中断服务函数* param flags 触发方式* param name 中断名* param dev 给中断服务函数传递的参数* return 成功返回0失败返回负值*/
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev);
中断服务函数声明
typedef irqreturn_t (*irq_handler_t)(int, void *);
触发方式 flagslinux/irq.h 中断名 name 设置以后可以在/proc/interrupts 文件中看到对应的中断名字以此来判断中断是否注册成功 3、释放中断
注册中断后如果模块被卸载需要释放中断释放中断 free_irq 的接口原型声明在 linux/interrupt.h
/*** param irq 映射后的中断号* param dev 给中断服务函数传递的参数*/
void free_irq(unsigned int irq, void * dev); 二、驱动完善
1、驱动入口函数
驱动入口函数主要是获取中断号并申请中断其他的诸如申请设备号、自动创建驱动节点等操作将不再赘述。下面使用变量 status 来代表某个外设的状态按键按下时中断触发此时反转设备状态。
struct chrdev_t
{// ...struct device_node* gpioNode; /* 设备树节点 */uint32_t gpioNum; /* gpio 引脚编号 */uint32_t irqNum; /* 中断号 */uint32_t status; /* 设备状态 */
};
static struct chrdev_t chrdev;/* 驱动入口函数 */
static int __init kerneltimer_init(void)
{uint32_t ret 0;/* 获取key0设备树节点 */chrdev.gpioNode of_find_node_by_path(/gpio-key0);if(chrdev.gpioNode NULL){ printk(node cannot be found!\n);return -1;}// 获取 gpio 编号chrdev.gpioNum of_get_named_gpio(chrdev.gpioNode, key-gpio, 0);if (chrdev.gpioNum 0){printk(gpio property fetch failed!\n);return -1;}// 配置 gpio 为输入ret gpio_direction_input(chrdev.gpioNum);if (ret 0){printk(gpio set failed!\n);return -1;}#if 1// 根据gpio编号获取中断信息chrdev.irqNum gpio_to_irq(chrdev.gpioNum);if (chrdev.irqNum 0){printk(irq number fetch failed!\n);return -1;}
#else// 根据节点获取中断号chrdev.irqNum irq_of_parse_and_map(chrdev.gpioNode, 0);if (chrdev.irqNum 0){printk(irq number fetch failed!\n);return -1;}
#endifprintk(中断号: %u\n, chrdev.irqNum);// 设备初始状态为 0chrdev.status 0;// 注册中断ret request_irq(chrdev.irqNum, key0_handler, IRQ_TYPE_EDGE_FALLING, key0-int, chrdev);if (ret 0){printk(irq subscribe failed!\n);return -1;}// ...
} 2、中断服务函数
上面在介绍注册中断 API 时已经提及了中断服务函数的声明第一个参数为 中断号第二个参数为注册时传递给中断服务函数的参数
static irqreturn_t key0_handler(int irq, void * dev)
{struct chrdev_t* pdev (struct chrdev_t*)dev;// 状态反转pdev-status !pdev-status;return IRQ_RETVAL(IRQ_HANDLED);
} 3、read 操作函数
static ssize_t chrdev_read(struct file *pfile, char __user * pbuf, size_t size, loff_t * poff)
{// 在 open 函数中 pfile-private_data chrdev;struct chrdev_t* pdev pfile-private_data;unsigned long ret 0;// 将设备状态返回给应用层ret copy_to_user(pbuf, pdev-status, sizeof(pdev-status));if (ret ! 0){printk(kernel send data failed!\n);return -1;}return sizeof(pdev-status);
} 4、驱动退出函数
驱动退出函数需要释放中断
static void __exit kerneltimer_exit(void)
{// ... /* 注销中断 */free_irq(chrdev.irqNum, chrdev);
} 三、测试
在应用程序中每隔 1s 调用 read 函数来获取设备状态
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include stdio.h
#include string.h#define delayms(x) usleep(x * 1000)void printHelp()
{printf(usage: ./xxxApp driver_path\n);
}int main(int argc, char* argv[])
{if (argc ! 2){printHelp();return -1;}char* driver_path argv[1]; // 位置0 保存的是 ./chrdevbaseAppint state 0;int ret 0;int fd 0;fd open(driver_path, O_RDONLY);if (fd 0){perror(open file failed);return -2;}while (1){ret read(fd, state, sizeof(state));if (ret 0){printf(read data error\n);break;}printf(中断触发状态%d\n, state);delayms(1000);}close(fd);return 0;
}
裸机开发时GPIO1_IO18 对应的中断号为99现在因为经过一层映射屏蔽了真正的中断号使用了虚拟中断号所以这里的中断号为 47 应用程序的测试结果如下按下按键时触发中断此时状态反转但因为没有做消抖处理灵敏性不高接下来的一步需要在上述驱动的基础上进行改进进行按键消抖。