SPI驱动(九) -- SPI_Master驱动程序

news2025/3/19 12:23:00

文章目录

  • 参考资料:
  • 一、SPI传输概述
  • 二、SPI传输的两种方法
    • 2.1 旧方法
    • 2.2 新方法


参考资料:

参考资料:

  • 参考内核源码: drivers\spi\spi.c

一、SPI传输概述

SPI控制器的作用是发起与它下面挂接的SPI设备之间的数据传输,那么控制器驱动程序的核心就是实现与设备之间的数据传输过程。在内核中,SPI传输的最小单位是spi_transfer,对于一个设备,可以发起多个spi_transfer。这些spi_transfer,会放入一个spi_message里面。每个SPI设备都有一个自己的spi_message,同一个spi_master下的spi_message,放在一个队里。

  • spi_transfer:指定tx_buf、rx_buf、len
	struct spi_transfer {
	const void	*tx_buf;
	void		*rx_buf;
	...
	}
  • 同一个SPI设备的spi_transfer,使用spi_message来管理:
struct spi_message {
	struct list_head	transfers; //管理spi_transfer
	...
}
  • 同一个SPI Master下的spi_message,放在一个队列里:
struct spi_master {
	...
	struct list_head		queue;   //存放每个spi_device的spi_message
	...
}

所以,反过来,SPI传输的流程是:

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

二、SPI传输的两种方法

spi_master结构中,有两个传输函数,函数指针transfer 代表旧方法,函数指针transfer_one代表新方法。

struct spi_master {
...
/* 旧方法 */
int	(*transfer) (struct spi_device *spi, struct spi_message *mesg);
/* 新方法 */
int (*transfer_one)(struct spi_master *master, struct spi_device *spi, struct spi_transfer *transfer);
...
}

2.1 旧方法

在这里插入图片描述
内核传输函数入口spi_sync()

int spi_sync(struct spi_device *spi, struct spi_message *message)
{
	ret = __spi_sync(spi, message);
}

继续调用__spi_sync(),里面设置了传输完成回调函数spi_complete,如果master->transfer == spi_queued_transfer表示使用新方法,新方法使用内核提供的transfer 函数,它会帮我们把spi_message放入queue并处理。else分支spi_async_locked表示的是旧方法,需要我们自己实现transfer函数,自己管理queue,自己触发传输。spi_async_locked是异步传输,触发传输后马上返回,随后wait_for_completion(&done)等待传输结果。

static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{
	DECLARE_COMPLETION_ONSTACK(done);
	int status;
	struct spi_master *master = spi->master;
	unsigned long flags;

	status = __spi_validate(spi, message);
	if (status != 0)
		return status;

	message->complete = spi_complete; //传输完成回调函数
	message->context = &done;
	message->spi = spi;

	SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_sync);
	SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync);

	/* If we're not using the legacy transfer method then we will
	 * try to transfer in the calling context so special case.
	 * This code would be less tricky if we could remove the
	 * support for driver implemented message queues.
	 */
	if (master->transfer == spi_queued_transfer) { //新方法
		spin_lock_irqsave(&master->bus_lock_spinlock, flags);

		trace_spi_message_submit(message);

		status = __spi_queued_transfer(spi, message, false);

		spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
	} else {
		status = spi_async_locked(spi, message); //老方法,异步传输
	}

	if (status == 0) {
		/* Push out the messages in the calling context if we
		 * can.
		 */
		if (master->transfer == spi_queued_transfer) {
			SPI_STATISTICS_INCREMENT_FIELD(&master->statistics,
						       spi_sync_immediate);
			SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics,
						       spi_sync_immediate);
			__spi_pump_messages(master, false);
		}

		wait_for_completion(&done); //等待传输结果
		status = message->status;
	}
	message->context = NULL;
	return status;
}

继续看spi_async_locked(), 它调用__spi_async(), 继续往下,最终调用master->transfer(spi, message),这个就是自己实现的transfer函数。

