SPI驱动学习七(SPI_Slave_Mode驱动程序框架)

目录

  • 一、SPI_Slave_Mode驱动程序框架
    • 1. Master和Slave模式差别
      • 1.1 主设备 (Master)
      • 1.2 从设备 (Slave)
      • 1.3 示例
    • 2. SPI传输概述
      • 2.1 数据组织方式
      • 2.2 SPI控制器数据结构
    • 3. SPI Slave Mode数据传输过程
    • 4. 如何编写程序
      • 4.1 设备树
      • 4.2 内核相关
      • 4.3 简单的示例代码
        • 4.3.1 master和slave驱动示例
        • 4.3.2 master和slave使用示例
  • 二、SPI_Slave_Mode驱动程序源码解读(drivers/spi/spi-imx.c)
    • 1. 设备树
    • 2. 控制器驱动程序
      • 2.1 分配一个spi_controller
      • 2.2 设置spi_controller
      • 2.3 注册spi_controller
      • 2.4 硬件操作
    • 3. 设备驱动程序
      • 3.1 Master模式
      • 3.2 Slave模式

一、SPI_Slave_Mode驱动程序框架

  • 参考内核源码: Linux-5.x\drivers\spi\spi-imx.c
  • 注意:Linux 4.9的内核未支持SPI Slave Mode
  • 参考文档:《Linux_as_an_SPI_Slave_Handouts.pdf》

1. Master和Slave模式差别

  SPI(串行外设接口,Serial Peripheral Interface)是一种常用的同步串行通信协议,用于微控制器与各种外设(如传感器、存储器、显示器等)之间的通信。在 SPI 通信中,通常有两个角色:主设备(Master)和从设备(Slave)。这两者之间的区别如下:

1.1 主设备 (Master)

  • 定义:主设备负责控制 SPI 通信,生成时钟信号,并管理从设备的选择。

  • 功能

    • 时钟产生:主设备生成 SPI 时钟信号(SCK),并控制数据传输的速率。
    • 选择从设备:通过选择线(如 Chip Select,CS)选择与其通信的特定从设备。通常在传输数据前,主设备会将 CS 线拉低以使对应的从设备准备接收数据。
    • 数据发送和接收:主设备负责读取从设备发送的数据,并向从设备发送命令和数据。
  • 例子:微控制器通常充当主设备,与多个传感器或存储器从设备通信。

1.2 从设备 (Slave)

  • 定义:从设备被动地响应主设备的命令。它不会主动启动通信,而是等待主设备进行操作。

  • 功能

    • 响应主设备:从设备在主设备的指令下工作,并在主设备发送时钟信号时提供数据。
    • 数据接收和发送:从设备接收来自主设备的数据,并将其处理后,发送响应或数据回主设备。
  • 例子:传感器、EEPROM、LCD 显示屏等通常作为从设备,实现数据的接收和响应。

1.3 示例

以IMX6ULL为例,Master和Slave模式的异同如下图:

  • 初始化

    • 使用的SPI寄存器有所不同
    • 引脚有所不同:Master模式下SS、CLK引脚是输出引脚,Slave模式下这些引脚是输入引脚
  • 准备数据:都需要准备好要发送的数据,填充TX FIFO

  • 发起传输:Master模式下要主动发起传输,等待发送完成(等待中断)

  • 使能接收中断:Slave模式下无法主动发起传输,只能被动地等待,使能接收中断即可

  • 中断函数里:读取RXFIFO得到数据

  • 传输完成

  • 在这里插入图片描述

2. SPI传输概述

2.1 数据组织方式

  使用SPI传输时,最小的传输单位是"spi_transfer",对于一个设备,可以发起多个spi_transfer,这些spi_transfer,会放入一个spi_message里。
在这里插入图片描述
所以,反过来,SPI传输的流程是这样的:

  • 从spi_master的队列里取出每一个spi_message
    • 从spi_message的队列里取出一个spi_transfer
      • 处理spi_transfer

2.2 SPI控制器数据结构

  参考内核文件:include\linux\spi\spi.h,Linux中SPI控制器struct spi_controller,有两套传输方法:
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/a3c374792e144463b6bb6113c558bbde.png

3. SPI Slave Mode数据传输过程

在这里插入图片描述

4. 如何编写程序

4.1 设备树

  • SPI控制器设备树节点中,需要添加一个空属性:spi-slave
  • 要模拟哪类slave设备?需要添加slave子节点,这是用来指定slave protocol
    在这里插入图片描述

