手把手带你写一个中断输入设备驱动

今天群里有人问,要开始驱动开发的话从什么开始比较好。

我说,应该开始去摸索触摸屏驱动,现在我想了下,触摸屏驱动可能会难了些,但是从一个GPIO开始,我觉得一定是一件很容易的事情。

所以这篇文章就来了。

b17801fb34dee8632d276ee241cbd72a.png

大家好,这是我的朋友逸珺。

首先说声抱歉,最近迷上钓鱼了,有时候晚上出去夜钓大板鲫了,停更了一段时间。来几张鱼获图片:

d7a87e58ec2edd8cd257c4b1869842ac.png

技术还是不太到家,遇到几次大鲤鱼都给溜了,心有不甘,所以最近花了比较多的时间。

言归正传,今天来分享一下以前写一个中断输入设备驱动案例,希望对有需要的朋友能有所帮助。

背景介绍

在一个项目中,有这样一个需求:

6bfc7fced8e60fa0f5c414d39f8df9c0.png

主控芯片采用ZYNQ,需要采集外部一个脉冲编码输入信号,这个信号是一个脉冲波形,脉冲数量代表测量结果。比如这有可能是一个电机的霍尔信号输出,代表电机的转速,也有可能是一个光栅编码器的脉冲输出,是什么并不重要。

这个电路本身,利用光耦实现了输入测设备信号与采集端的电气隔离。由于PS端该Bank的电平为3.3V,所以光耦的另一侧也是3.3V。

ZYNQ的PS端运行Linux程序,所以在这个场景下,要从应用程序的角度将外部输入信号用起来,就需要实现这样一个设备驱动程序:

c567cfe2e4065204e1aefa8b316f1b86.png

创建设备

在ZYNQ下,使用petalinux工具链,当然本文中对于写这个驱动程序本身换成其他的处理器从代码的角度是类似的。

1.先运行一下工具链环境变量脚本:

source /opt/pkg/petalinux/settings.sh

当然也可以不用手动这样运行,设置成linux开发主机开机自动运行,这里就不赘述怎么设置了,网上很多介绍。

2.创建设备

petalinux-create -t modules --name di-drv

这样在现有的工程下,就自动创建设备文件:

./project-spec/meta-user/recipes-modules/di-drv/files/di-drv.c

修改设备树

./project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi 

中添加

/include/ "system-conf.dtsi"
/ {   amba {pinctrl_di_default: di-default {   mux {   groups = "gpio0_0_grp";   function = "gpio0";   };   conf {   pins = "MIO0";   io-standard = <1>;   bias-high-impedance;   slew-rate = <0>;   };   };           };di {compatible = "di-drv";pinctrl-names = "default";pinctrl-0 = <&pinctrl_di_default>;di-gpios = <&gpio0 0 0>;   };      
};

本文中,假定使用的IO引脚为PS_MIO0。

驱动代码

修改上面生成的代码di-drv.c

