编写SPI_Master驱动程序_老方法

news2024/11/25 14:50:22

编写SPI_Master驱动程序


文章目录

  • 编写SPI_Master驱动程序
  • 参考资料:
    • 一、 SPI驱动框架
      • 1.1 总体框架
      • 1.2 怎么编写SPI_Master驱动
        • 1.2.1 编写设备树
        • 1.2.2 编写驱动程序
    • 二、 编写程序
      • 2.1 数据传输流程
      • 2.2 写代码
  • 致谢


参考资料:

  • 内核头文件:include\linux\spi\spi.h
  • 内核文档:Documentation\devicetree\bindings\spi\spi-bus.txt
    • 内核源码:drivers\spi\spi.cdrivers\spi\spi-sh.c

一、 SPI驱动框架

1.1 总体框架

在这里插入图片描述

1.2 怎么编写SPI_Master驱动

1.2.1 编写设备树

在设备树中,对于SPI Master,必须的属性如下:

  • #address-cells:这个SPI Master下的SPI设备,需要多少个cell来表述它的片选引脚
  • #size-cells:必须设置为0
  • compatible:根据它找到SPI Master驱动

可选的属性如下:

  • cs-gpios:SPI Master可以使用多个GPIO当做片选,可以在这个属性列出那些GPIO
  • num-cs:片选引脚总数

其他属性都是驱动程序相关的,不同的SPI Master驱动程序要求的属性可能不一样。

在SPI Master对应的设备树节点下,每一个子节点都对应一个SPI设备,这个SPI设备连接在该SPI Master下面。

这些子节点中,必选的属性如下:

  • compatible:根据它找到SPI Device驱动
  • reg:用来表示它使用哪个片选引脚
  • spi-max-frequency:必选,该SPI设备支持的最大SPI时钟

可选的属性如下:

  • spi-cpol:这是一个空属性(没有值),表示CPOL为1,即平时SPI时钟为低电平
  • spi-cpha:这是一个空属性(没有值),表示CPHA为1),即在时钟的第2个边沿采样数据
  • spi-cs-high:这是一个空属性(没有值),表示片选引脚高电平有效
  • spi-3wire:这是一个空属性(没有值),表示使用SPI 三线模式
  • spi-lsb-first:这是一个空属性(没有值),表示使用SPI传输数据时先传输最低位(LSB)
  • spi-tx-bus-width:表示有几条MOSI引脚;没有这个属性时默认只有1条MOSI引脚
  • spi-rx-bus-width:表示有几条MISO引脚;没有这个属性时默认只有1条MISO引脚
  • spi-rx-delay-us:单位是毫秒,表示每次读传输后要延时多久
  • spi-tx-delay-us:单位是毫秒,表示每次写传输后要延时多久

1.2.2 编写驱动程序

  • 核心为:分配/设置/注册spi_master结构体
  • 对于老方法,spi_master结构体的核心是transfer函数

二、 编写程序

2.1 数据传输流程

在这里插入图片描述

2.2 写代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/spi/spi.h>

static struct spi_master *g_virtual_master;
static struct work_struct g_virtual_ws;

static const struct of_device_id spi_virtual_dt_ids[] = {
	{ .compatible = "100ask,virtual_spi_master", },
	{ /* sentinel */ }
};


static void spi_virtual_work(struct work_struct *work)
{
	struct spi_message *mesg;
	
	while (!list_empty(&g_virtual_master->queue)) {
		mesg = list_entry(g_virtual_master->queue.next, struct spi_message, queue);
		list_del_init(&mesg->queue);
		
		/* 假装硬件传输已经完成 */

		mesg->status = 0;
		if (mesg->complete)
			mesg->complete(mesg->context);

	}	
}

static int spi_virtual_transfer(struct spi_device *spi, struct spi_message *mesg)
{
#if 0	
	/* 方法1: 直接实现spi传输 */
	/* 假装传输完成, 直接唤醒 */
	mesg->status = 0;
	mesg->complete(mesg->context);
	return 0;
	
#else
	/* 方法2: 使用工作队列启动SPI传输、等待完成 */
	/* 把消息放入队列 */
	mesg->actual_length = 0;
	mesg->status = -EINPROGRESS;
	list_add_tail(&mesg->queue, &spi->master->queue);
	
	/* 启动工作队列 */
	schedule_work(&g_virtual_ws);
	
	/* 直接返回 */
	return 0;
#endif	
}


