Linux学习第40天:Linux SPI 驱动实验(一):乾坤大挪移

Linux版本号4.1.15   芯片I.MX6ULL                                    大叔学Linux    品人间百味  思文短情长 


       主从工作方式完成数据交换,形象的说就是武侠中的乾坤大挪移。

        本章实验的最终目的就是驱动 I.MX6UALPHA 开发板上的 ICM-20608 这个 SPI 接口的六轴传感器,可以在应用程序中读取 ICM-20608的原始传感器数据。

        本章的思维导图如下:

一、Linux 下SPI驱动框架简介

        重点在SPI 设备驱动编写。


1、SPI主机驱动

        Linux 内核使用 spi_master 表示 SPI 主机驱动, spi_master 是个结构体:

315 struct spi_master {
316 struct device dev;
317
318 struct list_head list;
......
326 s16 bus_num;
327
328 /* chipselects will be integral to many controllers; some others
329 * might use board-specific GPIOs.
330 */
331 u16 num_chipselect;
332
333 /* some SPI controllers pose alignment requirements on DMAable
334 * buffers; let protocol drivers know about these requirements.
335 */
336 u16 dma_alignment;
337
338 /* spi_device.mode flags understood by this controller driver */
339 u16 mode_bits;
340
341 /* bitmask of supported bits_per_word for transfers */
342 u32 bits_per_word_mask;
......
347 /* limits on transfer speed */
348 u32 min_speed_hz;
349 u32 max_speed_hz;
350
351 /* other constraints relevant to this driver */
352 u16 flags;
359 /* lock and mutex for SPI bus locking */
360 spinlock_t bus_lock_spinlock;
361 struct mutex bus_lock_mutex;
362
363 /* flag indicating that the SPI bus is locked for exclusive use */
364 bool bus_lock_flag;
......
372 int (*setup)(struct spi_device *spi);
373
......
393 int (*transfer)(struct spi_device *spi,/*transfer 函数,和 i2c_algorithm 中的 master_xfer 函数一样,控制器数据传输函
数。*/
394 struct spi_message *mesg);/*transfer_one_message 函数,也用于 SPI 数据发送,用于发送一个 spi_message,
SPI 的数据会打包成 spi_message,然后以队列方式发送出去。*/
......
434 int (*transfer_one_message)(struct spi_master *master,
435 struct spi_message *mesg);
......
462 };

        SPI 主机驱动的核心就是申请 spi_master,然后初始化 spi_master,最后向 Linux 内核注册spi_master。

1)、spi_master 申请与释放

struct spi_master *spi_alloc_master(struct device *dev,
unsigned size)

dev:设备,一般是 platform_device 中的 dev 成员变量。
size: 私有数据大小,可以通过 spi_master_get_devdata 函数获取到这些私有数据。
返回值: 申请到的 spi_master。
        spi_master 的释放通过 spi_master_put 函数来完成,当我们删除一个 SPI 主机驱动的时候就
需要释放掉前面申请的 spi_master, spi_master_put 函数原型如下:

void spi_master_put(struct spi_master *master)

master:要释放的 spi_master。
 

2)、spi_master 的注册与注销

        当 spi_master 初始化完成以后就需要将其注册到 Linux 内核, spi_master 注册函数为
spi_register_master,函数原型如下:

int spi_register_master(struct spi_master *master)

master:要注册的 spi_master。
返回值: 0,成功;负值,失败。
        I.MX6U 的 SPI 主机驱动会采用 spi_bitbang_start 这个 API 函数来完成 spi_master 的注册,spi_bitbang_start 函数内部其实也是通过调用 spi_register_master 函数来完成 spi_master 的注册。
        如果要注销 spi_master 的话可以使用 spi_unregister_master 函数,此函数原型为:

void spi_unregister_master(struct spi_master *master)

        如果使用 spi_bitbang_start 注册 spi_master 的话就要使用 spi_bitbang_stop 来注销掉
spi_master。

2、SPI设备驱动

        Linux 内核使用 spi_driver 结构体来表示 spi 设备驱动。

