通信协议篇——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位地址,然后接收数据

标准接口

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通信相关推荐

  1. 通信协议篇——I2C通信

    通信协议篇--I2C通信 1.简介 I2C(Inter-Integrated Circuit)是一种串行通信总线,总线上可以挂多个设备,可实现同步半双工通信. 2.原理 通信方式 I2C通信属于串行通 ...

  2. 收藏 | 电子通信协议之SPI通信协议篇

    当你将微控制器连接到传感器,显示器或其他模块时,你是否考虑过这两种设备如何相互通信?他们到底在说什么?他们如何理解对方? 电子设备之间的通信就像人类之间的通信.双方都需要说同样的语言.在电子产品中,这 ...

  3. 通信协议整理之 SPI 通信

    通信协议整理之 SPI 通信 文章目录 通信协议整理之 SPI 通信 简介 工作原理 通信连接方式 补充 硬件 SPI 和模拟 SPI 简介 SPI是串行外设接口(Serial Peripheral ...

  4. spi通信协议_arduino的SPI通信

    SPI通信简介 SPI全称Serial Peripheral Interface,即串行外设接口. 由Motorola公司提出的一种同步串行数据传输标准. 所谓同步,即数据收发双方共用一个时钟: 所谓 ...

  5. 基于FPGA的 SPI通信 设计(1)

    引言 低速通信目前搞过 UART串口通信.IIC通信.其实 SPI 也算是中低速(有时也可以用作高速通信)串行通信的范畴,但是一直还没真正实现过,所以此系列就 SPI的协议以及FPGA设计作几篇博客记 ...

  6. TLE5012B ESP32驱动程序、硬件电路设计、4线SPI通信,驱动完美兼容4线SPI不用改MOSI开漏推挽输出

    一.TLE5012B 简介 TLE5012B 是一种款高分辨率旋转位置传感器,用于在整个 360 度范围内进行角度测量. 它通过使用单片集成 巨磁阻 (iGMR) 元件,来测量正弦和余弦角分量,这些原 ...

  7. STM32 OLED显示屏--SPI通信知识汇总

    备注:在OLED显示屏部分,单片机是通过SPI和OLED进行通信的. 文章目录 目录 文章目录 一.SPI时序通信 二.SPI通信的分类 1.硬件SPI 2.软件SPI 三.硬件SPI SPI特性 硬 ...

  8. ESP32-Arduino开发实例-与Arduino之间SPI通信

    NodeMCU-ESP32与Arduino之间SPI通信 SPI是串行外设接口(Serial Peripheral Interface)的缩写.是 Motorola 公司推出的一种同步串行接口技术,是 ...

  9. 串口通信,IIC通信,SPI通信的简单区别

    一.串口通信 串口通信就是RS-232通信,只有两根线txd和rxd,通信双方必须设定相同的波特率,传输距离比较近. 二.IIC通信 IIC通信是最简单的串行通信协议,只需要一根数据线和一根时钟线,就 ...

最新文章

  1. 整理前端css/js/jq常见问题及解决方法(3)
  2. brew更新的时候不更新某个应用_这样用 Git 想不升职加薪都难!
  3. C#中使用WebClient下载指定url的网络照片
  4. nRF51822 配对之device_manager_init 调用,以及保证 用户数据存储 的Flash 操作不与device manager 模块冲突...
  5. 这里是武汉(1)——汉阳造文化创意产业园
  6. mysql中用户线程作用,mysql用户线程的建立与用户线程的状态源码解析
  7. oracle 导入 导出 imp、exp
  8. Vertica系列: Vertica 数仓简单介绍
  9. 故障树手册(Fault Tree handbook)(2)
  10. 邮件服务器的功能以及相关工作原理
  11. COJ 3012 LZJ的问题 (有向图判环)
  12. adb命令查看手机电量_adb获取电池信息以及电量消耗信息
  13. java调用rapidminer_基于RapidMiner开发问题和解决
  14. 微生物多样性数据分析(16S)
  15. 缓存加速------Redis的五种数据类型(String,List,Hash,Set,Sorted Set)
  16. python计算线性回归方程
  17. 荣誉加持,驭势科技近期斩获奖项回顾
  18. 自研·学术·文献查找
  19. linux(CentOS7)服务器环境搭建之-supervisor
  20. Cursor——ChatGPT的替代品【笔记】

热门文章

  1. 智能锁技术有哪些呢?
  2. 计算机网络怎么区分a类b类c类地址,如何判断IP地址是A类B类还是C类
  3. 酒店价格【实际考试的trick】
  4. poj2226 Muddy Fields 填充棒子(二分匹配)
  5. 是奶就给孩子喝?这几种奶医生建议别再喝了!
  6. 廊坊师范学院吧网络舆情分析报告
  7. 解决Keil安装Pack包的“Loading PDSC Debug Description Failed”错误
  8. 实现类知乎android客户端关注和取消关注的按钮点击效果
  9. 责任链设计模式Demo
  10. Linux的基本学习(三)——目录配置、文件与目录管理