通信协议篇——SPI通信

article/2025/9/21 14:00:13

通信协议篇——SPI通信

1.简介

SPI(Serial Peripheral Interface)是一种高速、同步、全双工串行通信总线,采用主从机通信模式,主要应用在EEPROM,FLASH,实时时钟,AD转换器等。

2.原理

通信方式

SPI通信属于串行通信,利用芯片选择/使能线CS、串行时钟线SCLK、数据输入线DATAIN、数据输出线DATAOUT四线实现同步全双工通信。

通信模式

SIP通信有四种模式,由时钟极性和时钟相位设置不同模式;

CPOL:时钟极性选择,为0时SPI总线空闲为低电平,为1时SPI总线空闲为高电平;

CPHA:时钟相位选择,为0时在SCLK第一个跳变沿采样,为1时在SCLK第二个跳变沿采样;

Mode0: CPOL=0,CPHA=0;SPI总线空闲状态为低电平,SCLK第一个跳变沿是上升沿,所以在时钟上升沿对数据采样,在时钟下降沿发送数据;

Mode1: CPOL=0,CPHA=1;SPI总线空闲状态为低电平,SCLK第一个跳变沿是上升沿,所以在时钟上升沿发送数据,在时钟下降沿对数据采样;

Mode2: CPOL=1,CPHA=0;SPI总线空闲状态为高电平,SCLK第一个跳变沿是下降沿,所以在时钟上升沿发送数据,在时钟下降沿对数据采样;

Mode3: CPOL=1,CPHA=1;SPI总线空闲状态为高电平,SCLK第一个跳变沿是下降沿,所以在时钟上升沿对数据采样,在时钟下降沿发送数据;
在这里插入图片描述

数据格式

SPI通信并没有固定的数据格式,可以根据不同的应用进行灵活地运用。数据内容大致可以分为三类——指令、地址、数据。以FLASH中的SPI为例,指令长度为8位,地址长度为24位,数据长度以字节为单位,数据格式主要有指令、指令+地址、指令+数据、指令+地址+数据。

操作时序

以FLASH的操作控制为例,展示几种SPI读写时序(采用Mode0或Mode3):

  1. 写使能:发送命令字0X06

在这里插入图片描述

  1. Sector擦除:发送命令字0X20,再发送24位地址

在这里插入图片描述

  1. 数据读:发送命令字0X03,再发送24位地址,然后接收数据

在这里插入图片描述

标准接口

namedescriptiondirectionlength
clk系统时钟input1
rst复位信号input1
spi_cs从机选择信号output1
spi_clkSPI时钟信号output1
spi_mosiSPI数据输出output1
spi_misoSPI数据输入input1

3.程序实现

RTL视图

在这里插入图片描述

spi模块

