Linux驱动开发:SPI驱动开发原理

前言

本文章是根据韦东山老师的教学视频整理的学习笔记https://video.100ask.net/page/1712503

SPI 通信协议采用同步全双工传输机制,拓扑架构支持一主多从连接模式,这种模式在实际应用场景中颇为高效。其有效传输距离大致为 10m ,传输速率处于 1 - 70Mbps 区间,能够满足多种场景的速率要求。SPI 采用大端传输方式,电气类型为 TTL 电平标准,在常见数字电路中具有良好的兼容性。

一、SPI硬件框架

SPI主要由四条信号线组成,分别是 SCK、MOSI、MISO 和 SS。

1、串行时钟线(SCK)

  • 功能:SCK 是由主设备产生的时钟信号,用于同步主设备和从设备之间的数据传输。主设备通过控制 SCK 的频率来决定数据传输的速率,每一个时钟脉冲对应一位数据的传输。
  • 工作原理:在时钟信号的上升沿或下降沿,主设备和从设备会进行数据的发送和接收操作。具体是上升沿还是下降沿进行数据操作,取决于 SPI 的工作模式(由时钟极性 CPOL 和时钟相位 CPHA 决定)。

2、主输出从输入线(MOSI)

  • 功能:MOSI用于主设备向从设备发送数据。。当主设备需要向从设备传输数据时,它会将数据通过 MOSI 线逐位发送出去,从设备在相应的时钟信号控制下接收这些数据。
  • 工作原理:在 SCK 的有效沿(上升沿或下降沿),主设备将待发送的数据位放到 MOSI 线上,从设备在同一时刻读取该线上的数据位。这样,随着 SCK 时钟信号的不断变化,主设备就可以将数据依次发送给从设备。

3、主输入从输出线(MISO)

  • 功能:MISO 用于从设备向主设备发送数据。当从设备需要将数据反馈给主设备时,它会将数据通过 MISO 线逐位发送出去,主设备在相应的时钟信号控制下接收这些数据。
  • 工作原理:与 MOSI 类似,在 SCK 的有效沿,从设备将待发送的数据位放到 MISO 线上,主设备在同一时刻读取该线上的数据位。通过这种方式,从设备可以将数据传输给主设备。

4、从设备选择线(SS) 

  • 功能:SS 线用于主设备选择要与之通信的从设备。SPI 支持一主多从的拓扑结构,主设备通过拉低特定从设备的 SS 线来选中该从设备,使其进入工作状态,准备进行数据传输。
  • 工作原理:在 SPI 通信中,每个从设备都有一个独立的 SS 线,当主设备需要与某个从设备通信时,将该从设备的 SS 线拉低(通常为低电平有效),其他从设备的 SS 线保持高电平。被选中的从设备会响应主设备的时钟信号和数据传输请求,而未被选中的从设备则处于空闲状态,不参与数据传输。

二、SPI协议

SPI四种模式

时钟极性(CPOL)定义了时钟空闲状态电平

  • CPOL=0,SCK 信号在空闲状态(即没有数据传输时)保持低电平。在这种情况下,SCK 信号的有效沿为上升沿(从低电平到高电平的跳变),数据通常在上升沿被采样。
  • CPOL=1,SCK 信号在空闲状态保持高电平。此时,SCK 信号的有效沿为下降沿(从高电平到低电平的跳变),数据通常在下降沿被采样。

时钟相位(CPHA)定义了数据的采集时间

  • CPHA=0,在SCK的第一个跳变沿(上升沿或下降沿)进行数据采样。
  • CPHA=1,在SCK的第二个跳变沿(上升沿或下降沿)进行数据采样。

表1 SPI四种工作模式

模式CPOLCPHA描述
mode000
mode101
mode210
mode311

三、SPI总线设备驱动模型

1、平台总线驱动模型

先来回顾一下平台总线驱动模型。

平台总线驱动模型将驱动程序分成两部分:

一部分注册一个platform_driver结构体;

另一部分注册一个platform_device结构体

  • 可以使用C文件注册platform_device。
  • 也可以使用设备树创建一个节点,内核解析设备树时注册platform_device。

 

匹配机制概述 

在 Linux 内核中,platform_bus_type负责管理平台设备(platform_device)和平台驱动(platform_driver)的匹配过程。

