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

2023-05-16

[导读] 大家好,我是逸珺。

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

d4ae79304cfa3a005fc40fbad6ed8c13.png

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

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

背景介绍

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

2474bb7f7d7333fce39e6c04e552a193.png

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

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

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

786aeac53f137c657cc4eb46abb13aa0.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           0

struct 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

7e5569d675bfcb6a71a19ce7d0be4d3a.png

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

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

petalinux-build

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

insmod di-drv.ko

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

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

总结一下

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

—— The End ——

推荐阅读  点击蓝色字体即可跳转

☞ Linux驱动:手把手教hello world驱动配置、编译进内核或为模块

☞ 步进电机调速,S曲线调速算法

☞ 图文详解Modbus-RTU协议

☞ RS-485总线,这篇很详细

欢迎转发、留言、点赞、分享,感谢您的支持!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

手把手带你写一个中断输入设备驱动 的相关文章

  • ESP32 FreeRTOS-流缓冲区(12)

    提示 xff1a 好记性不如烂笔头 本博客作为学习笔记 xff0c 有错误的地方希望指正 文章目录 前言 xff1a 一 xStreamBufferCreate xStreamBufferCreateWithCallback 二 xStre
  • windows10下安装docker,并运行ubuntu

    先上三个参考链接 xff1a 安装参考链接docker安装 xff1b docker镜像仓库 xff1a docker hub xff1b docker官方windows安装参考文档 xff1a install docker desktop
  • windows10下使用docker开启Ubuntu桌面

    先安装docker xff1a https blog csdn net Mr FengT article details 107007999 然后随便在仓库中选择一个带有桌面的Ubuntu 比如我下载的 xff1a docker pull
  • Linux下的tar压缩解压缩命令详解

    tar c 建立压缩档案 x xff1a 解压 t xff1a 查看内容 r xff1a 向压缩归档文件末尾追加文件 u xff1a 更新原压缩包中的文件 这五个是独立的命令 xff0c 压缩解压都要用到其中一个 xff0c 可以和别的命令
  • Apex安装

    直接 xff1a pip install apex 最后发现会出错 xff0c 用不了 所以使用以下方法来安装 xff1a git clone https github com NVIDIA apex cd apex python setu
  • ROS 主从通信机制要点记录

    本文主机是PC xff0c 从机是树莓派4B 1 主从机器都处于同一局域网下 xff0c 即连接同一wifi 2 分别在主从机上终端输入 ifconfig 查看当前ip 3 分别打开主从机器的 etc hosts 文件 xff0c 使用su
  • 怎么制作自己的数据集

    我们在做深度学习时 xff0c 一般都是跑别人公开的数据集 xff0c 如果想要跑自己的数据集怎么办 xff1f 今天就记录一下我自己用的一种方法 1 假设待分类一共有n类 2 新建一个文件夹 xff0c 在该文件夹下新建n子个文件夹和n个
  • python 将列表中的字符串转为数字

    python 将列表中的字符串转为数字 转自 xff1a https www jb51 net article 86561 htm 本文实例讲述了Python中列表元素转为数字的方法 分享给大家供大家参考 xff0c 具体如下 xff1a
  • AI编程软件会取代程序员吗?

    最近听到同事问了这样一个问题 xff0c 今天就来好好的唠一唠 xff0c 随着科学技术的发展 xff0c 现在生命上都已经开始出现AI编程软件了 不由得感叹 xff0c 一句现在时代发展还真快呀 xff01 然后我就和他一样产生了一种小小
  • python的requests快速上手、高级用法和身份认证

    https www cnblogs com linkenpark p 10221362 html 快速上手 迫不及待了吗 xff1f 本页内容为如何入门 Requests 提供了很好的指引 其假设你已经安装了 Requests 如果还没有
  • MongoDB

    MongoDB简介 MongoDB是一个基于分布式文件存储的数据库 由C 43 43 语言编写 旨在为WEB应用提供可扩展的高性能数据存储 解决方案 MongoDB是一个介于关系数据库和非关系数据库之间的产品 xff0c 是非关系数据库当中
  • 路由器的连接——图解

    导读 现如今路由器的使用越来越普遍 xff0c 路由器有很多网线口 xff0c 你知道这些网线口该如何连接吗 xff1f 路由器后面都有哪些口 下图为一张最常用的路由器接口示意图 我们可以看到 xff0c 除了电源接口外 xff0c 路由器
  • 【PX4自动驾驶用户指南】距离传感器

    距离传感器 xff08 测距仪 xff09 距离传感器提供距离测量 xff0c 可用于地形跟踪 地形保持 如摄影时精确悬停 改进着陆行为 距离辅助 高度限制警告 碰撞预防等 本节列出了PX4支持的距离传感器 链接到更详细的文档 xff0c
  • 【MATLAB UAV Toolbox】使用指南(一)

    开始使用UAV Toolbox 设计 xff0c 仿真和部署无人机应用程序 UAV Toolbox给设计 仿真 测试和部署无人机应用程序提供了工具和参考应用 你能够设计自动飞行算法 无人机任务和飞行控制器 飞行日志分析仪应用程序可以让您交互
  • 【MATLAB UAV Toolbox】使用指南(二)

    可视化和回放MAVLink飞行日志 这个例子将展示如何将包含MAVLink包的遥测日志 xff08 TLOG xff09 加载进MATLAB 提取的详细信息用来绘图 然后再次仿真飞行 xff0c 这些消息通过MAVLink通信接口重新发布
  • ROS小乌龟程序在服务器通信的应用

    上次我们已经利用launch 文件和话题通信成功实现了小乌龟的位置与姿态的消息发布 xff0c 这次我们将利用服务通信来实现这一功能 我们将以spawn 产卵 xff0c 生成新的小乌龟 为例进行实践 首先是对相关信息的获取 xff1a r
  • STM32实验:串口接受和发送消息

    在STM32里 xff0c USART负责进行串口通信 STM32可以通过串口和其他设备进行传输并行数据 xff0c 是全双工 异步时钟控制 xff0c 设备之间进行的是点对点的数据传输 对应的STM32引脚分别是RX xff08 接收端
  • 关于传感器标定(imu标定,camera标定,camera-imu联合标定)

    博主最近在帮同门做实验 关于传感器这些标定也是初次接触 xff0c 使用orb slam3代码包 其中涉及一些传感器标定 xff0c 这里就把我用的东西汇总一下 目录 1 imu标定 1 1 使用imu utlies标定 1 1 1安装步骤
  • nuxt服务器渲染,获取数据赋值给组件 - nuxtServerInit

    前言 xff1a vue 43 nuxt js 项目中 xff0c 根据IP定位赋值 xff0c 头部可切换城市 xff0c header在layouts下调用 xff0c 首页根据定位接口返回一些数据 async asyncData ct

