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.mzph.cn/news/611968.shtml

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

相关文章

SpringMVC配置文件上传解析器实现文件上传项目实例

SpringMVC配置文件上传解析器实现文件上传项目实例 1、在pom.xml文件中添加相关依赖 <!--文件上传--> <dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</versio…

threejs 光带扩散动画

目录 一、创建光带 (1) 设置光带顶点 (2) 设置光带顶点透明度属性 二、光带动画 完整代码 html文件代码 js文件代码 最后展示一下项目里的效果&#xff1a; 最近项目中要求做一段光带效果动画&#xff0c;尝试着写了一下&#xff0c;下面是本次分享光带扩散动画的效果预…

C++11之智能指针

C11之智能指针 前言1、智能指针概念2. 智能指针的定义和使用2.1 auto_ptr&#xff08;C11已经抛弃&#xff09;2.2 share_ptr2.3 unique_ptr2.4 weak_ptr 前言 C程序设计中&#xff0c;动态内存的管理式通过一对运算符来完成的&#xff1a;new和delete。 使用堆内存是非常频繁…

代码随想录算法训练营第一天|数组理论基础、704二分查找、27移除元素

数组理论基础 一维数组 数组中的元素在内存空间中是连续的数组名与数组中第一个元素的地址相同&#xff08;一维数组&#xff09;数组的下标从0开始删除数组的元素其实是用后面的元素覆盖掉要删除的元素数组的长度不能改变 二维数组 二维数组是按照行存储的&#xff0c;也是…

关于json.dumps()写入文件时是utf8

json.dumps()默认情况下&#xff0c;该函数会自动处理Unicode编码。 不要直接在json.dumps()设置encodingutf-8&#xff0c;会报错 json.dumps got an unexpected keyword argument encoding 需要将json.dumps()中设置ensure_asciiFalse&#xff0c;结合open函数中的encodin…

Java解析第三方接口返回的json

在实际开发过程中&#xff0c;免不了和其他公司进行联调&#xff0c;调用第三方接口&#xff0c;这个时候我们就需要根据对方返回的数据进行解析&#xff0c;获得我们想要的字段 第一种 //这种是data里面有个list的格式 {"data": {"username": "s…

linux 网络设备驱动之报文接收

从网络上接收报文比发送它要难一些, 因为必须分配一个 sk_buff 并从一个原子性上下 文中递交给上层. 网络驱动可以实现 2 种报文接收的模式: 中断驱动和查询. 大部分驱 动采用中断驱动技术, 这是我们首先要涉及的. 有些高带宽适配卡的驱动也可能采用查询 技术; 我们在"接收…

Kali Linux——aircrack-ng无线教程

目录 一、准备 二、案例 1、连接usb无线网卡 2、查看网卡信息 3、开启网卡监听 4、扫描wifi信号 5、抓取握手包 6、强制断开连接 7、破解握手包 三、预防 一、准备 1、usb无线网卡&#xff08;笔记本也是需要用到&#xff09; 2、密码字典&#xff08;Kali 系统自带…

java句柄数过多解决办法

java句柄数过多解决办法 使用file-leak-detector指定为应用程序的javaagent&#xff0c;然后重启程序&#xff0c;通过file-leak-detector提供能力&#xff0c;可以查看句柄和线程名称的信息&#xff0c;可直接排查出是哪行代码问题 包下载地址&#xff1a;http://search.mav…

项目整体管理

整体管理之10大项目管理&#xff1a; 核心域&#xff1a;进度&#xff0c;成本&#xff0c;质量&#xff0c;范围 辅助域&#xff1a;风险&#xff0c;沟通&#xff0c;采购&#xff0c;人力资源&#xff0c;干系人 项目管理相关方&#xff1a; 招标&#xff1a;买方&#x…

Acrel-5000重点用能单位能耗在线监测系统的实际应用分析-安科瑞 蒋静