#include <linux/module.h>  
#include <linux/kernel.h>
#include <linux/init.h>  
#include <linux/ide.h>  
#include <linux/types.h>  
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/mach/map.h>
#include <asm/io.h>/* 设备节点名称 */  
#define DEVICE_NAME       "di-drv"
/* 设备号个数 */  
#define DEVID_COUNT       1
/* 驱动个数 */  
#define DRIVE_COUNT       1
/* 主设备号 */
#define MAJOR_U
/* 次设备号 */
#define MINOR_U           0struct di_dev {/* 字符设备框架 */dev_t         devid; //设备号struct cdev     cdev;  //字符设备struct class    *class;  //类struct device    *device; //设备struct device_node *nd;   //设备树的设备节点spinlock_t      lock; //自旋锁变量int          di_gpio; //DI gpio号__u32         di_pulses;//DI counter    unsigned int     di_irq; //DI 中断号
};static struct di_dev di_char = {.cdev = {.owner = THIS_MODULE,},
};/* 中断服务函数 */
static irqreturn_t di_handler(int irq, void *dev)
{di_char.di_pulses++;return IRQ_RETVAL(IRQ_HANDLED);
}/* open函数实现, 对应到Linux系统调用函数的open函数 */  
static int di_drv_open(struct inode *inode_p, struct file *file_p)  
{  printk("di_drv module opened\n");  file_p->private_data = &di_char; return 0;  
}  /* read函数实现, 对应到Linux系统调用函数的read操作 */  
static ssize_t di_drv_read(struct file *file_p, char __user *buf, size_t len, loff_t *loff_t_p)  
{  unsigned long flags;int ret;union e_int_conv{__u8  buf[8];__u32 di_raw;};  /* 获取锁 */spin_lock_irqsave(&di_char.lock, flags);union e_int_conv di;di.di_raw.di = di_char.di_pulses;ret  = copy_to_user(buf, di.buf, 8);/* 释放锁 */spin_unlock_irqrestore(&di_char.lock, flags);return ret ? ret : 4;
}  /* release函数实现, 对应到Linux系统调用函数的close函数 */  
static int di_drv_release(struct inode *inode_p, struct file *file_p)  
{  printk("di_drv module release\n");return 0;  
}  /* file_operations结构体声明 */  
static struct file_operations di_fops = {  .owner   = THIS_MODULE,  .open   = di_drv_open,  .read   = di_drv_read,     .release = di_drv_release,   
};  /* 模块加载时会调用的函数 */  
static int __init di_drv_init(void)  
{u32 ret = 0;/* 初始化自旋锁 */spin_lock_init(&di_char.lock);/** gpio框架 **/   /* 获取设备节点 */di_char.nd = of_find_node_by_path("/di");if(di_char.nd == NULL){printk("di node not foundr\r\n");return -EINVAL;}/* 获取节点中gpio标号 */di_char.di_gpio = of_get_named_gpio(di_char.nd, "di-gpios", 0);if(di_char.di_gpio < 0){printk("Failed to get di-gpios from device tree\r\n");return -EINVAL;}printk("di-gpio num = %d\r\n", di_char.di_gpio);/* 申请gpio标号对应的引脚 */ret = gpio_request(di_char.di_gpio, "di-drv");if(ret != 0){printk("Failed to request di_gpio\r\n");return -EINVAL;}/* 把这个io设置为输入 */ret = gpio_direction_input(di_char.di_gpio);if(ret < 0){printk("Failed to set di_gpio as input\r\n");return -EINVAL;}/* 获取中断号 */di_char.di_irq = gpio_to_irq(di_char.di_gpio);printk("di_irq number is %d \r\n", di_char.di_irq);/* 申请中断 */ret = request_irq(di_char.di_irq,di_handler,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"di-drv", NULL);if(ret < 0){printk("di_irq %d request failed\r\n", di_char.di_irq);return -EFAULT;}    /* 注册设备号 */alloc_chrdev_region(&di_char.devid, MINOR_U, DEVID_COUNT, DEVICE_NAME);/* 初始化字符设备结构体 */cdev_init(&di_char.cdev, &di_fops);/* 注册字符设备 */cdev_add(&di_char.cdev, di_char.devid, DRIVE_COUNT);/* 创建类 */di_char.class = class_create(THIS_MODULE, DEVICE_NAME);if(IS_ERR(di_char.class)) {return PTR_ERR(di_char.class);}/* 创建设备节点 */di_char.device = device_create( di_char.class, NULL, di_char.devid, NULL, DEVICE_NAME );if(IS_ERR(di_char.device)) {return PTR_ERR(di_char.device);}di_char.di_pulses = 0;    return 0;  
}/* 卸载模块 */  
static void __exit di_drv_exit(void)  
{  /* 释放gpio */gpio_free(di_char.di_gpio);/* 释放中断 */free_irq(di_char.di_irq, NULL);/* 注销字符设备 */cdev_del(&di_char.cdev);/* 注销设备号 */unregister_chrdev_region(di_char.devid, DEVID_COUNT);/* 删除设备节点 */device_destroy(di_char.class, di_char.devid);/* 删除类 */class_destroy(di_char.class);printk("DI dev exit ok\n");  
}  /* 标记加载、卸载函数 */  
module_init(di_drv_init);  
module_exit(di_drv_exit);  /* 驱动描述信息 */  
MODULE_AUTHOR("Embinn");  
MODULE_ALIAS("DI input");  
MODULE_DESCRIPTION("DIGITAL INPUT driver");  
MODULE_VERSION("v1.0");  
MODULE_LICENSE("GPL");

这是一个字符驱动的实现,在真实项目中,大部分驱动基本已经被芯片厂商给实现了,但是一些特殊项目的自定义需求,往往就需要去实现自己的驱动。

