Linux驱动 SPI子系统

1、SPI协议

SPI(Serial Peripheral Interface)是一种同步串行数据通信协议,通常用于连接微控制器和外部设备,如传感器、存储器、显示器等。SPI协议使用四根线进行通信,包括时钟线(SCLK)、数据输入线(MISO)、数据输出线(MOSI)和片选线(SS)。

SPI通信的基本过程如下:

  • 主设备通过片选线选择要与之通信的从设备
  • 主设备通过时钟线产生时钟信号,控制数据传输的时序
  • 主设备通过数据输出线(MOSI)发送数据到从设备
  • 从设备通过数据输入线(MISO)返回响应数据到主设备

时钟线在空闲时可以有高低电平两种状态,数据在采样可以在时钟线的奇数沿或者偶数沿,前者为极性,后者为相位,一共四种组合构成了SPI通信的4种模式:

模式极性相位说明
000clk空闲为低电平,在奇数沿采样
101clk空闲为低电平,在偶数沿采样
210clk空闲为高电平,在奇数沿采样
311clk空闲为高电平,在偶数沿采样

以模式0为例,clk空闲为低电平,第一个沿为奇数沿,是从低电平变高电平的上升沿,在此处进行数据采样:

2、SPI驱动子系统

2.1 主机驱动

主机驱动一般由SOC厂商编写,本文示例使用的全志H616的内核代码,源码位于drivers/spi/spi-sunxi.c,主机驱动使用了platform总线驱动模型, platform总线的详细匹配过程可以参考之前的文章:Linux驱动(四)platform总线匹配过程,在设备树中配置好对应节点后,能够与内核配置的of_match_table匹配成功就会调用probe函数:

probe函数主要完成两个任务,一个是根据设备树的配置获取spi通信的相关参数,申请-->初始化-->注册spi_master;二是将设备树配置的子节点转化为spi_device数据结构,供后续的设备驱动使用:

2.2 设备驱动

linux内核中spi设备驱动代码位置:/drivers/spi/spidev.c。驱动的入口函数如下,正常的字符设备驱动需要经过三个步骤:register_chrdev-->class_create-->device_create;但是在入口函数中只进行了前面两步,然后调用spi_register_driver向spi总线进行了驱动注册。

static int __init spidev_init(void)
{int status;BUILD_BUG_ON(N_SPI_MINORS > 256);status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);if (status < 0)return status;spidev_class = class_create(THIS_MODULE, "spidev");if (IS_ERR(spidev_class)) {unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);return PTR_ERR(spidev_class);}status = spi_register_driver(&spidev_spi_driver);if (status < 0) {class_destroy(spidev_class);unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);}return status;
}

spi_register_driver注册驱动,其本质也是使用总线设备驱动模型,不过其使用的是spi总线,设备树中的节点转换为spi_device与驱动进行匹配,匹配成功后则会调用驱动的probe函数,其具体的匹配过程如下:

设备和驱动通过spi总线匹配成功后执行probe函数,即spidev_probe函数,其内容如下,在31行使用device_create进行字符设备创建,在前面的入口函数中已经完成register_chrdev和class_create,在此处调用device_create创建了spidevxx设备,其中第一个x表示的事第几路SPI,第二个x表示的是该SPI下的第几个片选设备(设备树<reg>属性):

