FPGA实现SPI协议

SPI接口

1 简单的设计模块1

在这里插入图片描述
首先简单的想一下这个模块应该怎么设计。
拿到这个小题目你的思路是怎么样的呢?很多时候靠经验设计,并没有一个顺序的思路。

在这里插入图片描述
六步法:
第一步:输入输出波形的画出
在这里插入图片描述

第二步:画出计数器结构(搞清楚数的是什么东西)
在这里插入图片描述
cnt表示上一个时钟数到的结果。数x下,通用表达式:add_cnt&&cnt==x-1;

第三步:确认计数器加1条件(数什么)和结束条件(数多少个),注意先考虑加1,在考虑结束条件;
我们计数器cnt数的是什么呢?dout==1的时钟个数,cnt要数10个(10是功能要求来的)

第四步:确认其他信号的变化条件(dout 变化点,即0变1,1为0的条件)
dout由0变1 的条件是什么?是en1;
dout由1变0的条件是? dout
1的时钟个数为10

cnt add_cnt:dout ==1
cnt 数多少:10// 计数器模板
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		cnt <= 0;
	end
	else if(add_cnt) begin
		if(end_cnt)
			cnt <= 0;
		else
			cnt <= cnt + 1;
	end
end

assign add_cnt = dout == 1;   // 加1的条件
assign end_cnt = add_cnt && cnt == 10-1;  // 数10下



dout == 1 : en == 1
dout == 0 : 数到10个

