【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.rhkb.cn/news/120471.html

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

相关文章

[羊城杯 2023] web

文章目录 D0nt pl4y g4m3!!! D0n’t pl4y g4m3!!! 打开题目&#xff0c;可以判断这里为php Development Server 启动的服务 查询得知&#xff0c;存在 PHP<7.4.21 Development Server源码泄露漏洞(参考文章) 抓包&#xff0c;构造payload 得到源码 class Pro{private $ex…

Kubernetes(k8s) 架构原理一文详解

目录 一、k8s 概述 1.什么是k8s&#xff1f; 2.特性 3.主要功能 三、集群架构与组件 1.Master 组件 &#xff08;1&#xff09;Kube-apiserver &#xff08;2&#xff09;Kube-controller-manager &#xff08;3&#xff09;Kube-scheduler 2.配置存储中心 3.Node 组…

Flink基础实操-计算单词出现次数

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 个人主页&#xff1a;beixi 本文章收录于专栏&#xff08;点击传送&#xff09;&#xff1a;【大数据学习】 &#x1f493;&#x1f493;持续更新中&#xff0c;感谢各位前辈朋友们支持…

深入解析Kotlin类与对象:构造、伴生、单例全面剖析

前言 本篇文章将带您了解Kotlin编程中的重要概念&#xff1a;类及构造函数、访问修饰符、伴生对象和单例模式。就像搭积木一样&#xff0c;我们会逐步揭开这些概念的面纱&#xff0c;让您轻松理解它们的作用和用法。无论您是编程新手还是有经验的开发者&#xff0c;本文都将为…

笔记本家庭版本win11上win+r,运行cmd默认没有管理员权限,如何调整为有管理员权限的

华为matebookeGo 笔记本之前有段时间不知怎么回事&#xff0c;打开运行框&#xff0c;没有了那一行“使用管理权限创建此任务”&#xff0c;而且cmd也不再是默认的管理员下的&#xff0c;这很不方便,虽然每次winr &#xff0c;输入cmd后可以按ctrlshitenter以管理员权限运行&am…

驱动开发错误汇编

本博文将会不定期更新。以便记录我的驱动开发生涯中的一些点点滴滴的技术细节和琐事。 生成时link找不到导出函数&#xff0c;比如"LNK2019 无法解析的外部符号 _FltCreateCommunicationPort32"。出现这种情况的原因是&#xff0c;驱动的编译环境忽略了所有的默认库&…

windows的redis配置sentinel

1、先安装好redis主从&#xff0c;参考我的文章&#xff0c;链接如下 redis主从&#xff08;windows版本&#xff09;_rediswindows版本_veminhe的博客-CSDN博客 2、然后配置sentinel 参考在windows上搭建redis集群&#xff08;Redis-Sentinel&#xff09; 配置时&#xf…

mac 安装java1.8

1、下载jdk1.8 https://www.oracle.com/java/technologies/downloads/#java8-mac 2、 安装jdk1.8 一路默认&#xff0c;安装后的位置在这儿。 /Library/Java/JavaVirtualMachines/jdk-1.8.jdk 3、配置环境 打开终端&#xff0c;输入命令 sudo vim /etc/profile 添加以下配…

java八股文面试[JVM]——如何打破双亲委派模型

双亲委派模型的第一次“被破坏”是重写自定义加载器的loadClass(),jdk不推荐。一般都只是重写findClass()&#xff0c;这样可以保持双亲委派机制.而loadClass方法加载规则由自己定义&#xff0c;就可以随心所欲的加载类&#xff0c;典型的打破双亲委派模型的框架和中间件有tomc…

数学建模--一维插值法的多种插值方式的Python实现

目录 1.算法流程步骤 2.算法核心代码 3.算法效果展示 1.算法流程步骤 #算法的核心就是利用scipy中的interpolate来完成工作 #一共是5种一维插值算法形式: #插值方法&#xff1a;1.阶梯插值 2.线性插值 3.2阶样条插值 4.3阶样条插值 #"nearest"阶梯插值 #"zero&…

lv3 嵌入式开发-7 linux shell脚本编程(分支语句、循环语句)