static int spi_virtual_probe(struct platform_device *pdev)
{
	struct spi_master *master;
	int ret;
	
	/* 分配/设置/注册spi_master */
	g_virtual_master = master = spi_alloc_master(&pdev->dev, 0);
	if (master == NULL) {
		dev_err(&pdev->dev, "spi_alloc_master error.\n");
		return -ENOMEM;
	}

	master->transfer = spi_virtual_transfer;
	INIT_WORK(&g_virtual_ws, spi_virtual_work);

	master->dev.of_node = pdev->dev.of_node;
	ret = spi_register_master(master);
	if (ret < 0) {
		printk(KERN_ERR "spi_register_master error.\n");
		spi_master_put(master);
		return ret;
	}

	return 0;

	
}

static int spi_virtual_remove(struct platform_device *pdev)
{
	/* 反注册spi_master */
	spi_unregister_master(g_virtual_master);
	return 0;
}


static struct platform_driver spi_virtual_driver = {
	.probe = spi_virtual_probe,
	.remove = spi_virtual_remove,
	.driver = {
		.name = "virtual_spi",
		.of_match_table = spi_virtual_dt_ids,
	},
};

static int virtual_master_init(void)
{
	return platform_driver_register(&spi_virtual_driver);
}

static void virtual_master_exit(void)
{
	platform_driver_unregister(&spi_virtual_driver);
}

module_init(virtual_master_init);
module_exit(virtual_master_exit);

MODULE_DESCRIPTION("Virtual SPI bus driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Hilbert");

测试程序:

#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>
#include <errno.h>

/* dac_test /dev/spidevB.D <val> */

int main(int argc, char **argv)
{
	int fd;
	unsigned int val;
	struct spi_ioc_transfer	xfer[1];
	int status;

	unsigned char tx_buf[2];	
	unsigned char rx_buf[2];	
	
	if (argc != 3)
	{
		printf("Usage: %s /dev/spidevB.D <val>\n", argv[0]);
		return 0;
	}

	fd = open(argv[1], O_RDWR);
	if (fd < 0) {
		printf("can not open %s\n", argv[1]);
		return 1;
	}

	val = strtoul(argv[2], NULL, 0);
	val <<= 2;     /* bit0,bit1 = 0b00 */
	val &= 0xFFC;  /* 只保留10bit */

	tx_buf[1] = val & 0xff;
	tx_buf[0] = (val>>8) & 0xff;	

	memset(xfer, 0, sizeof xfer);


	xfer[0].tx_buf = tx_buf;
	xfer[0].rx_buf = rx_buf;
	xfer[0].len = 2;
	
	status = ioctl(fd, SPI_IOC_MESSAGE(1), xfer);
	if (status < 0) {
		printf("SPI_IOC_MESSAGE %d\n", errno);
		return -1;
	}

	/* 打印 */
	val = (rx_buf[0] << 8) | (rx_buf[1]);
	val >>= 2;
	printf("Pre val = %d\n", val);
	
	
	return 0;
}

设备树:

        vitural_spi_master {
                compatible = "hilbert,virtual_spi_master";
                status = "okay";
                cs-gpios = <&gpio4 27 GPIO_ACTIVE_LOW>;
                num-chipselects = <1>;
                #address-cells = <1>;
                #size-cells = <0>;

                virtual_spi_dev: virtual_spi_dev@0 {
                        compatible = "spidev";
                        reg = <0>;
                        spi-max-frequency = <100000>;
                };
        };

# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH,          比如: export PATH=$PATH:/work/imx-6ull/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin 
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
#       请参考各开发板的高级用户使用手册
KERN_DIR = /work/imx6ull-sdk/Linux-4.9.88

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o spi_test spi_test.c

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order spi_test

obj-m	+= virtual_spi_master.o


致谢