180 struct spi_driver {
181 const struct spi_device_id *id_table;
182 int (*probe)(struct spi_device *spi);
183 int (*remove)(struct spi_device *spi);
184 void (*shutdown)(struct spi_device *spi);
185 struct device_driver driver;
186 };

spi_driver 初始化完成以后需要向 Linux 内核注册, spi_driver 注册函数为
spi_register_driver,函数原型如下:
 

int spi_register_driver(struct spi_driver *sdrv)

        注销 SPI 设备驱动以后也需要注销掉前面注册的 spi_driver,使用 spi_unregister_driver 函
数完成 spi_driver 的注销,函数原型如下:

void spi_unregister_driver(struct spi_driver *sdrv)

spi_driver 注册示例程序如下:

1 /* probe 函数 *//*第 1~36 行, spi_driver 结构体,需要 SPI 设备驱动人员编写,包括匹配表、 probe 函数等。
和 i2c_driver、 platform_driver 一样*/
2 static int xxx_probe(struct spi_device *spi)
3 {
4 /* 具体函数内容 */
5 return 0;
6 }
7 8
/* remove 函数 */
9 static int xxx_remove(struct spi_device *spi)
10 {
11 /* 具体函数内容 */
12 return 0;
13 }
14 /* 传统匹配方式 ID 列表 */
15 static const struct spi_device_id xxx_id[] = {
16 {"xxx", 0},
17 {}
18 };
19
20 /* 设备树匹配列表 */
21 static const struct of_device_id xxx_of_match[] = {
22 { .compatible = "xxx" },
23 { /* Sentinel */ }
24 };
25
26 /* SPI 驱动结构体 */
27 static struct spi_driver xxx_driver = {
28 .probe = xxx_probe,
29 .remove = xxx_remove,
30 .driver = {
31 .owner = THIS_MODULE,
32 .name = "xxx",
33 .of_match_table = xxx_of_match,
34 },
35 .id_table = xxx_id,
36 };
37
38 /* 驱动入口函数 */
39 static int __init xxx_init(void)/*第 39~42 行,在驱动入口函数中调用 spi_register_driver 来注册 spi_driver。*/
40 
41 return spi_register_driver(&xxx_driver);
42 }
43
44 /* 驱动出口函数 */
45 static void __exit xxx_exit(void)/*第 45~48 行,在驱动出口函数中调用 spi_unregister_driver 来注销 spi_driver。*/
46 {
47 spi_unregister_driver(&xxx_driver);
48 }
49
50 module_init(xxx_init);
51 module_exit(xxx_exit);

3、SPI设备和驱动匹配

        SPI 设备和驱动的匹配过程是由 SPI 总线来完成的,SPI总线为 spi_bus_type,定义在 drivers/spi/spi.c 文件中,内容如下:

131 struct bus_type spi_bus_type = {
132 .name = "spi",
133 .dev_groups = spi_dev_groups,
134 .match = spi_match_device,
135 .uevent = spi_uevent,
136 };

        SPI 设备和驱动的匹配函数为 spi_match_device,函数内容如下:

99 static int spi_match_device(struct device *dev,
struct device_driver *drv)
100 {
101 const struct spi_device *spi = to_spi_device(dev);
102 const struct spi_driver *sdrv = to_spi_driver(drv);
103
104 /* Attempt an OF style match */
105 if (of_driver_match_device(dev, drv))/*第 105 行, of_driver_match_device 函数用于完成设备树设备和驱动匹配。比较 SPI 设备节
点的 compatible 属性和 of_device_id 中的 compatible 属性是否相等,如果相当的话就表示 SPI 设
备和驱动匹配。*/
106 return 1;
107
108 /* Then try ACPI */
109 if (acpi_driver_match_device(dev, drv))/*acpi_driver_match_device 函数用于 ACPI 形式的匹配。*/
110 return 1;
111
112 if (sdrv->id_table)
113 return !!spi_match_id(sdrv->id_table, spi);/*spi_match_id 函数用于传统的、无设备树的 SPI 设备和驱动匹配过程。比较 SPI
设备名字和 spi_device_id 的 name 字段是否相等,相等的话就说明 SPI 设备和驱动匹配。*/
114
115 return strcmp(spi->modalias, drv->name) == 0;/*比较 spi_device 中 modalias 成员变量和 device_driver 中的 name 成员变量是否
相等*/
116 }

        以下内容将在下一节学习:

