通信协议篇——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通信_第1张图片

数据格式

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

操作时序

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

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

通信协议篇——SPI通信_第2张图片

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

通信协议篇——SPI通信_第3张图片

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

通信协议篇——SPI通信_第4张图片

标准接口
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通信_第5张图片

spi模块
`timescale 1ns/1ps

//Module Name	:	spi
//Description	:	spi communication module
//Editor		:	Yongxiang
//Time			:	2020-02-03

module 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)
begin
	if(!flash_rstn)begin
		flash_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;
	end
	else begin
		case(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;					
				end
			end
			
			cmd_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;
				end
				else begin	//发送bit0
					flash_datain <= cmd_reg[0];
					if((cmd_type[2:0] == 3'b001) | (cmd_type[2:0] == 3'b100))begin	//如果是Write Enable/disable instruction
						spi_state <= finish_done;
					end						 
					else if(cmd_type[2:0] == 3'b011)begin	//如果是read register1
						spi_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;
					end
				end
			end
			
			address_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	//发送bit0
					flash_datain <= address_reg[0];   
               if(cmd_type[2:0] == 3'b010)begin	//如果是	sector erase
 						spi_state <= finish_done;	
               end
               else if(cmd_type[2:0] == 3'b101)begin	//如果是page program				
				      spi_state <= write_data;
						cnta <= 8'd7;                       
					end
					else if(cmd_type[2:0] == 3'b000)begin	//如果是读Device ID
					   spi_state <= read_wait;
						read_num <= 9'd2;		//接收2个数据的Device ID
               end						 
					else begin
					   spi_state <= read_wait;
						read_num <= 9'd256;	//如果是block读命令,接收256个数据							 
               end						 
				end
			end
			
			read_wait:begin	//等待flash数据读完成
				if(read_finish)begin
					spi_state <= finish_done;
					data_come <= 1'b0;
				end
				else begin
					data_come <= 1'b1;
				end
			end
			
			write_data:begin	//写flash block数据
				if(write_cnt < 9'd256)begin	// program 256 byte to flash
					if(cnta > 8'd0)begin	//如果data还没有发送完
						flash_datain <= write_cnt[cnta];	//发送bit7~bit1位
                  cnta <= cnta - 8'd1;						
					end				
					else begin                                 
						flash_datain <= write_cnt[0];	//发送bit0
					   cnta <= 8'd7;
					   write_cnt <= write_cnt + 9'd1;
					end
				end
				else begin
					spi_state <= finish_done;
					spi_clk_en <= 1'b0;
				end 
			end
			
			finish_done:begin	//flash操作完成
				flash_cs <= 1'b1;
				flash_datain <= 1'b1;
				spi_clk_en <= 1'b0;
				Done_Sig <= 1'b1;
				spi_state <= idle;
			end
			
			default:begin
				spi_state <= idle;
			end
			
		endcase;		
	end
end
	
//接收flash数据	
always @(posedge clock25M)
begin
	if(!flash_rstn)begin
		read_cnt <= 9'd0;
		cntb <= 8'd0;
		read_finish <= 1'b0;
		myvalid <= 1'b0;
		mydata <= 8'd0;
		mydata_o <= 8'd0;
	end
	else begin
		if(data_come)begin
			if(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;
				end
				else begin
					myvalid <= 1'b1;	//一个byte数据有效
					mydata_o <= {mydata[6:0], flash_dataout};	//接收bit7
					cntb <= 8'd0;
					read_cnt <= read_cnt + 9'd1;
				end
			end				 			 
			else begin 
				read_cnt <= 9'd0;
				read_finish <= 1'b1;
				myvalid <= 1'b0;
			end
		end
		else begin
			read_cnt <= 9'd0;
			cntb <= 8'd0;
			read_finish <= 1'b0;
			myvalid <= 1'b0;
			mydata <= 8'd0;
		end
	end
end	

endmodule

flash_control模块
`timescale 1ns/1ps

//Module Name	:	flash_control
//Description	:	flash read and write control
//Editor		:	Yongxiang
//Time			:	2020-02-03

