• 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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    第五步:写出计数器代码(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)
    
    然后就填填空啦!!!!!!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    `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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    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

  • 相关阅读:
    XSS之冷门事件
    软件工程 阶段测试三
    期权如何交易?期权如何做模拟交易?
    4.Spring EL运算符
    [附源码]JAVA毕业设计高校在线办公系统(系统+LW)
    springboot大学生就业招聘网站java ssm
    rar格式转换zip格式,如何做?
    Web of science,scopus,Google scholar的介绍和区别
    gateway和nginx网关的区别
    UE4/UE5像素流送云推流|程序不稳定、弱网画面糊怎么办?
  • 原文地址:https://blog.csdn.net/qq_30093417/article/details/127241641