platform_bus_type 定义了一个 match 函数,当有新的平台设备或平台驱动注册到系统时,内核会调用这个 match 函数来判断设备和驱动是否匹配。如果匹配成功,内核会调用驱动的 probe 函数来初始化设备。

platform_match源码如下所示:

static int platform_match(struct device *dev, struct device_driver *drv)
{struct platform_device *pdev = to_platform_device(dev);struct platform_driver *pdrv = to_platform_driver(drv);/* When driver_override is set, only bind to the matching driver */if (pdev->driver_override)return !strcmp(pdev->driver_override, drv->name);/* Attempt an OF style match first */if (of_driver_match_device(dev, drv))return 1;/* Then try ACPI style match */if (acpi_driver_match_device(dev, drv))return 1;/* Then try to match against the id table */if (pdrv->id_table)return platform_match_id(pdrv->id_table, pdev) != NULL;/* fall-back to driver name match */return (strcmp(pdev->name, drv->name) == 0);
}

platform_match函数会按照以下顺序来尝试匹配设备和驱动:

  1. 检查设备的driver_override字段;
  2. 尝试使用设备树(of)风格匹配;
  3. 尝试使用ACPI风格匹配;
  4. 尝试依据驱动的id_table进行匹配
  5. 若前面的匹配都失败了,就使用设备和驱动的名称进行匹配。

2、SPI数据结构

前面的硬件框架可以看出,SPI子系统涉及到2类硬件:SPI控制器、SPI设备。

在SPI总线设备驱动模型中,spi_master、spi_device和spi_driver是三个核心的数据结构和概念,它们共同协作实现对SPI设备的管理和驱动。

spi_master结构体

spi_master代表SPI主控制器,它是系统中负责管理SPI总线并与SPI从设备进行通信的硬件实体。在Linux内核中,它抽象魏一个spi_master结构体,用来描述和控制 SPI 主机控制器的各种属性与操作,里面最重要的成员是transfer函数指针。 

