SPI-Verilog

SPI通讯协议(简介)

SPl ( Serial Peripheral Interface,串行外围设备接口)通讯协议,是Motorola公司提出的一步串行接口技术,是一种高速、全双工、同步通信总线,在芯片中只占用四根管脚用来控制及数据传输。

应用:EEPROM、Flash、RTC、ADC、DSP等。

优缺点︰全双工通信,通讯方式较为简单,相对数据传输速率较快﹔没有应答机制确认数据是否接收,在数据可靠性上有一定缺陷(与l2C相比)。

SPI物理层连接

第一种:单对单(一主一从)

第二种:单对多(一主多从)

spi四根管脚的作用:

SCK(Serial Clock):时钟信号线,用于同步通讯数据;

MOSI (Master Output, Slave lnput):主设备输出/从设备输入引脚

MISO (Master Input,Slave Output):主设备输入/从设备输出引脚

cs (Chip Select):片选信号线,也称为CS_N。

SPI协议层

SPI串行同步时钟可以设置为不同的极性(Clock Polarity ,CPOL)与相位(Clock Phase ,CPHA)。

 

时钟的极性(CPOL):用来决定在总线空闲时,同步时钟(SCK)信号线上的电位是高电平还是低电平。当时钟极性为0时(CPOL=0),SCK信号线在空闲时为低电平;当时钟极性为1时(CPOL=1),SCK信号线在空闲时为高电平;

 

时钟的相位(CPHA):用来决定何时进行信号采样。当时钟相位为1时(CPHA=1),在SCK信号线的第二个跳变沿进行采样;这里的跳变沿究竟是上升沿还是下降沿?取决于时钟的极性。当时钟极性为0时,取下降沿;当时钟极性为1时,取上升沿。或者说是当时钟极性为0时,在奇数边沿取信号,当时钟极性为1时,在偶数边沿取信号。

根据CPOL和CPHA不同SPI具有四种工作模式,如下图:

SPI应用举例-AD7793芯片

芯片手册(网上查)

AD7793芯片是采用4线spi接口用于转换把传输进来的模拟信号转换为数字信号。

AD7793芯片读写时序

 

AD7793芯片写时序

 

 

 

 AD7793芯片读时序

 

 

 AD7793芯片工作主要引脚说明

AD7792/AD7793的串行接口由四个信号组成:CS、DIN、SCLK和DOUT/RDY。

DIN线路用于将数据传输至片内寄存器中,DOUT/RDY则用于从片内寄存器中获取数据。SCLK是器件的串行时钟输入,所有数据传输(无论是DIN上还是DOUT/RDY上)均与SCLK信号相关。

DOUT/RDY引脚也可输出数据就绪信号;当输出寄存器中有新数字字可用时,该线路变为低电平。对数据寄存器的读操作完成时,该线路复位为高电平。数据寄存器更新之前,该线路也会变为高电平,提示在此时不应对器件进行读操作,以确保在更新数据寄存器的过程中不会发生数据读取操作。

AD7793芯片一般工作流程

读写大体流程是:
1、先向通讯寄存器写入地址和读写请求;
2、然后写入或者读取指定bit位宽的数据;
一般流程:
1、读ID寄存器,判断芯片是否符合要求;
2、选择输入通道
3、配置需要增益;
4、进行校准设置
5、发起单次或者连续AD转换读取

 

举例:AD7793控制模块(仅供参考,程序以及过程未验证)

 

输入:

Sys_clk:系统时钟

Sys_rst_n:复位信号

Key_flag:开始信号

MISO(DUT):AD7793(从机)传给主机的信号

 

输出:

Sclk: AD7793的钟控制信号

Cs:AD7793的片选信号

MOSI(DIN):主机输入从机输出

adc_convertion_data:转换完成的数据

AD7793控制模块状态图:

AD7793控制模块时序图

中间变量说明:

State:状态变量;

cnt_sclk:系统时钟的计数变量,主要用于生成sclk时钟用于驱动AD7793,根据手册知道sclk时钟最小为20;所以计数到10时钟翻转;

Cnt_bit:bit计数器,用于计算传输的bit个数;

Cnt_byte:字节计数器,用于计算传输的字节个数;

miso_flag:有效数据标志位,用于提示有效数据生成

data:用于有效数据保存

data_vld:有效数据接收完成标志

 

1、复位------------>向通信寄存器写入001_0000表示写配置寄存器---------->配置寄存器0x6e10----------->通信寄存器写0000_1000

 

 

 

 

模式寄存器写入0x200a---------------->通信寄存器写0101_1100设置为连续读取模式------------------->等待转换完成

 

rdy_n为0-------------->读取转换结果

代码(仅供参考未-不要指望完全对,我是一个新手)

 

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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2023/03/03 19:42:16
// Design Name:
// Module Name: AD7793
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
 
 
module AD7793(
    input wire sys_clk,
    input wire sys_rst_n,
    input wire key_flag,
    input wire miso_dout,
 
    output reg sclk,
    output reg cs,
    output reg mosi_din,
    output reg [23:0] adc_convertion_data
    );
parameter IDLE = 4'b0001;
parameter WR = 4'b0010;
parameter WAIT = 4'b0100;
parameter READ = 4'b1000;
 