4.2 内核相关

  • 新配置项:CONFIG_SPI_SLAVE

  • 设备树的解析:增加对spi-slaveslave子节点的解析

  • sysfs:新增加/sys/devices/.../CTLR/slave,对应SPI Slave handlers

  • 新API

    • spi_alloc_slave( )
    • spi_slave_abort( )
    • spi_controller_is_slave( )
  • 硬件设置不一样

    • master:SS、SCLK引脚是输出引脚
    • slave:SS、SCLK引脚是输入引脚
  • 传输时等待函数不一样

    • master:master主动发起spi传输,它可以指定超时时间,使用函数wait_for_completion_timeout() 进行等待
    • slave:slave只能被动等待传输,它无需指定超时时间,使用函数wait_for_completion_interruptible()进行等待 ,使用.slave_abort() 来取消等待

4.3 简单的示例代码

4.3.1 master和slave驱动示例

在这里插入图片描述

4.3.2 master和slave使用示例

  对于设置为spi master模式的spi控制器,下面接的是一个一个spi slave设备,我们编写各类spi slave driver,通过spi master的函数读写spi slave 设备。

  对于设置为spi slave模式的spi控制器,它是作为一个spi slave设备被其他单板的spi master设备来访问,它如何接收数据、如何提供数据?我们编写对应的spi slave handler。

  对于master和slave,有两个重要概念:

  • SPI Slave Driver:通过SPI Master控制器跟SPI Slave设备通信
  • SPI Slave Handler:通过SPI Slave控制器监听远端的SPI Master
    在这里插入图片描述

二、SPI_Slave_Mode驱动程序源码解读(drivers/spi/spi-imx.c)

1. 设备树

下图是摘自《Linux_as_an_SPI_Slave_Handouts.pdf》:
在这里插入图片描述

2. 控制器驱动程序

  在Linux 5.x版本中,SPI控制器的驱动程序仍然使用原理的名字:struct spi_master,但是它已经是一个宏:

#define spi_master			spi_controller

  这意味着它可以工作于master模式,也可以工作于slave模式。

2.1 分配一个spi_controller

在这里插入图片描述

2.2 设置spi_controller

  工作于slave模式时,跟master模式最大的差别就是如下函数不一样:

  • bitbang.txrx_bufs函数不一样
    • 在IMX6ULL中,这个函数为spi_imx_transfer,里面对master、slave模式分开处理
    • master模式:使用spi_imx_pio_transfer函数
    • slave模式:使用spi_imx_pio_transfer_slave函数
  • 增加了bitbang.master->slave_abort函数
    在这里插入图片描述
      不同厂家的SOC的SPI 驱动实现会有所不同,有的就不会用到bitbang;spi_bitbang 结构体定义了一种软件模拟SPI通信的方式,通过软件控制GPIO引脚来实现SPI通信所需的时序和信号。这种方法通常用于那些没有内置SPI硬件控制器的微控制器或系统上。

2.3 注册spi_controller

  无论是master模式,还是slave模式,注册函数时一样的:
在这里插入图片描述
  在spi_bitbang_start内部,会处理设备树中的子节点,创建并注册spi_device:

spi_bitbang_startspi_register_masterspi_register_controllerof_register_spi_devices

  对于master模式,设备树中的子节点对应真实的spi设备;但是对于slave模式,这些子节点只是用来选择对应的spi slave handler:就是使用哪个驱动来模拟spi slave设备,比如:
在这里插入图片描述

