【嵌入式Linux内核驱动】SPI子系统 | 硬件原理 | 应用编程 | 内核驱动 | 总体框架

news2024/11/25 7:10:10

1. 硬件原理

1.1 SPI通信协议

  • SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线

  • 四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)

  • 同步,全双工

  • 支持总线挂载多设备(一主多从)

1.2 硬件连接

  • 多NSS独立片选方式
  • 菊花链方式

image-20221013101029283image-20221013101057997

引脚含义
DO(MOSI)Master Output, Slave Input,
SPI主控用来发出数据,SPI从设备用来接收数据,输出引脚设置为推挽输出
DI(MISO)Master Input, Slave Output,
SPI主控用来发出数据,SPI从设备用来接收数据,输入引脚设置为浮空或上拉输入,从机不输出时为高阻态。
SCKSerial Clock,时钟
CSChip Select,芯片选择引脚,NSS 信号线由高变低,是 SPI 通讯的起始信号。NSS 信号由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

移位寄存器示意图

image-20230707165907029 image-20230409213607428

1.3 时序

  • 起始:CS从高到低
  • 终止:CS从低到高

image-20230409213805870image-20230409213914435

SPI的时钟极性CPOL和时钟相位CPHA可以分别为0或1,由此构成了四种组合:

CPOLCPHA模式含义
000CLK初始电平为低电平,在第一个时钟沿采样数据
011CLK初始电平为低电平,在第二个时钟沿采样数据
102CLK初始电平为高电平,在第一个时钟沿采样数据
113CLK初始电平为高电平,在第二个时钟沿采样数据

常用的是模式0和模式3,因为它们都是在上升沿采样数据,不用去在乎时钟的初始电平是什么,只要在上升沿采集数据就行。

IIC有效字节流数据第一个字节是寄存器地址,之后是读写的数据,使用的是读写寄存器模型

SPI用指令码加读写数据模型,发送指令字节的方式来读取,从机中有指令集对应,

发送指令。

指定地址读。

指定地址写。

1.4 代码

IO模拟

void MySPI_Start(void)
{
	MySPI_W_SS(0);
}

void MySPI_Stop(void)
{
	MySPI_W_SS(1);
}

uint8_t MySPI_SwapByte(uint8_t ByteSend) //交换字节,发送的话就不需要读取返回值,接收的话就发送0XFF接收返回的数据。
{
	uint8_t i, ByteReceive = 0x00;
	
	for (i = 0; i < 8; i ++)  //模式0,其他模式对着时序图换一下MySPI_W_SCK就行
	{
		MySPI_W_MOSI(ByteSend & (0x80 >> i));
		MySPI_W_SCK(1);  
		if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}
		MySPI_W_SCK(0);
	}
	
	return ByteReceive;
}

1.5 SPI FLASH W25Qxx

1.5.1 硬件原理

  • W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器,常应用于数据存储、字库存储、固件程序存储等场景

  • 存储介质:Nor Flash(闪存)

  • 时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)

  • 存储容量(24位地址):

W25Q40: 4Mbit / 512KByte

W25Q80: 8Mbit / 1MByte

W25Q16: 16Mbit / 2MByte

W25Q32: 32Mbit / 4MByte

W25Q64: 64Mbit / 8MByte

W25Q128: 128Mbit / 16MByte

W25Q256: 256Mbit / 32MByte

image-20221013101502273

spi flash hold

spi flash hold
该引脚接收到低电平时,且 /CS=0,数据引脚为高阻态,芯片可以屏蔽总线的数据和时钟信号,当引脚为高电平时,可以继续恢复对芯片的操作,适用于多设备SPI控制,分时使用。这个引脚的意义是引进了3种设备情况:设备不被选中,被选中但不工作,被选中且工作;没有这个引脚功能时,芯片只有两种情况:不被选中,选中且工作。

该引脚通过控制寄存器可以有复用功能,作为数据引脚。

SPI Flash有三种工作模式

SPI Flash有三种工作模式:Standard SPI,Dual SPI,Quad SPI。这三种模式的区别在于数据引脚的数量和功能不一样。

Standard SPI
标准SPI,也就是我们常说的四线:片选 (/CS),时钟 (CLK),输入数据 (DI),输出数据 (DO)。另外配有写保护 (/WP) 和维持 (/HOLD) 功能。

Dual SPI
这种工作模式就是对标准SPI进行了改进,将DO,DI改成IO1和IO2,变成了双向IO口,这样一个时钟周期可以读写2位数据。写保护(/WP)和维持(/HOLD)功能仍然保留。

Quad SPI
这种工作模式是对Dual SPI模式进行改进,就是上面讲的,将写保护 (/WP) 和维持 (/HOLD) 引脚复用为IO口,标记为IO3,IO4,这样总共就是四个IO口,数据传送速度更快。

