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

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

相关文章

CSS要点总结

一、CSS 快速入门 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>css 快速入门</title><!-- 解读1. 在 head 标签内&#xff0c;出现了 <style type"text/css"></style…

Redis 的持久化机制是什么?各自的优缺点?

Redis 提供两种持久化机制 RDB&#xff08;默认&#xff09; 和 AOF 机制: RDB&#xff1a;是Redis DataBase缩写快照 RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中&#xff0c;对应产生的数据文件为dump.rdb。通过配置文件中的save参数来…

ChatLaw:基于LLaMA微调的法律大模型

文章目录 动机数据组成模型框架模型评估 北大团队发布首个的中文法律大模型落地产品ChatLaw&#xff0c;为大众提供普惠法律服务。模型支持文件、语音输出&#xff0c;同时支持法律文书写作、法律建议、法律援助推荐。 github地址&#xff1a;https://github.com/PKU-YuanGroup…

备份RK35XX 设备的ubuntu根文件系统的方法

简介 我们使用 RK35XX 提供的SDK包制作了一个完整的 ubuntu 镜像,烧录到设备中,会在设备中安装很多我们需要的软件,运行的一些自己写的脚本和业务程序,当我们有很多台设备时,不可能每台都一个个去安装,此时我们就需要一个工具来备份当前设备的根文件系统,然后再放到 SD…

2023年上-未来几年我要做什么

1月份&#xff0c;离职。 2月份&#xff0c;春节休假回来&#xff0c;中旬去参加了一个月的瑜伽培训&#xff0c;学会了倒立、鹤蝉。。。。 3月份&#xff0c;瑜伽培训结束&#xff0c;开始收拾房子&#xff0c;并调研各类项目。 4月份&#xff0c;参与了朋友的区块链项目 …

Leetcode—203. 移除链表元素【简单】

2024每日刷题&#xff08;一零九&#xff09; Leetcode—203. 移除链表元素 实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(n…

嵌入式linux移植篇之kernel

Linux的启动过程概述 Linux内核的启动过程是一个复杂而又有序的流程&#xff0c;涉及到硬件初始化、引导加载、内核初始化等多个步骤。以下是Linux内核的典型启动流程&#xff1a; BIOS/UEFI阶段&#xff1a; 电源启动&#xff1a;计算机通电后&#xff0c;BIOS&#xff08;…

#从零开始# 在深度学习环境中,如何用 pycharm配置使用 pipenv 虚拟环境

为Python项目创建虚拟环境 在深度学习环境和一般python环境中安装pipenv基本一致&#xff0c;只需要确认好pipenv指定的python版本即可,安装pipenv前&#xff0c;可以通过python --version来确认安装版本 快捷键&#xff1a;crtl alt S 查看interpreter&#xff0c;查看所有…

聊聊比特币----比特币地址

⽐特币地址是⼀个标识符&#xff08;帐号&#xff09;&#xff0c;包含27-34个字母数字拉丁字符&#xff08;0&#xff0c;O&#xff0c;I除外&#xff09;。地址可以以QR码形式表⽰&#xff0c;是匿名的&#xff0c;不包含关于所有者的信息。 地址⽰例&#xff1a;14qViLJfdG…

【51单片机】开发板和单片机的介绍(2)

前言 大家好吖&#xff0c;欢迎来到 YY 滴单片机系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过单片机的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

[Python] 什么是KMeans聚类算法以及scikit-learn中的KMeans使用案例

什么是无监督学习&#xff1f; 无监督学习是机器学习中的一种方法&#xff0c;其主要目的是从无标签的数据集中发现隐藏的模式、结构或者规律。在无监督学习中&#xff0c;算法不依赖于任何先验的标签信息&#xff0c;而是根据数据本身的特征和规律进行学习和推断。无监督学习…

论文分享:利用对象存储进行高性能数据分析

本次分享的是慕尼黑工业大学&#xff08;TUM&#xff09; Dominik Durner&#xff0c;Viktor Leis&#xff0c;和 Thomas Neumann 于 2023 年 7 月发表在 PVLDB&#xff08;Volume 16 No.11) 的论文&#xff1a; Exploiting Cloud Object Storage for High-Performance Analyt…

PyQT——蓝牙收发数据(上位机案例-小车控制器)

实现功能 由于本人水平有限&#xff0c;仅用了最简单的进行实现&#xff0c;主要功能&#xff1a; 蓝牙设备扫描以及刷新蓝牙连接蓝牙数据发送蓝牙数据接收 页面实现效果 代码目录结构 代码案例 代码已经全部添加注释&#xff0c;故不再做单独解释。 Main.py ble_contr…

大数据信用报告在线查询平台哪个好?

随着大数据技术在金融风控的运用&#xff0c;大数据信用越来越被人熟知&#xff0c;由于线下没有查询大数据信用的地方&#xff0c;想要查询大数据信用报告只有在线上查询&#xff0c;那大数据信用报告在线查询平台哪个好呢?本文贷你一起去了解市面上比较好的三个平台。 大数据…

【Springcloud篇】学习笔记九(十五、十六章):Cloud Alibaba介绍、Nacos服务注册、服务配置中心

第十五章_Cloud Alibaba简介 1.出现SpringCloud Alibaba的原因 SpringCloud Netflix项目进入维护模式 技术的发展 2.SpringCloud Alibaba简介 2.1是什么 2.2能干嘛 2.3去哪下 阿里巴巴中文文档下载网站&#xff1a; spring-cloud-alibaba/README-zh.md at 2022.x alibaba…

学成在线:采用XXL-JOB任务调度方案使用FFmpeg处理视频转码业务

分片技术方案 概述 XXL-JOB并不直接提供数据处理的功能&#xff0c;它只会给所有注册的执行器分配好分片序号&#xff0c;在向执行器下发任务调度的同时携带分片总数和当前分片序号等参数 设计作业分片方案保证多个执行器之间不会查询到重复的任务,保证任务不会重复执行 任…

由于误删了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. 算了…

JSR303参数校验-SpringMVC

文章目录 JSR303技术标准简介JSR303标准几个具体实现框架validation-apijakarta.validation-apihibernate-validatorspring-boot-starter-validation Spring Validationjavax.validation.constraints包下提供的注解org.hibernate.validator.constraints包扩展的注解校验注解默认…

内衣洗衣机是不是鸡肋?好用的小型洗衣机全自动推荐

随着大家工作的压力越来越大&#xff0c;下了班之后只能想躺平&#xff0c;在洗完澡之后看着还需要手洗的内衣裤真的很头疼。有些小伙伴还有会攒几天再丢进去洗衣机里面一起&#xff0c;而且这样子是非常不好的&#xff0c;用过的内衣裤长时间不清洗容易滋生细菌&#xff0c;而…

无人机遥感技术在地质灾害监测应用分析,多旋翼无人机应急救援技术探讨

地质灾害是指在地球的发展演变过程中&#xff0c; 由各种自然地质作用和人类活动所形成的灾害性地质事件。给人民的生命和财产安全带来严重威胁&#xff0c;因此有必要开展地质灾害预测预报、灾害应急和风险区划 遥感技术的快速发展为我们提供了一种获取实时灾害信息的可靠手段…