当前位置:   article > 正文

FPGA-DA模块学习 I2C接口(附源码)_fpga i2c代码

fpga i2c代码

学习内容

D/A芯片DAC5571的使用,包括I2C的学习,并对代码进行详解。

实现功能

通过程序产生一个0-255循环递增的数据,通过I2C接口不断写入到DAC中,输出的模拟电压可以控制开发板上的某个LED的亮暗变化。结构如下
在这里插入图片描述

开发环境

xilinx spartan6开发板、ISE14.7、modelsim10.5、verilog

I2C相关知识

I2C 通讯协议(Inter-Integrated Circuit)是由 Philips 公司开发的一种简单、双向二线制同步串行总线,只需要两根线即可在连接于总线上的器件之间传送信息。I2C 通讯协议和通信接口在很多工程中有广泛的应用,如数据采集领域的串行 AD,图像处理领域的摄像头配置,工业控制领域的 X 射线管配置等等。除此之外,由于 I2C 协议占用引脚特别少,两根线便可实现,硬件实现简单,可扩展型强,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。

1、I2C物理层

在这里插入图片描述

图1 I2C通讯设备连接

物理层的特点
(1) 它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线中,可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。
(2) 一个 I2C 总线只使用两条总线线路,一条**双向串行数据线(SDA)** ,一条**串行时钟线(SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
(3)每个连接到总线的设备都有一个
独立的地址**,主机可以利用这个地址进行不同设备之间的访问。
(4) 总线通过上拉电阻接到电源。当 I2C 设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
(5) 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
(6) 具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多 I2C 设备尚不支持高速模式。
(7) 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制 。

2、I2C协议层

主要介绍I2C协议的整体时序图、读写时序以及I2C设备的器件地址、存储地址I2C整体时序图
在这里插入图片描述
由图可知,I2C 协议整体时序图分为 4 个部分,图中标注的①②③④表示 I2C 协议的 4个状态,分别为“总线空闲状态”、“起始信号”、“数据读/写状态”和“停止信号”。

  1. 图中的1表示“总线空闲状态”。再次状态下SCL和SDA均为高电平,此时无I2C设备工作。
  2. 图中的2表示“起始信号”。SCL依旧保持高电平,SDA出现下降沿,产生一个起始信号,此时与总线相连的所有I2C设备在检查到起始信号后,均跳出空闲状态,等待控制字节的输入。
  3. 图中的3表示“数据读写状态”,时序图如下在这里插入图片描述
    通讯模式是主从通讯,双方有主从之分。
    当主机向从机进行指令或者数据的写入时,串行数据线SDA上的数据在串行时钟SCL的高电平时写入从机设备,每次写入一位数据SDA中的数据在SCL为低电平时进行数据更新,保证数据的稳定。
    当一个完整字节的指令或数据传输完成,会通过拉低SDA为低电平来向主机设备发送单比特的应答信号,表示数据写入成功。若正确应答则可以结束或开始下一字节数据或指令的传输。
  4. 图中的4表示“停止信号”,完成数据读写后,串口时钟SCL保持高电平,当串口数据信号SDA产生一个上升沿时,产生一个停止信号,I2C总线跳回“总线空闲状态”。
3、I2C读写操作

对传入从机的控制命令最低位读写控制位写入不同数据值,主机可实现对从机的读/写操作,读写控制位为 0 时,表示主机要对从机进行数据写入操作;读写控制位为 1 时,表示主机要对从机进行数据读出操作。根据依次写入数据量的不同,I2C的写操作可以分为单字节写、页写、随机写操作。下面只介绍单字节写操作
I2C单字节写操作
在这里插入图片描述
单字节写操作流程如下

  1. 主机产生并发送起始信号到从机,将控制命令写入从机设备,读写控制位设置为低
    电平,表示对从机进行数据写操作,控制命令的写入高位在前低位在后。
  2. 从机接收到控制指令后,回传应答信号,主机接收到应答信号后开始存储地址的写
    入。按高位在前低位在后的顺序写入单字节存储地址。
  3. 地址写入完成,主机接收到从机回传的应答信号后,开始单字节数据的写入。
  4. 单字节数据写入完成,主机接收到应答信号后,向从机发送停止信号,单字节数据
    写入完成。

DA芯片相关知识

DAC5571的接口是I2C接口,关于I2C通信的基本接口时序在上边已经简单的总结。
FPGA为I2C总线的主机,若要控制D1C5571完成依次转换,则一共需要传输三个字节的数据。
首字节内容是从机地址(SLAVE ADDRESS)和读/写指示位(R/W)。
第二次字节的高4bit是控制数据,低4bit是有效数据的高4bit。
第三字节的高4bit是有效数据的低4bit,第三个字节的低4bit无效。
给发放大的的的

DA芯片通信协议

设备地址

分为固定部分和可编程部分,如下图中的A0为可编程部分。本设备地址设为10011000

在这里插入图片描述

控制字前四位为0000

代码详解

顶层模块sp.v如下
其中pll_controller为PLL IP核、dac_dbgene为产生0-255数据的子模块、dac_controller为I2C写控制模块

module sp6(
			input ext_clk_25m,	//外部输入25MHz时钟信号
			input ext_rst_n,	//外部输入复位信号,低电平有效
			output dac_iic_sck,		//DAC5571的IIC接口SCL
			inout dac_iic_sda		//DAC5571的IIC接口SDA
		);													

//-------------------------------------
//PLL例化
wire clk_12m5;	//PLL输出12.5MHz时钟
wire clk_25m;	//PLL输出25MHz时钟
wire clk_50m;	//PLL输出50MHz时钟
wire clk_100m;	//PLL输出100MHz时钟
wire sys_rst_n;	//PLL输出的locked信号,作为FPGA内部的复位信号,低电平复位,高电平正常工作

  pll_controller uut_pll_controller
   (// Clock in ports
    .CLK_IN1(ext_clk_25m),      // IN
    // Clock out ports
    .CLK_OUT1(clk_12m5),     // OUT
    .CLK_OUT2(clk_25m),     // OUT
    .CLK_OUT3(clk_50m),     // OUT
    .CLK_OUT4(clk_100m),     // OUT
    // Status and control signals
    .RESET(~ext_rst_n),// IN
    .LOCKED(sys_rst_n));      // OUT		
	
//-------------------------------------		
//产生递增的DAC转换数据
wire[7:0] dac_data;	//DAC输出数据,模块内部自动判断该数据是否发生变化,若前后有变化,则通过IIC接口发起一次DAC转换数据写入操作,建议该数据变化速率不要超过1.5KHz

dac_dbgene		uut_dac_dbgene(
				.clk(clk_25m),		//时钟信号
				.rst_n(sys_rst_n),	//复位信号,低电平有效
				.dac_data(dac_data)	//DAC转换数据	
			);
		
//-------------------------------------
//DAC5571的IIC写DA转换数据模块

dac_controller		uut_dac_controller(
					.clk(clk_25m),		//时钟信号
					.rst_n(sys_rst_n),	//复位信号,低电平有效
					.dac_data(dac_data),	//DAC输出数据,模块内部自动判断该数据是否发生变化,若前后有变化,则通过IIC接口发起一次DAC转换数据写入操作,建议该数据变化速率不要超过1.5KHz
					.scl(dac_iic_sck),		//DAC5571的IIC接口SCL
					.sda(dac_iic_sda)		//DAC5571的IIC接口SDA	
				);
		
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

子模块dac_dbgene如下

module dac_dbgene(
			input clk,		//时钟信号,25MHz
			input rst_n,	//复位信号,低电平有效
			output reg[7:0] dac_data	//DAC转换数据	
		);

//-------------------------------------------------
//10ms定时计数
reg[17:0] cnt;	//10ms计数器

always @(posedge clk or negedge rst_n)
	if(!rst_n) cnt <= 18'd0;
	else if(cnt < 18'd249_999) cnt <= cnt+1'b1;
	else cnt <= 18'd0;
	
//-------------------------------------------------
//DA转换数据递增

always @(posedge clk or negedge rst_n)
	if(!rst_n) dac_data <= 18'd0;
	else if(cnt == 18'd249_999) dac_data <= dac_data+1'b1;


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

子模块dac_controller如下,其中包含一个10段状态机,主要结合上边的知识设置的,设置bnt来写入7——0的数据。

module dac_controller(
			input clk,		//时钟信号,25MHz
			input rst_n,	//复位信号,低电平有效
			input[7:0] dac_data,	//DAC输出数据,模块内部自动判断该数据是否发生变化,若前后有变化,则通过IIC接口发起一次DAC转换数据写入操作,建议该数据变化速率不要超过1.5KHz
			output scl,		//DAC5571的IIC接口SCL
			inout sda		//DAC5571的IIC接口SDA	
		);

//-------------------------------------------------	
//判断DAC输出数据是否变化,若变化则发起一次IIC数据写入操作					
reg[7:0] dac_datar;		//dac_data缓存寄存器
reg dac_en;		//DAC转换使能信号,高电平有效															

always @(posedge clk or negedge rst_n)										
	if(!rst_n) dac_datar <= 8'd0;
	else dac_datar <= dac_data;

always @(posedge clk or negedge rst_n)										
	if(!rst_n) dac_en <= 1'b0;
	else if(dac_datar != dac_data) dac_en <= 1'b1;
	else dac_en <= 1'b0;

//-------------------------------------------------						
reg[8:0] cnti;		//计数器,25MHz时钟频率下,产生5KHz的IIC时钟															

always @(posedge clk or negedge rst_n)										
	if(!rst_n) cnti <= 9'd0;
	else if(cnti < 9'd499 && cstate != IDLE) cnti <= cnti + 1'b1;
	else cnti <= 9'd0;

wire scl_low = (cnti == 9'd374);											
wire scl_high = (cnti == 9'd124);											

assign scl = ~cnti[8];														

//-------------------------------------------------
//IIC写操作状态机
parameter IDLE		= 4'd0;
parameter START		= 4'd1;
parameter ADDR		= 4'd2;
parameter ACK1		= 4'd3;
parameter CMSB		= 4'd4;
parameter ACK2		= 4'd5;
parameter LSBI		= 4'd6;
parameter ACK3		= 4'd7;
parameter ACK4		= 4'd8;
parameter STOP		= 4'd9;

parameter DEVICE_ADDR	= 8'b1001_1000;										
wire[7:0] dac_mdata = {4'b0000,dac_data[7:4]};	
wire[7:0] dac_ldata = {dac_data[3:0],4'b0000};	

reg[3:0] cstate,nstate;	
reg sdar;	
reg[2:0] bcnt;																
reg sdlink;																	

always @(posedge clk or negedge rst_n)
	if(!rst_n) cstate <= IDLE;
	else cstate <= nstate;
	
always @(cstate or dac_en or scl_high or scl_low or bcnt) begin				
	case(cstate)
		IDLE: 	if(dac_en) nstate <= START;
				else nstate <= IDLE;
		START:  if(scl_high) nstate <= ADDR;
				else nstate <= START;
		ADDR: 	if(scl_low && bcnt == 3'd0) nstate <= ACK1;
				else nstate <= ADDR;
		ACK1: 	if(scl_low) nstate <= CMSB;
				else nstate <= ACK1;
		CMSB: 	if(scl_low && bcnt == 3'd0) nstate <= ACK2;
				else nstate <= CMSB;
		ACK2: 	if(scl_low) nstate <= LSBI;
				else nstate <= ACK2;
		LSBI:	if(scl_low && bcnt == 3'd0) nstate <= ACK3;
				else nstate <= LSBI;
		ACK3: 	if(scl_low) nstate <= ACK4;
				else nstate <= ACK3;
		ACK4: 	if(scl_low) nstate <= STOP;
				else nstate <= ACK4;				
		STOP: 	if(scl_high) nstate <= IDLE;
				else nstate <= STOP;
		default: nstate <= IDLE;
		endcase
end

always @(posedge clk or negedge rst_n)										
	if(!rst_n) begin
			sdar <= 1'b1;
			sdlink <= 1'b1;
		end
	else begin
		case(cstate)
			IDLE: begin
					sdar <= 1'b1;
					sdlink <= 1'b1;	
				end
			START: 	if(scl_high) begin
					sdar <= 1'b0;
					sdlink <= 1'b1;	
				end			
			ADDR: if(scl_low) begin
					sdar <= DEVICE_ADDR[bcnt];	
					sdlink <= 1'b1;	
				end					
			CMSB: if(scl_low) begin 
					sdar <= dac_mdata[bcnt];	
					sdlink <= 1'b1;	
				end
			LSBI: if(scl_low) begin
					sdar <= dac_ldata[bcnt];	
					sdlink <= 1'b1;	
				end
			ACK1,ACK2,ACK3: if(scl_low) begin
					sdar <= 1'b0;
					sdlink <= 1'b0;	
				end
			ACK4: if(scl_low) begin
					sdar <= 1'b0;
					sdlink <= 1'b1;	
				end
			STOP: if(scl_high) begin 
					sdar <= 1'b1;
					sdlink <= 1'b1;	
				end
			default: ;
			endcase
		end
	
assign sda = sdlink ? sdar : 1'bz;											

always @(posedge clk or negedge rst_n)										
	if(!rst_n) bcnt <= 3'd0;
	else begin
		case(cstate)
			ADDR,CMSB,LSBI:	begin
					if(scl_low) bcnt <= bcnt-1'b1;
					else ;
				end
			default: bcnt <= 3'd7;
			endcase
		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

参考资料

特权同学
野火FPGA
DAC5571datesheet

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号