image-20230711202037441

  • 空间划分:块64KB,扇区4KB,页256B
  • SPI控制逻辑
  • 状态寄存器
  • 页缓存,会对一次性写入的数据缓存

1.5.2 Flash操作注意事项

写入操作时:

  • 写入操作前,必须先进行写使能

  • 每个数据位只能由1改写为0,不能由0改写为1(不能覆盖改写,要先擦除,发送擦除指令)

  • 写入数据前必须先擦除,擦除后,所有数据位变为1

  • 擦除必须按最小擦除单元进行

  • 连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入

  • 写入操作结束后,芯片进入忙状态,不响应新的读写操作

读取操作时:

  • 直接调用读取时序,无需使能,无需额外操作,没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取

BUSY

  • 擦除和写入会变为忙状态,每次操作都要先读是不是忙状态再操作。

WEL

  • 任何写入都要先写使能

1.5.3 代码

void W25Q64_Init(void)
{
	MySPI_Init();
}

void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{
	MySPI_Start();
	MySPI_SwapByte(W25Q64_JEDEC_ID); //发送读ID指令
	*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //交换读取8位厂商ID
	*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //交换读取高8位设备ID
	*DID <<= 8;
	*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);//交换读取低8位设备ID
	MySPI_Stop();
}

void W25Q64_WriteEnable(void)
{
	MySPI_Start();
	MySPI_SwapByte(W25Q64_WRITE_ENABLE); 
	MySPI_Stop();
}

void W25Q64_WaitBusy(void)
{
	uint32_t Timeout;
	MySPI_Start();
	MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1); //读状态寄存器
	Timeout = 100000;
	while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01) //查看是否忙状态
	{
		Timeout --; 
		if (Timeout == 0) //超时判断
		{
			break;
		}
	}
	MySPI_Stop();
}

//页编程:页的某个地址写一堆数据
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count) 
{
	uint16_t i;
	
	W25Q64_WriteEnable(); 
	
	MySPI_Start();
	MySPI_SwapByte(W25Q64_PAGE_PROGRAM);
	MySPI_SwapByte(Address >> 16); //先发地址最高位
	MySPI_SwapByte(Address >> 8);
	MySPI_SwapByte(Address);
	for (i = 0; i < Count; i ++)  //循环发送数据
	{
		MySPI_SwapByte(DataArray[i]);
	}
	MySPI_Stop();
	
	W25Q64_WaitBusy(); //事后等待,保证出了这个函数是不忙的,会浪费资源。
                       //也可以放在最前面,事前等待
}

//扇区擦除
void W25Q64_SectorErase(uint32_t Address)
{
	W25Q64_WriteEnable();
	
	MySPI_Start();
	MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);
	MySPI_SwapByte(Address >> 16);
	MySPI_SwapByte(Address >> 8);
	MySPI_SwapByte(Address);
	MySPI_Stop();
	
	W25Q64_WaitBusy();
}

//读数据
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
	uint32_t i;
	MySPI_Start();
	MySPI_SwapByte(W25Q64_READ_DATA);
	MySPI_SwapByte(Address >> 16);
	MySPI_SwapByte(Address >> 8);
	MySPI_SwapByte(Address);
	for (i = 0; i < Count; i ++)
	{
		DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);
	}
	MySPI_Stop();
}

1.6 常见面试问题

SPI总线的工作频率

SPI是一种事实标准,由Motorola开发,并没有一个官方标准。已知的有的器件SPI已达到50Mbps。具体到产品中SPI的速率主要看主从器件SPI控制器的性能限制。

SPI最大传输速率受以下几个条件影响:

1)SPI的最大时钟频率;

2)CPU处理SPI数据的能力;

3)输出端驱动能力(PCB所允许的最大信号传输速率);

W25Qxx系列时钟频率:80MHz/160MHz(Dual SPI)/320MHz(Quad SPI)

SPI优缺点

SPI的优点

  • 全双工串行通信;
  • 高速数据传输速率。
  • 简单的软件配置;
  • 极其灵活的数据传输,不限于8位,它可以是任意大小的字;
  • 非常简单的硬件结构。从站不需要唯一地址(与I2C不同)。从机使用主机时钟,不需要精密时钟振荡器/晶振(与UART不同)。不需要收发器(与CAN不同)。

SPI的缺点

  • 没有硬件从机应答信号(主机可能在不知情的情况下无处发送);
  • 通常仅支持一个主设备;
  • 需要更多的引脚(与I2C不同);
  • 没有定义硬件级别的错误检查协议;
  • 与RS-232和CAN总线相比,只能支持非常短的距离;

