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_start
    spi_register_master
    	spi_register_controller
    		of_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_us
	rem_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
在这里插入图片描述

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

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

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

相关文章

DarkLabel2.4版本导入MOT17数据集

目录 背景导入效果MOT17数据集说明DarkLabel导入视频导入gt文件 背景 做目标追踪&#xff0c;目前找了一圈开源工具&#xff0c;发现DarkLabel还是很好用的&#xff0c;提供自动目标跟踪&#xff0c;标注很方便。 由于目标追踪我用的是bytetrack&#xff0c;官网是用mot17数据…

python爬虫案例——抓取链家租房信息(8)

文章目录 1、任务目标2、分析网页3、编写代码1、任务目标 目标站点:链家租房版块(https://bj.lianjia.com/zufang/) 要求:抓取该链接下前5页所有的租房信息,包括:标题、详情信息、详情链接、价格 如: 2、分析网页 用浏览器打开链接,按F12或右键检查,进入开发者模式;因…

5.MySQL表的约束

目录 表的约束空属性&#xff08;非空约束&#xff09;默认值&#xff08;default约束&#xff09;列描述&#xff08;comment&#xff09;zerofill主键&#xff08;primary key约束&#xff09;自增长&#xff08;auto_increment&#xff09;唯一键&#xff08;unique约束&…

数据集-目标检测系列-鲨鱼检测数据集 shark >> DataBall

数据集-目标检测系列-鲨鱼检测数据集 shark >> DataBall 数据集-目标检测系列-鲨鱼检测数据集 shark 数据量&#xff1a;6k 数据样例项目地址&#xff1a; gitcode: https://gitcode.com/DataBall/DataBall-detections-100s/overview github: https://github.com/Te…

开启争对目标检测的100类数据集-信息收集

DataBall 助力快速掌握数据集的信息和使用方式。 目标检测项目数据集样例地址&#xff1a; gitcode: https://gitcode.com/DataBall/DataBall-detections-100s/overview github: https://github.com/TechLinkX/DataBall-detections-100s 请关注我们的专栏&#xff1a;DataBal…

Excel 绝对值怎么求?ABS 函数用法详解

大家好&#xff0c;这里是效率办公指南&#xff01; &#x1f4ca; ABS函数在Excel中用于计算数值的绝对值&#xff0c;这在处理财务、科学和日常办公等领域的数据时非常有用。今天&#xff0c;我们将通过一些具体的日常工作案例&#xff0c;来展示ABS函数的实际应用。 ABS函…

《深度学习》自然语言处理 统计、神经语言模型 结构、推导解析

目录 一、语言转换方法 1、如何将语言转换为模型可以直接识别的内容 1&#xff09;数据预处理 2&#xff09;特征提取 3&#xff09;模型输入 4&#xff09;模型推理 二、语言模型 1、统计语言模型 1&#xff09; 案例&#xff1a; • 运行结果&#xff1a; • 稀疏…

BAAI 团队发布多模态模型 Emu3

在人工智能的浩瀚海洋中&#xff0c;一艘名为Emu3的创新之船正在破浪前行&#xff0c;为我们展示了多模态AI的无限可能。这个由Meta AI研究团队开发的革命性模型&#xff0c;通过简单而巧妙的"下一步预测"机制&#xff0c;实现了文本、图像和视频的统一处理。 Emu3的…

linux服务器部署filebeat

# 下载filebeat curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.17.23-linux-x86_64.tar.gz # 解压 tar xzvf filebeat-7.17.23-linux-x86_64.tar.gz# 所在位置&#xff08;自定义&#xff09; /opt/filebeat-7.17.23-linux-x86_64/filebeat.ym…

k8s StorageClass 存储类

文章目录 一、概述1、StorageClass 对象定义2、StorageClass YAML 示例 二、StorageClass 字段1、provisioner&#xff08;存储制备器&#xff09;1.1、内置制备器1.2、第三方制备器 2、reclaimPolicy&#xff08;回收策略&#xff09;3、allowVolumeExpansion&#xff08;允许…

探索基于知识图谱和 ChatGPT 结合制造服务推荐前沿

0.概述 论文地址&#xff1a;https://arxiv.org/abs/2404.06571 本研究探讨了制造系统集成商如何构建知识图谱来识别新的制造合作伙伴&#xff0c;并通过供应链多样化来降低风险。它提出了一种使用制造服务知识图谱&#xff08;MSKG&#xff09;提高 ChatGPT 响应准确性和完整…

告别背锅侠!29个空场景及测试方法的实战指南

想必大家在日常的测试工作中&#xff0c;经常会碰到以下这些场景&#xff1a; 场景一&#xff1a; 测试人员&#xff1a;有一个数据为空的场景还没有验证。 研发人员&#xff1a;这个场景不会出现&#xff0c;因为没有删除逻辑。 场景二&#xff1a; 研发人员&#xff1a;…

蓝桥杯模块二:数码管的静态、动态实现

模块二训练 1.静态显示 一、数码管电路图 二、电路分析 1.数码管电路分析 端口分公共端和段码&#xff0c;先用公共端控制一个数码管&#xff0c;再用段码实现显示数字。共阳数码管公共端输入高电平&#xff0c;段码输入低电平实现点亮 2.锁存器 Y7控制段码&#xff0c;Y6控…

【含文档】基于Springboot+微信小程序 的中心医院用户移动端(含源码+数据库+lw)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统定…

全志科技发布T536高性能智慧工业芯片,飞凌嵌入式率先推出配套核心板

2024年9月24日下午&#xff0c;全志科技在中国国际工业博览会上成功举办了其最新产品——T536高性能智慧工业芯片的全球首发发布会。这款芯片采用创新的4核Cortex-A55与RISC-V混合架构&#xff0c;主频分别达到1.6GHz和600MHz&#xff0c;并集成了2TOPS算力的NPU&#xff0c;吸…

生信初学者教程(四):软件

文章目录 RRstudioLinux系统其他软件本书是使用R语言编写的教程,用户需要下载R和RStudio软件用于进行分析。 版权归生信学习者所有,禁止商业和盗版使用,侵权必究 R R语言是一种免费的统计计算和图形化编程语言,是一种用于数据分析和统计建模的强大工具。它具有丰富的统计…

耦合微带线单元的网络参量和等效电路公式推导

文档下载链接&#xff1a;耦合微带线单元的网络参量和等效电路资源-CSDN文库https://download.csdn.net/download/lu2289504634/89583027笔者水平有限&#xff0c;错误之处欢迎留言&#xff01; 一、耦合微带线奇偶模详细推导过程 二、2,4端口开路 三、2端口短路、3端口开路 四…

智能密码、指纹锁语音芯片ic方案 可存放40s语音内容 NVD语音芯片

随着科技的飞速发展&#xff0c;智能家居安全领域迎来了前所未有的变革。智能密码与指纹锁作为现代家庭安全防护的重要一环&#xff0c;其背后的语音芯片IC开发更是这一变革中的关键技术突破。 智能密码、指纹锁语音芯片ic方案 选型与简介&#xff1a; NVD语音芯片是一款低成…

quiz: python网络爬虫之规则1

下面答错了&#xff1a; B c 8A&#xff0c; 9A

STM32F407之超声波模块使用

#include "sys.h" #include "delay.h" #include "usart.h" #include "includes.h" #include "HC_SR04.h"int main() {OS_ERR err;//错误uart_init(9600);//串口初始化//超声波初始化HC_SR04();//OS初始化 他是第一个运行的函…