赞
踩
D/A芯片DAC5571的使用,包括I2C的学习,并对代码进行详解。
通过程序产生一个0-255循环递增的数据,通过I2C接口不断写入到DAC中,输出的模拟电压可以控制开发板上的某个LED的亮暗变化。结构如下
xilinx spartan6开发板、ISE14.7、modelsim10.5、verilog
I2C 通讯协议(Inter-Integrated Circuit)是由 Philips 公司开发的一种简单、双向二线制同步串行总线,只需要两根线即可在连接于总线上的器件之间传送信息。I2C 通讯协议和通信接口在很多工程中有广泛的应用,如数据采集领域的串行 AD,图像处理领域的摄像头配置,工业控制领域的 X 射线管配置等等。除此之外,由于 I2C 协议占用引脚特别少,两根线便可实现,硬件实现简单,可扩展型强,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
主要介绍I2C协议的整体时序图、读写时序以及I2C设备的器件地址、存储地址I2C整体时序图
由图可知,I2C 协议整体时序图分为 4 个部分,图中标注的①②③④表示 I2C 协议的 4个状态,分别为“总线空闲状态”、“起始信号”、“数据读/写状态”和“停止信号”。
对传入从机的控制命令最低位读写控制位写入不同数据值,主机可实现对从机的读/写操作,读写控制位为 0 时,表示主机要对从机进行数据写入操作;读写控制位为 1 时,表示主机要对从机进行数据读出操作。根据依次写入数据量的不同,I2C的写操作可以分为单字节写、页写、随机写操作。下面只介绍单字节写操作
I2C单字节写操作
单字节写操作流程如下
DAC5571的接口是I2C接口,关于I2C通信的基本接口时序在上边已经简单的总结。
FPGA为I2C总线的主机,若要控制D1C5571完成依次转换,则一共需要传输三个字节的数据。
首字节内容是从机地址(SLAVE ADDRESS)和读/写指示位(R/W)。
第二次字节的高4bit是控制数据,低4bit是有效数据的高4bit。
第三字节的高4bit是有效数据的低4bit,第三个字节的低4bit无效。
分为固定部分和可编程部分,如下图中的A0为可编程部分。本设备地址设为10011000
顶层模块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
子模块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
子模块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
特权同学
野火FPGA
DAC5571datesheet
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。