2. 应用编程–spidev应用程序分析

内核提供的测试程序:tools\spi\spidev_fdx.c

2.1 使用方法

spidev_fdx [-h] [-m N] [-r N] /dev/spidevB.D
  • -h: 打印用法
  • -m N:先写1个字节0xaa,再读N个字节,**注意:**不是同时写同时读
  • -r N:读N个字节

2.2 代码分析

2.2.1 显示设备属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ReVgcpf6-1689342862460)(pic/29_spidev_dumpstat.png)]

2.2.2 读数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hNsNpymg-1689342862460)(pic/30_spidev_do_read.png)]

2.2.3 先写再读

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AgQ7ji6m-1689342862460)(pic/31_spidev_write_then_read.png)]

2.2.4 同时读写

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-URIIsxc2-1689342862461)(pic/32_spidev_read_write_same_time.png)]

2.3 spidev的缺点

使用read、write函数时,只能读、写,这是半双工方式。

使用ioctl可以达到全双工的读写。

但是spidev有2个缺点:

  • 不支持中断
  • 只支持同步操作,不支持异步操作:就是read/write/ioctl这些函数只能执行完毕才可返回

3.内核驱动

总线-设备-驱动模型

spi_drv_frame

SPI子系统中:SPI控制器、SPI设备-驱动。

image-20230409215517035

image-20230409215537806image-20230409215943244

在设备树里,会有一个节点用来表示SPI控制器。

在这个SPI控制器下面,连接有哪些SPI设备?会在设备树里使用子节点来描述SPI设备。

image-20230409221954062

官方的通用dev驱动spidev驱动程序

自己编写设备spi驱动程序,如DAC4822,TLC5615,OLED SD1306

SPI_Master驱动程序框架

SPI_Slave_Mode驱动程序框架

4. 总体框架

image-20230425103401023

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

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

相关文章

jmeter主要函数助手功用说明

jmeter中虽然有很多的插件&#xff0c;但是有些需要安装&#xff0c;有些具有一定的局限性。函数助手是一个快捷的工具库。下面记录一下函数助手中一些主要的函数的使用方法。 注&#xff1a;不内容中所有的实例均基于3.2记录 1、_BeanShell 表达式请求值后的值&#xff1a;可…

Hadoop之Hive安装

一、嵌入模式安装 1、下载Hive安装包 https://archive.apache.org/dist/hive/hive-1.2.1/ 2、上传至/root/export/software/ rz apache-hive-1.2.1-bin.tar.gz 3、解压 tar apache-hive-1.2.1-bin.tar.gz -C /root/export/servers/ cd /root/export/servers/apache-hive-1.2.…

openGauss学习笔记-09 openGauss 简单数据管理-创建数据库

文章目录 openGauss学习笔记-09 openGauss 简单数据管理-创建数据库9.1 语法格式9.2 参数说明9.3 示例 openGauss学习笔记-09 openGauss 简单数据管理-创建数据库 数据库安装完成后&#xff0c;默认生成名称为postgres的数据库。您需要自己创建一个新的数据库。 9.1 语法格式…

【Docker】Docker高级网络(NetWork)

【Docker】Docker高级网络(NetWork) 文章目录 【Docker】Docker高级网络(NetWork)1. 概述2. 网络2.1 网桥类型2.2 创建网络自定义桥2.3 查看所有网络2.4 查看特定网络的细节2.5 删除特定网络2.6 多个容器使用指定网络 参考文档&#xff1a;高级网络配置 Docker – 从入门到实践…

手机pdf怎么转换为图片?看看这几个转换方法

手机pdf怎么转换为图片&#xff1f;将手机图片转为PDF有很多好处。首先&#xff0c;PDF文件通常比图片文件更小&#xff0c;可以节省手机存储空间。其次&#xff0c;PDF文件可以更轻松地与他人共享&#xff0c;并且可以在不同设备和操作系统上打开。最后&#xff0c;将图片转换…

superheat | 超级简单的热图绘制解决方案!~(二)(聚类和注释图的添加~)

1写在前面 前面写了superheat的教程&#xff0c;今天写一下第二波&#xff0c;如何进行聚类以及添加注释图吧。&#x1f929; 分分钟提升你的heatmap的颜值哦&#xff01;~&#x1f970; 2用到的包 # devtools::install_github("rlbarter/superheat")library(superhe…

二叉树(中)+Leetcode每日一题——“数据结构与算法”“剑指Offer55-I. 二叉树的深度”“100.相同的树”“965.单值二叉树”

