【IMX6ULL驱动开发学习】11.Linux之SPI驱动

参考:驱动程序开发:SPI设备驱动_spi驱动_邓家文007的博客-CSDN博客

目录

一、SPI驱动简介

1.1 SPI架构概述

1.2 SPI适配器(控制器)数据结构

1.2 SPI设备数据结构

1.3 SIP设备驱动

1.4 接口函数

 二、SPI驱动模板


一、SPI驱动简介

SPI驱动框架和I2C驱动框架是十分相似的,不同的是因为SPI是通过片选引脚来选择从机设备的,因此SPI不再需要像I2C那样先进行寻址操作(查询从机地址)后再进行对应寄存器的数据交互,并且SPI是全双工通信,通信速率要远高于I2C。

但是SPI显然占用的硬件资源也比I2C要多,并且SPI没有了像I2C那样指定的流控制(例如开始、停止信号)和没有了像I2C应当机制(导致无法确认数据是否接收到了)。

1.1 SPI架构概述

Linux的SPI体系结构可以分为3个组成部分:

  • spi核心(SPI Core):SPI Core是Linux内核用来维护和管理spi的核心部分,SPI Core提供操作接口函数,允许一个spi master,spi driver和spi device初始化时在SPI Core中进行注册,以及退出时进行注销。
  • spi控制器驱动或适配器驱动(SPI Master Driver):SPI Master针对不同类型的spi控制器硬件,实现spi总线的硬件访问操作。SPI Master 通过接口函数向SPI Core注册一个控制器。
  • spi设备驱动(SPI Device Driver):SPI Driver是对应于spi设备端的驱动程序,通过接口函数向SPI Core进行注册,SPI Driver的作用是将spi设备挂接到spi总线上。

Linux的软件架构图如下图所示: 

1.2 SPI适配器(控制器)数据结构

参考内核文件:include/linux/spi/spi.h

Linux中使用spi_master结构体描述SPI控制器,里面最重要的成员就是transfer函数指针:

transfer 函数,和 i2c_algorithm 中的 master_xfer 函数一样,控制器数据传输函数。
transfer_one_message 函数,也用于 SPI 数据发送,用于发送一个 spi_message,SPI 的数据会打包成 spi_message,然后以队列方式发送出去。

1.2 SPI设备数据结构

参考内核文件:include/linux/spi/spi.h

Linux中使用spi_device结构体描述SPI设备,里面记录有设备的片选引脚、频率、挂在哪个SPI控制器下面:

1.3 SIP设备驱动

参考内核文件:include/linux/spi/spi.h

Linux中使用spi_driver结构体描述SPI设备驱动:

可以看出,spi_driver 和 i2c_driver、 platform_driver 基本一样,当 SPI 设备和驱动匹配成功以后 probe 函数就会执行。 

比如:spi1下面接有两个设备(有两个片选信号),我们就可以把设备放入子节点里面,子节点将有内核解析后转换成一个spi_device,与某一个spi_driver匹配后,spi_driver里的probe函数就被调用,我们在probe函数里就可以注册字符设备驱动程序。

1.4 接口函数

函数原形:

  • 简易函数
/*** SPI同步写* @spi: 写哪个设备* @buf: 数据buffer* @len: 长度* 这个函数可以休眠** 返回值: 0-成功, 负数-失败码*/
static inline int
spi_write(struct spi_device *spi, const void *buf, size_t len);/*** SPI同步读* @spi: 读哪个设备* @buf: 数据buffer* @len: 长度* 这个函数可以休眠** 返回值: 0-成功, 负数-失败码*/
static inline int
spi_read(struct spi_device *spi, void *buf, size_t len);/*** spi_write_then_read : 先写再读, 这是一个同步函数* @spi: 读写哪个设备* @txbuf: 发送buffer* @n_tx: 发送多少字节* @rxbuf: 接收buffer* @n_rx: 接收多少字节* 这个函数可以休眠* * 这个函数执行的是半双工的操作: 先发送txbuf中的数据,在读数据,读到的数据存入rxbuf** 这个函数用来传输少量数据(建议不要操作32字节), 它的效率不高* 如果想进行高效的SPI传输,请使用spi_{async,sync}(这些函数使用DMA buffer)** 返回值: 0-成功, 负数-失败码*/
extern int spi_write_then_read(struct spi_device *spi,const void *txbuf, unsigned n_tx,void *rxbuf, unsigned n_rx);/*** spi_w8r8 - 同步函数,先写8位数据,再读8位数据* @spi: 读写哪个设备* @cmd: 要写的数据* 这个函数可以休眠*** 返回值: 成功的话返回一个8位数据(unsigned), 负数表示失败码*/
static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd);/*** spi_w8r16 - 同步函数,先写8位数据,再读16位数据* @spi: 读写哪个设备* @cmd: 要写的数据* 这个函数可以休眠** 读到的16位数据: *     低地址对应读到的第1个字节(MSB),高地址对应读到的第2个字节(LSB)*     这是一个big-endian的数据** 返回值: 成功的话返回一个16位数据(unsigned), 负数表示失败码*/
static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd);/*** spi_w8r16be - 同步函数,先写8位数据,再读16位数据,*               读到的16位数据被当做big-endian,然后转换为CPU使用的字节序* @spi: 读写哪个设备* @cmd: 要写的数据* 这个函数可以休眠** 这个函数跟spi_w8r16类似,差别在于它读到16位数据后,会把它转换为"native endianness"** 返回值: 成功的话返回一个16位数据(unsigned, 被转换为本地字节序), 负数表示失败码*/
static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd);
  •  复杂函数