module 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)
begin
   if(!RSTn)begin
		i <= 4'd0;
		flash_addr <= 24'd0;
		flash_cmd <= 8'd0;
		cmd_type <= 4'b0000;
		time_delay <= 8'd0;
	end
	else begin
	   case(i)
			4'd0:begin	//读Device ID
				if( Done_Sig )begin
					flash_cmd <= 8'h00;
					i <= i + 4'd1;
					cmd_type <= 4'b0000;
				end
				else begin
					flash_cmd <= 8'h90;
					flash_addr <= 24'd0;
					cmd_type <= 4'b1000;
				end	
			end
			
	      4'd1:begin	//写Write Enable instruction
				if(Done_Sig)begin
					flash_cmd <= 8'h00;
					i <= i + 4'd1;
					cmd_type <= 4'b0000;
				end
				else begin
					flash_cmd <= 8'h06;
					cmd_type <= 4'b1001;
				end
			end
			
			4'd2:begin	//Sector擦除
				if(Done_Sig)begin
					flash_cmd <= 8'h00;
					i <= i + 4'd1;
					cmd_type<=4'b0000;
				end
				else begin
					flash_cmd <= 8'h20;
					flash_addr <= 24'd0;
					cmd_type <= 4'b1010;
				end
			end
			
	      4'd3:begin	//waitting 100 clock
				if(time_delay < 8'd100)begin
					flash_cmd <= 8'h00;
					time_delay <= time_delay + 8'd1;
					cmd_type <= 4'b0000;
				end
				else begin
					i <= i + 4'd1;
					time_delay <= 8'd0;
				end	
			end
			
			4'd4:begin	//读状态寄存器1, 等待idle
				if(Done_Sig)begin 
					if(mydata_o[0] == 1'b0)begin
						flash_cmd <= 8'h00;
						i <= i + 4'd1;
						cmd_type <= 4'b0000;
					end
					else begin
						flash_cmd <= 8'h05;
						cmd_type <= 4'b1011;
					end
				end
				else begin
					flash_cmd <= 8'h05;
					cmd_type <= 4'b1011;
				end
			end
			
	      4'd5:begin	//写Write disable instruction
				if(Done_Sig)begin
					flash_cmd <= 8'h00;
					i <= i + 4'd1;
					cmd_type <= 4'b0000;
				end
				else begin
					flash_cmd <= 8'h04;
					cmd_type <= 4'b1100;
				end
			end
			
			4'd6:begin	//读状态寄存器1, 等待idle
				if(Done_Sig)begin
					if(mydata_o[0] == 1'b0)begin
						flash_cmd <= 8'h00;
						i <= i + 4'd1;
						cmd_type <= 4'b0000;
					end
					else begin
						flash_cmd <= 8'h05;
						cmd_type <= 4'b1011;
					end
				end
				else begin
					flash_cmd <= 8'h05;
					cmd_type <= 4'b1011;
				end
			end
			
	      4'd7:begin	//写Write Enable instruction
				if(Done_Sig)begin
					flash_cmd <= 8'h00;
					i <= i + 4'd1;
					cmd_type <= 4'b0000;
				end
				else begin
					flash_cmd <= 8'h06;
					cmd_type <= 4'b1001;
				end 
			end
			
	      4'd8:begin	//waitting 100 clock
				if(time_delay < 8'd100)begin
					flash_cmd <= 8'h00;
					time_delay <= time_delay + 8'd1;
					cmd_type <= 4'b0000;
				end
				else begin
					i <= i + 4'd1;
					time_delay <= 8'd0;
				end	
			end
			
	      4'd9:begin	//page program: write 0~255 to flash
				if(Done_Sig)begin
					flash_cmd <= 8'h00;
					i <= i + 4'd1;
					cmd_type <= 4'b0000;
				end
				else begin
					flash_cmd <= 8'h02;
					flash_addr <= 24'd0;
					cmd_type <= 4'b1101;
				end
			end
			
	      4'd10:begin	//waitting
				if(time_delay < 8'd100)begin
					flash_cmd <= 8'h00;
					time_delay <= time_delay + 8'd1;
					cmd_type <= 4'b0000;
				end
				else begin
					i <= i + 4'd1;
					time_delay <= 8'd0;
				end	
			end
			
			4'd11:begin	//读状态寄存器1, 等待idle
				if(Done_Sig)begin 
					if(mydata_o[0] == 1'b0)begin
						flash_cmd <= 8'h00;
						i <= i + 4'd1;
						cmd_type <= 4'b0000;
					end
					else begin
						flash_cmd <= 8'h05;
						cmd_type <= 4'b1011;
					end
				end
				else begin
					flash_cmd <= 8'h05;
					cmd_type <= 4'b1011;
				end
			end
			
	      4'd12:begin	//写Write disable instruction
				if(Done_Sig)begin
					flash_cmd <= 8'h00;
					i <= i + 4'd1;
					cmd_type <= 4'b0000;
				end
				else begin
					flash_cmd <= 8'h04;
					cmd_type <= 4'b1100;
				end		
			end
			
			4'd13:begin	//读状态寄存器1, 等待idle
				if(Done_Sig)begin
					if(mydata_o[0] == 1'b0)begin
						flash_cmd <= 8'h00;
						i <= i + 4'd1;
						cmd_type <= 4'b0000;
					end
					else begin
						flash_cmd <= 8'h05;
						cmd_type <= 4'b1011;
					end
				end
				else begin
					flash_cmd <= 8'h05;
					cmd_type <= 4'b1011;
				end
			end
			
			4'd14:begin	//read 256byte
				if(Done_Sig)begin
					flash_cmd <= 8'h00;
					i <= i + 4'd1;
					cmd_type <= 4'b0000;
				end
				else begin
					flash_cmd <= 8'h03;
					flash_addr <= 24'd0;
					cmd_type <= 4'b1110;
				end
			end
			
			4'd15:begin	//idle
				i <= 4'd15;
			end
			
		endcase
	end
end


//产生25Mhz的SPI Clock		  
always @(posedge CLK)
begin
   if(!RSTn)begin
		clock25M <= 1'b0;
	end
	else begin
		clock25M <= ~clock25M;
	end
end

endmodule

顶层模块
`timescale 1ns/1ps

//Module Name	:	flash
//Description	:	top_file
//Editor		:	Yongxiang
//Time			:	2020-02-03

module 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

你可能感兴趣的:(FPGA,fpga,verilog,spi)