always@(posedge clk or negedge rst_n) begin
	if(rst_n==1'b0) begin
		dout <= 0;
	end
	else if(en == 1) begin     // dout什么时候拉高
		dout <= 1;
	end
	else if (end_cnt) begin  //(add_cnt && cnt == 10-1) begin    // 数10下之后dout拉低
		dout <= 0;
	end
end

第五步:写出计数器代码(always除了名字外不能改变,加1条件即是要数什么东西,结束条件要记住格式后)

2 简单模块设计2

在这里插入图片描述
对于这种状态还是数cnt, 可以这种思考。
在这里插入图片描述
也可以这样计数。这是正确的计数方式
在这里插入图片描述
第一步:画出输入输出波形
第二步:画出计数器结构

cnt0的加1条件: flag == 1 ,加一个信号把flag
cnt0 要数多少:3个

cnt1的加1条件:end_cnt0  // 
cnt1 要数多少个:3个

flag >1: en == 1
flag >0: end_cnt1

dout>1: add_cnt0 && cnt0 == 1-1 // 当cnt0 数一个的时候拉高
dout>0: end_cnt0 (add_cnt0 && cnt0 == 3-1)

然后就填填空啦!!!!!!

3 SPI通信协议

3.1 SPI通信协议原理

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
CS 选择工作模式
DIN选择通道地址
DOUT 输出给FPGA

在这里插入图片描述

在这里插入图片描述

module SPI(
	input clk,
	input rst_n,
	input start,
	input [2:0]channel,

	//  ADC128s022
	input DOUT,
	output reg SCLK,
	output reg DIN,
	output reg CS_N,
	
	output reg done,
	output [11:0] data.
	

);

	reg en;
	reg [2:0] r_channel;
	// r_channel 使channel的信号稳定
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n)
			r_channel <= 'd0;
		else if(start)
			r_channel <= channel;
		else
			r_channel <= r_channel;
	end

	// 转换使能信号
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n)
			en <= 1'b0;
		else if(start)
			en <= 1'b1;
		else if(done)
			en <= 1'b0;
		else
			en <= en;
	end

	reg [4:0] cnt;
	reg cnt_flag;
	reg [5:0]SCLK_CNT;
	
	// cnt 的变化
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n)
			cnt <= 'd0;
		else if(en) begin
			if(cnt == 'd10)
				cnt <= 'd0;
			else
				cnt <= cnt + 1'b1;
		end
		else
			cnt <= 'd0;
	end

	// cnt flag 
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n)
			cnt_flag <= 1'b0;
		else if(cnt == 'd10)
			cnt_flag <= 1'b1;
		else
			cnt_flag <= 1'b0;
	end

	//  sclk_cnt
	always@	(posedge clk or negedge rst_n) begin
		if(!rst_n)
			sclk_CNT <= 'd0;
		else if(en) begin
			if(SCLK_CNT == 'd33)
				SCLK_CNT <= 'd0;
			else if(cnt_flag)
				SCLK_CNT <= SCLK + 1'b1;
			else
				SCLK_CNT <= SCLK_CNT;
		end
		else
			SCLK_CNT <= 'd0;
	end
	
	reg [11:0] r_data;
	// ============================================
	always@(posedge clk negedge rst_n) begin
		if(!rst_n) begin
			SCLK <= 1'b1;
			CS_N <= 1'b1;
			DIN <= 1'b1;
		end
		else if(en) begin
			case(SCLK_CNT)
				6'd0:begin CS_N <= 1'b0; end
				6'd1:begin SCLK <= 1'b0;DIN <= 1'b0; end
				6'd2:begin SCLK <= 1'b1; end
				6'd3:begin SCLK <= 1'b0; end
				6'd4:begin SCLK <= 1'b1; end
				6'd5:begin SCLK <= 1'b0; DIN <= r_channel[2];end
				6'd6:begin SCLK <= 1'b1; end
				6'd7:begin SCLK <= 1'b0; DIN <= r_channel[1];end
				6'd8:begin SCLK <= 1'b1; end
				6'd9:begin SCLK <= 1'b0; DIN <= r_channel[0];end
				6'd10,6'd12,6'd14,6'd16,6'd18,6'd20,6d'22,6d'24,6'd26,6'd28,6'd30,6'd32:
				begin SCLK <= 1'b1; r_data <= {
    
    r_data[10:0], DOUT}; end
				6'd11,6'd13,6'd15,6'd17,6'd19,6'd21,6d'23,6d'25,6'd27,6'd29,6'd31,6'd33:
				begin SCLK <= 1'b0;end
				6'd33:begin CS_N <= 1'b1;end
				default: begin CS_N <= 1'b1;end				
				
				
			endcase
		end
		else begin
			SCLK <= 1'b1;
			CS_N <= 1'b1;
			DIN <= 1'b1;			
		end

	end

	//   done 信号
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n)
			done <= 1'b0;
		else if(SCLK_CNT == 'd33)
			done <= 1'b1;
		else
			done <= 1'b0;
	end

	//  data 信号
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n)
			data <= 'd0;
		else if(SCLK_CNT == 'd33)
			data <= r_data;
		else
			data <= data;
	end


endmodule
`timescale 1ns/1ns
module SPI_tb;

	reg clk;
	reg rst_n;
	reg start;
	reg [2:0] channel;

	wire SCLK;
	DIN;
	CS_N;
	DOUT;
	
	wire done;
	wire [11:0]data;
	
	SPI SPI_inst(
		.clk(clk),
		.rst_n(rst_n),
		.start(start),
		.channel(channel),

		.SCLK(SCLK),
		.DIN(DIN),
		.CS_N(CS_N),
		.DOUT(DOUT),
		.done(done),
		.data(data)

	);
	
	initial clk = 1'b1;
	alwayss#10 clk = ~clk;
	initial begin
		rst_n = 1'b0;
		channel = 'd0;
		start = 1'b0;
		DOUT = 1'b0;
	
		
	end

	endmodule

3.1 SPI原理2

SPI是微控制器和外围IC 如传感器 adc 和dac 移位寄存器、SRAM等。之间使用最广泛的接口之一。SPI是一种同步、全双工、主从式接口。来自主机或从机的数据在时钟上升沿或下降沿同步。主机和从机可以同时传输数据。SPI接口可以是三线式或四线。

在这里插入图片描述
CS 低电平有效
SPI的环形数据收发模式,SPI在收发数据的原理很简单,就是两个移位寄存器,待发送的数据首先写入din_buf 中缓存,需要发送的时候输入进data_shift 中,通过移位的方式发送高位数据,同时接受到的数据存入低位,写满时存入到dout_buf 中,再由dout_buf 写入内部总线。
在这里插入图片描述
代码放到:https://download.csdn.net/download/qq_30093417/86757879

猜你喜欢

转载自blog.csdn.net/qq_30093417/article/details/127241641