二、I.MX6U SPI主机驱动分析

三、SPI设备驱动编写流程

1、SPI设备信息描述

2、SPI设备数据收发处理流程

四、硬件原理图分析

五、实验程序编写

1、修改设备树

2、编写ICM20608驱动

3、编写测试APP

六、运行测试

1、编译驱动程序和测试APP

1)、编译驱动程序

2)、编译测试APP

2、运行测试

七、总结


本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023.11.13 hive数据仓库之分区表与分桶表操作,与复杂类型的运用

目录 0.hadoop hive的文档 1.一级分区表 2.一级分区表练习2 3.创建多级分区表 4.分区表操作 5.分桶表 6. 分桶表进行排序 7.分桶的原理 8.hive的复杂类型 9.array类型: 又叫数组类型,存储同类型的单数据的集合 10.struct类型: 又叫结构类型,可以存储不同类型单数据的集合…

【函数讲解】pygmo中的函数 fast_non_dominated_sorting() + 利用支配关系,学习一个SVM分类器,将解分为两类

这个函数是用来执行非支配排序的,可以分层构建Pareto,并返回每一层的解以及每个解支配其他解的索引、解被其他解支配的次数、解所在的非支配层级。这个函数对这些解进行非支配排序,并返回四个数组:ndf, dl, dc, 和 ndr。 ndf (Non…

CentOS7、CentOS8 如何修改ip信息(修改网络信息)(无图形界面)(亲测可用)

文章目录 CentOS 7方法一:使用 nmcli 命令方法二:编辑配置文件(我的CentOS7是使用这种方法,亲测可用) CentOS 8方法一:使用 nmcli 命令方法二:编辑配置文件 在 CentOS 系统中,如果你…

【论文精读】DMVSNet

今天读的是一篇发表在ICCV 2023上的文章,作者来自华中科技大学。 文章地址:点击前往 项目地址:Github 文章目录 Abstract1 Introduction2 Relative Work3 Motivation3.1 Estimated bias and interpolated bias3.2 One-sided V.S. Saddle-shap…

怎么做到高性能网络IO?

为什么要做高性能网络IO。主要是解决c10,c10M问题 最开始的时候我们走的内核协议栈,走内核协议栈其实性能比较低,因为我们之前介绍的时候需要拷贝两次 但是我们采用用户态协议栈可以少拷贝一次,可以大大提高效率, 步骤…

基于粒子群算法优化概率神经网络PNN的分类预测 - 附代码

基于粒子群算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于粒子群算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于粒子群优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要:针对PNN神经网络…

职业迷茫,我该如何做好职业规划

案例25岁男,入职2月,感觉自己在混日子,怕能力没有提升,怕以后薪资也提不起来。完全不知道应该往哪个方向进修,感觉也没有自己特别喜欢的。感觉自己特别容易多想,想多年的以后一事无成的样子。 我觉得这个案…

Tektronix(泰克)示波器TBS1102B测试电压

对于 Tektronix TBS1102B 示波器来说,测试电压的步骤基本如下: 连接测量点: 将被测电路的测量点连接到示波器的输入通道。使用正确的探头并确保连接的极性正确。 选择通道: 选择示波器上的通道,你想要测量的电压可能连…

20231112_DNS详解

DNS是实现域名与IP地址的映射。 1.映射图2.DNS查找顺序图3.DNS分类和地址4.如何清除缓存 1.映射图 图片来源于http://egonlin.com/。林海峰老师课件 2.DNS查找顺序图 3.DNS分类和地址 4.如何清除缓存

单链表按位序与指定结点 删除

按位序删除(带头结点) #define NULL 0 #include<stdlib.h>typedef struct LNode {int data;struct LNode* next; }LNode, * LinkList;//按位序删除&#xff08;带头结点&#xff09; bool ListInsert(LinkList& L, int i, int& e) {if (i < 1)return false;L…

【Spring Cloud】声明性REST客户端:Feign

Spring Cloud Feign ——fallback 服务降级 1. Feign 简介2. Feign 的基础使用2.1 普通 HTTP 请求2.2 Feign 远程调用上传文件接口 1. Feign 简介 Feign 是一个声明式的 HTTP 客户端&#xff0c;它简化了编写基于 REST 的服务间通信代码的过程。在 Spring Cloud 中&#xff0c…

如何从零开始手写一个消息中间件(从宏观角度理解消息中间件的技术原理)

如何从零开始手写一个消息中间件&#xff08;从宏观角度理解消息中间件的技术原理&#xff09; 什么是消息中间件消息中间件的作用逐一拆解消息中间件的核心技术消息中间件核心技术总览IOBIONIOIO多路复用AIOIO多路复用详细分析selectpollepoll Java中的IO多路复用 协议序列化消…

【算法每日一练]-快速幂,倍增,滑动窗口(保姆级教程 篇1) #麦森数 #青蛙跳