随机推荐

  • Docker基础篇

    视频链接 xff1a https www bilibili com video BV1og4y1q7M4 xff08 大名鼎鼎的B站狂神说 xff09 1 概述 官网 xff1a https docs docker com 1 1 容器较为
  • Docker进阶篇

    视频链接 xff1a https www bilibili com video BV1og4y1q7M4 xff08 大名鼎鼎的B站狂神说 xff09 1 Docker镜像详解 1 1 UnionFS xff08 联合文件系统 xff09
  • sw模型转urdf,并在gazebo和rviz中显示

    sw模型转urdf xff0c 并在gazebo和rviz中显示 1 sw模型转urdf1 1建立机器人的三维模型1 2机器人三维模型预处理1 3装配1 4建立坐标系 2 显示2 1 在gazebo中显示2 2 在rviz中显示 3 让模型
  • JSON对象/数组与JSON字符串之间的相互转换

    文章目录 前言JSON介绍如何判断JS数据类型JSON数组转化为JSON字符串JSON对象转化为JSON字符串JSON字符串转化为JSON数组 对象注意点 前言 这里先介绍一个个人觉得很好用的谷歌浏览器的功能 xff1a snippet 就
  • js分片上传&断点续传

    一 基本介绍 1 xff0c 什么是 WebUploader xff1f WebUploader 是由百度公司团队开发的一个以 HTML5 为主 xff0c FLASH 为辅的现代文件上传组件 官网地址 xff1a http fex bai
  • JavaScript超大文件上传解决方案:分片断点上传(一)

    前段时间做视频上传业务 xff0c 通过网页上传视频到服务器 视频大小 小则几十M xff0c 大则 1G 43 xff0c 以一般的HTTP请求发送数据的方式的话 xff0c 会遇到的问题 xff1a 1 xff0c 文件过大 xff0c
  • 基于JS的大文件分片

    项目需要上传超大文件 xff0c 后台为DJANGO xff0c 不能直接用H5 的FILE API来POST xff0c 所以采用slice分片 在分片后为BLOB不能直接传 xff0c bolb转file有些浏览器又有支持问题 所以做一
  • 【网络安全】如何在 Apache 安装开源 WAF

    说明 xff1a 本文以 Windows 环境下的 Apache 安装 mod security 为例 xff0c 介绍开源 WAF 产品的安装使用 http www modsecurity cn https github com Spid
  • 手把手教系列之梳状滤波器设计实现

    导读 前面一篇文章关于IIR 移动平均滤波器设计的文章 本文来聊一聊陷波滤波器 该滤波器在混入谐波干扰时非常有用 算法简单 实现代价低 本文来一探其在机理 应用场景 注 尽量在每篇文章写写摘要 方便阅读 信息时代 大家时间都很宝贵 如此亦可
  • 看思维导图:一文带你学Verilog HDL语言

    关注 星标嵌入式客栈 xff0c 精彩及时送达 导读 基于FPGA的SOC在嵌入式系统应用越来越广了 xff0c 往往一个复杂系统使用一个单芯片基于FPGA的SOC就搞定了 比较流行的方案主要有Altera xilinx两家的方案 要用这样
  • 长文图解工业HART总线协议

    关注 星标嵌入式客栈 xff0c 精彩及时送达 导读 在AIOT蓬勃发展的今天 xff0c 也掀起传统工业设备IOT革新的浪潮 那么需要对工业设备进行IOT改造 xff0c 除了直接开发具有IOT互联协议的新型设备 xff0c 对原有总线设
  • RS-485总线,这篇很详细

    关注 星标嵌入式客栈 xff0c 精彩及时送达 导读 大家好 xff0c 我是逸珺 xff0c 前面一篇文章总结了一下工业HART总线 xff0c 今天来聊聊RS 485总线 RS 485也是应用非常广泛的一种通信接口 xff0c 本文来讨
  • 图文详解Modbus-RTU协议

    关注 星标嵌入式客栈 xff0c 精彩及时送达 导读 大家好 xff0c 我是逸珺 xff0c 前面总结了一下RS 485的一些要点 xff0c 今天来总结一下Modbus RTU协议 xff0c 原本想把实现思路也一起发出来 xff0c
  • 【干货】用FreeRTOS搭建Event-Driven应用框架

    关注 星标嵌入式客栈 xff0c 精彩及时送达 导读 大家好 xff0c 我是逸珺 今天来分享一下 xff0c 之前项目中使用FreeRTOS搭建的Event Driven事件驱动框架 什么是Event Driven xff1f Event
  • 【Spring Boot】1.解决IDEA中springboot整合mybatis中出现的Invalid bound statement(not found)的问题

    最近学习springboot的开发 xff0c 中间磕磕碰碰也是遇到了诸多问题 xff0c 以前编写java IDE都是用的eclipse xff0c 现在换成idea了倒有点不适应了 xff0c 中间遇到了一个让人很郁闷的问题 xff0c
  • (数据结构与算法分析 一)------快速求幂算法,Java递归实现

    快速求幂算法 xff0c 递归实现 xff0c 其实算法的思想很简单 xff0c 但是感觉非常经典 xff0c 这个也是我开始看数据结构与算法分析这本书的开始把 xff0c 大学期间感觉就得深究一下算法 xff0c 课堂学习的太肤浅 xff
  • 永磁同步电机PMSM负载状态估计(龙伯格观测器,各种卡尔曼滤波器)矢量控制,坐标变换

    永磁同步电机PMSM负载状态估计 xff08 龙伯格观测器 xff0c 各种卡尔曼滤波器 xff09 矢量控制 xff0c 坐标变换 xff0c 英文复现 xff0c 含中文报告 xff0c 可作为结课作业 仿真原理图结果对比完全一致 另外
  • 分享信号处理相关的几部名著

    导读 大家好 xff0c 我是逸珺 数字信号处理还是比较难的 xff0c 我也只是会一些简单的 但是一直对这个有兴趣 xff0c 曾经有朋友问我看什么书籍比较好 xff0c 今天分享几本我经常看的数字信号处理书籍给大家 数字信号导论 书有点
  • 钓鱼累了,写下断言怎么用

    导读 大家好 xff0c 我是逸珺 今天来分享整理如何正确的使用断言 何为断言 断言一般是用于检测在某个程序位置程序必须满足某些条件的宏 一般用的多的可以分两种种情况 xff1a 前置条件 xff1a 在某个程度点开始的地方后置条件 xff
  • 手把手带你写一个中断输入设备驱动

    导读 大家好 xff0c 我是逸珺 首先说声抱歉 xff0c 最近迷上钓鱼了 xff0c 有时候晚上出去夜钓大板鲫了 xff0c 停更了一段时间 来几张鱼获图片 xff1a 技术还是不太到家 xff0c 遇到几次大鲤鱼都给溜了 xff0c