struct spi_master {struct device	dev;struct list_head list;/* other than negative (== assign one dynamically), bus_num is fully* board-specific.  usually that simplifies to being SOC-specific.* example:  one SOC has three SPI controllers, numbered 0..2,* and one board's schematics might show it using SPI-2.  software* would normally use bus_num=2 for that controller.*/s16			bus_num;/* chipselects will be integral to many controllers; some others* might use board-specific GPIOs.*/u16			num_chipselect;/* some SPI controllers pose alignment requirements on DMAable* buffers; let protocol drivers know about these requirements.*/u16			dma_alignment;/* spi_device.mode flags understood by this controller driver */u16			mode_bits;/* bitmask of supported bits_per_word for transfers */u32			bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))/* limits on transfer speed */u32			min_speed_hz;u32			max_speed_hz;/* other constraints relevant to this driver */u16			flags;
#define SPI_MASTER_HALF_DUPLEX	BIT(0)		/* can't do full duplex */
#define SPI_MASTER_NO_RX	BIT(1)		/* can't do buffer read */
#define SPI_MASTER_NO_TX	BIT(2)		/* can't do buffer write */
#define SPI_MASTER_MUST_RX      BIT(3)		/* requires rx */
#define SPI_MASTER_MUST_TX      BIT(4)		/* requires tx *//** on some hardware transfer / message size may be constrained* the limit may depend on device transfer settings*/size_t (*max_transfer_size)(struct spi_device *spi);size_t (*max_message_size)(struct spi_device *spi);/* I/O mutex */struct mutex		io_mutex;/* lock and mutex for SPI bus locking */spinlock_t		bus_lock_spinlock;struct mutex		bus_lock_mutex;/* flag indicating that the SPI bus is locked for exclusive use */bool			bus_lock_flag;/* Setup mode and clock, etc (spi driver may call many times).** IMPORTANT:  this may be called when transfers to another* device are active.  DO NOT UPDATE SHARED REGISTERS in ways* which could break those transfers.*/int			(*setup)(struct spi_device *spi);/* bidirectional bulk transfers** + The transfer() method may not sleep; its main role is*   just to add the message to the queue.* + For now there's no remove-from-queue operation, or*   any other request management* + To a given spi_device, message queueing is pure fifo** + The master's main job is to process its message queue,*   selecting a chip then transferring data* + If there are multiple spi_device children, the i/o queue*   arbitration algorithm is unspecified (round robin, fifo,*   priority, reservations, preemption, etc)** + Chipselect stays active during the entire message*   (unless modified by spi_transfer.cs_change != 0).* + The message transfers use clock and SPI mode parameters*   previously established by setup() for this device*/int			(*transfer)(struct spi_device *spi,struct spi_message *mesg);/* called on release() to free memory provided by spi_master */void			(*cleanup)(struct spi_device *spi);/** Used to enable core support for DMA handling, if can_dma()* exists and returns true then the transfer will be mapped* prior to transfer_one() being called.  The driver should* not modify or store xfer and dma_tx and dma_rx must be set* while the device is prepared.*/bool			(*can_dma)(struct spi_master *master,struct spi_device *spi,struct spi_transfer *xfer);/** These hooks are for drivers that want to use the generic* master transfer queueing mechanism. If these are used, the* transfer() function above must NOT be specified by the driver.* Over time we expect SPI drivers to be phased over to this API.*/bool				queued;struct kthread_worker		kworker;struct task_struct		*kworker_task;struct kthread_work		pump_messages;spinlock_t			queue_lock;struct list_head		queue;struct spi_message		*cur_msg;bool				idling;bool				busy;bool				running;bool				rt;bool				auto_runtime_pm;bool                            cur_msg_prepared;bool				cur_msg_mapped;struct completion               xfer_completion;size_t				max_dma_len;int (*prepare_transfer_hardware)(struct spi_master *master);int (*transfer_one_message)(struct spi_master *master,struct spi_message *mesg);int (*unprepare_transfer_hardware)(struct spi_master *master);int (*prepare_message)(struct spi_master *master,struct spi_message *message);int (*unprepare_message)(struct spi_master *master,struct spi_message *message);int (*spi_flash_read)(struct  spi_device *spi,struct spi_flash_read_message *msg);bool (*flash_read_supported)(struct spi_device *spi);/** These hooks are for drivers that use a generic implementation* of transfer_one_message() provied by the core.*/void (*set_cs)(struct spi_device *spi, bool enable);int (*transfer_one)(struct spi_master *master, struct spi_device *spi,struct spi_transfer *transfer);void (*handle_err)(struct spi_master *master,struct spi_message *message);/* gpio chip select */int			*cs_gpios;/* statistics */struct spi_statistics	statistics;/* DMA channels for use with core dmaengine helpers */struct dma_chan		*dma_tx;struct dma_chan		*dma_rx;/* dummy data for full duplex devices */void			*dummy_rx;void			*dummy_tx;int (*fw_translate_cs)(struct spi_master *master, unsigned cs);
};

spi_device结构体

spi_device代表一个具体的 SPI 从设备,它连接到 SPI 总线上,是 SPI 通信的目标设备。在 Linux 内核中,它抽象为一个spi_device结构体,用于描述 SPI 从设备的各种属性。

struct spi_device {struct device		dev;struct spi_master	*master;u32			max_speed_hz;u8			chip_select;u8			bits_per_word;u16			mode;
#define	SPI_CPHA	0x01			/* clock phase */
#define	SPI_CPOL	0x02			/* clock polarity */
#define	SPI_MODE_0	(0|0)			/* (original MicroWire) */
#define	SPI_MODE_1	(0|SPI_CPHA)
#define	SPI_MODE_2	(SPI_CPOL|0)
#define	SPI_MODE_3	(SPI_CPOL|SPI_CPHA)
#define	SPI_CS_HIGH	0x04			/* chipselect active high? */
#define	SPI_LSB_FIRST	0x08			/* per-word bits-on-wire */
#define	SPI_3WIRE	0x10			/* SI/SO signals shared */
#define	SPI_LOOP	0x20			/* loopback mode */
#define	SPI_NO_CS	0x40			/* 1 dev/bus, no chipselect */
#define	SPI_READY	0x80			/* slave pulls low to pause */
#define	SPI_TX_DUAL	0x100			/* transmit with 2 wires */
#define	SPI_TX_QUAD	0x200			/* transmit with 4 wires */
#define	SPI_RX_DUAL	0x400			/* receive with 2 wires */
#define	SPI_RX_QUAD	0x800			/* receive with 4 wires */int			irq;void			*controller_state;void			*controller_data;char			modalias[SPI_NAME_SIZE];int			cs_gpio;	/* chip select gpio *//* the statistics */struct spi_statistics	statistics;/** likely need more hooks for more protocol options affecting how* the controller talks to each chip, like:*  - memory packing (12 bit samples into low bits, others zeroed)*  - priority*  - drop chipselect after each word*  - chipselect delays*  - ...*/
};

