Linux下SPI设备驱动实验:向SPI驱动框架中加入字符设备驱动框架代码

一.  简介

前一篇文章编写了SPI设备驱动框架代码,文章如下:

Linux下SPI设备驱动实验:SPI设备驱动框架编写-CSDN博客

本文继续SPI驱动代码的编写。向SPI驱动框架中加入字符设备驱动框架代码。

二.  向SPI驱动框架中加入字符设备驱动框架代码

1.  添加字符设备驱动框架的代码

打开 ubuntu系统,通过 vscode 打开18_spi 工程。向SPI设备驱动框架中添加字符设备驱动框架的代码。添加如下:

(1)  字符设备注册的一套流程,放在 spi_driver的 probe函数中。

(2)  字符设备注销的一套流程,放在 spi_driver的 remove函数中。

添加字符设备驱动框架的代码后,spi_icm20608.c文件中代码如下:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/of.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>

#define  ICM20608_NAME   "icm20608"
#define  ICM20608_CNT    1

//设备结构体
struct icm20608_Dev{
    dev_t devid;  //设备号
    int major;     //主设备号
    int minor;     //次设备号 
    struct cdev led_cdev; 
    struct class * class;   //类(自动创建设备节点用)
    struct device * device; //设备(自动创建设备节点用) 
};

struct icm20608_Dev icm20608_dev;

/*打开设字符备函数 */
static int icm20608_open(struct inode *inode, struct file *filp)
{
    printk("icm20608_open\n");
    return 0;
}

/*读字符设备中数据的函数*/
ssize_t icm20608_read(struct file * filp, char __user * buf, size_t count, loff_t * ppos)
{
    int ret = 0;
    printk("icm20608_read!\n");
    return ret;
}

/*关闭设备函数*/
int icm20608_release(struct inode * inode, struct file * filp)
{
    printk("led_release\n");
    return 0;
}

//字符设备的函数集
const struct file_operations fops = {  
    .owner = THIS_MODULE,
    .open = icm20608_open,
    .read = icm20608_read,
    .release = icm20608_release,
};

static int icm20608_probe(struct spi_device* spi_dev)
{
    int ret = 0;
    printk("icm20608_probe!\n");
        //设备号的分配
    icm20608_dev.major = 0;
    if(icm20608_dev.major) //给定主设备号
    {
        icm20608_dev.devid = MKDEV(icm20608_dev.major, 0);
        ret = register_chrdev_region(icm20608_dev.devid, ICM20608_CNT, ICM20608_NAME);
    }
    else{ //没有给定主设备号
        ret = alloc_chrdev_region(&(icm20608_dev.devid), 0, ICM20608_CNT, ICM20608_NAME);	
        icm20608_dev.major = MAJOR(icm20608_dev.devid);
        icm20608_dev.minor = MINOR(icm20608_dev.devid);
    }
    printk("dev.major: %d\n", icm20608_dev.major);
    printk("dev.minor: %d\n", icm20608_dev.minor);
	if (ret < 0) {
		printk("register-chrdev failed!\n");
		goto fail_devid;
	}
    //初始化设备
    icm20608_dev.led_cdev.owner = THIS_MODULE;
    cdev_init(&icm20608_dev.led_cdev, &fops);
    
    //注册设备
    ret = cdev_add(&icm20608_dev.led_cdev, icm20608_dev.devid, ICM20608_CNT);
    if(ret < 0)
    {
        printk("cdev_add failed!\r\n");
        goto fail_adddev;
    }

/* 3.自动创建设备节点 */
    //创建类
    icm20608_dev.class = class_create(THIS_MODULE, ICM20608_NAME); 
    if (IS_ERR(icm20608_dev.class)) {
		ret = PTR_ERR(icm20608_dev.class);
		goto fail_class;
	}   
    //创建设备
    icm20608_dev.device = device_create(icm20608_dev.class, NULL, icm20608_dev.devid, NULL, ICM20608_NAME);
    if (IS_ERR(icm20608_dev.device)) {
		ret = PTR_ERR(icm20608_dev.device);
		goto fail_dev_create;
	}  

    return 0;

fail_dev_create:
    class_destroy(icm20608_dev.class);
fail_class:
    cdev_del(&icm20608_dev.led_cdev);
fail_adddev:
    unregister_chrdev_region(icm20608_dev.devid, ICM20608_CNT);
fail_devid:
    return ret;
}

static int icm20608_remove(struct spi_device* spi_dev)
{
    printk("icm20608_remove!\n");
    /*注销字符设备*/
    //1. 删除设备
    cdev_del(&icm20608_dev.led_cdev);
    //2. 注销设备号
    unregister_chrdev_region(icm20608_dev.devid, ICM20608_CNT);

    /*摧毁类与设备(自动创建设备节点时用) */
    //3. 摧毁设备
    device_destroy(icm20608_dev.class, icm20608_dev.devid);
    //4. 摧毁类
    class_destroy(icm20608_dev.class);
    return 0;
}