int spi_async_locked(struct spi_device *spi, struct spi_message *message)
{
	struct spi_master *master = spi->master;
	int ret;
	unsigned long flags;

	ret = __spi_validate(spi, message);
	if (ret != 0)
		return ret;

	spin_lock_irqsave(&master->bus_lock_spinlock, flags);

	ret = __spi_async(spi, message);

	spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);

	return ret;

}

static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
	struct spi_master *master = spi->master;

	message->spi = spi;

	SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_async);
	SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_async);

	trace_spi_message_submit(message);

	return master->transfer(spi, message); //需要自己实现transfer
}

2.2 新方法

在这里插入图片描述
新方法第一步会调用__spi_queued_transfer()spi_message放入队列,然后在调用__spi_pump_messages()压出数据进行处理。流程如上图。

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

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

相关文章

Linux 进程的创建、终止、等待与程序替换函数 保姆级讲解

目录 一、 进程创建 fork函数 二、进程的终止: 1. 想明白:终止是在做什么? 2.进程终止的3种情况? a.退出码是什么?存在原因?为什么int main()return 0? b.第三种进程终止的情况…

BSCAN2-1:load design

1. DFT Flow Using Tessent Shell Tessent BoundaryScan 具有一个基本的高层次流程顺序。下图展示了将 Tessent BoundaryScan 插入设计所需的高层次步骤顺序。图中的每个步骤都链接到有关可测试性设计(DFT)流程的更详细信息,包括示例。 Desi…

【css酷炫效果】纯CSS实现立体旋转立方体

【css酷炫效果】纯CSS实现立体旋转立方体 缘创作背景html结构css样式完整代码效果图 想直接拿走的老板,链接放在这里:https://download.csdn.net/download/u011561335/90492014 缘 创作随缘,不定时更新。 创作背景 刚看到csdn出活动了&am…

LLM中lora的梯度更新策略公式解析

LLM中lora的梯度更新策略公式解析 目录 LLM中lora的梯度更新策略公式解析区别如何使用LoRA代码中的参数更新方式二阶导数(如右侧公式关联的Fisher信息)的作用区别 定义与理论来源: 左公式 F ( w i ) = 1 n

Mac下Ollama安装全攻略:开启本地大模型之旅

文章目录 Mac下Ollama安装全攻略:开启本地大模型之旅一、Ollama 是什么功能特点优势应用场景 二、安装前准备(一)系统要求(二)硬件要求 三、下载安装包(一)官网下载(二)其…

线程大乱斗:从入门到精通,解锁Java并发编程的终极秘籍

目录 什么是线程? jave创建线程方式有几种? 线程中常用的方法 线程状态 多线程 解决线程安全问题 线程通信 何为并发编程? 并发执行和并行执行 线程的三个主要问题: 1、不可见性: 2、乱序性: …

Web3游戏行业报告

一,gamefi经济 什么是gamefi GameFi是一个缩写,它结合了游戏和去中心化金融(“DeFi”)这两个术语,关注的是游戏玩法如何在去中心化系统中实现货币化。对于游戏而言,只要开放了交易市场,允许玩家自由买卖,…

hibernate 自动生成数据库表和java类 字段顺序不一致 这导致添加数据库数据时 异常

hibernate 自动生成的数据库表和java类 字段顺序不一致 这导致该书写方式添加数据库数据时 异常 User user new User( null, username, email, phone, passwordEncoder.encode(password) ); return userRepository.save(user);Hibernate 默认不会保证数据库表字段的顺序与 Ja…

Cursor在内网环境配置自定义DeepSeek API

关键字 Cursor、DeepSeek、API配置、内网代理、HTTP/2 背景环境 使用Cursor集成环境开发程序。但是我使用公司的内网并不能使用cursor自带的模型,于是我就想使用DeepSeek官方的API服务。 环境:Windows 11系统 解决过程 网络检测 首先进行环境检测&am…

【初学者】解释器和脚本各是什么?有什么区别与联系?

