编写SPI设备驱动程序

news2025/1/12 20:38:30

编写SPI设备驱动程序


文章目录

  • 编写SPI设备驱动程序
  • 参考资料:
    • 一、 SPI驱动程序框架
    • 二、 怎么编写SPI设备驱动程序
      • 2.1 编写设备树
      • 2.2 注册spi_driver
      • 2.3 怎么发起SPI传输
        • 2.3.1 接口函数
        • 2.3.2 函数解析
  • 致谢



参考资料:

  • 内核头文件:include\linux\spi\spi.h

  • 内核文档:Documentation\spi\spidev


一、 SPI驱动程序框架

在这里插入图片描述

二、 怎么编写SPI设备驱动程序

2.1 编写设备树

  • 查看原理图,确定这个设备链接在哪个SPI控制器下

  • 在设备树里,找到SPI控制器的节点

  • 在这个节点下,创建子节点,用来表示SPI设备

  • 示例如下:

    &ecspi1 {
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_ecspi1>;
    
        fsl,spi-num-chipselects = <2>;
        cs-gpios = <&gpio4 26 GPIO_ACTIVE_LOW>, <&gpio4 24 GPIO_ACTIVE_LOW>;
        status = "okay";
    
        dac: dac {
            compatible = "100ask,dac";
            reg = <0>;
            spi-max-frequency = <10000000>;
        };
    };
    

2.2 注册spi_driver

SPI设备的设备树节点,会被转换为一个spi_device结构体。

我们需要编写一个spi_driver来支持它。

示例如下:

static const struct of_device_id dac_of_match[] = {
	{.compatible = "100ask,dac"},
	{}
};

static struct spi_driver dac_driver = {
	.driver = {
		.name	= "dac",
		.of_match_table = dac_of_match,
	},
	.probe		= dac_probe,
	.remove		= dac_remove,
	//.id_table	= dac_spi_ids,
};

2.3 怎么发起SPI传输

2.3.1 接口函数

