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

news2024/9/28 22:32:25

目录

  • 一、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
在这里插入图片描述

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

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

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

相关文章

测试用例的进阶二

1. 按开发阶段划分 1.1 测试金字塔 从上到下&#xff0c;对于测试人员代码就是要求越来越低&#xff1b; 从下到上&#xff0c;越来越靠近用户&#xff1b; 从下到上&#xff0c;定位问题的成本越来越高&#xff1b; 1.2 单元测试(Unit Testing) 单元测试是对软件组成单元进…

如何使用ssm实现北关村基本办公管理系统的设计与实现

TOC ssm721北关村基本办公管理系统的设计与实现jsp 第一章 绪论 1.1 选题背景 目前整个社会发展的速度&#xff0c;严重依赖于互联网&#xff0c;如果没有了互联网的存在&#xff0c;市场可能会一蹶不振&#xff0c;严重影响经济的发展水平&#xff0c;影响人们的生活质量。…

终端AI大变身:大模型普惠时代的“魔法钥匙”

当AI遇见你的手机&#xff0c;日常秒变科幻片&#xff01; 嘿&#xff0c;小伙伴们&#xff01;想象一下&#xff0c;你早晨醒来&#xff0c;不是先摸手机看时间&#xff0c;而是手机先跟你打招呼&#xff1a;“早安&#xff0c;主人&#xff0c;今天天气不错&#xff0c;适合晨…

支付宝远程收款跳转码接口api之工作证跳转收款码

1、在制作工作证跳转收款之前需要在支付宝上开通工作证 2、然后获取支付宝账户信息、收款码等信息 3、将所需信息填入如下代码之中 const axios require(axios); const authCode 从客户端接收到的授权码;axios({method: post,url: https://openapi.alipay.com/alipay.syst…

前缀和(包括一维和二维)

前缀和 什么是前缀和&#xff1f;用在哪里&#xff1f;有什么好处&#xff1f; 前缀和是在反复求一个序列中不同区间处的元素之和。 例如有以下一个数组&#xff1a;1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5 我们要求a[2]~a[4]&#xff08;不包括a[2]&#xff0…

五、人物持有武器攻击

一、手部添加预制体&#xff08;武器&#xff09; 1、骨骼&#xff08;手&#xff09; 由于人物模型有骨骼和动画&#xff0c;在添加预制体后&#xff0c;会抓握武器 建一个预制体在手部位置 二、添加武器拖尾 下载拖尾特效 赋值特效中的代码&#xff0c;直接使用 清空里面…

计算机毕业设计 助农产品采购平台的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

代码随想录算法训练营第60天 | 1、城市间货物运输I,2、城市间货物运输II,3、城市间货物运输III

目录 1、城市间货物运输I 2、城市间货物运输II 3、城市间货物运输III 1、城市间货物运输I 题目描述 某国为促进城市间经济交流&#xff0c;决定对货物运输提供补贴。共有 n 个编号为 1 到 n 的城市&#xff0c;通过道路网络连接&#xff0c;网络中的道路仅允许从某个城市单…

opencv:实现图像的自动裁剪与优化

随着计算机视觉技术的发展&#xff0c;图像处理已成为一项重要的技能。今天&#xff0c;我们将探讨如何使用Python中的OpenCV库来实现对图像的自动裁剪以及一些基本的图像优化技巧。我们的目标是对一张发票图片进行处理&#xff0c;使其更加清晰且便于阅读。 准备工作 首先&a…

【Matlab元胞自动机】《高速公路人工—自动驾驶混行交通流临界特征研究》

一、项目介绍 高速公路是交通流领域研究的重点&#xff0c;自动驾驶车辆的介入势必会对高速公路交通流 产生影响。本文从基础交通流理论研究出发&#xff0c;在三相交通流理论框架下拟定人工-自动 驾驶混行交通流模型规则&#xff0c;进而通过模拟仿真分析自动驾驶车辆对高速公…

AIGC学习笔记—minimind详解+训练+推理

前言 这个开源项目是带我的一个导师&#xff0c;推荐我看的&#xff0c;记录一下整个过程&#xff0c;总结一下收获。这个项目的slogan是“大道至简”&#xff0c;确实很简。作者说是这个项目为了帮助初学者快速入门大语言模型&#xff08;LLM&#xff09;&#xff0c;通过从零…

如何使用ssm实现航空信息管理系统+vue

TOC ssm728航空信息管理系统vue 第一章 课题背景及研究内容 1.1 课题背景 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是…

Cpp::STL—string类的使用与理解(上)(8)

文章目录 前言一、string类对象的构造函数string()string(const char* s)string(size_t n, char c)string(const string& s)string(const string& str&#xff0c;size_t pos&#xff0c;size_t len npos) 二、string类对象的容量操作size与lengthcapacitycapacity返回…

将CSS OKLCH颜色转换为十六进制HEX颜色和十六进制整数格式

我查找了全网都查不到OKLCH&#xff08;&#xff09;方法是颜色转换方法&#xff0c;那今天小编就给大家分享我的方法&#xff0c;可能会有点点误差&#xff0c;但是大体不影响。 程序员必备宝典https://tmxkj.top/#/示例&#xff1a;oklch(0.253267 0.015896 252.418) 得到H…

Go基础学习08-并发安全型类型-通道(chan)深入研究

文章目录 chan基础使用和理解通道模型&#xff1a;单通道、双通道双向通道单向通道单向通道的作用 非缓冲通道 通道基本特性通道何时触发panicChannel和Select结合使用Select语句和通道的关系Select语句的分支选择规则有那些Select和Channel结合使用案例一Select和Channel结合使…

Java底层并发:线程、volatile

在Java的并发编程中&#xff0c;线程、volatile关键字、原子性、临界区以及DelayQueue是一些重要概念。理解这些内容对于编写高效且线程安全的程序至关重要。 1. 线程的基本概念 Java中的线程是程序执行的最小单位。Java提供了多种创建线程的方式&#xff0c;最常用的方式是继…

英特尔终于找到了Raptor Lake处理器崩溃与不稳定问题的根源

技术背景 在过去的几个月里&#xff0c;一些用户报告称他们的第13代和第14代Intel Core“Raptor Lake”处理器遇到了系统崩溃和不稳定的情况。这些问题最初在2024年7月底被英特尔识别出来&#xff0c;并且初步的诊断显示&#xff0c;这些问题与微码有关&#xff0c;该微码使CP…

【JavaEE】——各种“锁”大总结

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯&#xff0c; 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01;希望本文内容能够帮助到你&#xff01; 目录 一&#xff1a;乐观锁和悲观锁 1&#xff1a;乐观锁 2&#xff1a;悲观锁 3&#xff1a;总结 二&am…

人工智能实战用折线图解读产业GDP发展态势

内容提要 项目分析项目实战 一、项目分析 1、问题提出 我们拿到一大堆关于GDP的数据&#xff0c;如何从这些表面看起来杂乱无章的数据中解读出一些有价值的信息呢? 显然&#xff0c;如果能将这些数据以图形的方式展现出来&#xff0c;例如将这些数据值随时间&#xff08;…

备考中考的制胜法宝 —— 全国历年中考真题试卷大全

在中考这场重要的战役中&#xff0c;每一分都至关重要。为了帮助广大考生更好地备考&#xff0c;我们精心整理了这份全国历年中考真题试卷大全&#xff0c;旨在为大家提供最全面、最权威的备考资料。 文章目录 1. 全科覆盖&#xff0c;无遗漏2. 历年真题&#xff0c;权威可靠3.…