编译部署

运行以下命令:

petalinux-config -c rootfs

1ff1b9972fe9ed41419471fa57731933.png

进入modules,使能刚刚创建的模块,退出保存。

运行下面的命令进行编译:

petalinux-build

最终在工程目录下,搜索di-drv.ko,就得到这个驱动的内核模块文件了,拷贝到目标板的某个文件夹下,运行下面的命令装载就完成了:

insmod di-drv.ko

这样在/dev下就会发现新增一个di-drv设备。

当然也可以直接将该驱动放进内核里,这就需要在内核代码树里,添加文件了,这个思路之前有分享过。

总结一下

字符设备是做驱动开发比较容易掌握的驱动类型,也是大多数项目中,需要自己动手写的最多的驱动类型。所以还是应该掌握它。才能实现不同的项目需求。至于用户空间怎么访问这个设备,这里就不赘述了,一个文件打开操作,再来一个读取操作就完事了。


推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/465113.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

配置sudo访问

具体操作步骤 1.首先我们建立一个账户&#xff0c;设置密码 [rootVM_0_13_centos home]# useradd 123 [rootVM_0_13_centos home]# passwd 123 Changing password for user better407. New password: BAD PASSWORD: it is WAY too short BAD PASSWORD: is too simple Retype n…

读取Xml文档的元素和属性

<?xml version"1.0" encoding"utf-8" ?><StuInfo> <student> <ID>1001</ID> <Name>张三</Name> <Sex>男</Sex> <Birthday age"23">1987-1-12</Birthday> &l…

计算机专业需要注意什么细节,计算机专业考生复试注意事项

计算机专业考生复试注意事项随着计算机相关技术的突飞猛进&#xff0c;对人才的职业发展也提出了更高的要求。近年来&#xff0c;计算机及相关专业一直是研究生报考的热门专业&#xff0c;复试中的竞争日益激烈。如何在复试中脱颖而出呢?导师通常看重学生的那些能力?下面就计…

驱动调试神器printk你掌握了吗?

[导读] 刚刚开始做Linux相关开发工作时&#xff0c;深感Linux内核代码庞大&#xff0c;要加些自己的驱动进内核代码树&#xff0c;常常深陷bug的泥沼难以自拔&#xff0c;今天来分享一下内核调试利器printk的使用心得。前面一段时间很忙&#xff0c;后期更文频率会渐渐回归正常…

腾讯云技术专家卢萌凯手把手教你Demo一个人脸识别程序!

欢迎大家前往腾讯云社区&#xff0c;获取更多腾讯海量技术实践干货哦~ 本文来自腾讯云技术沙龙&#xff0c;本次沙龙主题为Serverless架构开发与SCF部署实践 卢萌凯&#xff1a;毕业于东南大学&#xff0c;曾就职于华为&#xff0c;熟悉云行业解决方案。目前负责腾讯云中间件产…

计算机硬件知识考证题,计算机硬件知识题(答案)资料

信息技术过关考试题库(“计算机基础知识”部分)1、计算机的核心部件中央处理器的简称是________。CA 主机B 存储器C CPUD 输入设备2、世界上第一台电子计算机诞生于_____________。AA 1946年B 1950年C 1949年D 1941年3、下列全部是硬件的选项是______。AA 键盘、显示器B Window…

活动目录排错笔记

经过几年的网络管理工作&#xff0c;总结了一些关于AD的排错经验现在拿出来同大家分享&#xff0c;其中一些说明抄了微软的KB在这里说明一下&#xff0c;排错笔记有些地方可能写的不是很全&#xff0c;看不明白的地方大家可以GOOGLE一下&#xff0c;不对的地方也请大家提出 活…

CCF 201712-3 Crontab

本地AC&#xff0c;CCF编译失败。 通过对每条任务进行处理&#xff0c;将可能的月&#xff0c;日处理出来&#xff0c;并且比对对应星期是否符合要求。再对时分进行处理&#xff0c;判断整体时间在所给区域内。 思路借鉴自https://blog.csdn.net/gl486546/article/details/7905…

二十世纪最伟大的算法,你了解哪个?

导读&#xff1a;作者July总结了一篇关于计算方法的文章《 细数二十世纪最伟大的10大算法 》。一、1946 蒙特卡洛方法[1946: John von Neumann, Stan Ulam, and Nick Metropolis, all at the Los Alamos Scientific Laboratory, cook up the Metropolis algorithm, also known …