/* drivers/spi/spi-slave-time.c* SPI从机处理器,接收上一条SPI消息后报告系统运行时间** 该SPI从机处理器以二进制格式和网络字节顺序发送上一条SPI消息接收时间* 的两个32位无符号整数,分别表示自系统启动以来的秒数和小数秒数(以微秒为单位)。** 版权所有 (C) 2016-2017 Glider bvba** 本文件受 GNU 通用公共许可证条款和条件的约束。请参阅此存档主目录中的 "COPYING" 文件以获取更多详细信息。** 使用方法(假设 /dev/spidev2.0 对应远程系统上的 SPI 主设备):**   # spidev_test -D /dev/spidev2.0 -p dummy-8B*   spi 模式: 0x0*   每字的位数: 8*   最大速度: 500000 Hz (500 KHz)*   RX | 00 00 04 6d 00 09 5b bb ...*		^^^^^    ^^^^^^^^*		秒数    微秒数*/#include <linux/completion.h>
#include <linux/module.h>
#include <linux/sched/clock.h>
#include <linux/spi/spi.h>struct spi_slave_time_priv {struct spi_device *spi;struct completion finished;struct spi_transfer xfer;struct spi_message msg;__be32 buf[2];
};static int spi_slave_time_submit(struct spi_slave_time_priv *priv);/*** @brief SPI从机时间补偿函数* * 当SPI从机传输完成或者发生错误时,此函数被调用以处理相应的后续操作。主要功能包括检查上一次传输状态,* 并根据状态决定是否继续提交时间补偿处理或者终止并通知上层。* * @param arg 传递给此函数的参数,实际类型为struct spi_slave_time_priv*,用于访问SPI从机时间补偿相关的数据。*/
static void spi_slave_time_complete(void *arg)
{// 将参数转换为相应的结构体指针,以便访问私有数据struct spi_slave_time_priv *priv = arg;// 存储操作结果的变量int ret;// 检查上一次SPI从机消息的状态,如果存在错误,则不继续操作ret = priv->msg.status;if (ret)goto terminate;// 尝试提交下一次时间补偿处理,如果提交失败,则跳转到terminate块ret = spi_slave_time_submit(priv);if (ret)goto terminate;// 如果没有错误,正常返回return;terminate:// 当出现错误或者传输完成时,记录日志并通知上层处理已经完成dev_info(&priv->spi->dev, "Terminating\n");complete(&priv->finished);
}/*** 提交SPI从机时间同步请求** 该函数用于从SPI从机角度,向SPI主机请求时间同步。它会将本地时钟和剩余微秒封装成数据,* 并通过SPI异步传输的方式发送给SPI主机。时间同步对于需要在分布式系统中保持时间一致性的* 应用非常关键。** @param priv 指向SPI从机时间私有数据结构的指针,包含必要的传输数据和配置信息。* @return 返回spi_async调用的结果,如果失败则记录错误信息。*/
static int spi_slave_time_submit(struct spi_slave_time_priv *priv)
{// 剩余的微秒部分u32 rem_us;// 返回值int ret;// 时间戳,以纳秒为单位u64 ts;// 获取当前的本地时钟值,以纳秒为单位ts = local_clock();// 从纳秒转换到微秒,并将结果保存到rem_usrem_us = do_div(ts, 1000000000) / 1000;// 将纳秒和微秒部分分别转换为大端字节序,并存入缓冲区priv->buf[0] = cpu_to_be32(ts);priv->buf[1] = cpu_to_be32(rem_us);// 初始化SPI消息,只包含一个传输操作spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1);// 设置SPI消息的完成回调函数和上下文数据priv->msg.complete = spi_slave_time_complete;priv->msg.context = priv;// 异步发送SPI消息ret = spi_async(priv->spi, &priv->msg);// 如果发送失败,记录错误信息if (ret)dev_err(&priv->spi->dev, "spi_async() failed %d\n", ret);return ret;
}/*** spi_slave_time_probe - SPI从机时间延迟自动探测函数* @spi: 指向SPI设备结构体的指针** 本函数用于在SPI从机模式下,自动探测和调整时间延迟参数,以确保SPI通信的正确性和稳定性。* 它通过分配私有数据空间,初始化必要的结构,然后提交一个SPI传输请求来进行时间延迟探测。* 若探测过程出错,则返回相应的错误码。** 返回: 0表示成功,负值表示遇到的相应错误。*/
static int spi_slave_time_probe(struct spi_device *spi)
{struct spi_slave_time_priv *priv;int ret;/* 分配内存用于存储私有数据,包括时间延迟探测所需的各种数据 */priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);if (!priv)return -ENOMEM;/* 初始化私有数据结构中的成员 */priv->spi = spi;init_completion(&priv->finished);priv->xfer.tx_buf = priv->buf;priv->xfer.len = sizeof(priv->buf);/* 提交SPI传输请求,进行时间延迟探测 */ret = spi_slave_time_submit(priv);if (ret)return ret;/* 将私有数据结构与SPI设备驱动模型数据关联,以便后续使用 */spi_set_drvdata(spi, priv);return 0;
}/*** 从SPI设备中移除slave定时器功能* * 本函数主要用于从SPI设备中移除slave定时器功能在一些情况下,可能需要* 中止当前的SPI传输操作,并确保所有相关的定时器处理已经完成此函数通过* 调用spi_slave_abort来尝试中止当前的SPI操作,并通过等待完成来确保所有* 相关的定时器处理已经结束这对于在设备移除或者重新配置时清理资源非常有用* * @param spi 指向SPI设备结构的指针该参数是必需的,用于标识需要进行操作的SPI设备* * @return 该函数返回0,表示执行成功这里不使用其他的返回值,因为当前的实现* 不需要区分更多的错误类型* * 注意:该函数假定传入的spi参数是非空的,并且spi所指向的设备已经正确初始化并* 具有slave定时器功能*/
static int spi_slave_time_remove(struct spi_device *spi)
{// 获取与SPI设备相关的私有数据struct spi_slave_time_priv *priv = spi_get_drvdata(spi);// 尝试中止当前的SPI操作spi_slave_abort(spi);// 等待直到所有定时器相关的操作完成wait_for_completion(&priv->finished);return 0;
}static struct spi_driver spi_slave_time_driver = {.driver = {.name	= "spi-slave-time",},.probe		= spi_slave_time_probe,.remove		= spi_slave_time_remove,
};
module_spi_driver(spi_slave_time_driver);MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
MODULE_DESCRIPTION("SPI slave reporting uptime at previous SPI message");
MODULE_LICENSE("GPL v2");

