SPI以及IIC的verilog实现以及两者之间的对比
一、SPI是一种常用的串行通信接口,与UART不同的地方在于。SPI可以同时挂多个从机,但是UART只能点对点的传输数据,此外SPI有四条线实现数据的传输,而UART采用的是2条实现串行数据的传输
1.SPI的主从机的接口模型
(master和slave在时钟的上升沿采样,下降沿发送数据。数据从最高位(MSB)开始发送。)
用3条通讯总线和1条片选线。
- MOSI:Master Output Slave Input,顾名思义,即主设备输出/从设备输入。数据从主机输出到从机,主机发送数据。
- MISO:Master Iutput Slave Onput,主设备输入/从设备输出,数据由从机输出到主机,主机接收数据。
- SCK:即时钟信号线,用于通讯同步。该信号由主机产生,其支持的最高通讯速率为fpclk/2,即所挂载总线速率的一半。如SPI2挂载在APB1总线上,则其最高速率为36MHz / 2 = 18MHz。类似木桶效应,两个设备之间通讯时,通讯速率受限于较低速的设备。
- NSS:即片选信号线,用于选择通讯的从设备,也可用CS表示。每个从设备都有一条独立的NSS信号线,主机通过将某个设备的NSS线置低电平来选择与之通讯的从设备。所以SPI通讯以NSS线电平置低为起始信号,以NSS线电平被拉高为停止信号。
2.SPI如何使用,以及对应的有几种配置模式(相位、极性)
SPI配置模式分类根据的是时钟信号空闲状态。、以及上升沿采样还是下降沿采样,
CPOL=0表示的是时钟空闲的时候为低电平,反之是高电平
CPHA=0表示的是时钟信号的第一个边沿是采样沿
CPHA=1表示的是时钟信号的第二个边沿是采样沿
对应的时序图如下:
CPOL、CPHA
- CPOL:即在没有数据传输时,时钟的空闲状态的电平。
- CPHA:即数据的采样时刻。
有一点需要注意的是,主机和从机需要工作在相同的模式下才能正常通讯。
3.起始、停止信号(转于知乎)
如上图,编号1和6即为起始和停止信号的发生区域。NSS电平由高变低,则产生起始信号;NSS电平由低变高,则产生停止信号。从机检测到自己的NSS线电平被置低,则开始与主机进行通讯;反之,检测到NSS电平被拉高,则停止通讯。
4.数据有效性
MOSI和MISO线在SCK的每个时钟周期传输一位数据,开发者可以自行设置MSB或LSB先行,不过需要保证两个通讯设备都使用同样的协定。从图16-1看到,在SCK的上升沿和下降沿时进行触发和采样。
SPI有四种通讯模式,在SCK上升沿触发,下降沿采样只是其中一种模式。四种模式的主要区别便是总线空闲时SCK的状态及数据采样时刻。这涉及到“时钟极性CPOL”和“时钟相位CPHA”,由CPOL和CPHA的组合而产生了四种的通讯模式。
5.SPI的verilog实现:结合实际的应用场景对该通信协议进行分析:在一个网络通信模型中,可以将基带部分作为主控,RF部分作为受控部分,把SPI接口作两者之间传输数据的接口,它完成的主要工作是
(1)将从base band接收到的16位的并行数据,转换为RF所能接收的串行数据,并将该数据根据SPI协议送给RF。
(2)产生RF所需的时钟信号SCLK,使能信号CSB。
(3)接收从RF传回的串行数据,并将其转换为并行数据。
(4)将base band发送的数据,与RF返回的数据进行比较,并把比较结果传给base band。
module Serial2Parallel_Master #(
parameter SCLK_DIVIDER = 'd0 //Sclk Freq = Clk/2 / (SCLK_DIVIDER + 1)
)(
input rst_n,
input clk,
input sDataRd,
input [:] pDataWr,
output dataCS,
output dataSclk,
output sDataWr,
output [:] pDataRd
); // counter,used to generate dataSclk signal
reg dataCS_reg;
reg dataSclk_reg;
reg[:] Count1;
always @(posedge clk or negedge rst_n)
if(!rst_n)
Count1 <= 'd0;
else if(Count1 == SCLK_DIVIDER)
Count1 <= 'd0;
else
Count1 <= Count1 + 'b1; // generate CS and Sclk sequence
reg [:] i;//Step number
always @(posedge clk or negedge rst_n)
if(!rst_n)begin
i <= 'd0;
dataSclk_reg <= 'b1;//Sclk high at reset
dataCS_reg <= 'b1; //CS high at reset
end
else begin
case(i)
//pull down CS at the beginning
'd0:
if(Count1 == SCLK_DIVIDER) begin
i <= i + 'b1;
dataSclk_reg <= 'b1;
dataCS_reg <= 'b0;
end
else;
//generate 1st to 17th Sclk falling edge
'd1,6'd3,'d5,6'd7,'d9,6'd11,'d13,6'd15,'d17,6'd19,'d21,6'd23,'d25,5'd27,'d29,6'd31,'d33:
if(Count1 == SCLK_DIVIDER) begin
dataSclk_reg <= 'b0;
dataCS_reg <= 'b0;
i <= i + 'b1;
end
else;
//generate 1st to 16th Sclk rising edge
'd2,6'd4,'d6,6'd8,'d10,6'd12,'d14,6'd16,'d18,6'd20,'d22,6'd24,'d26,6'd28,'d30,6'd32:
if(Count1 == SCLK_DIVIDER) begin
dataSclk_reg <= 'b1;
dataCS_reg <= 'b0;
i <= i + 'b1;
end
else;
'd34://CS and Sclk go high
if(Count1 == SCLK_DIVIDER) begin
dataSclk_reg <= 'b1;
dataCS_reg <= 'b1;
i <= i + 'b1;
end
else;
'd35://CS keep high, Sclk go low
if(Count1 == SCLK_DIVIDER) begin
dataSclk_reg <= 'b0;
dataCS_reg <= 'b1;
i <= 'd0;
end
else ;
default ;
endcase
end ; // - receive and send SPI data
reg sDataWr_reg;
reg [:] pDataRd_reg;
reg rxDone_reg;
reg [:] j;
always @(negedge dataSclk or negedge rst_n)
if(!rst_n) begin
j <= 'd0;
sDataWr_reg <= 'b0;
pDataRd_reg <= 'd0;
rxDone_reg <= 'b0;
end
// - CS high,clear j & AD data
else if(dataCS) begin
j <= 'd0;
sDataWr_reg <= 'b0;
pDataRd_reg <= 'd0;
rxDone_reg <= 'b0;
end
else begin
// - first falling of Sclk, send MSB of send data
if(j == 'd0) begin
j <= j + 'b1;
sDataWr_reg <= pDataWr[];//send data
pDataRd_reg <= 'd0;//receive data clear
rxDone_reg <= 'b0;
end // - 2nd to 16th falling of Sclk
else if(j <= 'd15) begin
j <= j + 'b1;
sDataWr_reg <= pDataWr[-j];//send data
pDataRd_reg[-j] <= sDataRd;//receive data
rxDone_reg <= 'b0;
end // - at 17th falling of sclk_fbk, CS is still low, receive LSB of receive data
else if(j == 'd16) begin
j <= j + 'b1;
sDataWr_reg <= 'b0;//send data clear
pDataRd_reg[] <= sDataRd;//receive data
rxDone_reg <= 'b1;//receive done
end
else begin
j <= j;
sDataWr_reg <= sDataWr_reg;
pDataRd_reg <= pDataRd_reg;
rxDone_reg <= rxDone_reg;
end
end
// - data latch for pDataRd
reg [:] pDataRd_l;
always @(posedge clk or negedge rst_n)
if(!rst_n)
pDataRd_l <= 'd0;
else if(rxDone_reg) begin
pDataRd_l <= pDataRd_reg;
end
else begin
pDataRd_l <= pDataRd_l;
end
// - delay sDataWr for 1 main clk(10ns)
reg sDataWr_dly;
always @(posedge clk or negedge rst_n)
if(!rst_n)
sDataWr_dly <= 'b0;
else if(sDataWr_reg) begin
sDataWr_dly <= 'b1;
end
else begin
sDataWr_dly <= 'b0;
end
// - output assignment
assign dataCS = dataCS_reg;
assign dataSclk = dataSclk_reg;
assign sDataWr = sDataWr_dly;
assign pDataRd = pDataRd_l;
endmodule
Serial2Parallel_Master
module Serial2Parallel_Slave ( input rst_n,
input clk, input dataCS,
input dataSclk, input sDataRd,
input [:] pDataWr, output sDataWr,
output [:] pDataRd
); // - SPI read and write
reg sDataWr_reg;
reg [:] pDataRd_reg;
reg rxDone_reg;
reg [:] j;//operation steps
always @(negedge dataSclk or negedge rst_n)
if(!rst_n) begin
j <= 'd0;
sDataWr_reg <= 'b0;
pDataRd_reg <= 'd0;
rxDone_reg <= 'b0;
end
// - CS high,clear j & AD data
else if(dataCS) begin
j <= 'd0;
sDataWr_reg <= 'b0;
pDataRd_reg <= 'd0;
rxDone_reg <= 'b0;
end
else begin
// - first falling of Sclk, send MSB of send data
if(j == 'd0) begin
j <= j + 'b1;
sDataWr_reg <= pDataWr[];//send data
pDataRd_reg <= 'd0;//receive data clear
rxDone_reg <= 'b0;
end // - 2nd to 16th falling of Sclk
else if(j <= 'd15) begin
j <= j + 'b1;
sDataWr_reg <= pDataWr[-j];//send data
pDataRd_reg[-j] <= sDataRd;//receive data
rxDone_reg <= 'b0;
end // - at 17th falling of sclk_fbk, CS is still low, receive LSB of receive data
else if(j == 'd16) begin
j <= j + 'b1;
sDataWr_reg <= 'b0;//send data clear
pDataRd_reg[] <= sDataRd;//receive data
rxDone_reg <= 'b1;//receive done
end
else begin
j <= j;
sDataWr_reg <= sDataWr_reg;
pDataRd_reg <= pDataRd_reg;
rxDone_reg <= rxDone_reg;
end
end // - data latch for pDataRd
reg [:] pDataRd_l;
always @(posedge dataCS or negedge rst_n)
if(!rst_n)
pDataRd_l <= 'd0;
else if(rxDone_reg) begin
pDataRd_l <= pDataRd_reg;
end
else begin
pDataRd_l <= pDataRd_l;
end
// - output assignment
assign sDataWr = sDataWr_reg;
assign pDataRd = pDataRd_l; endmodule
Serial2Parallel_Slave
二、IIC通信
IIC的通信模式示意图:
IIC的verilog实现
`timescale 1ns / 1ps module IIC_AD(
clk,rst_n,
scl,sda); input clk; // 50MHz
input rst_n; //��λ�źţ�����Ч
output scl; // 24C02��ʱ�Ӷ˿�
inout sda; // 24C02�����ݶ˿� reg[:] cnt; // cnt=0:scl上升沿,cnt=1:scl高电平中间,cnt=2:scl下降沿,cnt=3:scl低电平中间
reg[:] cnt_delay; //500循环计数,产生iic所需要的时钟
reg scl_r=; //时钟脉冲寄存器 always @ (posedge clk or negedge rst_n)
if(!rst_n)
cnt_delay <= 'd0;
else if(cnt_delay == 'd499)
cnt_delay <= 'd0; //计数到10us为scl的周期,即100KHz
else
cnt_delay <= cnt_delay+'b1; always @ (posedge clk or negedge rst_n)
begin
if(!rst_n) cnt <= 'd5;
else
begin
case (cnt_delay)
'd124: cnt <= 3'd1; //cnt=1:scl高电平中间,用于数据采样
'd249: cnt <= 3'd2; //cnt=2:scl下降沿
'd280: cnt <= 3'd3; //cnt=3:scl低电平中间,用于数据变化
'd499: cnt <= 3'd0; //cnt=0:scl上升沿
default: cnt <= 'd5;
endcase
end
end
`define SCL_POS (cnt=='d0)
`define SCL_HIG (cnt=='d1)
`define SCL_NEG (cnt=='d2)
`define SCL_LOW (cnt=='d3) always @ (posedge clk or negedge rst_n)
if(!rst_n)
scl_r <= 'b1;
else if(cnt=='d0)
scl_r <= 'b1; //scl上升沿
else if(cnt=='d2)
scl_r <= 'b0; //scl下降沿 assign scl = scl_r; //产生iic所需要的时钟
//---------------------------------------------
reg[:] num;
reg [:] db_r; //在IIC上传送的数据寄存器
reg sda_link; //输出数据sda信号inout方向控制 0z-c,1c-z
reg sda_r=; //输出数据寄存器 //需要写入24C02的地址和数据
parameter DEVICE_WRITE='b0101_1000;//被寻址器件地址(写操作)
parameter BYTE_ADDR='b0000_0011;//写入/读出EEPROM的地址寄存器
parameter WRITE_DATA='b0001_1011;//写入EEPROM的数据 reg [:] cstate;//状态寄存器 parameter START = 'd0;//状态机的进行步骤编号
parameter ADD1 = 'd1;
parameter ACK1 = 'd2;
parameter ADD2 = 'd3;
parameter ACK2 = 'd4;
parameter DATA = 'd5;
parameter ACK3 = 'd6;
parameter STOP1 = 'd7;
//---------------------------------------------
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
num <= 'd0;
sda_link <= 'b1;
sda_r <= 'b1;
cstate <= START;
end else begin
case (cstate)
START: begin
if(`SCL_HIG) begin //scl为高电平期间
sda_link <= 'b1; //确定数据传输方向,数据线sda为output
db_r <= DEVICE_WRITE;
sda_r <= 'b0; //拉低数据线sda,产生起始位信号
cstate <= ADD1;
num <= 'd0; //num计数清零
end
else
cstate <= START; //等待scl高电平中间位置到来 等待数据开始传输
end
ADD1: begin
if(`SCL_LOW) begin
if(num == 'd8)
begin
num <= 'd0; //num清零
sda_r <= 'b1; //提高数据线sda,开始数据变化
sda_link <= 'b0; //确定数据传输方向,数据线sda为input
cstate <= ACK1;
end
else begin
cstate <= ADD1;
num <= num+'b1;
case (num)
'd0: sda_r <= db_r[7];
'd1: sda_r <= db_r[6];
'd2: sda_r <= db_r[5];
'd3: sda_r <= db_r[4];
'd4: sda_r <= db_r[3];
'd5: sda_r <= db_r[2];
'd6: sda_r <= db_r[1];
'd7: sda_r <= db_r[0];
default: ;
endcase
end
end
else cstate <= ADD1;
end
ACK1: begin
if(/*!sda*/`SCL_NEG) //注:24C01/02/04/08/16器件可以不考虑应答位
begin
cstate <= ADD2; //从机响应信号
db_r <= BYTE_ADDR; // 1地址
end
else
cstate <= ACK1; //等待从机响应
end
ADD2: begin
if(`SCL_LOW)
begin
if(num == 'd8) begin
sda_link <= 'b0;
sda_r <= 'b1;
num <= 'd0;
cstate <= ACK2;
end
else begin
sda_link <= 'b1;//sda作为output
cstate <= ADD2;
num <= num+'b1;
case (num)
'd0: sda_r <= db_r[7];
'd1: sda_r <= db_r[6];
'd2: sda_r <= db_r[5];
'd3: sda_r <= db_r[4];
'd4: sda_r <= db_r[3];
'd5: sda_r <= db_r[2];
'd6: sda_r <= db_r[1];
'd7: sda_r <= db_r[0];
default: ;
endcase
end
end
else cstate <= ADD2;
end
ACK2: begin
if(/*!sda*/`SCL_NEG) begin //从机响应操作
cstate <= DATA; //写操作
db_r <= WRITE_DATA; //写入数据 end
else cstate <= ACK2; //等待从机响应
end
DATA: begin
if(`SCL_LOW)
begin
if(num == 'd8) begin
sda_link <= 'b0;
sda_r <= 'b1;
num <= 'd0;
cstate <= ACK3;
end
else begin
sda_link <= 'b1;
cstate <= DATA;
num <= num+'b1;
case (num)
'd0: sda_r <= db_r[7];
'd1: sda_r <= db_r[6];
'd2: sda_r <= db_r[5];
'd3: sda_r <= db_r[4];
'd4: sda_r <= db_r[3];
'd5: sda_r <= db_r[2];
'd6: sda_r <= db_r[1];
'd7: sda_r <= db_r[0];
default: ;
endcase
end
end
else cstate <= DATA;
end
ACK3: begin
if(/*!sda*/`SCL_NEG) begin
sda_r <= 'b0; //拉低数据线sda,产生停止信号
sda_link <= 'b1;//sda作为output
cstate <= STOP1;
end
else cstate <= ACK3;
end
STOP1: begin
if(`SCL_HIG) begin
sda_link <= 'b1;
sda_r <= 'b1; //拉低数据线sda,产生停止信号
cstate <= START;
end
else cstate <= STOP1;
end
default: cstate <= START;
endcase end
end
assign sda = sda_link ? sda_r:'bz; endmodule
IIC的关键词:两线和低速,该总线采用开漏的结构可以很好地实现数据的双向传输,也就是说在要用到sda或者scl线的时候,可以通过内部的NMOS下拉为零,否则上拉为高电平
还需要注意的就是开始和结束条件,以及IIC协议的要求:
scl为高的器件,sda必须保持稳定,sda变化相对于scl变高有建立时间的要求,而sda变化相对于scl变低有保持时间的要求。scl低电平期间,数据sda才可以发生变化。而这里的建立保持时间是微秒级别的,所以IIC的速度慢,1MHz左右。从器件不适合高速数字逻辑单路。
然后具体的执行顺序为:
s1:Start+器件地址+应答信号+要发送的数据+应答信号。
通过scl和sda两条线的控制来实现数据的传输,其中的sda信号线是inout,因为作为串行数据传输线,它不仅要传输上位机的数据到下位机,此外还需要下位机发送一个响应到上位机去,所以实际上需要它是inout。
IIC通过器件的地址来区分从机,而SPI主要是通过作为主机的CS来区分从机的编码。
三、关于两者的对比:
首先是SPI的优点在于:
1.利于硬件实现,不需要多个器件地址,只用到4根数据线,封装简单
2.全双工传输,可以同时发送和接收数据
3.三态输出端口,相对IIC的开漏输出,抗干扰能力强,传输稳定
4.传输数据的速度在几百MHz远远高于IIC的几十Mhz
5.输入输出的比特数没有限制
缺点
1.信号线多,而且随着从器件的个数增加,芯片选择线会增加
2.传输过程没有确认信号,不知道从器件的接收情况。
3.没有校验机制,传输错误不会有提示。
IIC的优点:
控制线少,结构简单
IIC的缺点:
传输速度慢而且麻烦。
SPI以及IIC的verilog实现以及两者之间的对比的更多相关文章
- SPI、IIC、IIS、UART、CAN、SDIO、GPIO、USB总线协议
SPI.IIC.IIS.UART.CAN.SDIO.GPIO总线协议 SPI(Serial Peripheral Interface:串行外设接口)SPI总线由三条信号线组成:串行时钟(SCLK).串 ...
- spi、iic、can高速传输速度与选择
uart: 无限制,常用9600.115200bps等保证双方通信速度相同. iic: 通讯速率400Kbps can: 一般为1Mbps SPI: 通信速率 fosc/4其传输速度可达几Mb/s 缺 ...
- 对SPI、IIC、IIS、UART、CAN、SDIO、GPIO的解释
SPI SPI(Serial Peripheral Interface:串行外设接口); SPI总线由三条信号线组成:串行时钟(SCLK).串行数据输出(SDO).串行数据输入(SDI).SPI总线可 ...
- SPI、IIC、IIS、UART、JTAG的应用场合级区别
SPI SPI接口的全称是"Serial Peripheral Interface",意为串行外围接口,是Motorola首先在其MC68HCXX系列处理器上定义的. SPI接口 ...
- SPI的通信试验 --verilog (从机-全双工)
SPI的 有关知识参考FPGA作为主机的通信实验. 本实验中FPGA作为从机通过SPI与MCU等通信的试验,可以在时钟上升沿接收数据并且在时钟下降沿发送数据,模仿全双工模式.接收的 数据作为地址,通过 ...
- UART, SPI, IIC的详解及三者的区别和联系
UART.SPI.IIC是经常用到的几个数据传输标准,下面分别总结一下: UART(Universal Asynchronous Receive Transmitter):也就是我们经常所说的串口,基 ...
- UART,SPI,IIC的一点理解
转自:http://bbs.21ic.com/icview-253715-1-1.html UART通用异步收发器,UART是通用的异步传输模式,在它这种基础上加上其他接口或者解码器就衍生出多种异步传 ...
- Uart,IIC和SPI的区别
1.UART, SPI, IIC的详解 UART.SPI.IIC是经常用到的几个数据传输标准,下面分别总结一下: UART(Universal Asynchronous Receive Transmi ...
- SMBUS(IIC)总线
1.SPI和IIC一般都作为板上通信,UART.SMBUS和USB一般都作为板间通信. 其中SMBUS是参考IIC制定出来的眼生病,两者很像. 2.SMBUS的I/O接口是由两条线组成的双向串行总线. ...
随机推荐
- java课程之团队开发冲刺阶段1.8
一.总结昨天进度 1.实现预装sqlite数据库,将数据库放在app的assets目录下,该目录在打包的时候不会压缩,所以数据库文件可以在安装之后继续使用,然后APP安装之后检测外部存储空间是否有这个 ...
- 实验吧web-易-Forms
打开网页,查看源码, 第二行,showsource的value是0,我们在查看器中将showsource的value值改为1,然后随便输入一个数,可以看到页面出现 意思就是我们输入的PIN的值应该是代 ...
- linux上大文件切割成小文件传输
使用tar命令进行压缩,使用split进行切割 压缩并分割: tar -zcvf - admin- |split -b 100m -d admin-.tar.gz 解压: 先合并成tar包在解压 ca ...
- JavaWeb之Servlet入门(二)
1. 准备 在JavaWeb之Servlet入门(一)中,我们完成了第一个Servlet程序,完成了从URL到后台控制器的中转过程,接下来我们延续JavaWeb之Servlet入门(一)学习下如何传参 ...
- shell 实现war包的配置更新和自动发布
此脚本主要用来实现非maven tomcat项目的war包手动发布, 1.将测试war包上传至指定目录 2.备份目前生产代码 3.自动配置文件替换 4.新版本代码的发布 #!/bin/bash ### ...
- php多态模拟
在PHP中,多态是最常用到的一种特性.所谓多态,是指同一个东西不同形态的展示.在PHP中,我们这样定义多态,一个类被多个子类继承,如果这个类的某个方法在多个子类中表现不同的功能,那么这种行为我们就称其 ...
- log4j2和logback动态修改日志级别工具类
工作中,在排查线上问题时,有以下场景在不重新部署或重启服务的情况下,需要动态调整线上日志级别 1.线上有些日志打印过多干扰有用的日志,需要动态修改线上日志记录器的打印日志级别,调高一些日志级别,打印出 ...
- python base 64
python中base64编码与解码 引言: 在一些项目中,接口的报文是通过base64加密传输的,所以在进行接口自动化时,需要对所传的参数进行base64编码,对拿到的响应报文进行解码: Bas ...
- Maven--导出pom中依赖的jar包
参考:https://my.oschina.net/cloudcoder/blog/212648 mvn dependency:copy-dependencies -DoutputDirectory= ...
- 如何判断Office是32位还是64位?
对于持续学习VBA的老铁们,有必要了解Office的位数. 如果系统是32位的,则不需要判断Office位数了,因为只能安装32位Office. 下面只讨论64位系统中,Office的位数判断问题. ...