static int spidev_probe(struct spi_device *spi)
{struct spidev_data	*spidev;int			status;unsigned long		minor;if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) {dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n");WARN_ON(spi->dev.of_node &&!of_match_device(spidev_dt_ids, &spi->dev));}spidev_probe_acpi(spi);spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);if (!spidev)return -ENOMEM;spidev->spi = spi;spin_lock_init(&spidev->spi_lock);mutex_init(&spidev->buf_lock);INIT_LIST_HEAD(&spidev->device_entry);mutex_lock(&device_list_lock);minor = find_first_zero_bit(minors, N_SPI_MINORS);if (minor < N_SPI_MINORS) {struct device *dev;spidev->devt = MKDEV(SPIDEV_MAJOR, minor);dev = device_create(spidev_class, &spi->dev, spidev->devt,spidev, "spidev%d.%d",spi->master->bus_num, spi->chip_select);status = PTR_ERR_OR_ZERO(dev);} else {dev_dbg(&spi->dev, "no minor number available!\n");status = -ENODEV;}if (status == 0) {set_bit(minor, minors);list_add(&spidev->device_entry, &device_list);}mutex_unlock(&device_list_lock);spidev->speed_hz = spi->max_speed_hz;if (status == 0)spi_set_drvdata(spi, spidev);elsekfree(spidev);return status;
}

在上述代码中定义了struct spidev_data    *spidev,其存储了spi设备对应的master结构体和设备本身的设备号,因此当使用open函数打开该设备节点的时候就可以反向查找到该设备对应的master结构体,进而可以使用其中的收发函数实现数据传输。

2.3 应用测试

目前测试的设备中设备树配置了两路SPI,每一路配置了一个设备:

根据上述分析,会生成两个字符设备,查看/dev下相关文件:

内核中spi驱动程序使用示例在/tools/spi/spidev_fdx.c中,重点查看do_msg接口:

static void do_msg(int fd, int len)
{struct spi_ioc_transfer	xfer[2];unsigned char		buf[32], *bp;int			status;memset(xfer, 0, sizeof xfer);memset(buf, 0, sizeof buf);if (len > sizeof buf)len = sizeof buf;buf[0] = 0xaa;xfer[0].tx_buf = (unsigned long)buf;xfer[0].len = 1;xfer[1].rx_buf = (unsigned long) buf;xfer[1].len = len;status = ioctl(fd, SPI_IOC_MESSAGE(2), xfer);if (status < 0) {perror("SPI_IOC_MESSAGE");return;}printf("response(%2d, %2d): ", len, status);for (bp = buf; len; len--)printf(" %02x", *bp++);printf("\n");
}

根据上述示例编写一个简单的测试用例,将SPI的输入和输出进行短接,测试数据发送与数据接收是否一致,测试代码如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>#include <linux/types.h>
#include <linux/spi/spidev.h>int main(int argc, char **argv)
{int fd, ret = 0;struct spi_ioc_transfer xfer = {0};unsigned char sendbuf[6] = "hello";unsigned char recvbuf[6] = {0};fd = open("/dev/spidev1.1", O_RDWR);if (fd < 0){printf("error, can't open /dev/spidev1.1\n");return 0;}xfer.tx_buf =  (unsigned long)sendbuf;xfer.rx_buf =  (unsigned long)recvbuf;xfer.len =  6;ret = ioctl(fd, SPI_IOC_MESSAGE(1), &xfer);if(ret < 0){printf("err\n");}else{printf("send data : %s\n", sendbuf);printf("recv data : %s\n", recvbuf);}return 0;
}

编译程序进行测试,测试结果正常:

3、总结

文章简单阐述了spi设备的协议和特性,结合内核代码总结了SPI驱动子系统的架构,最后编写了简单测试用例进行测试,其中设备树的详细配置项和数据收发中内核的详细处理后续再另起篇幅阐述。

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

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

相关文章

linux中的mtime,ctime,atime

目录 结论 文件 touch新文件 调整文件内容 echo直接修改 vi修改 修改文件属性 调整归属 调整权限 读取文件 目录 增加文件 调整目录下文件属性 访问目录下文件 删除文件 结论 mtime&#xff1a;文件内容的修改时间&#xff08;不含权限、属组修改&#xff09; …

2024/2/3学习记录

微信小程序 小程序中组件的分类 视图容器 view 普通视图区域&#xff0c;类似于 div 常用来实现页面的布局效果。 scroll-view 可滚动的视图区域&#xff0c;常用来实现滚动列表效果 swiper 和 swiper-item 常用 swiper 组件的常用属性 轮播图容器组件和轮播图item组件 基…