/*** spi_async - 异步SPI传输函数,简单地说就是这个函数即刻返回,它返回后SPI传输不一定已经完成* @spi: 读写哪个设备* @message: 用来描述数据传输,里面含有完成时的回调函数(completion callback)* 上下文: 任意上下文都可以使用,中断中也可以使用** 这个函数不会休眠,它可以在中断上下文使用(无法休眠的上下文),也可以在任务上下文使用(可以休眠的上下文) ** 完成SPI传输后,回调函数被调用,它是在"无法休眠的上下文"中被调用的,所以回调函数里不能有休眠操作。* 在回调函数被调用前message->statuss是未定义的值,没有意义。* 当回调函数被调用时,就可以根据message->status判断结果: 0-成功,负数表示失败码* 当回调函数执行完后,驱动程序要认为message等结构体已经被释放,不能再使用它们。** 在传输过程中一旦发生错误,整个message传输都会中止,对spi设备的片选被取消。** 返回值: 0-成功(只是表示启动的异步传输,并不表示已经传输成功), 负数-失败码*/
extern int spi_async(struct spi_device *spi, struct spi_message *message);/*** spi_sync - 同步的、阻塞的SPI传输函数,简单地说就是这个函数返回时,SPI传输要么成功要么失败* @spi: 读写哪个设备* @message: 用来描述数据传输,里面含有完成时的回调函数(completion callback)* 上下文: 能休眠的上下文才可以使用这个函数** 这个函数的message参数中,使用的buffer是DMA buffer** 返回值: 0-成功, 负数-失败码*/
extern int spi_sync(struct spi_device *spi, struct spi_message *message);/*** spi_sync_transfer - 同步的SPI传输函数* @spi: 读写哪个设备* @xfers: spi_transfers数组,用来描述传输* @num_xfers: 数组项个数* 上下文: 能休眠的上下文才可以使用这个函数** 返回值: 0-成功, 负数-失败码*/
static inline int
spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers,unsigned int num_xfers);

 二、SPI驱动模板

spi_drv.c