之前是考试准备&#xff0c;所以有几天没更新&#xff0c;今天开始继续更新 目录 快速幂模板 题目&#xff1a;麦森数 思路&#xff1a; 题目&#xff1a;青蛙跳 思路&#xff1a; 快速幂模板 #include <bits/stdc.h> #define ll long long using namespa…

.net在使用存储过程中IN参数的拼接方案,使用Join()方法

有时候拼接SQL语句时&#xff0c;可能会需要将list中的元素都加上单引号&#xff0c;并以逗号分开&#xff0c;但是Join只能简单的分开&#xff0c;没有有单引号&#xff01; 1.第一种拼接方案 List<string> arrIds new List<string>(); arrIds.Add("aa&qu…

JavaScript_动态表格_删除功能

1、动态表格_删除功能 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>动态表格_添加和删除功能</title><style>table{border: 1px solid;margin: auto;width: 100%;}td,th{text-align: …

杂记 | 使用FRP搭建内网穿透服务(新版toml配置文件,搭配反向代理食用)

文章目录 01 需求与回顾02 下载程序包03 编辑.toml文件3.1 编辑frps.toml3.2 编辑frpc.toml 04 启动服务4.1 启动服务端4.2 启动客户端 05 配置反向代理&#xff08;可选&#xff09;06 windows设置为默认启动&#xff08;可选&#xff09;6.1 创建启动脚本6.2 设置为开机自启 …

【Java 进阶篇】Java与JQuery选择器:解锁前端开发的魔法大门

在前端开发的世界中&#xff0c;选择器是我们与HTML文档进行互动的钥匙&#xff0c;而Java和JQuery则为我们提供了强大的工具&#xff0c;使得前端开发不再是一个艰深的谜题。本篇博客将围绕Java与JQuery选择器展开&#xff0c;深入解析选择器的奥秘&#xff0c;为你打开前端开…

你一定要学会的Java语法 -- 【继承】

书接上回&#xff0c;我们已经学完了类和对象&#xff0c;今天内容可能有一点难&#xff0c;相信自己能跨过这道坎。 目录 一. 继承 1.什么是继承 2. 继承的概念 3. 继承的语法 4.父类成员访问 子类和父类成员变量同名 子类和父类成员方法同名 5.super关键字 6.子类构…

003、Nvidia Jetson Nano Developer KIT(b01)-深度学习环境配置

之——深度学习环境 杂谈 网上到处淘金&#xff0c;pytorch、opencv、torchvision。 正文 1.各种依赖库 1.1 pytorch的底层依赖库 sudo apt install build-essential make cmake cmake-curses-gui -ysudo apt install git g pkg-config curl -ysudo apt install libatlas-ba…

Java图像编程之:Graphics

一、概念介绍 1、Java图像编程的核心类 Java图像编程的核心类包括&#xff1a; BufferedImage&#xff1a;用于表示图像的类&#xff0c;可以进行像素级的操作。Image&#xff1a;表示图像的抽象类&#xff0c;是所有图像类的基类。ImageIcon&#xff1a;用于显示图像的类&a…