//传统驱动与设备匹配方法
static struct spi_device_id spi_device_id_table[] = {
    { "icm20608", 0},
    { }
};

//设备树匹配方法
static struct of_device_id of_device_table[] = {
    { .compatible = "alientek,icm20608" },  //必须与设备树中设备节点compatible值一致
    { }
};

/*SPI驱动结构体*/
struct spi_driver icm20608_driver = {
    .driver = {
        .name = "icm20608",
        .owner = THIS_MODULE,
        .of_match_table = of_match_ptr(of_device_table),
    },
    .id_table = spi_device_id_table,
    .probe = icm20608_probe,
    .remove = icm20608_remove,
};

/*模块加载 */
static int __init icm20608_init(void)
{
    return spi_register_driver(&icm20608_driver);
}

/*模块卸载 */
static void __exit icm20608_exit(void)
{
    spi_unregister_driver(&icm20608_driver);
}

/*驱动加载与卸载 */
module_init(icm20608_init);
module_exit(icm20608_exit);
MODULE_LICENSE("GPL"); //模块 Licence
MODULE_AUTHOR("WeiWuXian"); //作者

2.  编译驱动模块

对以上的驱动代码进行模块化编译:

wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/18_spi$ make
make -C /home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/wangtian/zhengdian_Linux/Linux_Drivers/18_spi modules
make[1]: 进入目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”
  CC [M]  /home/wangtian/zhengdian_Linux/Linux_Drivers/18_spi/spi_icm20608.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/wangtian/zhengdian_Linux/Linux_Drivers/18_spi/spi_icm20608.mod.o
  LD [M]  /home/wangtian/zhengdian_Linux/Linux_Drivers/18_spi/spi_icm20608.ko
make[1]: 离开目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”

可以看出,驱动模块正常编译通过。

三. 编写应用程序(测试程序) 

在 18_spi工程目录下,创建应用程序 icm20608_app.c文件。icm20608_app.c文件代码实现如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

/*
* 打开/关闭 Led灯
* 参数:
* ./icm20608_app /dev/icm20608 
*/
int main(int argc, char* argv[])
{
    int fd = 0,ret = 0;
    char * device_name = NULL;
    unsigned short data[3] = {0};

    if(argc != 2)
    {
        printf("main's param number error!\n");
        return -1;
    }

    device_name = argv[1];
    fd = open(device_name, O_RDWR);
    if(fd < 0)
    {
        printf("open led device failed!\n");
        return -1;
    }
    
    ret = read(fd, data, sizeof(data));

    close(fd);
    return 0;
}

终端进入 18_spi工程的根目录下,输入如下命令编译应用程序:

arm-linux-gnueabihf-gcc icm20608_app.c -o icm20608_app

这里可以编译通过。生成 icm20608_app。

接下来通过运行应用程序,对驱动模块进行测试,确定是否会正常运行到 open函数,read函数,release函数。

测试方法以及确认SPI驱动是否运行正常,可以参考如下文章:

I2C驱动实验:测试I2C驱动框架代码-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软考中级网络工程师-2024上岸宝典

1.软考是什么 简单说就是计算机技术 相关的国家级证书考试&#xff0c;想听专业点给大家截一张官网的图&#xff0c;不想听废话直接往下。 同为国家级证书的&#xff1a;注册会计师、法律职业资格证、一级建筑师&#xff0c;证书的价值是比较高的。 很多人都是在求职前或者大…

【面试经典 150 | 二叉树层序遍历】二叉树的右视图

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;层序遍历方法二&#xff1a;深度优先搜索 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于…

全球媒体发稿:海外发稿数字期刊Digital Journal

全球媒体发稿&#xff1a;海外发稿数字期刊Digital Journal ​官网&#xff1a; digitaljournal.com 数字期刊&#xff0c;加拿大知名门户&#xff0c;月访量超过30万。 是一个全球媒体平台和内容合作伙伴&#xff0c;通过捕捉和报道第一&#xff0c;提升新闻周期中的声…

快手本地生活服务商系统怎么操作?

当下&#xff0c;抖音和快手两大短视频巨头都已开始布局本地生活服务&#xff0c;想要在这一板块争得一席之地。而这也很多普通人看到了机遇&#xff0c;选择成为抖音和快手的本地生活服务商&#xff0c;通过将商家引进平台&#xff0c;并向其提供代运营服务&#xff0c;而成功…

工厂数字化系统是自研,还是对外采购

数字化转型在企业中变得越来越普遍&#xff0c;众多数字化项目的增加也引发了自研和采购数字化系统的讨论。自研和采购各有优劣&#xff0c;需要根据企业的实际情况和需求来做出明智的选择。 自研数字化系统 适用情况&#xff1a;重要核心业务、复用率高、需要长期优化迭代的系…

用队列实现栈(力扣第225题)

#include "stdio.h" #include "stdbool.h" #include "string.h" #include "stdlib.h" #include "assert.h"//初始化队列 typedef int QueueDataType;typedef struct queue {QueueDataType val;struct queue* next; }Qnode;t…

