通信协议篇——SPI通信
通信协议篇——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):
- 写使能:发送命令字0X06
- Sector擦除:发送命令字0X20,再发送24位地址
- 数据读:发送命令字0X03,再发送24位地址,然后接收数据
标准接口
name | description | direction | length |
---|---|---|---|
clk | 系统时钟 | input | 1 |
rst | 复位信号 | input | 1 |
spi_cs | 从机选择信号 | output | 1 |
spi_clk | SPI时钟信号 | output | 1 |
spi_mosi | SPI数据输出 | output | 1 |
spi_miso | SPI数据输入 | input | 1 |
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
通信协议篇——SPI通信相关推荐
- 通信协议篇——I2C通信
通信协议篇--I2C通信 1.简介 I2C(Inter-Integrated Circuit)是一种串行通信总线,总线上可以挂多个设备,可实现同步半双工通信. 2.原理 通信方式 I2C通信属于串行通 ...
- 收藏 | 电子通信协议之SPI通信协议篇
当你将微控制器连接到传感器,显示器或其他模块时,你是否考虑过这两种设备如何相互通信?他们到底在说什么?他们如何理解对方? 电子设备之间的通信就像人类之间的通信.双方都需要说同样的语言.在电子产品中,这 ...
- 通信协议整理之 SPI 通信
通信协议整理之 SPI 通信 文章目录 通信协议整理之 SPI 通信 简介 工作原理 通信连接方式 补充 硬件 SPI 和模拟 SPI 简介 SPI是串行外设接口(Serial Peripheral ...
- spi通信协议_arduino的SPI通信
SPI通信简介 SPI全称Serial Peripheral Interface,即串行外设接口. 由Motorola公司提出的一种同步串行数据传输标准. 所谓同步,即数据收发双方共用一个时钟: 所谓 ...
- 基于FPGA的 SPI通信 设计(1)
引言 低速通信目前搞过 UART串口通信.IIC通信.其实 SPI 也算是中低速(有时也可以用作高速通信)串行通信的范畴,但是一直还没真正实现过,所以此系列就 SPI的协议以及FPGA设计作几篇博客记 ...
- TLE5012B ESP32驱动程序、硬件电路设计、4线SPI通信,驱动完美兼容4线SPI不用改MOSI开漏推挽输出
一.TLE5012B 简介 TLE5012B 是一种款高分辨率旋转位置传感器,用于在整个 360 度范围内进行角度测量. 它通过使用单片集成 巨磁阻 (iGMR) 元件,来测量正弦和余弦角分量,这些原 ...
- STM32 OLED显示屏--SPI通信知识汇总
备注:在OLED显示屏部分,单片机是通过SPI和OLED进行通信的. 文章目录 目录 文章目录 一.SPI时序通信 二.SPI通信的分类 1.硬件SPI 2.软件SPI 三.硬件SPI SPI特性 硬 ...
- ESP32-Arduino开发实例-与Arduino之间SPI通信
NodeMCU-ESP32与Arduino之间SPI通信 SPI是串行外设接口(Serial Peripheral Interface)的缩写.是 Motorola 公司推出的一种同步串行接口技术,是 ...
- 串口通信,IIC通信,SPI通信的简单区别
一.串口通信 串口通信就是RS-232通信,只有两根线txd和rxd,通信双方必须设定相同的波特率,传输距离比较近. 二.IIC通信 IIC通信是最简单的串行通信协议,只需要一根数据线和一根时钟线,就 ...
最新文章
- 整理前端css/js/jq常见问题及解决方法(3)
- brew更新的时候不更新某个应用_这样用 Git 想不升职加薪都难!
- C#中使用WebClient下载指定url的网络照片
- nRF51822 配对之device_manager_init 调用,以及保证 用户数据存储 的Flash 操作不与device manager 模块冲突...
- 这里是武汉(1)——汉阳造文化创意产业园
- mysql中用户线程作用,mysql用户线程的建立与用户线程的状态源码解析
- oracle 导入 导出 imp、exp
- Vertica系列: Vertica 数仓简单介绍
- 故障树手册(Fault Tree handbook)(2)
- 邮件服务器的功能以及相关工作原理
- COJ 3012 LZJ的问题 (有向图判环)
- adb命令查看手机电量_adb获取电池信息以及电量消耗信息
- java调用rapidminer_基于RapidMiner开发问题和解决
- 微生物多样性数据分析(16S)
- 缓存加速------Redis的五种数据类型(String,List,Hash,Set,Sorted Set)
- python计算线性回归方程
- 荣誉加持,驭势科技近期斩获奖项回顾
- 自研·学术·文献查找
- linux(CentOS7)服务器环境搭建之-supervisor
- Cursor——ChatGPT的替代品【笔记】