各位CSDN的uu们你们好呀&#xff0c;今天继续数据结构与算法专栏中的二叉树&#xff0c;下面&#xff0c;让我们进入二叉树的世界吧&#xff01;&#xff01;&#xff01; 二叉树&#xff08;上&#xff09;——“数据结构与算法”_认真学习的小雅兰.的博客-CSDN博客 二叉树链…

TypeScript 中对【泛型】的定义使用方式解读

目录 泛型函数多个泛型参数泛型约束泛型别名泛型接口泛型类 泛型&#xff08;Generics&#xff09;是指在定义函数、接口或类的时候&#xff0c;不预先指定具体的类型&#xff0c;而在使用的时候再指定类型的一种特性。使用泛型 可以复用类型并且让类型更加灵活 泛型实现类型参…

Pycharm安装 leetcode 插件

目录 本节演示Pycharm安装 leetcode 插件做算法题 打开设置&#xff1a; 点击插件&#xff1a; 搜索leetcode并安装&#xff1a; 点这里的 leetcode&#xff1a; 初次使用点这里&#xff1a; 这里输入账号和密码&#xff1a; 点击确定后还是点登录&#xff1a; 登…

【MySQL】SQL入门(一)

&#x1f697;MySQL学习起始站~ &#x1f6a9;本文已收录至专栏&#xff1a;数据库学习之旅 ❤️每章节附章节思维导图&#xff0c;文末附全文思维导图&#xff0c;感谢各位点赞收藏支出~ 一.引入 (1) SQL分类 SQL语句&#xff0c;根据其功能&#xff0c;主要分为四类&#x…

巧妙使用 CSS 渐变来实现波浪动画

目录 一、波浪的原理 二、曲面的绘制 三、波浪动画 四、文字波浪动画 五、总结一下 参考资料 之前看到coco[1]的这样一篇文章&#xff1a;纯 CSS 实现波浪效果&#xff01;[2]&#xff0c;非常巧妙&#xff0c;通过改变border-radius和不断旋转实现的波浪效果&#xff0c…

网络安全—全知识点解析(课程学习笔记)

概括来说&#xff0c;网络安全课程的主要内容包括&#xff1a; 安全基本知识 应用加密学 协议层安全 Windows安全&#xff08;攻击与防御&#xff09; Unix/Linux安全&#xff08;攻击与防御&#xff09; 防火墙技术 入侵监测系统 审计和日志分析 下面分别对每部分知识介绍相应…

Stable Diffusion - ControlNet 插件中扩展局部重绘 InpaintOnly + LaMa 算法与应用

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/131643131 LaMa: https://github.com/advimman/lama Paper: Resolution-robust Large Mask Inpainting with Fourier Convolutions LaMa: Large…

哇~~真的是你呀!今天是在LINUX上简单部署LAMP平台。

目录 一、概述 二、PHP安装配置 三、安装 四、启动 五、书写测试页面 六、客户端访问 七、安装论坛 一、概述 LAMP组成&#xff1a; &#xff08;1&#xff09; Linux 其他组件的平台 &#xff08;2&#xff09;Apache提供web服务 &#xff08;3&#xff09;MySQL|Ma…

004-Triple协议底层原理分析

目录 底层分析Http 2.0 底层分析 Http 2.0 为了解决Http 1.0 和 1.1 头信息无法压缩有很多比如空格、换行等无用字符请求和相应不能并行处理&#xff1a;一个Socket连接如果接受到Request 就必须要等到服务返回Response了才能继续发送另一个Request 就更新了Http的协议到2.…

SQL语法与数据库快速入门(1)

目录 数据库简介数据库分类常用数据库简介使用场景MySql 的安装与配置数据库客户端工具MySql 介绍SQL 简介DDL 数据库操作-创建DDL 数据库操作-查看DDL 数据库操作-修改DDL 数据库操作-删除DDL 数据库表操作简介DDL 数据库表操作-创建DDL 数据库表操作-查看DDL 数据库表操作-修…

国内值得去的外企之Google攻略

外企在国内非常的乖&#xff0c;基本上都是正常上下班&#xff0c;更有甚者&#xff0c;上班不到点不进门&#xff0c;下班一到点就跑路&#xff0c;中途还能去楼下咖啡店喝咖啡。 年假多、生活工作平衡、待遇还不错&#xff0c;下班以后同事之间几乎再无联系&#xff0c;如果…

解决matplotlib子图重叠问题

代码修改前&#xff1a; import matplotlib.pyplot as plt import seaborn as snsdef on_resize(event):print(当前画布大小为&#xff1a;{}x{}.format(event.width, event.height))if __name__ __main__:x list(range(1, 6))y1 [i ** 2 for i in x]y2 [i ** 3 for i in …

C++之std::set有序容器用法(一百六十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Redis安装与配置指南:适用于Windows、Mac和Linux系统的详细教程

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…