符文协议的演变历程:从挑战到创新

在比特币网络长期面临的挑战中&#xff0c;与主流去中心化金融功能的兼容性一直是一大难题。相比之下&#xff0c;以太坊通过ERC-721和ERC-1155代币标准&#xff0c;为NFT和去中心化金融应用提供了支持&#xff0c;而比特币的应用范围却相对有限。然而&#xff0c;近年来&#…

Linux知识点(4)

文章目录 13. 线程13.1 什么是线程13.2 Linux下的线程13.2.1 pthread_create13.2.2 线程为什么高效&#xff1f;13.2.3 线程的优缺点13.2.4 线程异常13.2.5 线程用途 13.4 虚拟地址空间13.5 Linux线程控制13.5.1 POSIX线程库13.5.2 创建线程13.5.3 线程ID及进程地址空间布局13.…

如何构建企业技术架构-解决内部系统连接的问题

随着企业信息化建设的深入&#xff0c;各类管理系统在运营管理中发挥着关键作用。为了实现数据共享、业务流程自动化和决策支持的无缝对接&#xff0c;往往搭建一个高效协同的技术架构至关重要。本文将以人事系统、泛微OA&#xff08;Office Automation&#xff09;及ERP&#…

基于Springboot+Vue的Java项目-网上点餐系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

【御控物联】Java JSON结构转换(4):对象To对象——规则属性重组

文章目录 一、JSON结构转换是什么&#xff1f;二、术语解释三、案例之《JSON对象 To JSON对象》四、代码实现五、在线转换工具六、技术资料 一、JSON结构转换是什么&#xff1f; JSON结构转换指的是将一个JSON对象或JSON数组按照一定规则进行重组、筛选、映射或转换&#xff0…

Nginx莫名奇妙返回了404

描述 nginx作为反向代理&#xff0c;代理python的服务&#xff0c;但是通过代理访问服务的时候&#xff0c;报了404的错误。 难受的是客户现场没有查看日志的权限&#xff0c;只有查看配置文件的权限&#xff0c;我们检测了几遍配置文件也没有找到问题&#xff0c;哎~ 问题引…

Python兼职:只需要一台电脑宅在家,轻松实现月入过万!

Python兼职副业 Python是一种简单易学、高效强大的编程语言&#xff0c;正变成越来越多人选择的热门技能。不论你是否有编程基础&#xff0c;在学习Python的道路上&#xff0c;坚持每天投入2小时&#xff0c;你将看到巨大的回报。 学习Python不仅可以为你提供更多就业机会&am…

【情侣博客网站】

效果图 PC端 建塔教程 第一步&#xff1a;下载网站源码&#xff08;在文章下方有下载链接&#xff09; 第二步&#xff1a;上传到服务器或虚拟主机&#xff0c;解压。 第三步&#xff1a;这一步很关键&#xff0c;数据库进行连接&#xff0c;看图 admin/connect.php就是这…

链表带环问题——leetcode环形链表1 2

证明链表带环 链表的带环问题指的是本该指向NULL的最后一个节点指向了之前的节点&#xff0c;导致链表成环&#xff0c;找不到尾结点的情况&#xff0c;那么我们该如何证明链表带环呢&#xff1f; 我们可以类比物理中的追及问题&#xff0c;让快慢指针同时走&#xff0c;两者相…

element-ui form表单自定义label的样式、内容

element-ui form表单自定义label的样式、内容 效果截图 代码 <el-form size"small" :inline"true" label-width"120px"><el-form-item prop"name"><div slot"label"><i style"color: red;"…

步步精科技获得发明型专利,提升Type-C连接器行业竞争力

在电子科技日新月异的时代&#xff0c;连接器作为电子设备中不可或缺的一部分&#xff0c;其安全性、稳定性和性能水平直接关系到设备的使用效果和用户体验。深圳市步步精科技有限公司&#xff08;以下简称“步步精科技”&#xff09;一直致力于连接器领域的技术创新和产品研发…

盒子模型之弹性盒模型

经常适用于手机端图标布局 display: flex;让这个盒子显示成弹性盒&#xff08;很适合移动端布局&#xff09; 影响&#xff1a;1.让里面的子元素默认横向排列 2.如果子元素是行内元素&#xff0c;则直接变成块元素 3.只有一个元素&#xff0c;margin: auto;自动居中 <!DOCT…

学习Python先从了解Python开始

Python是一种高级编程语言&#xff0c;它的语法简洁易读&#xff0c;功能强大&#xff0c;应用领域广泛。Python不仅适用于数据科学、机器学习、Web开发等领域&#xff0c;还可以用于自动化脚本编写、游戏开发等。在本文中&#xff0c;我们将探讨Python的特点、应用领域以及未来…

[leetcode] 54. 螺旋矩阵

文章目录 题目描述解题方法模拟java代码复杂度分析 相似题目 题目描述 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;…