FPGA的SPI从机模块实现

一. SPI总线协议

        SPI(Serial Peripheral Interface)接口,中文为串行外设接口。它只需要3根线或4根线即可完成通信工作(这里讨论4根线的情况)。
        这4根通信线分别为NCS/NSS(片选信号)、SCK/SCLK(串行同步时钟)、MOSI/SDO(主机输出从机输入,Master Output Slave Input)、MISO/SDI(主机输入从机输出)。
        SPI通信有四种方式,由CPOL(时钟极性)、CPHA(时钟相位)的4种组合决定的。CPOL决定总线空闲时,SCK是高电平还是低电平(CPOL=,0,无数据传输时,SCK=0;CPOL=1,无数据传输时,SCK=1)。CPHA决定在数据开始传输时,SCK第几个跳变沿采集数据(CPHA=0,开始传输时,在第一个跳变沿采集数据,第二个跳变沿改变发送数据(即改变MISO或者MOSI线上电平);CPHA=1,开始传输是,在第一个跳变沿改变发送的数据,在第二个跳变沿采集数据)(见图1)。

图1
        确立可靠通信前,必须保证主从机处于同一种的传输方式,这里为方便起见,专门以CPOL=0,CPHA=0的传输方式进行讨论。 需要注意的是:在CPOL=0,CPHA=0的情况下,主从机都在SCK上跳沿对数据进行采集,SCK下跳沿改变总线电平(见图2)。


图2
这里在使用FPGA实现SPI模块时,做一个规定:
1. 使用CPOL=0,、CPHA=0的传输方式;
2. 传输时,以最高位先输出,最后输出最低位;
3. FPGA实现的SPI模块作从机,SCK由外部主机提供;
4. 通信数据长度为8位。

二. FPGA的SPI从机实现

        实现SPI从机,可以分为两个模块:一个是SPI接收模块,另一个则是SPI发送模块。

1. 首先确定模块的输出输入管脚

        由标题一可以知道,SPI通信脚有4根线,我们还是用到时钟总线和模块复位脚,因此模块管脚可以定义为
module myspi(nrst, clk, ncs, mosi, miso, sck);
input clk, nrst;
input ncs, mosi, sck;
output miso;

2. SCK跳变沿检测

        原理十分简单:使用寄存器记录SCK状态,由状态判断SCK是否出现跳变沿。
reg[2:0] sck_edge;
	always @ (posedge clk or negedge nrst)
	begin
		if(~nrst)
		begin
			sck_edge <= 3'b000;
		end
		else
		begin
			sck_edge <= {sck_edge[1:0], sck};
		end
	end