#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/poll.h>#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>/* 主设备号                                                                 */
static int major = 0;
static struct class *my_spi_class;static struct spi_device *g_spi;static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);
struct fasync_struct *spi_fasync;/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t spi_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{// int err;// struct spi_transfer msgs[2];/* 初始化 spi_transfer */// static inline int//   spi_sync_transfer(struct   spi_device *spi, struct spi_transfer *xfers,//   unsigned int num_xfers);/* copy_to_user  */return 0;
}static ssize_t spi_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{//int err;/* copy_from_user  */// struct spi_transfer msgs[2];/* 初始化 spi_transfer */// static inline int//   spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers,//   unsigned int num_xfers);return 0;    
}static unsigned int spi_drv_poll(struct file *fp, poll_table * wait)
{//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);poll_wait(fp, &gpio_wait, wait);//return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;return 0;
}static int spi_drv_fasync(int fd, struct file *file, int on)
{if (fasync_helper(fd, file, on, &spi_fasync) >= 0)return 0;elsereturn -EIO;
}/* 定义自己的file_operations结构体                                              */
static struct file_operations spi_drv_fops = {.owner	 = THIS_MODULE,.read    = spi_drv_read,.write   = spi_drv_write,.poll    = spi_drv_poll,.fasync  = spi_drv_fasync,
};static int spi_drv_probe(struct spi_device *spi)
{// struct device_node *np = client->dev.of_node;/* 记录spi_device */g_spi = spi;/* 注册字符设备 *//* 注册file_operations 	*/major = register_chrdev(0, "100ask_spi", &spi_drv_fops);  /* /dev/gpio_desc */my_spi_class = class_create(THIS_MODULE, "100ask_spi_class");if (IS_ERR(my_spi_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "100ask_spi");return PTR_ERR(my_spi_class);}device_create(my_spi_class, NULL, MKDEV(major, 0), NULL, "myspi"); /* /dev/myspi */return 0;
}static int spi_drv_remove(struct spi_device *spi)
{/* 反注册字符设备 */device_destroy(my_spi_class, MKDEV(major, 0));class_destroy(my_spi_class);unregister_chrdev(major, "100ask_spi");return 0;
}static const struct of_device_id myspi_dt_match[] = {{ .compatible = "100ask,spidev" },{},
};
static struct spi_driver my_spi_driver = {.driver = {.name = "100ask_spi_drv",.owner = THIS_MODULE,.of_match_table = myspi_dt_match,},.probe = spi_drv_probe,.remove = spi_drv_remove,
};static int __init spi_drv_init(void)
{/* 注册spi_driver */return spi_register_driver(&my_spi_driver);
}static void __exit spi_drv_exit(void)
{/* 反注册spi_driver */spi_unregister_driver(&my_spi_driver);
}/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */module_init(spi_drv_init);
module_exit(spi_drv_exit);MODULE_LICENSE("GPL");

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

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

相关文章

718. 最长重复子数组

718. 最长重复子数组 原题链接&#xff1a;完成情况&#xff1a;题解&#xff1a;方法一&#xff1a;动态规划方法二&#xff1a;滑动窗口方法三&#xff1a;二分查找 哈希 原题链接&#xff1a; 718. 最长重复子数组 https://leetcode.cn/problems/maximum-length-of-repe…

Android App的设计规范

Android App 设计规范是为开发者和设计师提供的一系列准则和建议&#xff0c;以确保应用在 Android 设备上的外观、交互和用户体验保持一致。以下是一些常见的 Android App 设计规范要点&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开…

Apifox下载安装步骤

我们先访问网址 https://apifox.com/?utm_sourcebaidu&utm_mediumsem&utm_campaign251430236&utm_content7810722111&utm_termapifox%E6%9F%A5%E7%9C%8B%E7%89%88%E6%9C%AC&bd_vid8323327349775096324 然后 这里这个免费下载已经写的这么明显了 那就直接点…

大华摄像头有问题,海康摄像头也有问题

买了个大华摄像头&#xff0c;除了抗噪方面效果不好&#xff0c;我是很满意的。前一段时间摄像头启动出了点问题&#xff08;忘记拔掉SD卡&#xff09;&#xff0c;于是买了个海康的。 大华摄像头是3寸&#xff0c;海康是2寸。视频效果差多了。看来大有大的道理。更可恨的是&a…

PMO(Project Management Office)

PMO 是项目管理办公室&#xff08;Project Management Office&#xff09;的缩写。它是组织内的一个部门或团队&#xff0c;负责支持和促进项目管理活动&#xff0c;以确保项目按时、按预算、按要求完成。 PMO 的职责和角色可以因组织的性质和需求而有所不同&#xff0c;但通常…

java中 while 和 for 两种循环写法的使用场景

目录 场景 能否将声明变量写在while的小括号里&#xff1f; 能否将声明变量写在for的小括号里&#xff1f; 省流&#xff1a; BufferedReader br new BufferedReader(null);for(String line; (line br.readLine()) ! null;){System.out.println(line);}正文&#xff1a; …

c++学习之string实现

字符串 - C引用 (cplusplus.com)这里给出标准官方的string实现&#xff0c;可以看到设计还是较为复杂的&#xff0c;有成员函数&#xff0c;迭代器&#xff0c;修饰符&#xff0c;容量&#xff0c;元素访问&#xff0c;字符串操作等&#xff0c;将字符尽可能的需求都设计出来&a…

Jmeter(三十):并发测试(设置集合点)

集合点:让所有请求在不满足条件的时候处于等待状态。 如:我集合点设置为50,那么不满足50个请求的时候,这些请求都会集合在一起,处于等待状态,当达到50的时候,就一起执行。从而达到并发的效果。 那么Jmeter中可以通过同步定时器 Synchronizing Timer 来完成。 Number …

计算机竞赛 基于机器视觉的手势检测和识别算法

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的手势检测与识别算法 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng…