2.4 硬件操作

在这里插入图片描述

3. 设备驱动程序

3.1 Master模式

在这里插入图片描述

3.2 Slave模式

  参考代码:Linux-5.x\drivers\spi\spi-slave-time.c
在这里插入图片描述

  本文章参考了韦东山老师驱动大全部分笔记,其余内容为自己整理总结而来。水平有限,欢迎各位在评论区指导交流!!!😁😁😁

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

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

相关文章

Anaconda虚拟环境默认路径在C盘怎么更改

笔者已经新建好了虚拟环境并且安装了对应库&#xff0c;输入conda env list查询发现虚拟环境竟然安装到了C盘(&#xff61;•́︿•̀&#xff61;)&#xff0c;为避免下一次创建虚拟环境出错&#xff0c;笔者现在修改默认路径置D盘&#xff08;软件安装盘&#xff09; 参考两…

二分——二分查找

题目描述 输入n个不超过109的单调不减的&#xff08;就是后面的数字不小于前面的数字&#xff09;非负整数a1,a2,…,an&#xff0c;然后进行m次询问。对于每次询问&#xff0c;给出一个整数q&#xff0c;要求输出这个数字在序列中第一次出现的编号&#xff0c;如果没有找到的话…

【Oauth2整合gateway网关实现微服务单点登录】

文章目录 一.什么是单点登录&#xff1f;二.Oauth2整合网关实现微服务单点登录三.时序图四.代码实现思路1.基于OAuth2独立一个认证中心服务出来2.网关微服务3产品微服务4.订单微服务5.开始测试单点登录 一.什么是单点登录&#xff1f; 单点登录&#xff08;Single Sign On&…

【YOLO目标检测车牌数据集】共10000张、已标注txt格式、有训练好的yolov5的模型

目录 说明图片示例 说明 数据集格式&#xff1a;YOLO格式 图片数量&#xff1a;10000&#xff08;2000张绿牌、8000张蓝牌&#xff09; 标注数量(txt文件个数)&#xff1a;10000 标注类别数&#xff1a;1 标注类别名称&#xff1a;licence 数据集下载&#xff1a;车牌数据…

小程序-生命周期与WXS脚本

生命周期 什么是生命周期 生命周期&#xff08;Life Cycle&#xff09;是指一个对象从创建 -> 运行 -> 销毁的整个阶段&#xff0c;强调的是一个时间段。 我们可以把每个小程序运行的过程&#xff0c;也概括为生命周期&#xff1a; 小程序的启动&#xff0c;表示生命…

ant design vue中带勾选表格报Tree missing follow keys: ‘undefined‘解决方法

1、这里一定要给columns和data-source设置key即可。 <div><a-table:row-selection"rowSelection":dataSource"tableList":columns"columns":scroll"{ x: 100% }":pagination"false":loading"loading"&g…

配置STM32F103的高级定时器TIM1用于PWM功能

配置STM32F103的高级定时器TIM1用于PWM功能 之前在使用stm32f103的PA9引脚复用为高级定时器TIM1_CH2&#xff0c;用它来输出PWM波时发现无法正常输出PWM波形。出现这种问题的情况一般是将PA9先初始化成了串口&#xff0c;然后又配置成PWM功能&#xff0c;这样会导致无法输出PW…

RestSharp简介