以上笔记源自韦东山老师的视频课程,感谢韦老师,韦老师是嵌入式培训界一股清流,为嵌入式linux开发点起的星星之火,也愿韦老师桃李满园。聚是一团火,散是满天星!

在这样一个速食的时代,坚持做自己,慢下来,潜心琢磨,心怀敬畏,领悟知识,才能向下扎到根,向上捅破天,背着世界往前行!
仅此向嵌入行业里的每一个认真做技术的从业者致敬!



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

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

相关文章

数字IC手撕代码--联发科(总线访问仲裁)

题目描述当A、B两组的信号请求访问某个模块时&#xff0c;为了保证正确的访问&#xff0c;需要对这些信号进行仲裁。请用Verilog实现一个仲裁器&#xff0c;对两组请求信号进行仲后&#xff0c;要求&#xff1a;协议如图所示&#xff0c;请求方发送req&#xff08;request&…

数据推荐 | 人体行为识别数据集

人体行为识别任务旨在通过对人体姿态进行分析&#xff0c;识别出人体的具体动作&#xff0c;为人体行为预测、突发事件处理、智能健身、智能看护等领域提供技术支持。 图片 图片 人体行为识别数据标注方式 人体行为数据通用的标注方式包括人体关键点标注和动作标签标注&#…

Spring Boot 整合分布式缓存 Memcached

Memcached是一个开源、高性能&#xff0c;将数据分布于内存中并使用key-value存储结构的缓存系统。它通过在内存中缓存数据来减少向数据库的频繁访问连接的次数&#xff0c;可以提高动态、数据库驱动之类网站的运行速度。 Memcached在使用是比较简单的&#xff0c;在操作上基本…

经典面试题:“从输入URL到展示出页面“这个过程发生了什么?

目录 &#x1f433;今日良言:在逆境中善待自己 &#x1f407;一、输入网址(URL) &#x1f407;二、域名查询(DNS解析) &#x1f407;三、建立TCP连接 &#x1f407;四、发送HTTP/HTTPS请求 &#x1f407;五、服务器响应请求 &#x1f407;六、浏览器解析渲染页面 &…

Simple_SSTI_2

Simple_SSTI_2前言一、python类的内置属性二、解题步骤1.查看当前目录2. 打开flag文件得到flag前言 要想做到这个题&#xff0c;先要了解SSTI_2模板注入详解 一、python类的内置属性 先看如下一段代码&#xff1a; class Restaurant:"""类"""def…

存储的本质-学习笔记

1 经典案例 1.1 数据的流动 一条用户注册数据流动到后端服务器&#xff0c;持久化保存到数据库中。 1.2 数据的持久化 校验数据的合法性修改内存写入存储介质2 存储&数据库简介 2.1 存储系统特点 性能敏感、容易受硬件影响、存储系统代码既“简单”又“复杂”。 2.2 数…

从0到1实现单机记账APP原理与细节uniApp内含源码 (二)

单机记账APP演示及源码 具体演示如下面视频所示。免费下载地址&#xff1a;点击进入 预览APP下载地址&#xff1a;http://8.142.10.182:8888/down/aWHWeGaEQE2W.apk &#xff08;带宽很小所以下载速度慢&#xff09; 由于资源已经通过了&#xff0c;页面的样式这里就不写了&am…

蓝桥杯单片机组省赛十二届第一场(关于矩阵,温度ds18b20,时间ds1302的学习,以及继电器等外设的综合利用)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录一、该题目如下二、使用步骤1.矩阵键盘实现2.温度传感器ds18b20的实现总结提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、该题目如下 分…

使用Containerd搭建K8s集群【v1.25】

[toc] 一、安装要求 在开始之前,部署Kubernetes集群机器需要满足以下几个条件: 一台或多台机器,操作系统 CentOS7.x-86_x64硬件配置:2GB或更多RAM,2个CPU或更多CPU,硬盘30GB或更多集群中所有机器之间网络互通可以访问外网,需要拉取镜像禁止swap分区二、准备环境 角色IP…

重磅新品 / 酷炫展品 / 强大生态,广和通玩转 MWC Barcelona 2023