医学影像PACS系统源码,患者登记、图像采集和处理、图像存储、报告产生的影像系统

PACS系统是医院影像科室中应用的一种系统&#xff0c;主要用于获取、传输、存档和处理医学影像。它通过各种接口&#xff0c;如模拟、DICOM和网络&#xff0c;以数字化的方式将各种医学影像&#xff0c;如核磁共振、CT扫描、超声波等保存起来&#xff0c;并在需要时能够快速调取…

高忆管理:新手炒股入门零基础学?

炒股是一些人为了取得高额回报和更好的财政自由而进行的活动。但对许多新手而言&#xff0c;这是一个全新的领域&#xff0c;需求掌握许多根底常识才能够开始加入炒股商场。本文将为零根底的新手炒股入门供给一些主张和技巧&#xff1a; 一、学习根底常识 关于炒股入门的新手而…

spark内置函数笔记

spark常用的内置函数 一、统计函数 1. approx_percentile selectapprox_percentile( id, array(0.5, 0.7, 0.9), 1000) as res from (select explode(sequence(1,1000)) as id) aa ; [499,699,899] 二、数组函数 1. array -- 构造数据组 select array(1, 2, 3); [1,2,3]-- 数…

在ros中利用串口serial发布fdilink的gps话题

文章目录 介绍FDILink通讯协议数据帧组成数据包 数据处理打开串口在头文件中定义参数串口读取 代码运用依赖&#xff1a;使用&#xff1a; 源码 介绍 DETA100系列 是一个提供 GNSS/INS & AHRS 系统的模组&#xff0c;在最苛刻的条件下提供准确的位置、速度、加速度和姿态数…

Java抽象类——抽象类与普通类的区别

abstract抽象类 1、含义&#xff1a;类和类之间具有共同特征&#xff0c;将这些共同特征提取出来&#xff0c;就形成抽象类。。。 在 java 中&#xff0c;被 abstract 关键字修饰的类叫做抽象类&#xff0c;被abstract关键字修饰的方法叫做抽象方法。 抽象类本身是不存在的&…

量化面试-概率题

文章目录 一、题目1.糖果罐&#xff08;绿皮书79页&#xff09;2 折木棍&#xff08;绿皮书89页&#xff09;3 第一张ACE&#xff08;绿皮书95页&#xff09;4 n个均匀分布之和&#xff08;绿皮书95页&#xff09; 二、答案1. 糖果罐2 折木棍3 第一张ACE4 n个均匀分布之和 一、…

spring事件和线程池区别

Spring事件&#xff08;Spring Event&#xff09;和线程池&#xff08;Thread Pool&#xff09;是两个不同的概念。 Spring事件是Spring框架中的一种机制&#xff0c;用于在应用程序中实现发布-订阅模式。通过定义事件和监听器&#xff0c;可以在特定事件发生时&#xff0c;通…

pytest笔记: pytest单元测试框架

第一步&#xff1a;安装 和查看版本 pycharm settings 查看 第二步&#xff1a; 编写test_example.py def inc(x):return x1 def test_answer():assert inc(4) 5 第三步&#xff1a;在当前路径下执行pytest 命令 PS E:\data\web测试\Selenium3自动化测试实战——基于Pyth…

专题:曲面的切平面、法线

假设曲面方程为隐函数 F ( x , y , z ) 0 &#xff0c;点 M ( x 0 , y 0 , z 0 ) 是其上一点 又在点 M 处任意引一条在曲面上的曲线&#xff0c;设该曲线参数方程为&#xff1a; { x φ ( t ) y ψ ( t ) z ω ( t ) &#xff0c;且当 t t 0 时&#xff0c; x x 0 , y y…

Flink流批一体计算(18):PyFlink DataStream API之计算和Sink

目录 1. 在上节数据流上执行转换操作&#xff0c;或者使用 sink 将数据写入外部系统。 2. File Sink File Sink Format Types Row-encoded Formats Bulk-encoded Formats 桶分配 滚动策略 3. 如何输出结果 Print 集合数据到客户端&#xff0c;execute_and_collect…

鼎盛合方案——充气泵的原理和结构

充气泵是一种常见的气体压缩设备&#xff0c;它可以将空气或其他气体压缩成高压气体&#xff0c;用于充气、输送、压缩等工业和生活领域。充气泵的原理是利用机械或电动力量将气体压缩&#xff0c;使其体积减小&#xff0c;压力增大&#xff0c;从而达到充气的目的。 充气泵的…