RestSharp是一个轻量级HTTP客户端库&#xff0c;主要功能是通过HTTP对远程资源进行同步异步调用&#xff0c;可将请求主体序列化为JSON或XML并反序列化相应。 请求主体的方式&#xff1a;JSON、XML和表单数据 参数类型&#xff1a;查询、URL段、标头、cookie、正文 官方的例…

C++ -- 异常

C中的异常是用于处理程序执行过程中出现的错误情况。通过异常处理&#xff0c;程序可以在遇到错误时优雅地处理这些问题&#xff0c;而不是直接崩溃。 C语言处理错误的方式 C语言传统的处理错误的方式主要有两种&#xff1a; 终止程序&#xff1a;使用如assert这样的宏来检查…

Chromium 如何禁用自动加载指定路径扩展 c++

之前文章提到过浏览器启动会从[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Google\Chrome\Extensions\ 注册表下自动搜索需要加载的扩展&#xff0c;那么如何禁用此功能呢&#xff1f; 直接看源码 chrome\browser\extensions\external_registry_loader_win.cc chrome\browser\…

制作 rootfs步骤

1. 准备工作 地平线开发板通过root访问权限&#xff0c; 在开发板中安装所有的依赖库 2. 在开发板创建 rootfs 通过地平线开发板创建一个跟文件系统(rootfs)的方法是通过 chroot 工具将文件系统打包 2.1 挂载必需的文件系统 确保挂载必要的虚拟文件系统&#xff0c;以便正…

隐藏SpringBoot自动生成的文件

第一种方法——删除 第二种方法——Settings——Editor——fail types

做数据抓取工作要如何选择ip池

选择合适的IP池对于数据抓取工作至关重要。一个优质的IP池可以提高抓取的效率和成功率&#xff0c;同时减少被目标网站封禁的风险。以下是选择IP池时需要考虑的一些关键因素&#xff1a; 1. IP类型 住宅IP&#xff1a;住宅IP通常来自真实用户&#xff0c;难以被识别为代理。它…

idea 创建多模块项目

一、新建项目&#xff0c;创建父工程 新建项目&#xff0c;选择 spring initializr 填写相关信息后提交 删除不相关的目录&#xff0c;如下 修改打包方式为 pom&#xff0c;在 pom.xml 文件中新增一行&#xff0c;如下 二、创建子模块 新增子模块 三、修改 pom 文件 修…

2024首届人工智能计量学术大会在合肥成功召开

9月25日至9月26日&#xff0c;由中国计量测试学会主办&#xff0c;北京航天计量测试技术研究所承办的首届人工智能计量学术大会在安徽省合肥市召开。本次大会以“人工智能计量助力新质生产力发展”为主题&#xff0c;汇集人工智能及其计量测试等领域院士、专家和学者&#xff0…

怎样用python+sqlalchemy获得mssql视图对应物理表关系(二)

话不多说 目标:为了实现低代码数据视图对接,有必要得到视图所对应物理表及字段名称,字段类型等 1)约束:视图中用到的物理表不能起别名,所以修改上一篇中存储过程建立语句 USE [agui_conn] GO /****** Object: StoredProcedure [dbo].[sp_GetOrdersByTimestamp] Script D…

云服务升级的兼容性测试

云服务升级后&#xff0c;用户使用的前端版本和升级服务可能存在兼容问题&#xff0c;需要进行兼容性验证。 最复杂的兼容性测试&#xff0c;是对所有支持版本都进行完整回归验证&#xff0c;但这种方式耗时耗力&#xff0c;对大多数公司来说&#xff0c;均不太可行。 从风险…

04 面部表情识别:Pytorch实现表情识别-表情数据集训练代码

总目录&#xff1a;人脸检测与表情分类 https://blog.csdn.net/whiffeyf/category_12793480.html 目录 0 相关资料1 面部表情识数据集2 模型下载3 训练 0 相关资料 面部表情识别2&#xff1a;Pytorch实现表情识别(含表情识别数据集和训练代码)&#xff1a;https://blog.csdn.n…

vscode配置Eslint后保存出现大量波浪线

解决问题&#xff1a;配置代码格式化 快捷键打开设置&#xff1a;ctrlshiftP 输入&#xff1a; format code 选择&#xff1a;

UE5 项目缓存文件删除、版本控制说明(工程目录结构)

文章目录 前言一、项目文件示例二、缓存文件删除、版本控制说明前言 我们在拷贝项目或者使用 Git 进行版本控制,如果不对文件选择性的控制,大量缓存文件会导致传输速度变慢;或者我们的项目报错了,想要删除缓存文件又不知如何下手,哪些是可删除的,哪些又是不可删除的,本…