12.1SPI驱动框架

SPI硬件基础

总线拓扑结构

在这里插入图片描述

引脚含义

DO(MOSI):Master Output, Slave Input, SPI主控用来发出数据,SPI从设备用来接收数据
DI(MISO) :Master Input, Slave Output, SPI主控用来发出数据,SPI从设备用来接收数据
SCK: Serial Clock,时钟
CS:Chip Select,芯片选择引脚

SPI模式

在SPI协议中,有两个值来确定SPI的模式,分别是:
CPOL(时钟极性)表示SPI CLK的初始电平,0为电平,1为高电平
CPHA (时钟相位)即第一个还是第二个时钟沿采样数据,0为第一个时钟沿,1为第二个时钟沿

CPOL	CPHA	模式	含义
0		0		0		SPI CLK初始电平为低电平,在第一个时钟沿采样数据
0		1		1		SPI CLK初始电平为低电平,在第二个时钟沿采样数据
1		0		2		SPI CLK初始电平为高电平,在第一个时钟沿采样数据
1		1		3		SPI CLK初始电平为高电平,在第二个时钟沿采样数据

此外部分SPI控制器还可以配置数据流顺序,分别是MSB(高位在前)、LSB(低位在前)

SPI 驱动框架组成

SPI驱动框架包括以下几个部分:

  1. SPI 核心;管理 SPI 控制器驱动驱动、 SPI 设备驱动、 SPI 设备,此部分由linux提供
  2. SPI 控制器驱动:用于驱 SOC 上的SPI控制器,此部分由主控芯片厂家提供
  3. SPI 设备:用于描述 SPI 设备信息和此设备对SPI总线的配置,一般在设备树中编写。
  4. SPI 设备驱动:用于驱动 SPI 总线上的设备,一移植厂家驱动或自己编写
  5. spidev :一个通用的SPI设备驱动,提供一种用户空间访问SPI总线的功能(需要设备树支持)

SPI 控制器驱动

对象 struct spi_controller 表示一个 SPI 控制器,其核心成员如下:

	//表示继承于struct device,其中的of_node需要指定,否则无法解析设备树struct device dev;//总线编号s16 bus_num;//片选数量,从设备的片选号不能大于这个数量u16 num_chipselect;//控制器所支持的模式u32 mode_bits;//最小传输速率u32 min_speed_hz;//最大传输速率u32 max_speed_hz;//标志,表示控制器的一些特性u16 flags;//指示此控制器是否时从设备bool slave;//配置SPI控制器int (*setup)(struct spi_device *spi);//设置CS的时序void (*set_cs_timing)(struct spi_device *spi, u8 setup_clk_cycles, u8 hold_clk_cycles, u8 inactive_clk_cycles);//SPI数据包传输int (*transfer)(struct spi_device *spi, struct spi_message *mesg);int (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *transfer);//维护 queue 的自旋锁spinlock_t queue_lock;//管理 SPI 控制器需要传输的 spi_message,一个 spi_message 表示一系列需要传输的数据struct list_head queue;//SPI 控制器正在传输的 spi_messagestruct spi_message *cur_msg;//片选引脚编号列表int *cs_gpios;//片选引脚描述符列表struct gpio_desc **cs_gpiods;//是否使用gpio描述符接口控制控制片选引脚bool use_gpio_descriptors;

注册、注销SPI控制器驱动

SPI 控制器驱动的核心就是完成对 struct spi_controller 的分配和初始化,然后将其添加到系统中,如下是分配 SPI 控制器并向系统添加和删除 SPI 控制器驱动的函数:

	//分配SPI控制器struct spi_controller *spi_alloc_master(struct device *host, unsigned int size)//释放SPI控制器void spi_controller_put(struct spi_controller *ctlr)#define spi_master_put(_ctlr) spi_controller_put(_ctlr)//注册SPI控制器int spi_register_master(struct spi_controller *ctlr)int spi_register_controller(struct spi_controller *ctlr)int devm_spi_register_master(struct device *dev, struct spi_controller *ctlr);int devm_spi_register_controller(struct device *dev, struct spi_controller *ctlr);//注销SPI控制器void spi_unregister_master(struct spi_controller *ctlr)

SPI 设备和驱动

SPI 设备用 struct spi_device 表示,它用于描述一个 SPI 设备, SPI 设备的驱动用 struct spi_driver 表示,它用于描述 SPI 设备的驱动。
struct spi_device 的核心成员如下:

	//继承的device对象struct device dev;//所属控制器struct spi_controller *controller;//最大总线频率u32 max_speed_hz;//片选号,与控制器的片选列表对应u8 chip_select;//总线模式u32 mode;//片选引脚,不使用时为-ENOENTint cs_gpio;//驱动程序的名称或别名char modalias[SPI_NAME_SIZE];//强制匹配字符串const char *driver_override;

