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))
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)