(四)SPI协议

简介

SPI(serial peripheral interface):串行外设接口,高速全双工同步通信总线

接口信号

信 号 名方 向位 宽描 述
sclk/sckoutput1SPI时钟
csnoutput1片选信号
mosioutput1master output slave input
misooutput1master input slave output

可以一对一,一对多的连接:

 注意是同名相连,MOSI—>MOSI,MISO—>MISO

 四种传输模式:

时钟极性CPOL(clock polarity):时钟的初始状态是高电平还是低电平

  1. 0 :时钟高电平时有效,空闲时为低
  2. 1 :时钟低电平时有效,空闲时为高

时钟相位CPHA(clock phase) :采样(按MISO方向来说)在第一个边沿还是第二个边沿

  1. 0 :MISO数据采样发生在时钟(sclk)第一个边沿,MOSI反之
  2. 1 :MISO数据采样发生在时钟(sclk)第二个边沿,MOSI反之

 

 下列代码按模式0,描述的是master:

`timescale 1ns/1ps
module m_spi #(
		parameter  			 		MCLK       =100,
		parameter  			 		SCLK       = 10,
		parameter  			 		DATA_WIDTH =  8,
		parameter  			 		CMD_DATA   = 12)(
		input      			 		clk      ,
		input	   			 		rst_n    ,
		input      			 		cmd_vld  ,
		input      [CMD_DATA-1:0] 	cmd_in   ,
		output reg           		cmd_rdy  ,
		output reg           		sclk     ,
		output reg           		csn      ,
		output               		mosi     ,
		input                		miso     ,
		output reg	         		read_vld ,
		output reg [DATA_WIDTH-1:0] read_data
);
		localparam BAUD_NUM = MCLK/SCLK;
		localparam   [2:0] IDLE  = 3'b000;
		localparam   [2:0] WADDR = 3'b001;
		localparam   [2:0] WDATA = 3'b010;
		localparam   [2:0] RWAIT = 3'b011;
		localparam   [2:0] RDATA = 3'b100;
		
		reg [CMD_DATA-1:0] cmd_in_buf;
		reg         [11:0] shift_data;
		reg          [4:0] baud_cnt;
		reg          [4:0] bit_cnt ;
		reg          [2:0] wait_time;
		reg 			   swr_done;
		wire      		   sclk_pdg;
		wire      		   sclk_ndg;
		reg          [2:0] cur_sta;
		reg          [2:0] nxt_sta;	
	
	// cmd_in buffer
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			cmd_in_buf <= {(CMD_DATA){1'b0}};
		else if(cmd_vld&&cmd_rdy)
			cmd_in_buf <= cmd_in;
		else
			cmd_in_buf <= cmd_in_buf;
	end
	
	//generate cmd_rdy
	always @(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			cmd_rdy <= 1'b1;
		else if(cmd_vld && cmd_rdy)
			cmd_rdy <= 1'b0;
		else if((cur_sta==WDATA&&swr_done) || (cur_sta==RDATA&&read_vld))
			cmd_rdy <= 1'b1;
		else
			cmd_rdy <= cmd_rdy;
	end
	
	//generate baud
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			baud_cnt <= 4'b0;
		else if(baud_cnt==BAUD_NUM-1)
			baud_cnt <= 4'b0;
		else if(cur_sta==IDLE)
			baud_cnt <= 4'b0;
		else if(cur_sta!=IDLE&&cur_sta!=RWAIT)
			baud_cnt <= baud_cnt+1'b1;
	end

	// bit cnt
	always@( posedge clk or negedge rst_n)
	begin
		if( !rst_n)
			bit_cnt<=4'b0;
		else if(bit_cnt==CMD_DATA-1&&sclk_ndg)
			bit_cnt <= 4'b0;
		else if(sclk_ndg)
			bit_cnt <= bit_cnt+1'b1;
	end
	
	//wait_time
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			wait_time = 3'b0;
		else if(wait_time==3'd2)//set wait time
			wait_time <= 3'b0;
		else if(cur_sta==RWAIT)
			wait_time <= wait_time+1'b1;
		else 
			wait_time <= wait_time;
	end

	// generate csn singal
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			csn <= 1'b1;
		else if(cmd_vld&&cmd_rdy || wait_time==3'd2)//低电平有效
			csn <= 1'b0;
		else if(cur_sta==WDATA&&swr_done)
			csn <= 1'b1;
		else if(cur_sta==RDATA&&swr_done)
			csn <= 1'b1;
		else if(cur_sta==RWAIT)
			csn <= 1'b1;
		else 
			csn <= csn;
	end
	
	//generate sclk singal
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			sclk <= 1'b0;
		else if(sclk_pdg||sclk_ndg)
			sclk <= ~sclk;
		else if(csn)
			sclk <= 1'b0;
	end
	
	//generate mosi singal
	always@(posedge clk or negedge rst_n)
	begin
	if(!rst_n)
		shift_data <= 12'b0;
	else if(!cmd_in_buf[11]&&bit_cnt==4'd3&&sclk_ndg)//读
		shift_data <= 12'b0;
	else if(cmd_vld)
		shift_data <= cmd_in;
	else if((cur_sta==WADDR||cur_sta==WDATA)&&sclk_ndg)
		shift_data<= {shift_data[10:0],1'b0};
	end
	
	assign mosi = shift_data[11];//mosi下降沿发送数据,在模式0下工作,时钟极性CPOL=0,时钟相位CPHA=0(miso第一个沿采样)
	
	//generate read data
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			read_data <= 8'b0;
		else if(cur_sta==RDATA&&sclk_pdg)
			read_data= {read_data[6:0],miso};
	end
	
	//generate read_vld singal
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			read_vld <= 1'b0;
		else if (cur_sta==RDATA&&swr_done)
			read_vld <= 1'b1;
		else
			read_vld <= 1'b0;
	end
	
	assign sclk_pdg  = (baud_cnt==(BAUD_NUM>>1)-1);//表示sclk的上升沿
	assign sclk_ndg  = (baud_cnt==BAUD_NUM-1);     //表示sclk的下降沿
	assign swr_done  = (bit_cnt==4'd11&&sclk_ndg); //write、read finish
	
	//state machine
	always@( posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			cur_sta <= IDLE;
		else
			cur_sta <= nxt_sta;
	end	
	
	always@(*)
	begin
		case(cur_sta)
			IDLE  : 
					nxt_sta = (cmd_vld && cmd_rdy) ? WADDR : IDLE;
			WADDR : begin
						if(cmd_in_buf[CMD_DATA-1]&&(bit_cnt==4'd3)&&sclk_ndg)//写地址完成
							nxt_sta = WDATA;
						else if (!cmd_in_buf[CMD_DATA-1]&&(bit_cnt==4'd3)&&sclk_ndg)//读
							nxt_sta = RWAIT;
						else
							nxt_sta = WADDR;
					end
			WDATA : 
					nxt_sta = swr_done ? IDLE : WDATA;
			RWAIT : 
					nxt_sta = wait_time==3'd2 ? RDATA : RWAIT;
			RDATA : 
					nxt_sta = read_vld ? IDLE : RDATA;
		   default: 
					nxt_sta = IDLE;
		endcase
	end

endmodule
`timescale 1ns/1ps
module s_spi(
	input	sclk ,
	input	rst_n,
	input	csn  ,
	input	mosi ,
	output  miso
);
	reg  [3:0] bit_cnt;
	reg  	   wr_flag;
	reg  [2:0] addr;
	reg  [7:0] data;
	reg  [7:0] shift_data;
	reg  [7:0] mem [0:7];
	integer i;

	//bit cnt
	always@(posedge sclk or negedge rst_n)
	begin
		if(!rst_n)
			bit_cnt <= 4'b0;
		else if(bit_cnt==4'd11)
			bit_cnt <= 4'b0;
		else
			bit_cnt <= bit_cnt+1'b1;
	end
	//wr_flag
	always @( posedge sclk or negedge rst_n)
	begin
		if(!rst_n)
			wr_flag <= 1'b0;
		else if(bit_cnt == 4'b0 && !csn)
			wr_flag <= mosi;
	end
	// addr
	always @(posedge sclk or negedge rst_n)begin
		if(!rst_n)
			addr <= 3'b0;
		else if(!csn && bit_cnt >= 1 && bit_cnt <= 3)
			addr <= {addr[1:0],mosi};
	end
	
	//write_data
	always @(posedge sclk or negedge rst_n)
	begin
		if(!rst_n)
			data <= 8'b0;
		else if(!csn && bit_cnt >= 4&& bit_cnt<= 11)
			data<= {data[6:0],mosi};
	end
	
	// mem
	always @(negedge sclk or negedge rst_n)
	begin
		if(!rst_n)begin
			for(i=0;i<=7;i=i+1)
				mem[i] <= {8{1'b0}};
		end
		else if(bit_cnt == 4'd0 && wr_flag)
			 mem[addr] <= data;
	end
	
	always@(negedge sclk or negedge rst_n or negedge csn)
	begin
		if(!rst_n || csn)
			shift_data <= 8'b0;
		else if(bit_cnt == 4'd4 && !wr_flag && !csn)
			shift_data <= mem[addr] ;
		else if(!csn)
			shift_data <= {shift_data[6:0],1'b0};
	end
	
	assign miso = shift_data[7] ;
	
endmodule

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值