摘要&#xff1a;根据《重点用能节能办法》&#xff08;国家发展改革委等第七部委2018年15号令&#xff09;、《重点用能单位能耗在线监测系统推广建设工作方案》&#xff08;发改环资[2017]1711号&#xff09;和《关于加速推进重点用能单位能耗在线监测系统建设的通知》&#…

评估LLM在细胞数据上的实用性(1)-基本概述

基于LLM的基础模型在工业和科学领域都取得了重大进展。本报告通过八个与单细胞数据相关的下游任务的综合实验&#xff0c;评估了LLM在单细胞测序数据分析中的性能。通过将七种不同的单细胞LLM与特定任务下的baselines进行比较&#xff0c;结果发现单细胞LLMs在所有任务中可能并…

Js-基础语法(二)

运算符 赋值运算符 赋值运算符&#xff1a;对变量进行赋值的运算符 已经学过的赋值运算符&#xff1a; 将等号右边的值赋予给左边, 要求左边必须是一个容器 其他赋值运算符&#xff1a; - */% 使用这些运算符可以在对变量赋值时进行快速操作 一元运算符 众多的 JavaScrip…

固定翼仿真的切换

delta固定翼飞行器模型 接着这篇文章文章链接&#xff0c;我们对飞行器模型进行改进&#xff0c; 我们知道&#xff0c;我们打开仿真模型 gazebo --verbose zephyr_ardupilot_demo.world 我们注意这最后一个语句 <model name"zephyr_delta_wing_demo">//加载z…

图像分类任务的可视化脚本,生成类别json字典文件

1. 前言 之前的图像分类任务可视化&#xff0c;都是在train脚本里&#xff0c; 用torch中dataloader将图片和类别加载&#xff0c;然后利用matplotlib库进行可视化。 如这篇文章中&#xff1a;CNN 卷积神经网络对染色血液细胞分类(blood-cells) 在分类任务中&#xff0c;必定…

零基础学习数学建模——(一)什么是数学建模

本篇博客将详细介绍什么是数学建模。 文章目录 个人简介什么是数学建模&#xff08;一&#xff09;引例&#xff1a;高中数学里的简单线性规划问题数学建模的定义及用途数学建模的定义数学建模的用途 正确认识数学建模 个人简介 ​ 本人在本科阶段获得过国赛省一、mathorcup数…

ssm基于Web的汽车客运订票系统的设计与实现论文

毕业设计&#xff08;论文&#xff09; 汽车客运订票系统 姓 名 ______________________ 学 号 ______________________ 班 级 ______________________ 专 业 ______________________ 院 部 ______________________ 指导教师 ______________________ 年 月 日 目 录 目 录 …

Unity3d 实现直播功能(无需sdk接入)

Unity3d 实现直播功能 需要插件 :VideoCapture 插件地址(免费的就行) 原理:客户端通过 VideoCapture 插件实现推流nodejs视频流转服务进行转发,播放器实现rtmp拉流 废话不多说,直接上 CaptureSource我选择的是屏幕录制,也可以是其他源 CaptureType选择LIVE–直播形式 LiveSt…

python函数装饰器保存信息

1 python函数装饰器保存信息 python函数装饰器&#xff0c;可以通过实例属性、全局变量、非局部变量和函数属性&#xff0c;来保存被装饰函数的状态信息。 1.1 统计调用并跟踪 描述 通过装饰器统计函数调用次数&#xff0c;并且用打印来跟踪调用记录。 此装饰器用类的__ca…

02 Singleton单例

抽丝剥茧设计模式 之 Singleton单例 - 更多内容请见 目录 文章目录 一、Singleton单例二、单例模式的八种实现1、饿汉式1Java实现go实现 2、饿汉式2Java实现go实现 3、懒汉式Java实现go实现 4、懒汉式-加锁Java实现go实现 5、懒汉式-缩小加锁代码块Java实现go实现 6、懒汉式-双…