`timescale 1ns/1ps//Module Name	:	spi
//Description	:	spi communication module
//Editor		:	Yongxiang
//Time			:	2020-02-03module spi(output	wire	flash_clk,output 	reg		flash_cs,output	reg		flash_datain,input	wire	flash_dataout,input	wire	clock25M,input	wire	flash_rstn,input	wire[3:0]	cmd_type,output	reg		Done_Sig,input	wire[7:0]	flash_cmd,input	wire[23:0]	flash_addr,output 	reg[7:0]	mydata_o,output	wire	myvalid_o);assign myvalid_o = myvalid;
assign flash_clk = spi_clk_en ? clock25M : 1'b0;reg myvalid;
reg[7:0] mydata;
reg spi_clk_en = 1'b0;
reg data_come;parameter idle = 3'b000;
parameter cmd_send = 3'b001;
parameter address_send = 3'b010;
parameter read_wait = 3'b011;
parameter write_data = 3'b101;
parameter finish_done = 3'b110;reg[2:0] spi_state;
reg[7:0] cmd_reg;
reg[23:0] address_reg;
reg[7:0] cnta;
reg[8:0] write_cnt;
reg[7:0] cntb;
reg[8:0] read_cnt;
reg[8:0] read_num;
reg read_finish;//发送读flash命令
always @(negedge clock25M)
beginif(!flash_rstn)beginflash_cs <= 1'b1;		spi_state <= idle;cmd_reg <= 8'd0;address_reg <= 24'd0;spi_clk_en <= 1'b0;		//SPI clock输出不使能cnta <= 8'd0;write_cnt <= 9'd0;read_num <= 9'd0;	Done_Sig <= 1'b0;endelse begincase(spi_state)idle:begin	//idle 状态		  spi_clk_en <= 1'b0;flash_cs <= 1'b1;flash_datain <= 1'b1;	cmd_reg <= flash_cmd;address_reg <= flash_addr;Done_Sig <= 1'b0;				if(cmd_type[3] == 1'b1)begin	//bit3为命令请求,高表示操作命令请求spi_state <= cmd_send;cnta <= 8'd7;		write_cnt <= 9'd0;read_num <= 9'd0;					endendcmd_send:begin	//发送命令状态	spi_clk_en <= 1'b1;	//flash的SPI clock输出flash_cs <= 1'b0;	//cs拉低if(cnta > 8'd0)begin	//如果cmd_reg还没有发送完flash_datain <= cmd_reg[cnta];	//发送bit7~bit1位cnta <= cnta - 8'd1;endelse begin	//发送bit0flash_datain <= cmd_reg[0];if((cmd_type[2:0] == 3'b001) | (cmd_type[2:0] == 3'b100))begin	//如果是Write Enable/disable instructionspi_state <= finish_done;end						 else if(cmd_type[2:0] == 3'b011)begin	//如果是read register1spi_state <= read_wait;cnta <= 8'd7;read_num <= 9'd1;	//接收一个数据end	 else begin	//如果是sector erase, page program, read data,read device ID      spi_state <= address_send;cnta <= 8'd23;endendendaddress_send:begin	//发送flash address	if(cnta > 8'd0)begin	//如果cmd_reg还没有发送完flash_datain <= address_reg[cnta];	//发送bit23~bit1位cnta <= cnta - 8'd1;						end				else begin	//发送bit0flash_datain <= address_reg[0];   if(cmd_type[2:0] == 3'b010)begin	//如果是	sector erasespi_state <= finish_done;	endelse if(cmd_type[2:0] == 3'b101)begin	//如果是page program				spi_state <= write_data;cnta <= 8'd7;                       endelse if(cmd_type[2:0] == 3'b000)begin	//如果是读Device IDspi_state <= read_wait;read_num <= 9'd2;		//接收2个数据的Device IDend						 else beginspi_state <= read_wait;read_num <= 9'd256;	//如果是block读命令,接收256个数据							 end						 endendread_wait:begin	//等待flash数据读完成if(read_finish)beginspi_state <= finish_done;data_come <= 1'b0;endelse begindata_come <= 1'b1;endendwrite_data:begin	//写flash block数据if(write_cnt < 9'd256)begin	// program 256 byte to flashif(cnta > 8'd0)begin	//如果data还没有发送完flash_datain <= write_cnt[cnta];	//发送bit7~bit1位cnta <= cnta - 8'd1;						end				else begin                                 flash_datain <= write_cnt[0];	//发送bit0cnta <= 8'd7;write_cnt <= write_cnt + 9'd1;endendelse beginspi_state <= finish_done;spi_clk_en <= 1'b0;end endfinish_done:begin	//flash操作完成flash_cs <= 1'b1;flash_datain <= 1'b1;spi_clk_en <= 1'b0;Done_Sig <= 1'b1;spi_state <= idle;enddefault:beginspi_state <= idle;endendcase;		end
end//接收flash数据	
always @(posedge clock25M)
beginif(!flash_rstn)beginread_cnt <= 9'd0;cntb <= 8'd0;read_finish <= 1'b0;myvalid <= 1'b0;mydata <= 8'd0;mydata_o <= 8'd0;endelse beginif(data_come)beginif(read_cnt < read_num)begin	//接收数据			  if(cntb < 8'd7)begin	//接收一个byte的bit0~bit6		  myvalid <= 1'b0;mydata <= {mydata[6:0], flash_dataout};cntb <= cntb + 8'd1;endelse beginmyvalid <= 1'b1;	//一个byte数据有效mydata_o <= {mydata[6:0], flash_dataout};	//接收bit7cntb <= 8'd0;read_cnt <= read_cnt + 9'd1;endend				 			 else begin read_cnt <= 9'd0;read_finish <= 1'b1;myvalid <= 1'b0;endendelse beginread_cnt <= 9'd0;cntb <= 8'd0;read_finish <= 1'b0;myvalid <= 1'b0;mydata <= 8'd0;endend
end	endmodule

flash_control模块

`timescale 1ns/1ps//Module Name	:	flash_control
//Description	:	flash read and write control
//Editor		:	Yongxiang
//Time			:	2020-02-03module flash_control(input	wire	CLK,input	wire	RSTn,output	reg		clock25M,output	reg[3:0]	cmd_type,input	wire	Done_Sig,output	reg[7:0]	flash_cmd,output	reg[23:0]	flash_addr,input	wire[7:0]	mydata_o,input	wire	myvalid_o);reg[3:0] i;
reg[7:0] time_delay;//FLASH 擦除,Page Program,读取程序	
always @(posedge clock25M)
beginif(!RSTn)begini <= 4'd0;flash_addr <= 24'd0;flash_cmd <= 8'd0;cmd_type <= 4'b0000;time_delay <= 8'd0;endelse begincase(i)4'd0:begin	//读Device IDif( Done_Sig )beginflash_cmd <= 8'h00;i <= i + 4'd1;cmd_type <= 4'b0000;endelse beginflash_cmd <= 8'h90;flash_addr <= 24'd0;cmd_type <= 4'b1000;end	end4'd1:begin	//写Write Enable instructionif(Done_Sig)beginflash_cmd <= 8'h00;i <= i + 4'd1;cmd_type <= 4'b0000;endelse beginflash_cmd <= 8'h06;cmd_type <= 4'b1001;endend4'd2:begin	//Sector擦除if(Done_Sig)beginflash_cmd <= 8'h00;i <= i + 4'd1;cmd_type<=4'b0000;endelse beginflash_cmd <= 8'h20;flash_addr <= 24'd0;cmd_type <= 4'b1010;endend4'd3:begin	//waitting 100 clockif(time_delay < 8'd100)beginflash_cmd <= 8'h00;time_delay <= time_delay + 8'd1;cmd_type <= 4'b0000;endelse begini <= i + 4'd1;time_delay <= 8'd0;end	end4'd4:begin	//读状态寄存器1, 等待idleif(Done_Sig)begin if(mydata_o[0] == 1'b0)beginflash_cmd <= 8'h00;i <= i + 4'd1;cmd_type <= 4'b0000;endelse beginflash_cmd <= 8'h05;cmd_type <= 4'b1011;endendelse beginflash_cmd <= 8'h05;cmd_type <= 4'b1011;endend4'd5:begin	//写Write disable instructionif(Done_Sig)beginflash_cmd <= 8'h00;i <= i + 4'd1;cmd_type <= 4'b0000;endelse beginflash_cmd <= 8'h04;cmd_type <= 4'b1100;endend4'd6:begin	//读状态寄存器1, 等待idleif(Done_Sig)beginif(mydata_o[0] == 1'b0)beginflash_cmd <= 8'h00;i <= i + 4'd1;cmd_type <= 4'b0000;endelse beginflash_cmd <= 8'h05;cmd_type <= 4'b1011;endendelse beginflash_cmd <= 8'h05;cmd_type <= 4'b1011;endend4'd7:begin	//写Write Enable instructionif(Done_Sig)beginflash_cmd <= 8'h00;i <= i + 4'd1;cmd_type <= 4'b0000;endelse beginflash_cmd <= 8'h06;cmd_type <= 4'b1001;end end4'd8:begin	//waitting 100 clockif(time_delay < 8'd100)beginflash_cmd <= 8'h00;time_delay <= time_delay + 8'd1;cmd_type <= 4'b0000;endelse begini <= i + 4'd1;time_delay <= 8'd0;end	end4'd9:begin	//page program: write 0~255 to flashif(Done_Sig)beginflash_cmd <= 8'h00;i <= i + 4'd1;cmd_type <= 4'b0000;endelse beginflash_cmd <= 8'h02;flash_addr <= 24'd0;cmd_type <= 4'b1101;endend4'd10:begin	//waittingif(time_delay < 8'd100)beginflash_cmd <= 8'h00;time_delay <= time_delay + 8'd1;cmd_type <= 4'b0000;endelse begini <= i + 4'd1;time_delay <= 8'd0;end	end4'd11:begin	//读状态寄存器1, 等待idleif(Done_Sig)begin if(mydata_o[0] == 1'b0)beginflash_cmd <= 8'h00;i <= i + 4'd1;cmd_type <= 4'b0000;endelse beginflash_cmd <= 8'h05;cmd_type <= 4'b1011;endendelse beginflash_cmd <= 8'h05;cmd_type <= 4'b1011;endend4'd12:begin	//写Write disable instructionif(Done_Sig)beginflash_cmd <= 8'h00;i <= i + 4'd1;cmd_type <= 4'b0000;endelse beginflash_cmd <= 8'h04;cmd_type <= 4'b1100;end		end4'd13:begin	//读状态寄存器1, 等待idleif(Done_Sig)beginif(mydata_o[0] == 1'b0)beginflash_cmd <= 8'h00;i <= i + 4'd1;cmd_type <= 4'b0000;endelse beginflash_cmd <= 8'h05;cmd_type <= 4'b1011;endendelse beginflash_cmd <= 8'h05;cmd_type <= 4'b1011;endend4'd14:begin	//read 256byteif(Done_Sig)beginflash_cmd <= 8'h00;i <= i + 4'd1;cmd_type <= 4'b0000;endelse beginflash_cmd <= 8'h03;flash_addr <= 24'd0;cmd_type <= 4'b1110;endend4'd15:begin	//idlei <= 4'd15;endendcaseend
end//产生25Mhz的SPI Clock		  
always @(posedge CLK)
beginif(!RSTn)beginclock25M <= 1'b0;endelse beginclock25M <= ~clock25M;end
endendmodule

