SPI主机实现
发布日期:2021-05-07 22:56:10 浏览次数:21 分类:精选文章

本文共 8040 字,大约阅读时间需要 26 分钟。

SPI主机实现

一、硬件层

(1)外部引线

SPI通信的最小结构为一主一从结构,主机向从机提供信号发送接收时钟SCLK。主机与从机之间存有四根引线,即MOSI(主收从发)、MISO(主发从收)、SCK(通信时钟)、CS(从机片选信号)。其中,通过在主机上增加片选信号输出端的数目,或引入编码,译码结构可以使得主机控制更多的从机,实现一主多从的SPI通信。本设计中默认为一主一从最小结构。

(2)四种工作模式

SPI通信协议规定了4钟工作模式,在实际应用中应当保证主机和从机工作在相同的工作模式下。SPI工作模式通过时钟极性CPOL和时钟相位CPHA联合指定。其中CPOL指定SCK在空闲状态时的电平,CPHA指定在SCK的何种边缘进行数据采样。其标识如下:

在这里插入图片描述
在这里插入图片描述

二、FPGA代码实现

(1)思路

SPI模式0主机控制器在SCK上升沿时对数据进行采样。由于在时钟上升沿进行数据采样,那么,接收/发送状态机的状态应当早于SCK时钟上升沿提前准备好接收/发送状态,也就是说在低电平期间就需要准备好

对于发送状态机,由于数据在SCK时钟的下降沿进行状态变换,又因为从机在上升沿采样,因此选择在SCK的低电平中心将需发送的数据压至MOSI线上。

对于接收状态机,同样采用SCK下降沿进行状态跳变。接收状态机在SCK上升沿时将数据采集至接收缓冲口,并在采集完一字节数据后生成信号标志脉冲。

对于SPI主机控制器,另一个重要的模块是控制模块,它是用来生成SCK时钟与CS片选信号的模块。由于本文设计只有一个从机,因此只单纯的控制CS的电平。SCK时钟的生成也是依靠状态机,其机理为当状态机处于IDLE空闲状态时,检测到读/写请求,在时钟相位变为下降沿时进入工作状态,并输出8个完整的SCK时钟。此后,状态机转入STOP状态,并在一个周期内检测是否有新的请求信号到来,若无抵达空闲状态,若有,转入工作状态。

(2)代码实现

宏定义文件

//// Company: NanJing University of Information Science & Technology// Engineer: Yang Cheng Yu// // Create Date: 2020/01/13 20:01:50// Design Name: spi_master// Module Name: global_define// Project Name: SPI2// Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision:// Revision 0.01 - File Created// Additional Comments:// //`define SCK_PEDG (phase==9'd499)//默认使用100KHZ的频率,50M/100k = 500`define SCK_HIGH (phase==9'd124)`define SCK_NEDG (phase==9'd249)`define SCK_LOW  (phase==9'd374)

控制模块

`include "global_definition.v"//// Company: NanJing University of Information Science & Technology// Engineer: Yang Cheng Yu// // Create Date: 2020/01/13 20:01:50// Design Name: spi_master// Module Name: control_module// Project Name: SPI2// Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision:// Revision 0.01 - File Created// Additional Comments:// //module control_module(	input 				clk,	input 				rst,	output reg 			sck,	output reg 			cs_n,	output reg[8:0] 	phase,	input 				wr_req,	input 				rd_req,	output reg[3:0] 	cnt//位数计数器);	reg[1:0] state;	reg[1:0] next_state;		localparam IDLE=2'b00;	localparam WORK=2'b01;	localparam STOP=2'b11;		always@(posedge clk or negedge rst)begin		if(!rst)			phase <= 9'd0;		else			if(phase==9'd499)				phase <= 9'd0;			else				phase <= phase + 1'b1;	end		//状态机状态转移逻辑	always@(posedge clk or negedge rst)begin		if(!rst)			state <= IDLE;		else			if(`SCK_NEDG)				state <= next_state;			else				state <= state;	end		//状态组合判断	always@(wr_req or rd_req or cnt)begin		case(state)			IDLE:				if(wr_req||rd_req)					next_state <= WORK;				else					next_state <= IDLE;			WORK:				if(cnt==4'd8)					next_state <= STOP;				else					next_state <= WORK;			STOP:				if(wr_req||rd_req)					next_state <= WORK;				else					next_state <= IDLE;			default:				next_state <= IDLE;		endcase	end		//状态机输出	always@(posedge clk or negedge rst)begin		if(!rst)begin			sck <= 1'b0;			cs_n <= 1'b1;			cnt <= 4'd0;		end		else begin			case(state)				IDLE:begin					sck <= 1'b0;					cs_n <= 1'b1;					cnt <= 4'd0;				end				WORK:begin					cs_n <= 1'b0;					if(`SCK_PEDG)begin						sck <= 1'b1;						cnt <= cnt + 1'b1;					end					else if(`SCK_NEDG)						sck <= 1'b0;					else						sck <= sck;				end				STOP:begin					cnt <= 4'd0;					sck <= 1'b0;					cs_n <= 1'b1;				end				default:begin					cnt <= cnt;					sck <= sck;					cs_n <= cs_n;				end			endcase		end	endendmodule

发送模块