reg [3:0] state;
reg [3:0] cnt_clk;
reg [2:0] cnt_bit;
reg [2:0] cnt_byte;
reg miso_flag;
reg data_vld;
reg [23:0] data;
 
 
//状态机的生成
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(~sys_rst_n)
        state <= IDLE;
    else
         case(state)
            IDLE:if (cs == 1'b0)
                    state <= WR;
                else
                    state <= IDLE;
            WR  :if ((cnt_clk == 4'd9)&&(cnt_bit == 3'd7)&&(cnt_byte == 3'd6))
                    state <= WAIT;
                else
                    state <= WR;
            WAIT:if (miso_dout == 1'b0)
                    state <= READ;
                else if(cs == 1'b1)
                    state <= IDLE;
                else
                    state <= WAIT;
            READ:if (cs == 1'b1)
                    state <= IDLE;
                else if((state == READ)&&(cnt_clk == 4'd9)&&(cnt_bit == 3'd7)&&(cnt_byte == 3'd2))
                    state <= WAIT;
                else
                    state <= READ;
            default:state <= IDLE;
         endcase
     
end
 
//片选
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(~sys_rst_n)
        cs <= 1'b1;
    else if(key_flag == 1'b1)
        cs <= ~cs;
    else
        cs <= cs;
end
 
//系统时钟计数
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(~sys_rst_n)
        cnt_clk <= 4'd0;
    else if(cnt_clk == 4'd9)
        cnt_clk <= 4'd0;
    else if(state == WAIT)
        cnt_clk <= 4'd0;
    else if(cs == 1'b1)
        cnt_clk <= 4'd0;
    else
        cnt_clk <= cnt_clk + 1'b1;
end
 
//bit计数
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(~sys_rst_n)
        cnt_bit <= 3'd0;
    else if(cnt_clk == 4'd9)
        cnt_bit <= cnt_bit + 1'b1;
    else
        cnt_bit <= cnt_bit;
end
 
//字节计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(~sys_rst_n)
        cnt_byte <= 3'd0;
    else if((cnt_byte == 3'd6)&&(cnt_bit == 4'd7)&&(cnt_clk == 4'd9))
        cnt_byte <= 3'd0;
    else if((cnt_bit == 4'd7)&&(cnt_clk == 4'd9))
        cnt_byte <= cnt_byte + 1'b1;
    else
        cnt_byte <= cnt_byte;
end
 
//sclk产生
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(~sys_rst_n)
        sclk <= 1'd1;
    else if(state == WAIT)
        sclk <= 1'd1;
    else if(cs == 1'b1)
        sclk <= 1'd1;
    else if(cnt_clk == 4'd4 ||cnt_clk == 4'd9)
        sclk = ~sclk;
    else
        sclk <=sclk;
end
 
//mosi的输出波形
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(~sys_rst_n)
        mosi_din <= 1'b1;
    else if((state == WR)&&(cnt_clk == 4'd4)&&(cnt_bit == 3'd0)&&(cnt_byte == 3'd0))
        mosi_din <= 1'b0;
    else if((state == WR)&&(cnt_clk == 4'd4)&&(cnt_bit == 3'd3)&&(cnt_byte == 3'd0))
        mosi_din <= 1'b1;
    else if((state == WR)&&(cnt_clk == 4'd4)&&(cnt_bit == 3'd4)&&(cnt_byte == 3'd0))
        mosi_din <= 1'b0;
    else if(((state == WR)&&(cnt_byte == 3'd1)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd1||cnt_bit == 3'd4))
        mosi_din <= 1'b1;
    else if(((state == WR)&&(cnt_byte == 3'd1)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd3||cnt_bit == 3'd7))
        mosi_din <= 1'b0;
    else if(((state == WR)&&(cnt_byte == 3'd2)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd3))
        mosi_din <= 1'b1;
    else if(((state == WR)&&(cnt_byte == 3'd2)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd4))
        mosi_din <= 1'b0;
    else if(((state == WR)&&(cnt_byte == 3'd3)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd4))
        mosi_din <= 1'b1;
    else if(((state == WR)&&(cnt_byte == 3'd3)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd5))
        mosi_din <= 1'b0;
    else if(((state == WR)&&(cnt_byte == 3'd5)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd4))
        mosi_din <= 1'b1;
    else if(((state == WR)&&(cnt_byte == 3'd6)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd0||cnt_bit == 3'd2||cnt_bit == 3'd6))
        mosi_din <= 1'b0;
    else if(((state == WR)&&(cnt_byte == 3'd6)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd1||cnt_bit == 3'd3))
        mosi_din <= 1'b1;
    else if(cs == 1'd1)
        mosi_din <= 1'b1;
    else if (miso_dout == 1'd0)
        mosi_din <= 1'b0;
    else
        mosi_din <= mosi_din;
end
 
//miso_flag 接收的bit奇数
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(~sys_rst_n)
        miso_flag <=1'b0;
    else if((state == READ)&&(cnt_clk == 4'd8))
        miso_flag <=1'b1;
    else
        miso_flag <=1'b0;
end
 
//串并转化标识符
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(~sys_rst_n)
        data <= 24'd0;
    else if(miso_flag <=1'b1)
        data <={data,miso_dout};
    else
        data <=data;
end
 
//串并转换完成标识符
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(~sys_rst_n)
        data_vld <= 1'b0;
    else if(state == READ && cnt_bit == 3'd7 && cnt_clk == 4'd9 && cnt_byte == 3'd2)
        data_vld <= 1'b1;
    else
        data_vld <= 1'b0;
end
 
//有效转换数据
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(~sys_rst_n)
        adc_convertion_data <= 24'd0;
    else if(data_vld == 1'b1)
        adc_convertion_data <= data;
    else
        adc_convertion_data <= adc_convertion_data ;
end
 
 
endmodule

  

 

 

 

 

 

 

 

 

图源以及参考(17. 基于spi协议的flash驱动控制 — [野火]FPGA Verilog开发实战指南——基于Altera EP4CE10 征途Pro开发板 文档 (embedfire.com)

 

 

posted @   太平洋3333  阅读(763)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示