2月27日&#xff0c;2023世界移动通信大会&#xff08;MWC Barcelona 2023&#xff09;在西班牙巴塞罗那正式开幕。全球知名移动运营商、设备制造商、技术提供商、物联网企业齐聚一堂&#xff0c;以领先的技术、创新的场景、前瞻的洞察向全行业输送最新鲜的行业观点。作为全球领…

python的opencv操作记录13——区域生长及分水岭算法

文章目录图像区域基本算法——形态学运算腐蚀与膨胀开运算与闭运算opencv中的形态学运算距离计算——distanceTransform函数连通域连通的定义计算连通域——connectedComponents连通域实验基于区域的分割区域生长算法自定义一个最简单区域生长算法实现区域分割一般区域分割open…

解决vscode无法自动更新

一&#xff0e;前言 要在&#xff56;&#xff53;&#xff43;&#xff4f;&#xff44;&#xff45;里面安装插件&#xff0c;被提示版本不匹配&#xff0c;然后得更新&#xff0c;然后我发现我的&#xff07;帮助&#xff07;菜单栏下没有检查更新&#xff0c;然后我去&…

19- CNN进行Fashion-MNIST分类 (tensorflow系列) (项目十九)

项目要点 Fashion-MNIST总共有十个类别的图像。代码运行位置 CPU: cputf.config.set_visible_devices(tf.config.list_physical_devices("CPU"))fashion_mnist keras.datasets.fashion_mnist # fashion_mnist 数据导入训练数据和测试数据拆分: x_valid, x_train…

WebAPI

WebAPI知识详解day11.Web API 基本认知作用和分类什么是DOM&#xff1f;DOM树的概念DOM对象2.获取DOM对象通过css选择器获取dom对象通过其他方法获取dom3.设置/修改DOM元素内容方法1. document.write() 方法方法2. 对象.innerText 属性方法3. 对象.innerHTML4.设置/修改DOM元素…

【TypeScript】的上手学习指南!

目录TS简介TypeScript是什么&#xff1f;为什么要推荐使用TypeScript生态支持安装TypeScriptTS简介 TypeScript是什么&#xff1f; TypeScript官网 简介&#xff1a;TypeScript是JavaScript类型的超集&#xff0c;它可以编译成纯JavaScript。TypeScript可以在任何浏览器、任何计…

JVM之执行引擎详解

1、概述javac将.java文件编译成.class文件&#xff0c;然后.class文件通过类加载子系统将字节码加载到运行时数据区&#xff0c;这时运行时数据区中的数据是java字节码&#xff0c;字节码是不能直接运行在操作系统之上&#xff0c;因为字节码指令并非等价于本地机器指令&#x…

Python练习题

作业1&#xff1a;判断一个数&#xff0c;是否是2的指数 2的指数 0000 0010 0000 0001 0000 0100 0000 0011 0000 1000 0000 0111 0001 0000 0000 1111 提示&#xff1a;所有2的指数&#xff1a;n&(n - 1) 0 exponent int(…

mybatisplus复习(黑马)

学习目标能够基于MyBatisPlus完成标准Dao开发能够掌握MyBatisPlus的条件查询能够掌握MyBatisPlus的字段映射与表名映射能够掌握id生成策略控制能够理解代码生成器的相关配置一、MyBatisPlus简介MyBatisPlus&#xff08;简称MP&#xff09;是基于MyBatis框架基础上开发的增强型工…

Linux:进程间通信

目录 进程间通信目的 进程间通信分类 管道 System V IPC POSIX IPC 什么是管道 站在文件描述符角度-深度理解管道 管道使用 管道通信的四种情况 管道通信的特点 进程池管理 命名管道 创建一个命名管道 命名管道的打开规则 命名管道通信实例 匿名管道与命名管道的…

C语言实现三子棋【详解+全部源码】

大家好&#xff0c;我是你们熟悉的恒川 今天我们用C语言来实现三子棋 实现的过程很难&#xff0c;但我们一定要不放弃 三子棋1. 配置运行环境2. 三子棋游戏的初步实现2.1 建立三子棋分布模块2.2 创建一个名为board的二维数组并进行初始化2.3 搭建棋盘3. 接下来该讨论的事情3.1 …