桌面计算机恢复出厂设置,windows7电脑怎么恢复出厂设置

我们使用电脑一段时间&#xff0c;由于各种问题&#xff0c;希望将电脑恢复出厂设置&#xff0c;那么windows7电脑怎么恢复出厂设置呢&#xff1f;下面跟着学习啦小编来一起了解下windows7电脑恢复出厂设置的方法吧。windows7电脑恢复出厂设置方法一按下开机键&#xff0c;启动…

关于Exchange管理控制台报“您的权限不足,无法此查看数据”的解决办法

今天朋友突然来电话&#xff0c;说自己的Exchange 2010 EMC突然报“you dont have sufficient permissions to view this data”&#xff08;您的权限不足&#xff0c;无法查看此数据&#xff09;&#xff0c;同时所有的cmdlet命令也不可以执行。询问我解决办法&#xff0c;出错…

C语言程序返回值为int的时候,不同值代表不同的意义

这个是我自己给自己的代码定的标准&#xff0c;方便自己阅读与编码。他人代码情况不可套用 1  执行成功 0  出现错误&#xff0c;不影响程序执行 -1  执行失败 -2  程序内部致命错误&#xff0c;退出程序转载于:https://www.cnblogs.com/kgtone/p/9570388.html

内存为什么还有管理?

本文作者&#xff1a;度白嵌入式任何程序运行起来都需要分配内存空间存放该进程的资源信息的&#xff0c;C程序也不例外。C程序中的变量、常量、函数、代码等等的信息所存放的区域都有所不同&#xff0c;不同的区域又有不同的特性。C语言学习者、尤其是在学习嵌入式的朋友&…

ajax html fileupload,fileUpload.html

文件上传测试说明: 最大上传量:100M&#xff0c;单个文件最大长度:100Menctype"multipart/form-data" method"post">Element.hide(progressBar);Event.observe(fileUploadForm,submit,startProgress,false);Event.observe(cancelUploadButton,click,ca…

HDOJ1106 排序

题目&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid1106 代码: #include<stdio.h> #include<string.h> #include<stdlib.h> int cmp(const void *a,const void *b){ return *(int *)a-*(int *)b; } int main(){ char s[1001]; int len,i,sum,cnt; …

TCP实现可靠传输

1. TCP 和 UDP的区别&#xff1a; TCP面向连接&#xff1b;UDP无连接TCP保证数据的可靠传输&#xff0c;数据传送无差错&#xff0c;不丢失&#xff0c;无重复&#xff0c;按序到达&#xff1b;UDP不保证可靠交付TCP连接一对一&#xff1b;UDP支持更广泛UDP实时性好&#xff0c…

旧手机别扔,手把手教你DIY一台Linux服务器

作者&#xff1a;Hannah Lee编译&#xff1a;弯月 欧阳姝黎来源&#xff1a;CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;本文将向你展示如何使用 UrBackup 和 Linux Deploy在一台 Android 旧手机上搭建一台备份服务器。旧手机的污染问题众所周知&#xff0c;我有一台旧…

学计算机买电脑显卡1605ti够吗,GTX1650和GTX1050Ti哪个好?GTX1050ti和GTX1650性能差距对比评测...

GTX1650显卡在2019年4月22日进行发售&#xff0c;不少用户认为GTX1650是智商检测卡&#xff0c;真的是吗&#xff1f;从命名上来看&#xff0c;GTX1650应该是GTX1050的升级产品&#xff0c;不过根据英伟达的说法&#xff0c;GTX1650相比GTX1050提升幅度达到了70%&#xff0c;但…

Cacti Plugin Architecture安装

1.下载Cacti PluginArchitecture ,http://mirror.cactiusers.org/downloads/plugins/cacti-plugin-0.8.7g-PA-v2.8.tar.gz2.解压Cacti Plugin Architecture 3.安装cd cacti-plugin-archcp cacti-plugin-0.8.7b-PA-v2.1.diff /var/www/cactipatch -p1 -N < cacti-plugin-0.8.…

P1547 Out of Hay

传送门 练习 只是一个最小生成树的水题&#xff0c;拿来练练模板 AC代码&#xff1a; #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; #define maxn 10001 struct hh {int x,y,z; }t[maxn]; int …