spi_driver结构体

spi_driver代表 SPI 设备的驱动程序,它实现了对 SPI 从设备的具体操作和功能。在 Linux 内核中,它抽象为一个spi_driver结构体,包含了驱动程序的各种回调函数。

struct spi_driver {const struct spi_device_id *id_table;int			(*probe)(struct spi_device *spi);int			(*remove)(struct spi_device *spi);void			(*shutdown)(struct spi_device *spi);struct device_driver	driver;
};

三者关系

spi_master负责管理 SPI 总线,为spi_device提供通信的基础;spi_device代表具体的 SPI 从设备,通过spi_master注册到系统中;spi_driver则负责驱动spi_device,通过与spi_device匹配后,实现对设备的具体操作和数据传输。它们共同构成了 Linux 内核中 SPI 总线设备驱动模型的核心部分。

3、SPI驱动框架

在 Linux 系统中,SPI 控制器通常是通过平台总线驱动模型进行创建和管理的,而 SPI 设备则是基于 SPI 总线驱动模型来创建和操作的。

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

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

相关文章

Android Hilt 教程

Android Hilt 教程 —— 一看就懂,一学就会 1. 什么是 Hilt?为什么要用 Hilt? Hilt 是 Android 官方推荐的 依赖注入(DI)框架,基于 Dagger 开发,能够大大简化依赖注入的使用。 为什么要用 Hi…

【算法手记11】NC41 最长无重复子数组 NC379 重排字符串

🦄个人主页:修修修也 🎏所属专栏:刷题 ⚙️操作环境:牛客网 目录 一.NC41 最长无重复子数组 题目详情: 题目思路: 解题代码: 二.NC379 重排字符串 题目详情: 题目思路: 解题代码: 结语 一.NC41 最长无重复子数组 牛客网题目链接(点击即可跳转):NC41 最长…

C语言:字符串处理函数strstr分析

在 C 语言中,strstr 函数用于查找一个字符串中是否存在另一个字符串。它的主要功能是搜索指定的子字符串,并返回该子字符串在目标字符串中第一次出现的位置的指针。如果没有找到子字符串,则返回 NULL。 详细说明: 头文件&#xf…

在windows下安装spark

在windows下安装spark完成 安装过程:

MongoDB常见面试题总结(上)

MongoDB 基础 MongoDB 是什么? MongoDB 是一个基于 分布式文件存储 的开源 NoSQL 数据库系统,由 C 编写的。MongoDB 提供了 面向文档 的存储方式,操作起来比较简单和容易,支持“无模式”的数据建模,可以存储比较复杂…

【Java设计模式】第2章 UML急速入门

2-1 本章导航 UML类图与时序图入门 UML定义 统一建模语言(Unified Modeling Language):第三代非专利建模语言。特点:开放方法,支持可视化构建面向对象系统,涵盖模型、流程、代码等。UML分类(2.2版本) 结构式图形:系统静态建模(类图、对象图、包图)。行为式图形:事…

【4】搭建k8s集群系列(二进制部署)之安装master节点组件(kube-apiserver)

一、下载k8s二进制文件 下载地址: https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG -1.20.md 注:打开链接你会发现里面有很多包,下载一个 server 包就够了,包含了 Master 和 Worker Node 二进制文件。…

电子电气架构 --- AUTOSAR 的信息安全架构

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧! 旧人不知我近况,新人不知我过…

ROS2与OpenAI Gym集成指南:从安装到自定义环境与强化学习训练

1.理解 ROS2 和 OpenAI Gym 的基本概念 ROS2(Robot Operating System 2):是一个用于机器人软件开发的框架。它提供了一系列的工具、库和通信机制,方便开发者构建复杂的机器人应用程序。例如,ROS2 可以处理机器人不同组…