wire sck_riseedge, sck_falledge;
	assign sck_riseedge = (sck_edge[2:1] == 2'b01);  //检测到SCK由0变成1,则认为发现上跳沿
	assign sck_falledge = (sck_edge[2:1] == 2'b10);  //检测到SCK由1变成0,则认为发现下跳沿

3. SPI接收部分

        SPI接收部分使用有限状态机:
状态1:等待SCK上跳沿,并将MOSI的数据移入 移位寄存器byte_received, 接收位数寄存器bit_received_cnt记录接收到的数据位数,接收到8位数据后转入状态2;
状态2:保存移位寄存器byte_received数据到 接收缓存器rec_data, 接收标志位/接收缓存器非空标志位rec_flag置高4个clk时钟周期后转入状态3;
状态3:清除rec_flag并转入状态1。
reg[7:0] byte_received;
	reg[3:0] bit_received_cnt;
	reg rec_flag;
	reg[1:0] rec_status;  //SPI接收部分状态机
	reg[7:0] rec_data;
	reg[2:0] rec_flag_width;  //SPI接收完成标志位脉冲宽度寄存器
	always @ (posedge clk or negedge nrst)  //每次sck都会接收数据,spi的顶端模块状态机决定是否取用
	begin
		if(~nrst)
		begin
			byte_received <= 8'h00;
			bit_received_cnt <= 4'h0;
			rec_flag <= 1'b0;
			rec_status <= 2'b00;
			rec_flag_width <= 3'b000;
		end
		else
		begin
			if(~ncs)
			begin
				case (rec_status)
				2'b00: begin
					if(sck_riseedge)
					begin
						byte_received <= {byte_received[6:0], mosi};
						if(bit_received_cnt == 4'h7)
						begin
							bit_received_cnt <= 4'b0000;
							rec_status <= 2'b01;
						end
						else
						begin
							bit_received_cnt <= bit_received_cnt+1;
						end
					end
				end
				2'b01: begin
					rec_data <= byte_received;
					rec_flag <= 1'b1;
					if(rec_flag_width==3'b100) begin
						rec_flag_width <= 3'b000;
						rec_status <= 2'b11;
					end
					else begin
						rec_flag_width <= rec_flag_width+1;
					end
				end
				2'b11: begin
					rec_flag <= 1'b0;
					rec_status <= 2'b00;
				end
				endcase
			end		
		end
	end

     这里,使用rec_flag的原因是通知另一个模块处理接收数据(后面将会提到),rec_data若在下一次数据传输完成前不做处理则会丢失。

4. SPI发送部分

        SPI从机一般在解析主机发送的命令后,主动发出主机所需数据,所以,SPI发送部分,需要其他模块的触发,并将数据送往MISO管脚。
   SPI发送部分也离不开状态机:
状态1:等待 发送触发标志位send_flag置高,一旦标志位send_flag置高, 发送移位寄存器 byte_sended存储外部触发模块的数据send_data,miso管脚输出发送数据最高位send_data[7],置位 正在发送标志位sending_flag,转入状态2;
状态2:等待SCK上跳沿,即等待主机接收数据最高位后进入状态3;(其实这个状态可有可无的状态)
状态3:在SCK下跳沿,将发送移位寄存器byte_sended最高位移入miso管脚,当发送移位寄存器被移空,清除正在发送标志位sending_flag,进入状态4;
状态4:置低miso管脚,转入状态1。
reg miso;
	reg sending_flag;  //正在发送标志位
	reg[7:0] byte_sended;  //发送移位寄存器
	reg[3:0] bit_sended_cnt;  //SPI发送位计数器
	reg[1:0] send_status;  //SPI发送部分状态机
	always @ (posedge clk or negedge nrst)
	begin
		if(~nrst)
		begin
			byte_sended <= 8'h00;
			bit_sended_cnt <= 4'b0000;
			send_status <= 2'b00;
			sending_flag <= 1'b0;
		end
		else
		begin
			if(~ncs)
			begin
				case (send_status)
				2'b00: begin
					if(send_flag) 
					begin  //锁存发送数据
						send_status <= 2'b01;  //2'b01;
						byte_sended <= send_data;
						sending_flag <= 1'b1;
						miso <= send_data[7];
					end
				end
				2'b01: begin  //发送数据移入移位寄存器
					if(sck_riseedge) begin
						//miso <= byte_sended[7];
						//byte_sended <= {byte_sended[6:0], 1'b0};
						send_status <= 2'b11;
					end
				end
				2'b11: begin  //根据sck下降沿改变数据
					miso <= byte_sended[7];
					if(sck_falledge)   ///---------------------------------------这里多移了一位
					begin
						//miso <= byte_sended[7];
						byte_sended <= {byte_sended[6:0], 1'b0};
						if(bit_sended_cnt == 4'b0111)
						begin
							send_status <= 2'b10;
							bit_sended_cnt <= 4'b0000;
							sending_flag <= 1'b0;
						end
						else
						begin
							bit_sended_cnt <= bit_sended_cnt+1;
						end
					end
				end
				2'b10: begin  //数据发送完毕
					send_status <= 2'b00;
					//sending_flag <= 1'b0;
					miso <= 1'b0;
				end
				endcase
			end
		end
	end

经过实测,SCK频率低于clk频率8倍以上,通信可靠稳定,测试芯片为XC3S50-TQ144,平台为ISE,clk为25MHz。

  • 9
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
SPI主机是指在SPI通信中控制通信的设备。在SPI通信中,主机负责发送和接收数据,而从机则负责接收和发送数据。SPI主机通过提供时钟信号和控制信号来控制从机的操作。SPI主机与从机之间通过几根引线进行连接,包括MOSI(主机发送,从机接收)、MISO(主机接收,从机发送)、SCK(时钟信号)和nSS(从机片选信号)。SPI主机可以控制多个从机实现一主多从的通信结构。\[2\] 在FPGA实现SPI主机功能需要使用相应的SPI驱动模块SPI驱动模块负责控制SPI通信的时序和数据传输。例如,SPI驱动模块spi_drive可以提供SPI模式0的读写驱动功能。SPI驱动模块的具体实现可以参考相关文档和资料。\[3\] 因此,如果你需要在FPGA实现SPI主机功能,你可以使用相应的SPI驱动模块,并根据需要进行配置和连接。这样就可以通过FPGA实现SPI主机与从机之间的通信。 #### 引用[.reference_title] - *1* [FPGA(主机)STM32(从机)SPI通信(HAL库实现)](https://blog.csdn.net/Harry_CHL/article/details/119940708)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [基于FPGASPI主机数据接收发送控制器](https://blog.csdn.net/zhtysw/article/details/90646919)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [FPGA实现SPI协议](https://blog.csdn.net/qq_37041791/article/details/126464479)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值