李升伟 整理 解释器和脚本的定义 1. 解释器(Interpreter) 定义:解释器是一个程序,负责逐行读取并执行代码。它将源代码翻译成机器能理解的指令,并立即执行。特点: 逐行执行代码。适合交互式编程&#xf…

Kafka跨集群数据备份与同步:MirrorMaker运用

#作者:张桐瑞 文章目录 前言MirrorMaker是什么运行MirrorMaker各个参数的含义 前言 在大多数情况下,我们会部署一套Kafka集群来支撑业务需求。但在某些特定场景下,可能需要同时运行多个Kafka集群。比如,为了实现灾难恢复&#x…

设计模式(创建型)-抽象工厂模式

摘要 在软件开发的复杂世界中,设计模式作为解决常见问题的最佳实践方案,一直扮演着至关重要的角色。抽象工厂模式,作为一种强大的创建型设计模式,在处理创建一系列或相关依赖对象的场景时,展现出了独特的优势和灵活性。它通过提供一个创建对象的接口,让开发者能够在不指定…

观察者模式详解:用 Qt 信号与槽机制深入理解

引言 你是否曾遇到这样的需求:一个对象的状态发生变化后,希望通知其他对象进行相应的更新?比如: 新闻订阅系统:当新闻发布后,所有订阅者都会收到通知。股票行情推送:股价变化时,所…

OSWorld:开启多模态智能体的真实计算机环境革命

OSWorld:开启多模态智能体的真实计算机环境革命 在人工智能技术突飞猛进的今天,多模态智能体正逐步突破实验室的限制,试图融入人类的日常工作场景。然而,如何评估这些智能体在真实计算机环境中处理开放式任务的能力,成为学术界和产业界共同关注的难题。2024年,由xlang-ai…

LabVIEW烟气速度场实时监测

本项目针对燃煤电站烟气流速实时监测需求,探讨了静电传感器结构与速度场超分辨率重建方法,结合LabVIEW多板卡同步采集与实时处理技术,开发出一个高效的烟气速度场实时监测系统。该系统能够在高温、高尘的复杂工况下稳定运行,提供高…

强化学习基础篇二:马尔可夫决策过程

写在前面 本文是对李沐等“动手学强化学习”教程的个人阅读总结,原文链接:动手学强化学习。 第3章 马尔可夫决策过程 3.1 重要性 马尔可夫决策过程是强化学习中的基础概念,强化学习中的环境就是一个马尔可夫决策过程,与多臂老虎…

EtherCAT转profinet网关集成汽车变速箱制造生产线自动化升级

客户的汽车零部件制造商需要升级其变速箱齿轮加工生产线,面临的关键挑战是整合新引进的欧洲齿轮精密检测设备(基于EtherCAT协议)与现有使用profinet协议自动化系统通信。 企业核心控制平台基于西门子PLC,而现场各工位采用分布式I/…

tongweb7控制台无法访问

tongweb7控制台无法访问 排查 1.首先确认版本,如果版本是轻量级版本,轻量版不支持会话(session)的备份和复制、管理控制台、APM 运维工具等企业级增量功能。 2.查看端口 命令:ss -tnlp 或者netstat -tnlp 确认控制台端口是否开启 3.在conf…

【STM32】从新建一个工程开始:STM32 新建工程的详细步骤

STM32 开发通常使用 Keil MDK、STM32CubeMX、IAR 等工具来创建和管理工程。此处是 使用 Keil MDK5 STM32CubeMX 创建 STM32 工程的详细步骤。 新建的标准库工程文件已上传至资源中,下载后即可直接使用。 标准库新建 STM32 工程的基本目录结构:STD_STM…

搞定python之九----常用内置模块

本文是《搞定python》系列文章的第九篇,介绍常用的内置模块的使用。到此为止python的基础用法就彻底说完了,大家可以在此基础上学习爬虫、web处理等框架了。 本文的代码相对比较多,大家注意看代码即可。python的文档我贴出来,毕竟…