顶层模块

`timescale 1ns/1ps//Module Name	:	flash
//Description	:	top_file
//Editor		:	Yongxiang
//Time			:	2020-02-03module flash(input	wire	CLK,input	wire	RSTn,output	wire	flash_clk,		//spi flash clock output	wire	flash_cs,		//spi flash cs output	wire	flash_datain,	//spi flash data input  input	wire	flash_dataout	//spi flash data output);wire[7:0] flash_cmd;
wire[23:0] flash_addr;
wire clock25M;
wire[3:0] cmd_type;
wire Done_Sig;
wire[7:0] mydata_o;
wire myvalid_o;//spi通信
spi spi_inst(.flash_clk(flash_clk),.flash_cs(flash_cs),.flash_datain(flash_datain),  .flash_dataout(flash_dataout),    .clock25M(clock25M),		//input clock.flash_rstn(RSTn),		//input reset .cmd_type(cmd_type),		// flash command type		  .Done_Sig(Done_Sig),		//output done signal.flash_cmd(flash_cmd),	// input flash command .flash_addr(flash_addr),// input flash address .mydata_o(mydata_o),		// output flash data .myvalid_o(myvalid_o)	// output flash data valid 		);//flash控制
flash_control flash_control_inst(.CLK(CLK),.RSTn(RSTn),.clock25M(clock25M),.cmd_type(cmd_type),.Done_Sig(Done_Sig),.flash_cmd(flash_cmd),.flash_addr(flash_addr),.mydata_o(mydata_o),.myvalid_o(myvalid_o));endmodule

http://chatgpt.dhexx.cn/article/XQ3WkVdE.shtml

相关文章

SPI接口通信协议详解:SPI时序、2线、3线、4线SPI及4种常用工作模式

简介 SPI通信原理比I2C要简单&#xff0c;它主要是主从方式通信。这种模式通常只有一个主机和一个或者多个从机&#xff0c;标准的SPI是4根线&#xff0c;分别是SSEL(片选&#xff0c;也写作 SCS)、SCLK(时钟&#xff0c;也写作SCK)、MOSI(主机输出从机输入Master Output/Slav…

详解UART、I2C、SPI常用通信协议(全是细节)

前言 UART、I2C和SPI是我们在嵌入式开发中比较常见的通信协议了&#xff0c;没有最好的通信协议&#xff0c;每个通信协议都有自己的优缺点。如果想要通信速度快&#xff0c;SPI 将是理想的选择&#xff1b;如果用户想要连接多个设备而不是过于复杂&#xff0c;I2C 将是理想的选…

【总线】SPI 通信协议

目录 SPI总线协议概述 串行与并行通信 SPI通信介绍 SPI的工作原理 时钟 从属选择 多个从机 常规方法 菊花链方法 MOSI 和 MISO SPI 数据传输的步骤 SPI 的优缺点 优点 缺点 文章参考 SPI总线协议概述 SPI是许多不同设备使用的通用通信协议。例如&#xff0c;SD卡…

SPI通信协议详解,一篇就够!

一、什么是SPI&#xff1f; SPI 的英文全称为 Serial Peripheral Interface&#xff0c;顾名思义为串行外设接口。SPI 是一种同步串行通信接口规范&#xff0c;主要应用于嵌入式系统中的短距离通信。该接口由摩托罗拉在20世纪80年代中期开发&#xff0c;后发展成了行业规范。 …

2021年游戏项目的十大编程语言:C++、Java、C#均上榜

在这个技术驱动的世界里&#xff0c;游戏行业是全球增长最快的行业之一。在每一款华丽精美的电子游戏背后&#xff0c;都有一种编程语言&#xff0c;为用户提供优质的体验。游戏开发者利用顶级的编程语言来构建游戏。不同的游戏项目使用了不同的编程语言&#xff0c;这取决于游…

2021 编程语言排行榜

点击“终码一生”&#xff0c;关注&#xff0c;置顶公众号 每日技术干货&#xff0c;第一时间送达&#xff01; IEEE Spectrum 发布了 2021 年编程语言排行榜&#xff0c;官方的标题是&#xff1a;Python 在新技术领域依然是主导地位。 Python 近几年随着大数据、数据挖掘、人…

2020 年最牛逼的 10 门编程语言

先看再点赞&#xff0c;给自己一点思考的时间&#xff0c;微信搜索【沉默王二】关注这个有颜值却假装靠才华苟且的程序员。 本文 GitHub github.com/itwanger 已收录&#xff0c;里面还有我精心为你准备的一线大厂面试题。 对于很多初学编程的人来说&#xff0c;尤其是马上要入…

世界上到底有多少种编程语言?

最近&#xff0c;网站上看到一个很有意思的问题&#xff1a;世界上到底有多少种编程语言&#xff1f; 查遍网络之后&#xff0c;仍然没有找到准确答案&#xff0c;只知道几千的数量是有的&#xff0c;但是我们常用的也就几十来个&#xff0c;其中最常见的便是Java、Python、C、…

世界上最难的5种编程语言

世界上最难的5种编程语言 每个程序员都熟悉许多编程语言。许多编程语言都是高级的&#xff0c;它们的语法是人类可读的。然而&#xff0c;也有一些低级语言&#xff0c;对于一个人来说&#xff0c;读起来很困难&#xff0c;但是可以理解。然而&#xff0c;您是否遇到过一种既不…

2018年最流行的十大编程语言,有你用的吗?

对于编程界的初学者来说&#xff0c;最大的困难是决定从何处入手&#xff0c;或者应掌握哪种语言才能在职场上平步青云。有时&#xff0c;专业程序员也面临学习一门新语言似乎更卓有成效的情形。 无论是什么原因&#xff0c;下面列出了世界上最流行的编程语言&#xff0c;以便了…

5月编程排行榜出炉,最佳编程语言是谁?

技术的发展日新月异&#xff0c;作为开发者&#xff0c;应该时刻关注这些变化&#xff0c;不断学习才能跟上时代步伐。 编程语言层出不穷&#xff0c;关于“ 最佳编程语言 ”的争论也从未停止&#xff0c;网友们各抒己见...... 网友A&#xff1a; 人生苦短&#xff0c;我选Pyt…

十大热门编程语言的介绍

小编给大家分享一篇关于现阶段十大热门编程语言的文章&#xff1a;经过流行的搜索引擎&#xff0c;如谷歌&#xff0c;必应&#xff0c;雅虎&#xff0c;维基百科&#xff0c;亚马逊&#xff0c;YouTube和百度&#xff0c;用于计算评级&#xff1b;得出十大热门编程语言排行榜的…

GitHub2022年十大热门编程语言榜单

全球知名代码托管平台 GitHub发布的2022年GitHub Octoverse年度报告公布了全球最流行的十大编程语言&#xff0c;其中JavaScript蝉联第一&#xff0c;Python位列次席。 编程是技术革新的核心&#xff0c;对于所有的编程开发人员来说&#xff0c;对世界范围内编程语言发展和趋势…

2021年十大热门编程语言

几乎可以肯定&#xff0c;每个人都知道&#xff0c;在当今数字先进的世界中&#xff0c;技术是如何快速变化的。经常通过定期更新和改进来观察替代技术之间的相互超越已成为一种正常现象。在这一切之中&#xff0c;一个领域因技术世界的如此多变的性质而受到很大的影响&#xf…

十大编程语言,每一个都不容易学,但每一个又很有用,黑客必备

一定要注意&#xff0c;您选择的编程将在很大程度上取决于您要定位的系统类型和计划使用的漏洞。因此&#xff0c;根据您的策略&#xff0c;任何语言都会很棒。 1. C语言 它被称为“所有编程语言之母”&#xff0c;也是Hacking社区中的关键语言。今天&#xff0c;我们拥有的大…

抖音照片图集怎么制作,如何将图片做成视频上传抖音?

抖音最近被很多人当做茶余饭后不可或缺的娱乐项目&#xff0c;丰富了很多人的业余生活&#xff0c;也成就了很多抖音人&#xff0c;经常在抖音上会看到很多不是直接拍摄的视频&#xff0c;而是通过图片图集的方式展示出来的视频&#xff0c;图片中还可以配上相应的文字&#xf…

手把手教你抖音怎么用图片做视频!

抖音是一款短视频APP&#xff0c;在抖音里人们可以上传视频&#xff0c;录制视频&#xff0c;看到别人的视频&#xff0c;抖音目前很受年轻人的欢迎&#xff0c;使用抖音的人高达几个亿&#xff1b;我们在抖音上看到的照片视频&#xff0c;其实大部分是在电脑制作后上传的一般比…

如何用照片做抖音视频?这样剪辑电子相册

如果只是在抖音中拍摄视频的话&#xff0c;相信这个操作大家都会吧&#xff1f;毕竟现如今的抖音可以说是全民在用了&#xff0c;关于如何拍摄视频发布到抖音&#xff0c;这个操作也几乎是人人都会的。但是拍摄视频你会&#xff0c;你又知道应该如何用照片来制作一个抖音视频吗…

[短视频运营] 抖音最新风口,漫改图文号,变现模式非常简单

这几天抖音出现了一个新的风口&#xff0c;千万不要错过&#xff0c;抖音召开了抖音创作者大会&#xff0c;在大会上有几件事的披露引起了广泛关注&#xff0c;对于每一个在抖音上的创作者来说都是重大的事件。 ​第一、抖音在大会上确定未来一年将会把更多的重点放在图文和中…

抖音开放平台-视频切片-视频分片上传-不合法的参数ID-不合法的对象ID

问题描述 1、最近遇到个问题&#xff0c;做业务需要管理几个抖音账号&#xff0c;用抖音开放平台做分片上传视频&#xff0c;多次返回不合法参数id&#xff0c;提交工单之后给的回复没有任何参考价值。 2、例如视频文件按15M进行切片&#xff0c;调用分片上传初始化接口&#…