前言
IIC协议中文版大概40页左右文档,我们今天开始花费一周左右时间一步步用Verilog把IIC控制器写出来,话不多说,干就完了
一、IIC总线协议
1.IIC总线是什么
I2C 总线是一个多主机、多从机的总线 。I2C总线可以连接多于一个能控制总线的器件到总线。 主机通常是MCU、DSP、FPGA;从机通常是除主机外的所有设备。 一个时刻只能有唯一的设备作为主机。
简而言之:IIC总线就是一根数据线,一根时钟线。N个设备都连接在数据线和时钟线上。
2.IIC总线特征
? 只要求两条总线线路 一条串行数据线 SDA 一条串行时钟线 SCL。
注意:SDA和SCL两根线,数据流和时钟流都是双向的。
我们在设计控制器时候一般把SCL设计为单向的,由主机发出的,以便控制不同时钟器件,规范时钟源。
? SDA SCL都通过一个电流源或上拉电阻连接到正的电源电压。当总线空闲时 这两条线路都是高电平 连接到总线的器件输出级必须是漏极开路或集电极开路才能执行线与的功能。
特别注意:控制器设计中SDA与SCL总线上的
高电平信号是由外部总线上的上拉电平驱动;
低电平信号是由主机控制。
? 每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机 从机关系软件设定地址 主机可以作为主机发送器或主机接收器
? 它是一个真正的多主机总线 如果两个或更多主机同时初始化数据传输可以通过冲突检测和仲裁防止数据被破坏
? 串行的 8 位双向数据传输位速率在标准模式下可达 100kbit/s 快速模式下可达 400kbit/s 高速模式下可达 3.4Mbit/s
注意:SDA数据是串行8位数据
? 片上的滤波器可以滤去总线数据线上的毛刺波 保证数据完整
? 连接到相同总线的 IC 数量只受到总线的最大电容 400pF 限制
?IIC总线SDA数据是以SCL为看考时钟,以高位在前,地位在后标准一位一位发送。
注意:高位在前,地位在后
二、数据传输剖析
1.数据有效性
SDA 线上的数据必须在时钟的高电平周期保持稳定 数据线的高或低电平状态只有在 SCL 线的时钟信号是低电平时才能改变。
用通俗的语言描述下哈:
SCL低电平的时候,SDA准备好要发送的数据(0或1),
SCL高电平时候,SDA必须保持数据稳定。
2.起始信号、停止信号
那么我们用IIC总线传输数据,怎么告诉同一总线上的设备,我要开始传输数据了呢?什么时候结束呢?
答案就是IIC总线发送数据前会有起始信号,也会有停止信号。
我们分别命名为(Start)、(stop)信号
1.SCL 线是高电平时 SDA 线从高电平向低电平切换 这个情况表示起始条件
2.当 SCL 是高电平时 SDA 线由低电平向高电平切换表示停止条件
3.应答(ACK)与非应答信号(NACK)
1.数据传输必须带响应;相关的响应时钟脉冲由主机产生;
注意:应答时钟SCL是有主机产生
2.在响应的时钟脉冲期间,主机释放 SDA 线高。在响应的时钟脉冲期间 接收器必须将 SDA 线拉低 使它在这个时钟脉冲的高电平期间保持稳定的低电平
简而言之:总线上传输完第8个数据后,主机释放SDA数据总线,从机返回一个应答信号,
此应答信号要保持一个SCL高电平时钟。(低电平 0 表示应答, 1 表示非应答),见下图红框
注意:1.此时的SCL仍然是主机提供的。
强调:2.数据传输高位在前,低位在后
4.字节传输
首先,主机发起起始信号(Start);
其次,依照SCL以及数据传输规则(数据有效性)以此发送8位数据;
再次,主机释放SDA,同时,SDA信号线由输出变为输入,以便来检测应答(非应答)
最后,停止信号先不管......
这不就是一个序列机吗。。。。。。。大家信心爆棚了 把代码放上去,免得大家说我懒 此代码到底行不行,我们后面揭晓
module iic_send(
Clk,
Rst_n,
Data,
SDA,
SCL,
Once_Done
);
input Clk;
input Rst_n;
input [7:0]Data;
inout SDA;
output reg SCL;
output reg Once_Done;
wire sda_in;
assign sda_in=SDA;
reg sda_out;
assign SDA=Dir?sda_out:1'bz;
reg[12:0]cnt_div;
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt_div<=13'b0;
else if(cnt_div==4999)
cnt_div<=0;
else
cnt_div<=cnt_div+1'b1;
reg[5:0]cnt_scl;
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt_scl<=6'b0;
else if(cnt_div==4999)
cnt_scl<=cnt_scl+1'b1;
else
cnt_scl<=cnt_scl;
reg Dir;
reg sda_reg;
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
SCL<=1'b1;
sda_out<=1'b1;
Once_Done<=1'b0;
Dir<=1'b1;
sda_reg<=1'b0;
end
else begin
case(cnt_scl)
0:begin SCL<=1'b1;sda_out<=1'b1;Dir<=1'b1;Once_Done<=1'b0;sda_reg<=1'b0;end
1:begin SCL<=1'b1;sda_out<=1'b0;end
2:begin SCL<=1'b0;sda_out<=Data[7];end
3:begin SCL<=1'b1;end
4:begin SCL<=1'b0;sda_out<=Data[6];end
5:begin SCL<=1'b1;end
6:begin SCL<=1'b0;sda_out<=Data[5];end
7:begin SCL<=1'b1;end
8:begin SCL<=1'b0;sda_out<=Data[4];end
9:begin SCL<=1'b1;end
10:begin SCL<=1'b0;sda_out<=Data[3];end
11:begin SCL<=1'b1;end
12:begin SCL<=1'b0;sda_out<=Data[2];end
13:begin SCL<=1'b1;end
14:begin SCL<=1'b0;sda_out<=Data[1];end
15:begin SCL<=1'b1;end
16:begin SCL<=1'b0;sda_out<=Data[0];end
17:begin SCL<=1'b1;end
18:begin SCL<=1'b0;Dir<=1'b0;end
19:begin SCL<=1'b1;sda_reg<=sda_in;Once_Done<=1'b1;end
20:begin SCL<=1'b0;end
default:begin sda_out<=sda_out;Once_Done<=Once_Done;sda_reg<=sda_reg;end
endcase
end
endmodule
欲知后事如何,请听下回分解 …
|