目录 1 分支语句 2 多路分支语句 3 for的用法 4 while的用法 5 循环控制语句 6 练习 1 分支语句 语法结构: if 表达式then 命令表fi 如果表达式为真, 则执行命令表中的命令; 否则退出if语句, 即执行fi后面的语句。 if和fi是条件语句的语句括号, 必须成对使用; …

LinkedList(4):多线程LinkedList 不安全情况

多线程不安全演示&#xff0c;线程越多&#xff0c;现象越明显&#xff0c;这边只启了四个线程。 package com.example.demo;import java.util.LinkedList; import java.util.UUID;public class LInkedListThread {public static void main(String[] args) {final LinkedList&…

Notepad++下载安装

自己在 找Notepad发现网上的网址参差不齐&#xff0c;自己找到的一个不错下载链接见文末&#xff01; Notepad 是一个免费的代码编辑器&#xff0c;专为在微软 Windows 环境下使用。它是一个开源项目&#xff0c;采用 GPL 许可证&#xff0c;并使用 C 编程语言结合 Win32 API 和…

ZLMeidaKit在Windows上启动时:计算机中丢失MSVCR110.dll,以及rtmp推流后无法转换为flv视频流解决

场景 ZLMediaKit在Windows上实现Rtmp流媒体服务器以及模拟rtmp推流和http-flv拉流播放&#xff1a; ZLMediaKit在Windows上实现Rtmp流媒体服务器以及模拟rtmp推流和http-flv拉流播放_zlm流媒体服务器_霸道流氓气质的博客-CSDN博客 按照以上教程启动MediaServer.exe时提示&am…

深入浅出AXI协议(4)——猝发传输

一、前言 在之前的文章中&#xff0c;我们着重介绍了关于AXI4的握手协议它可以使得传输的双方都可以自如地控制传输的速率&#xff0c;我们主要介绍了握手协议出现的3种可能情况。然后对于AXI4交易通信的握手信号的关系做出了介绍&#xff1a;&#xff08;1&#xff09;在AXI4互…

【webrtc】接收/发送的rtp包、编解码的VCM包、CopyOnWriteBuffer

收到的rtp包RtpPacketReceived 经过RtpDepacketizer 解析后变为ParsedPayloadRtpPacketReceived 分配内存,执行memcpy拷贝:然后把 RtpPacketReceived 给到OnRtpPacket 传递:uint8_t* media_payload = media_packet.AllocatePayload(rtx_payload.size());RTC

ExpressLRS开源代码之框架结构

ExpressLRS开源代码之框架结构 1. 源由2. Arduino应用框架3. ExpressLRS应用框架4. 硬件设计框架4.1 单天线4.2 双天线单PA4.3 双天线双PA 5. 应用软件设计6. 参考资料 1. 源由 最近为了理解《ExpressLRS开源之基本调试数据含义》&#xff0c;做了一些源代码的研读。 概念、文…

二进制搭建kubernetes(K8S)

二进制搭建kubernetes&#xff08;K8S&#xff09; 一、常见的K8S部署方式1.Minikube2.Kubeadmin3.二进制安装部署 二、二进制搭建K8S(单台master)1.部署架构规划2.系统初始化配置3.部署 docker引擎4.部署 etcd 集群4.部署 Master 组件5.部署 Worker Node 组件6.部署网络组件 三…

数据可视化工具中的显眼包:奥威BI自带方案上阵

根据经验来看&#xff0c;BI数据可视化分析项目是由BI数据可视化工具和数据分析方案两大部分共同组成&#xff0c;且大多数时候方案都需从零开始&#xff0c;反复调整&#xff0c;会耗费大量时间精力成本。而奥威BI数据可视化工具别具匠心&#xff0c;将17年经验凝聚成标准化、…

【juc】ReentrantReadWriteLock之缓存(仅当学习)

目录 一、说明二、代码示例2.1 pom依赖2.2 示例代码2.3 实体类 三、示例截图 一、说明 1.针对于读多写少的情况 2.先查缓存&#xff0c;没有再去查库 二、代码示例 2.1 pom依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"h…