【设计模式】创建型 -- 单例模式 (c++实现)

文章目录 单例模式使用场景c实现静态局部变量饿汉式(线程安全)懒汉式(线程安全)懒汉式(线程安全) 智能指针懒汉式(线程安全)智能指针call_once懒汉式(线程安全)智能指针call_onceCRTP 单例模式 单例模式是…

C语言之九九乘法表

一、代码展示 二、运行结果 三、代码分析 首先->是外层循环是小于等于9的 然后->是内层循环是小于等于外层循环的 最后->就是\n让九九乘法表的格式更加美观(当然 电脑不同 有可能%2d 也有可能%3d) 四、与以下素数题目逻辑相似 五、运行结果

自动化备份全网服务器数据平台

自动化备份全网服务器数据平台 项目背景知识 总体需求 某企业里有一台Web服务器,里面的数据很重要,但是如果硬盘坏了数据就会丢失,现在领导要求把数据做备份,这样Web服务器数据丢失在可以进行恢复。要求如下:1.每天0…

stm32+esp8266+机智云手机app

现在很多大学嵌入式毕设都要求云端控制,本文章就教一下大家如何使用esp8266去连接机智云的app去进行显示stm32的外设传感器数据啊,控制一些外设啊等。 因为本文章主要教大家如何移植机智云的代码到自己的工程,所以前面的一些准备工作&#x…

时序数据库 TDengine Cloud 私有连接实战指南:4步实现数据安全传输与成本优化

小T导读:在物联网和工业互联网场景下,企业对高并发、低延迟的数据处理需求愈发迫切。本文将带你深入了解 TDengineCloud 如何通过全托管服务与私有连接,帮助企业实现更安全、更高效、更低成本的数据采集与传输,从架构解析到实际配…

【Java面试系列】Spring Boot中自动配置原理与自定义Starter开发实践详解 - 3-5年Java开发必备知识

【Java面试系列】Spring Boot中自动配置原理与自定义Starter开发实践详解 - 3-5年Java开发必备知识 引言 Spring Boot作为Java生态中最流行的框架之一,其自动配置机制和Starter开发是面试中的高频考点。对于3-5年经验的Java开发者来说,深入理解这些原理…

解决Spring Boot Test中的ByteBuddy类缺失问题

目录 解决Spring Boot Test中的ByteBuddy类缺失问题前奏问题描述问题解决第一步:移除ByteBuddy的特定版本号第二步:更新maven-surefire-plugin配置第三步:清理并重新构建项目 结语 解决Spring Boot Test中的ByteBuddy类缺失问题 前奏 今天&…

IntelliJ IDEA使用技巧(json字符串格式化)

文章目录 一、IDEA自动格式化json字符串二、配置/查找格式化快捷键 本文主要讲述idea中怎么将json字符串转换为JSON格式的内容并且有层级结构。 效果: 转换前: 转换后: 一、IDEA自动格式化json字符串 步骤一:首先创建一个临…

眨眼睛查看密码工具类

“眨眼睛查看密码”工具类实现思路: 一、核心功能 实现点击眼睛图标切换密码明文/星号显示,提升表单输入体验。包含以下关键功能: • 初始状态:密码框显示为星号,闭眼图标可见。 • 点击闭眼图标:切换为明…

【GPT入门】第33课 从应用场景出发,区分 TavilyAnswer 和 TavilySearchResults,代码实战

【GPT入门】第33课 从应用场景出发,区分 TavilyAnswer 和 TavilySearchResults,代码实战 1. 区别应用场景 2. 代码使用3.代码执行效果 在langchain_community.tools.tavily_search中,TavilyAnswer和TavilySearchResults有以下区别和应用场景&…

【Java设计模式】第10章 外观模式讲解

10. 外观模式 10.1 外观模式讲解 定义:为子系统提供统一接口,简化调用。类型:结构型模式适用场景: 子系统复杂需简化调用分层系统需统一入口优点: 降低耦合符合迪米特法则(最少知道原则)缺点: 扩展子系统需修改外观类,违反开闭原则10.2 外观模式 Coding // 子系统:…