`include "global_definition.v"//// Company: NanJing University of Information Science & Technology// Engineer: Yang Cheng Yu// // Create Date: 2020/01/13 20:01:50// Design Name: spi_master// Module Name: spi_tx// Project Name: SPI2// Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision:// Revision 0.01 - File Created// Additional Comments:// //module spi_tx(	input 			clk,	input 			rst,	input 			wr_req,	input [7:0]		data_wr,	input [8:0]		phase,	output reg 		wr,	output reg 		mosi,	input [3:0]		cnt);	reg[1:0] state;	reg[1:0] next_state;	localparam IDLE=2'b00;	localparam WORK=2'b01;	localparam STOP=2'b11;		//状态机状态转移	always@(posedge clk or negedge rst)begin		if(!rst)			state <= IDLE;		else			state <= next_state;	end		//状态组合判定	always@(wr_req or cnt)begin		case(state)			IDLE:				if(wr_req)					next_state <= WORK;				else					next_state <= IDLE;			WORK:				if(cnt==4'd8)					next_state <= STOP;				else					next_state <= WORK;			STOP:				if(wr_req)					next_state <= WORK;				else					next_state <= IDLE;			default:				next_state <= next_state;		endcase	end		//状态机输出	always@(posedge clk or negedge rst)begin		if(!rst)begin			mosi <= 1'b0;			wr <= 1'b1;		end		else begin			case(state)				IDLE:begin					mosi <= 1'b0;					wr <= 1'b1;				end				WORK:begin					wr <= 1'b0;					//由于上升沿采样,所以在上升沿到来前把数据给到MOSI					if(`SCK_LOW)begin						case(cnt)							4'd0:mosi <= data_wr[7];							4'd1:mosi <= data_wr[6];							4'd2:mosi <= data_wr[5];							4'd3:mosi <= data_wr[4];							4'd4:mosi <= data_wr[3];							4'd5:mosi <= data_wr[2];							4'd6:mosi <= data_wr[1];							4'd7:mosi <= data_wr[0];							default:mosi <= 1'b0;						endcase					end				end				STOP:begin					wr <= 1'b1;					mosi <= 1'b0;				end				default:begin					wr <= wr;					mosi <= mosi;				end			endcase		end	endendmodule

接收模块

`include "global_definition.v"//// Company: NanJing University of Information Science & Technology// Engineer: Yang Cheng Yu// // Create Date: 2020/01/13 20:01:50// Design Name: spi_master// Module Name: spi_rx// Project Name: SPI2// Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision:// Revision 0.01 - File Created// Additional Comments:// //module spi_rx(	input 				clk,	input 				rst,	input 				rd_req,	output reg[7:0] 	data_rd,	input[8:0] 			phase,	output reg 			rd,	input 				miso,	input[3:0] 			cnt);	reg[1:0] state;	reg[1:0] next_state;	localparam IDLE=2'b00;	localparam WORK=2'b01;	localparam STOP=2'b11;	//状态机状态转移	always@(posedge clk or negedge rst)begin		if(!rst)			state <= IDLE;		else			state <= next_state;	end		//状态组合判定	always@(rd_req or cnt)begin		case(state)			IDLE:				if(rd_req)					next_state <= WORK;				else					next_state <= IDLE;			WORK:				if(cnt==4'd8)					next_state <= STOP;				else					next_state <= WORK;			STOP:				if(rd_req)					next_state <= WORK;				else					next_state <= IDLE;				default:					next_state <= next_state;		endcase	end		//状态机输出	always@(posedge clk or negedge rst)begin		if(!rst)begin			rd <= 1'b1;			data_rd <= 8'b0000_0000;		end		else begin			case(state)				IDLE:begin					rd <= 1'b1;					data_rd <= 8'b0000_0000;				end				WORK:begin					rd <= 1'b0;					if(`SCK_PEDG)begin//下降沿采样						case(cnt)							4'd0:data_rd[7] <= miso;							4'd1:data_rd[6] <= miso;							4'd2:data_rd[5] <= miso;							4'd3:data_rd[4] <= miso;							4'd4:data_rd[3] <= miso;							4'd5:data_rd[2] <= miso;							4'd6:data_rd[1] <= miso;							4'd7:data_rd[0] <= miso;							default:data_rd <= data_rd;						endcase					end					else						data_rd <= data_rd;				end				STOP:begin					rd <= 1'b1;					data_rd <= data_rd;				end				default:begin					rd <= rd;					data_rd <= data_rd;				end			endcase		end	endendmodule

顶层模块及例化

//// Company: NanJing University of Information Science & Technology// Engineer: Yang Cheng Yu// // Create Date: 2020/01/13 20:01:50// Design Name: spi_master// Module Name: spi_master// Project Name: SPI2// Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision:// Revision 0.01 - File Created// Additional Comments:// //module spi_master(	input 				clk,	input 				rst,	input 				miso,	output 	 			mosi,	output 	 			sck,	output 	 			cs_n,	output 	 			wr,	output 	 			rd,	input[7:0] 			data_wr,	output[7:0]	data_rd,	input 				wr_req,	input 				rd_req);	wire[8:0] 			phase;	wire[3:0] 			cnt;		control_module control_module(	.clk		(clk),	.rst		(rst),	.sck		(sck),	.cs_n		(cs_n),	.phase		(phase),	.wr_req		(wr_req),	.rd_req		(rd_req),	.cnt		(cnt)//位数计数器);	spi_tx spi_tx(	.clk		(clk),	.rst		(rst),	.wr_req		(wr_req),	.data_wr	(data_wr),	.phase		(phase),	.wr			(wr),	.mosi		(mosi),	.cnt        (cnt));	spi_rx spi_rx(	.clk		(clk),	.rst		(rst),	.rd_req		(rd_req),	.data_rd	(data_rd),	.phase		(phase),	.rd			(rd),	.miso		(miso),	.cnt        (cnt));endmodule

RTL视图

在这里插入图片描述
状态机图
在这里插入图片描述

上一篇:4*4矩阵键盘的FPGA驱动
下一篇:基于FPGA的数字钟——(二)按键消抖模块设计

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2025年04月01日 12时23分50秒