CSS:水平垂直居中

公共的 CSS 样式&#xff1a; .parent {width: 300px;height: 300px;background-color:#d0e4fe; }.child {width: 100px;height: 100px;background-color:orange; }HTML: <div class"parent"><div class"child"></div> </div>最…

新春营销活动推广策略和拓客方式

对于企业而言&#xff0c;新春不仅仅是庆祝和分享欢乐的时刻&#xff0c;更是一个黄金的营销机会&#xff0c;通过精心策划的节日促销活动来提升品牌知名度和销售业绩的绝佳时机。巧妙地将产品、服务与节日气氛相结合&#xff0c;企业能够创造出独特的购物体验&#xff0c;吸引…

LabVIEW电能质量监测系统

LabVIEW电能质量监测系统 随着全球能源需求的增加以及能源危机的加剧&#xff0c;对电能的有效利用和质量监控变得越来越重要。特别是在电力系统中&#xff0c;电能质量的监测对于保证电力设备的稳定运行和提高能源利用效率具有重要意义。采用LabVIEW软件开发了一套高效的电能…

智能设备管理系统:PreMaint助力药厂攻克设备管理难题

在药品生产中&#xff0c;设备管理是确保质量的关键环节。传统的手工操作和纸笔记录方式已经难以适应当今药厂的需求&#xff0c;存在诸多问题。这些问题包括设备管理工作的不成体系&#xff0c;难以随时掌握设备的状态&#xff0c;以及设备无法满足工艺流程的需求。面对这些挑…

原型中之filter()-过滤

注意&#xff1a;filter()不会对空数组进行检测、不会改变原始数组 array.filter(function(currentValue, indedx, arr), thisValue) currentValue&#xff1a;必须。当前元素的值;index&#xff1a;可选。当前元素的索引值&#xff1b;arr&#xff1a;可选。当前元素属于的数…

某人寿保险公司:超融合承载核心业务系统,支撑信创改造与数据中心异地迁移

案例亮点 45 节点超融合承载个险核心、单证管理、统信报送、销管系统等核心业务系统与关键数据库&#xff0c;采用 SmartX 原生虚拟化 ELF。基于鲲鹏&#xff08;ARM 芯片&#xff09;完成信创化改造&#xff0c;顺利通过验收。生产业务虚拟机 guest OS&#xff08;Redhat、ce…

由于误删了node依赖,导致这后面的一系列操作

文章目录 1. 事发原因&#xff1a;Delete select files2. Delete select files引起的cross-env报错3. cross-env是node_modules的依赖工具4. 那么Delete selected files到底是什么操作5. 重装node_modules依赖包&#xff0c;也报错6. 报错&#xff1a;cb() never called!7. 算了…

初识webpack(一)概念、入口配置、输出配置、loader等

目录 (一)概念 webpack的依赖图 (二)webpack的基本使用 (三)webpack的配置文件 1.入口(entry)配置 2.输出(output)配置 (三)loader 1.css文件处理 (1)安装css-loader和style-loader (2)在webpack.config.js中配置loader 2.less文件处理 3.postcss的使用 (1)安装…

分析网站加载性能各项指标、Lighthouse工具的使用

分析网站首次加载的性能可以通过查看多个关键指标来实现。以下是一些常见的性能指标以及如何分析它们&#xff1a; 如何分析网站首次加载的性能各项指标 首次内容绘制&#xff08;First Paint&#xff09;&#xff1a; 定义&#xff1a; 首次内容绘制是指浏览器第一次将像素呈…

《云原生安全攻防》-- 容器安全风险分析

在本节课程中&#xff0c;我们将提供一个全面的视角&#xff0c;来深入探讨容器环境下的安全风险&#xff0c;帮忙大家建立起容器环境下安全风险的整体认知。 在这个课程中&#xff0c;我们将学习以下内容&#xff1a; 容器技术概述&#xff1a;什么是容器技术以及它解决了什么…