接口函数都在这个内核文件里:include\linux\spi\spi.h

  • 简易函数

    /**
     * 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);

2.3.2 函数解析

在SPI子系统中,用spi_transfer结构体描述一个传输,用spi_message管理过个传输。

SPI传输时,发出N个字节,就可以同时得到N个字节。

  • 即使只想读N个字节,也必须发出N个字节:可以发出0xff
  • 即使只想发出N个字节,也会读到N个字节:可以忽略读到的数据。

spi_transfer结构体如下图所示:

  • tx_buf:不是NULL的话,要发送的数据保存在里面
  • rx_buf:不是NULL的话,表示读到的数据不要丢弃,保存进rx_buf里

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tAQpdQiQ-1677557002345)(pic/70_spi_transfer.png)]
可以构造多个spi_transfer结构体,把它们放入一个spi_message里面。

spi_message结构体如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nO5cBuq4-1677557002345)(pic/71_spi_message.png)
SPI传输示例:

在这里插入图片描述



致谢

以上笔记源自韦东山老师的视频课程,感谢韦老师,韦老师是嵌入式培训界一股清流,为嵌入式linux开发点起的星星之火,也愿韦老师桃李满园。聚是一团火,散是满天星!

在这样一个速食的时代,坚持做自己,慢下来,潜心琢磨,心怀敬畏,领悟知识,才能向下扎到根,向上捅破天,背着世界往前行!
仅此向嵌入行业里的每一个认真做技术的从业者致敬!



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

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

相关文章

Python实现GWO智能灰狼优化算法优化BP神经网络回归模型(BP神经网络回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。1.项目背景灰狼优化算法(GWO)&#xff0c;由澳大利亚格里菲斯大学学者 Mirjalili 等人于2014年提出来的一种群智能优…

Shell高级——Linux中的文件描述符的本质(数组的下标)

以下内容源于C语言中文网的学习与整理&#xff0c;非原创&#xff0c;如有侵权请告知删除。 前言 Linux中一切接文件&#xff0c;比如 C 源文件、视频文件、Shell脚本、可执行文件等&#xff0c;就连键盘、显示器、鼠标等硬件设备也都是文件。 一个 Linux 进程可以打开成百上…

第15天-商城系统架构,首页渲染三级分类及搭建域名访问商城

1.商城系统介绍 1.1.技术方案 前后端分离服务端模板渲染&#xff08;选择&#xff09;商品详情页面静态化改造&#xff08;优化&#xff09; 1.2.动静分离架构 2.模板引擎Thymeleaf 2.1.Thymeleaf介绍 官方文档&#xff1a;https://www.thymeleaf.org/doc/tutorials/3.0/usin…

今天,小灰37岁了!

人们常常说&#xff0c;35岁是互联网人的中年危机。现在&#xff0c;小灰已经跨过了中年危机&#xff0c;倒不是因为小灰财务自由了&#xff0c;而是因为今天是小灰37岁的生日。年轻时候&#xff0c;小灰总觉得30岁是一个很遥远的年龄&#xff0c;而现在&#xff0c;小灰距离40…

KingbaseES V8R6 运维系列 --单机小版本升级

​ 案例说明&#xff1a; 在KingbaseES V8R6版本提供了sys_upgrade的升级工具&#xff0c; 本案例描述了KingbaseES V8R6单机环境下数据库的小版本升级操作&#xff0c;案例涉及的版本从‘(Kingbase) V008R006C005B0041’通过sys_upgrade升级到‘ (Kingbase) V008R006C005B0054…

【Java】Java进阶学习笔记(四)—— 抽象类与接口

【Java】Java进阶学习笔记&#xff08;四&#xff09;—— 抽象类与接口一、抽象类1、抽象类的概念抽象类的定义格式2、抽象类的注意点抽象方法的介绍3、抽象类的具体作用4、抽象类实例二、接口&#xff08;一&#xff09;、接口的概念1、接口与类的区别2、接口特性3、抽象类和…

MyBatis 查出数据不一致 MyBatis返回数据和数据库查询不一致

MyBatis 查出数据不一致 MyBatis返回数据和数据库查询不一致 --- MyBatis查询到的数据 和 Sql 查询到的数据不一致 一、背景 近期工作中&#xff0c;遇到一个MyBatis查询数据不一致的问题&#xff0c;表现是&#xff1a; sql在数据库中查询出10条数据&#xff0c;mybatis 返回的…

【服务器数据恢复】VSAN数据迁移中断导致容量盘故障的数据恢复案例

VSAN简介&#xff1a; VSAN是以vSphere内核为基础开发&#xff0c;可以扩展使用的分布式存储架构。该架构在vSphere集群主机中安硬盘及闪存构建VSAN存储层&#xff0c;通过存储进行管理与控制&#xff0c;最终形成一个共享存储层。 VSAN数据存储是一个对象存储&#xff0c;以文…

MySQL事务的12连问

事务的12连问&#xff0c;相信大家看完肯定会有帮助的。 1. 什么是数据库事务&#xff1f; 事务&#xff0c;由一个有限的数据库操作序列构成&#xff0c;这些操作要么全部执行,要么全部不执行&#xff0c;是一个不可分割的工作单位。 假如A转账给B 100 元&#xff0c;先从A的…

windows@系统环境变量备份@注册表操作@reg命令行操作注册表

文章目录备份注册表&#x1f388;备份环境变量powershell函数从注册表文件还原命令行操作注册表更新某个key备份注册表&#x1f388; 完整的注册表备份可能达到500MB打开register editor 命令行里可以输入regedit.exe打开 可以局部备份 备份环境变量powershell函数 function …

JavaEE进阶第五课:SpringBoot的创建和使用

上篇文章介绍了Bean 作用域和生命周期&#xff0c;这篇文章我们将会介绍SpringBoot的创建和使用 目录1.为什么要学习StringBoot1.1什么是SpringBoot1.2SpringBoot的优点2.如何用Idea创建SpringBoot项目3.项目目录介绍和运行3.1输入Helloworld结尾1.为什么要学习StringBoot 在前…

Python 算法交易实验49 Step1 DataETL

说明 万丈高楼平地起 按照前面的规划&#xff0c;开始有序推进我的【15% 资金加速器】计划。这一步是通过某个源&#xff0c;获取分钟级数据&#xff0c;然后送到第一个ADBS。 Sniffer : 读取数据并发送到入队列。一开始我会把文件以离线形式上传到某个folder&#xff0c;所以…

VMware16安装MacOS【详细教程】

安装VMware workstation 双击安装包&#xff0c;然后一直下一步就行了。 进行VMware安装&#xff0c;一直 下一步 在输入产品密钥这一步&#xff0c;如果有查找到可用密钥就填进去&#xff0c;没有就跳过&#xff0c;进入软件后也能输入密钥的。 输入密钥。 最后一步&#xff…

MyBatis——进阶操作

resultMap xml中可以通过returnType来指定返回的对象&#xff0c;只需要一个对象名就可以返回所有的属性 但是&#xff0c;如果sql中的属性名和对象的名称不一致&#xff0c;那么就需要resultMap来指定返回的数据了 当数据库中是username&#xff0c;而对象是name时&#xf…

zabbix主机发现、zabbix下的API、服务的监控部署

文章目录前言一、zabbix主机发现1.手动添加2.自动发现3.自动注册二、zabbix api1.获取token2.使用api检索主机3.通过api删除主机4.使用api添加主机5.纯代码过程三、服务监控1.nginx监控2.mysql监控&#xff08;1&#xff09;zabbix自带mysql模板&#xff08;2&#xff09;perco…

大数据技术之Maxwell基础知识

大数据技术之Maxwell基础知识 文章目录大数据技术之Maxwell基础知识0、写在前面1、Maxwell 概述1.1 Maxwell 定义1.2 Maxwell 工作原理1.2.1 MySQL 主从复制过程1.2.2 Maxwell 的工作原理1.2.3 MySQL 的 binlog1.3 Maxwell与Cannal对比2. Maxwell 使用2.1 Maxwell 安装部署2.1.…

Semaphore类原理剖析

1.什么是Semaphore Semaphore也是Java中的同步器&#xff0c;与CountDownLatch和CyclicBarrier不同的是&#xff0c;他的内部计数器是递增的。在一开始我们不需要知道有多少个需要同步的线程&#xff0c;只需要在需要同步的地方调用acquire方法指定需要同步的线程个数。 2.Se…

【C语言】结构体进阶

一、结构体 1. 结构体的声明 &#xff08;1&#xff09; 结构的基础知识 结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量。&#xff08;2&#xff09;结构的声明 struct tag {member-list; }variable-list;例如描述一个学生&#x…

「需求分析」业务架构师需求分析技术权威指南

需求分析&#xff0c;也称为需求工程&#xff0c;是定义用户对正在构建或修改的新软件的期望的过程。在软件工程中&#xff0c;它有时被一些松散的名称所引用&#xff0c;例如需求收集或需求捕获。需求分析包括那些为一个新的或改变的产品或项目确定需要或满足的条件的任务&…

进程内存空间

我们直接用linux演示 我们将这个代码编译成目标文件&#xff0c;然后查看内容 #include <stdlib.h> #include <pthread.h> int sum(int x,int y) {return xy; }int main(){sum(2,3);return 0; }编译 我们反汇编这个目标文件 我们发现这些汇编代码不好阅读&#…