struct spi_driver 的核心成员如下:

	//ID匹配表const struct spi_device_id *id_table;//设备和驱动匹配成功执行int (*probe)(struct spi_device *spi);//设备或驱动卸载执行int (*remove)(struct spi_device *spi);//继承的device_driverstruct device_driver driver;

注册/注销 SPI 设备和驱动

可以使用如下函数注册/注销 SPI 设备:

	//注册PSI设备int spi_add_device(struct spi_device *spi);struct spi_device *spi_new_device(struct spi_controller *, struct spi_board_info *);//注销SPI设备void spi_unregister_device(struct spi_device *spi);

可以使用如下函数注册/注销 SPI 设备驱动:

	//注册SPI设备驱动int spi_register_driver(struct spi_driver *sdrv)//注销SPI设备驱动void spi_unregister_driver(struct spi_driver *sdrv)

SPI 设备和驱动匹配过程

SPI 注销基于总线设备驱动模型框架,在向 SPI 添加设备或驱动时会执行 SPI 总线的设备去匹配函数,其函数内容如下:

	static int spi_match_device(struct device *dev, struct device_driver *drv){const struct spi_device	*spi = to_spi_device(dev);const struct spi_driver	*sdrv = to_spi_driver(drv);//采用driver_override进行强制匹配if (spi->driver_override)return strcmp(spi->driver_override, drv->name) == 0;//设备树匹配if (of_driver_match_device(dev, drv))return 1;//ACPI匹配if (acpi_driver_match_device(dev, drv))return 1;//id_table匹配if (sdrv->id_table)return !!spi_match_id(sdrv->id_table, spi);//采用驱动程序的名称匹配return strcmp(spi->modalias, drv->name) == 0;}

SPI 控制器注册过程

构造并初始化一个 struct spi_controller 对象调用 spi_register_controller 注册 SPI 控制器使用 spi_controller_check_ops 检查必要参数是否配置正确通过 idr_alloc 将 SPI 控制器放入到一个 idr 对象中(如果 bus_num 无效会尝试从设备树中获取)初始化 struct spi_controller 各种资源从设备树中解析片选引脚,这里根据配置选择gpio编号模式和gpio描述符模式,若选择个屁哦编号模式在调用 spi_register_controller 函数前对相应引脚进行请求操作使用 device_add 将 struct spi_controller 的 dev 添加到内核若未提供 transfer 函数,但是提供了 transfer_one 函数或者 transfer_one_message 函数则调用 spi_controller_initialize_queue 函数使能队列参数模式将 struct spi_controller 对象的 transfer 设置为 spi_queued_transfer初始化 SPI 队列模式相关资源将 struct spi_controller 放入 spi_controller_list 链表中调用 of_register_spi_devices 解析 SPI 设备树遍历 spi 控制器的设备树子节点,并调用 of_register_spi_device 完成一个子节点的解析使用 spi_alloc_device 分配一个 SPI 设备使用 of_modalias_node 读取设备树的 compatible 来配置 SPI 设备的 modalias 参数使用 of_spi_parse_dt 解析设备树,并根据设备树配置 SPI 设备使用 spi_add_device 将设备注册到 SPI 总线检查片选是否合法调用 spi_dev_set_name ,利用总线名称和片选编号配置设备名称调用 bus_for_each_dev 遍历 SPI 设备,检查有设备是否存再片选相同问题(通过回调检查)绑定片选引脚调用 spi_setup 设置 SPI 设备调用 device_add 将设备加到系统(如果指定了总线会注册到对应总线,并进行设备驱动匹配,这里应该是 SPI 总线)

SPI 驱动注册过程

构造并初始化 struct spi_driver 对象调用 spi_register_driver  注册 SPI 驱动再次配置 struct spi_driver 对象,主要是内部 driver 成员的 bus 、 probe 、 remove 等调用 driver_register 注册驱动,这里指定的 bus 为 spi_bus_type 所以会注册到 spi_bus_type 总线中,然后执行设备驱动匹配操作

SPI 驱动传输数据

SPI 驱动采用 struct spi_transfer 描述一个数据包,其核心成员如下:

	//发送缓存const void *tx_buf;//接收缓存void *rx_buf;//缓存长度unsigned len;//此次传输完成后是否重新获取片选并配置unsigned cs_change;//片选无效时间u16 cs_change_delay;//cs_change_delay的单位,由us、ns、clk3种选择u8 cs_change_delay_unit;//字长u8 bits_per_word;//每个字传输完成后延迟多少usu8 word_delay_usecs;//每个字传输完成后延迟多少个时钟周期u16 word_delay;//在此传输后更改片选状态前之前延迟多少usu16 delay_usecs;//时钟速率u32 speed_hz;//spi_transfer链表节点struct list_head transfer_list;

struct spi_transfer 描述的是单个数据包,在 SPI 框架中还需要利用 struct spi_message 将一个或多个数据包组合成一个 message 后才能进行发送, struct spi_message 的核心成员如下:

	//要传输的数据包链表struct list_head transfers;//所属的 SPI 设备,其中包含了 SPI 设备的各种信息,如SPI控制器、片选、模式、时钟速率等struct spi_device *spi;//传输完成回调函数,用于异步传输void (*complete)(void *context);//用于再 SPI 控制器中形成一个队列,方便控制器管理需要发送的数据struct list_head queue;

SPI 传输数据相关的函数如下:

	//初始化一个SPI messagevoid spi_message_init(struct spi_message *m)//向SPI message中添加一个数据包void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)//启动SPI,进行同步传输int spi_sync(struct spi_device *spi, struct spi_message *message)//启动SPI,进行异步传输,传输完成后调用void (*complete)(void *context)函数int spi_async(struct spi_device *spi, struct spi_message *message)

SPI 传输数据的流程

以 spi_sync 为例, SPI 驱动框架提供了两种 spi_message 传输方案,分别如下:

  1. 阻塞模式
    在注册 SPI 控制器时如果提供了 transfer 函数即工作在阻塞模式。
构造 spi_message (发送过程可能会使用DMA)调用 spi_sync 进行传输再 spi_sync 中加锁后直接调用 __spi_sync (这里采用的互斥量加锁)调用 __spi_validate 检查 spi_message为 spi_message 绑定传输完成回调函数和 SPI 控制器检查 spi_controller 的 transfer 是否等于 spi_queued_transfer 函数,若不等于则调用 spi_async_locked 函数然后在 spi_async_locked 中加锁后调用 __spi_async (这里采用的自旋锁加锁)通过 spi_controller 控制器中的 transfer 指针调用驱动层的传输函数(应该是一个非阻塞函数,在控制器参数完成后调用 spi_message 传输完成回调函数)调用 wait_for_completion 等待传输完成获取传输结果并返回
  1. 队列模式
    在注册 SPI 控制器时不提供 transfer 函数,但提供了 transfer_one 函数,即工作在队列模式。
构造 spi_message ,因为发送过程可能会使用DMA,所以内存最好使用 kmalloc 分配调用 spi_sync 进行传输再 spi_sync 中加锁后直接调用 __spi_sync (这里采用的互斥量加锁)调用 __spi_validate 检查 spi_message为 spi_message 绑定传输完成回调函数和 SPI 控制器检查 spi_controller 的 transfer 是否等于 spi_queued_transfer ,等于则利用自旋锁加锁,然后调用 __spi_queued_transfer 将 spi_message 放入 spi_controller 的队列中调用 __spi_pump_messages 启动传输通过 spi_controller 的 cur_msg 判断是否正在传输,若正在传输则直接返回从 spi_controller 中取出的一个 spi_message ,并将其从队列中删除调用 spi_controller 控制器中的 transfer_one_message 传输一个 spi_message (默认的传输函数是 spi_transfer_one_message )在 spi_transfer_one_message 函数中多次调用 spi_controller 的 transfer_one 去完成一个 spi_message 的传输(每次调用 transfer_one 后还需要调用 spi_transfer_wait 去等待transfer_one 传输完成)传输完成后再调用 spi_finalize_current_message在 spi_finalize_current_message 中启动一个内核线程传输剩余的 spi_message ,然后执行当前 spi_message 的传输完成回调函数调用 wait_for_completion 函数等待 spi_message 传输完成获取传输结果并返回

SPI 设备树节点编写

在那条 SPI 总线下挂载设备就在那条总线的设备树节点下添加对应设备的子节点,节点命名规则 [标签:]名称[@地址],节点内容必须包含 reg 属性、 compatible 属性、 spi-max-frequency 属性, reg 属性用于描述片选索引, compatible 属性用于设备和驱动的匹配, spi-max-frequency 用于描述设备可支持的最大 SPI 总线频率,如下是在 SPI1 中添加一个 icm20608 设备节点的示例:

&spi1 {//描述SPI控制器引脚和片选引脚,并使能I2C控制器pinctrl-names = "default", "sleep";pinctrl-0 = <&spi1_pins_a>;pinctrl-1 = <&spi1_sleep_pins_a>;cs-gpios = <&gpioz 3 GPIO_ACTIVE_LOW>;status = "okay";//描述icm20608设备spidev: icm20608@0 {compatible = "alientek,icm20608";reg = <0>;						/* CS #0 */spi-max-frequency = <8000000>;	/* 最大时钟频率 */spi-cpha;						/* cpha=1 */spi-cpol;						/* cpol=1 */spi-cs-high;					/* 片选高电平有效 */spi-lsb-first;					/* 低位在前 */spi-rx-bus-width = <1>;			/* 接收数据线宽度为1B */spi-tx-bus-width = <1>;			/* 发送数据线宽度为1B */};
};

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

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

相关文章

大气精美网站APP官网HTML源码

源码介绍 大气精美网站APP官网源码&#xff0c;好看实用&#xff0c;记事本修改里面的内容即可&#xff0c;喜欢的朋友可以拿去研究 下载地址 蓝奏云&#xff1a;https://wfr.lanzout.com/itqxN1ko2ovi CSDN免积分下载&#xff1a;https://download.csdn.net/download/huayu…

报错解决方法——http404(Spring MVC)

一.检查静态资源是否加载成功 成功的标志就是在项目跑起来之后再target文件夹中的classes文件夹中可以找到自己写的配置文件。 1.查看resources文件夹是否被识别为资源文件夹 如图所示&#xff0c;文件夹图标右下角有三条杠代表被识别为资源文件 2.在pox.xml文件中插入如下…

FineBI实战项目一(4):指标分析之每日订单总额/总笔数

1 明确数据分析目标 统计每天的订单总金额及订单总笔数 2 创建用于保存数据分析结果的表 use finebi_shop_bi;create table app_order_total(id int primary key auto_increment,dt date,total_money double,total_cnt int ); 3 编写SQL语句进行数据分析 selectsubstring(c…

Redis内存策略:「过期Key删除策略」+ 「内存淘汰策略」

Redis之所以性能强&#xff0c;最主要的原因就是基于内存存储&#xff0c;然而单节点的Redis其内存大小不宜过大&#xff0c;否则会影响持久化或主从同步的性能。 Redis内存满了&#xff0c;会发生什么&#xff1f; 在Redis的运行内存达到了某个阈值&#xff0c;就会触发内存…

Docker-Compose部署Redis(v7.2)主从模式

文章目录 一、前提准备1. redis配置文件2. 下载redis镜像3. 文件夹结构 二、docker-compose三、主从配置1.主节点配置文件2.从节点配置文件 四、运行五、测试 环境 docker desktop for windows 4.23.0redis 7.2 一、前提准备 1. redis配置文件 因为Redis 7.2 docker镜像里面…

用户管理第一节课,阿里生成代码包

鱼皮教程生成所用到的 一、网址 网址&#xff1a; Cloud Native App Initializer (aliyun.com) 二、仿照生成 2.1 Maven Project & Java 2.2 Spring Boot版本 2.3 高级选项 2.3.1 项目名称可根据需求改 注意&#xff1a;不要有空格 2.4 应用架构 选择&#xff1a;单…

RabbitMQ高级

文章目录 一.消息可靠性1.生产者消息确认2.消息持久化3.消费者确认4.消费者失败重试 MQ的一些常见问题 1.消息可靠性问题:如何确保发送的消息至少被消费一次 2.延迟消息问题:如何实现消息的延迟投递 3.高可用问题:如何避免单点的MQ故障而导致的不可用问题 4.消息堆积问题:如…

统一网关 Gateway【微服务】

文章目录 1. 前言2. 搭建网关服务3. 路由断言工厂4. 路由过滤器4.1 普通过滤器4.2 全局过滤器4.3 过滤器执行顺序 5. 跨域问题处理 1. 前言 通过前面的学习我们知道&#xff0c;通过 Feign 就可以向指定的微服务发起 http 请求&#xff0c;完成远程调用。但是这里有一个问题&am…

力扣最热一百题——只出现一次的数字

这个合集已经很久没有更新了&#xff0c;今天来更新更新~~~ 目录 力扣题号 题目 题目描述 示例 提示 题解 Java解法一&#xff1a;Map集合 Java解法二&#xff1a;位运算 C位运算代码 力扣题号 136. 只出现一次的数字 - 力扣&#xff08;LeetCode&#xff09; 下述题…

Linux 常用指令汇总

Linux 常用指令汇总 文章目录 Linux 常用指令汇总[toc]前言一、文件目录指令pwd 指令ls 指令cd 指令mkdir 指令rmdir 指令tree 指令cp 指令rm 指令mv 指令cat 指令more 指令less 指令head 指令tail 指令echo 指令> 指令>> 指令 二、时间日期指令date 指令cal 指令 三、…

MySQL语法及IDEA使用MySQL大全

在项目中我们时常需要写SQL语句&#xff0c;或简单的使用注解直接开发&#xff0c;或使用XML进行动态SQL之类的相对困难的SQL&#xff0c;并在IDEA中操控我们的SQL&#xff0c;但网上大都图方便或者觉得太简单了&#xff0c;完全没一个涵盖两个方面的讲解。 单表&#xff1a; …

[C#]winform部署PaddleDetection的yolo印章检测模型

【官方框架地址】 https://github.com/PaddlePaddle/PaddleDetection.git 【算法介绍】 PaddleDetection 是一个基于 PaddlePaddle&#xff08;飞桨&#xff09;深度学习框架的开源目标检测工具库。它提供了一系列先进的目标检测算法&#xff0c;包括但不限于 Faster R-CNN, …

音量控制软件sound control mac功能亮点

sound control mac可以帮助用户控制某个独立应用程序的音量&#xff0c;通过每应用音量&#xff0c;均衡器&#xff0c;平衡和音频路由独立控制每个应用的音频&#xff0c;还有整个系统的音量。 sound control mac功能亮点 每个应用程序的音量控制 独立控制应用的数量。 键盘音…

接口功能测试策略

由于平台服务器是通过接口来与客户端交互数据提供各种服务&#xff0c;因此服务器测试工作首先需要进行的是接口测试工作。测试人员需要通过服务器接口功能测试来确保接口功能实现正确&#xff0c;那么其他测试人员进行客户端与服务器结合的系统测试过程中&#xff0c;就能够排…

【现代密码学】笔记3.4-3.7--构造安全加密方案、CPA安全、CCA安全 《introduction to modern cryphtography》

【现代密码学】笔记3.4-3.7--构造安全加密方案、CPA安全、CCA安全 《introduction to modern cryphtography》 写在最前面私钥加密与伪随机性 第二部分流加密与CPA多重加密 CPA安全加密方案CPA安全实验、预言机访问&#xff08;oracle access&#xff09; 操作模式伪随机函数PR…

ChatGPT知名开源项目有哪些

ChatGPT-Next-Web&#xff1a;基于ChatGPT API的私有化部署网页聊天系统 主要功能&#xff1a; 只需在 1 分钟内即可在 Vercel 上一键免费部署&#xff0c;支持私有服务器快速部署&#xff0c;支持使用私有域名支持ChatGPT3.5、4等常见模型Linux/Windows/MacOS 上的紧凑型客户…

数据库开发工具Navicat Premium 15 mac软件特色

Navicat Premium 15 mac版是一款数据库开发工具&#xff0c;Navicat Premium 15 Mac版可以让你以单一程序同時连接到 MySQL、MariaDB、SQL Server、SQLite、Oracle 和 PostgreSQL 数据库。 Navicat Premium mac软件特色 无缝数据迁移 数据传输&#xff0c;数据同步和结构同步…

Mysql InnoDB行锁深入理解

Record Lock记录锁 Record Lock 称为记录锁&#xff0c;锁住的是一条记录。而且记录锁是有 S 锁和 X 锁之分的&#xff1a; 当一个事务对一条记录加了 S 型记录锁后&#xff0c;其他事务也可以继续对该记录加 S 型记录锁&#xff08;S 型与 S 锁兼容&#xff09;&#xff0c;…

唠一唠Java线程池

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;咱们今天来聊聊Java线程池&#xff0c;如果没有线程池&#xff0c;每个线程都需要手动创建和销毁线程&#xff0c;那将是多么低效和耗资源啊&#xff01; 线程池的核心作用就是复用已创建的线程&#xff0c;减少…

固乔快递查询助手:批量、快速、全面的快递信息查询软件

在快递行业飞速发展的今天&#xff0c;如何高效、准确地掌握快递信息成为了很多人的需求。而固乔快递查询助手正是解决这一难题的利器。 固乔快递查询助手是一款专注于快递信息查询的软件&#xff0c;支持多家主流快递公司查询。用户只需输入单号&#xff0c;即可快速查询到实时…