完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1)实验平台:正点原子开拓者FPGA 开发板
2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html 第三十一章 AD/DA实验 PCF8591是具有I2C总线接口的8位AD/DA转换芯片。因其功耗低、控制简单、封装小而广泛 应用于远程数据采集的低功耗转换器、电源监控等领域。本章我们将使用FPGA开发板上的 PCF8591器件实现AD/DA的转换。 本章包括以下几个部分: 31.1 PCF8591简介 31.2 实验任务 31.3 硬件设计 31.4 程序设计 31.5 下载验证 PCF8591简介 PCF8591是一个单片集成、单电源供电、低功耗的8位CMOS数据采集转换(AD/DA)器件, 具有4个模拟输入、1个模拟输出和1个串行I2C总线接口。PCF8591的功能包括多路复用模拟输 入、片上跟踪和保持、8位AD(模数)转换和8位DA(数模)转换,其最大转换速率取决于I2C 总线的最高速率。 PCF8591内部功能模块的框图如图 31.1.1所示: 图 31.1.1 PCF8591功能框图 当使用I2C总线配置完状态寄存器(STATUS REGISTER)后,传送数模转换的数据到DAC数 据寄存器(DAC DATA REGISTER)。经片内数模转换器(DAC)转换成对应的模拟电压。片内DAC 由连接至外部参考电压的具有256个接头的电阻分压电路和选择开关组成。接头译码器切换一 个接头至DAC输出线经AOUT接口输出,如下图 31.1.2所示。 图 31.1.2 DAC电阻电路 片内DAC也可用于逐次逼近式ADC。为了在AD转换进行时能够保持DA转换的电压输出,器件 内配备了采样和保持电路(SAMPLE AND HOLD)。 AIN0至AIN3为多路复用模拟输入接口(ANALOGUE MULtiPLEXER)。外部的模拟信号通过多 路复用模拟输入接口后被采样和保持(SAMPLE AND HOLD),并通过比较器(COMPARATOR)采 用逐次逼近转换技术转换成数字信号。 PCF8591采用I2C总线协议与控制器(FPGA)进行通信,有关I2C总线协议详细的介绍请大 家参考“EEPROM读写实验”。PCF8591的器件地址(7位)由固定部分和可编程部分组成。可编 程部分根据地址引脚A0、A1、A2来设置,第8位为读写控制位R/W,格式如下图所示 图 31.1.3 器件地址格式图 发送完器件地址和读写控制位后就发送控制字。PCF8591将控制字存储在控制寄存器,用 于控制器件功能。控制字格式如下图所示: 图 31.1.4 控制字格式图 控制字的第6位用于使能模拟输出(高有效),第5和第4位用于模拟输入编程。当第5位和 第4位为00时,AIN0至AIN3为四个单端输入;为01时AIN0至AIN3为三个差分输入;为10时AIN0 至AIN3为两单端一差分输入;为11时AIN0至AIN3为两差分输入。我们这次实验使用的是四个单 端输入(00)。第2位为自增标志位,高有效。如果自增标志位置1,每次AD转换后通道号将自 动增加。如果选择一个不存在的输入通道将导致分配到最高可用的通道号,此时如果自增标志 有效,那么下一个被选择的通道将总是通道0。第1位和第0位用于选择通道号:00为通道0;01 为通道1;10为通道2;11为通道3。第7位和第3位是预留未来使用的,必须设置为0。上电复位 后,控制寄存器所有位为0。 如果是进行DA转换,在发送完控制字后发送需要转换的8位数据,这8位数据被PCF8591存 储在DAC数据寄存器,并使用片上DA转换器转换成对应的模拟电压输出。转换电压如图 31.1.5 所示: 图 31.1.5 DA转换电压 图 31.1.6 DA转换时序图 从DA转换时序图(图 31.1.6)中,我们可以看到在发送第一个字节的转换数据(DATA BYTE1) 时,DAC输出的还是先前的转换的值,在PCF8591发送应答信号(A)后DAC才开始输出相应的模 拟电压。 在开始进行AD转换时,一个AD转换周期总是开始于发送一个有效读模式命令给PCF8591。 AD转换周期在每次应答时钟脉冲的后沿被触发,并在传输当前AD数据寄存器的数据时执行转换 操作,如下图 31.1.7所示。 图 31.1.7 AD转换时序图 图 31.1.7中,DATA BYTE0是ADC寄存器(ADC DATA REGISTER)当前存储的值,也就是先 前转换的值。PCF8591在传送DATA BYTE0时采样所选模拟输入通道的输入电压,并转换为对应 的8位二进制码即DATA BYTE1的数据。转换结果被保存在ADC数据寄存器。上电复位之后ADC寄 存器中的数据默认为0x80,故读取的第一个ADC数据为0x80。 本次实验采用的是单端输入,其转换特性如下图所示: 图 31.1.8 单端输入的AD转换特性 可见转换精度Vl***与参考电压VREF和模拟地AGND有关,而ADC转换的数据(ADC DATA)与转 换精度有关。AGND引脚因为必须连接到系统模拟地,所以一般为0。 实验任务 本节实验任务是使用开拓者FPGA开发板上的PCF8591模块实现数模、模数转换。FPGA输出 从0~255变化的数字信号,经DAC转换后得到模拟信号。然后利用ADC采集该模拟信号,并将采 集的电压值显示在数码管上。 硬件设计 开拓者开发板上PCF8591接口部分的原理图如图 31.3.1所示。 图 31.3.1 PCF8591接口原理图 由上图可见器件地址的可编程引脚(A0/A1/A2)全部接地,所以PCF8591的器件地址为 7’h48。参考电压为3.3V,AGND接地,所以AD转换精度Vl*** = 3 .3−0 256 ≈ 0.01289,转换的数值为 VAIN = 256× 3. 3 。 由实验任务可知,我们需要把DA转换输出引脚AOUT连接至任一AD输入引脚,这里我们选择 AIN0,相应的控制字设置为8’h40。 本实验中,各端口信号的管脚分配如下表所示: 表 31.3.1 ADC模数DAC数模转换(PCF8591)实验管脚分配 程序设计 根据实验任务,我们可以大致规划出系统的控制流程:FPGA首先通过I2C总线向PCF8591写 入DA转换的数据,然后再从PCF8591中读取AD转换的值,将读取的到值转换为实际的模拟电压, 并用数码管显示出来。由此画出系统的功能框图如下所示: 图 31.4.1 ADC模数DAC数模转换(PCF8591)系统框图 程序中各模块端口及信号连接如图 31.4.2所示: 图 31.4.2 顶层模块原理图 顶层模块(adda_top):例化了IIC驱动模块(i2c_dri)、PCF8591 AD和DA转换模块(pcf8591) 以及数码管驱动模块(seg_led)三个模块。并实现各模块控制及数据信号的交互。PCF8591 AD 和DA转换模块调用IIC驱动模块与PCF8591进行通信,并将处理得到的AD值送入数码管显示。 PCF8591 DA和AD转换模块模块(pcf8591):通过调用IIC驱动模块来实现DA和AD的转换, 并将读到的AD数值转换成模拟电压(num)传递给数码管驱动模块(segled_dri_x)显示。 IIC驱动模块(i2c_dri):由于PCF8591采用IIC协议,所以使用IIC驱动模块实现FPGA与 PCF8591间的通信。 数码管驱动模块(seg_led):数码管驱动模块显示AD转换得到的电压值。 顶层模块的代码如下: 1 module adda_top( 2 //system clock 3 input sys_clk , // 系统时钟 4 input sys_rst_n , // 系统复位 5 6 //PCF8591 interface 7 output scl , // i2c时钟线 8 inout sda , // i2c数据线 9 10 //user interface 11 output [5:0] sel , // 数码管位选 12 output [7:0] seg_led // 数码管段选 13 ); 14 15 //parameter define 16 parameter SLAVE_ADDR = 7'h48 ; // 器件地址(SLAVE_ADDR) 17 parameter BIT_CTRL = 1'b0 ; // 字地址位控制参数(16b/8b) 18 parameter CLK_FREQ = 26'd50_000_000; // i2c_dri模块的驱动时钟频率(CLK_FREQ) 19 parameter I2C_FREQ = 18'd250_000 ; // I2C的SCL时钟频率 20 parameter POINT = 6'b00_1000 ; // 控制点亮数码管小数点的位置 21 22 //wire define 23 wire clk ; // I2C操作时钟 24 wire i2c_exec ; // i2c触发控制 25 wire [15:0] i2c_addr ; // i2c操作地址 26 wire [ 7:0] i2c_data_w; // i2c写入的数据 27 wire i2c_done ; // i2c操作结束标志 28 wire i2c_rh_wl ; // i2c读写控制 29 wire [ 7:0] i2c_data_r; // i2c读出的数据 30 wire [19:0] num ; // 数码管要显示的数据 31 32 //***************************************************** 33 //** main code 34 //***************************************************** 35 36 //例化AD/DA模块 37 pcf8591 u_pcf8591( 38 //global clock 39 .clk (clk ), // 时钟信号 40 .rst_n (sys_rst_n ), // 复位信号 41 //i2c interface 42 .i2c_exec (i2c_exec ), // I2C触发执行信号 43 .i2c_rh_wl (i2c_rh_wl ), // I2C读写控制信号 44 .i2c_addr (i2c_addr ), // I2C器件内地址 45 .i2c_data_w (i2c_data_w), // I2C要写的数据 46 .i2c_data_r (i2c_data_r), // I2C读出的数据 47 .i2c_done (i2c_done ), // I2C一次操作完成 48 //user interface 49 .num (num ) // 采集到的电压 50 ); 51 52 //例化i2c_dri 53 i2c_dri #( 54 .SLAVE_ADDR (SLAVE_ADDR), // slave address从机地址,放此处方便参数传递 55 .CLK_FREQ (CLK_FREQ ), // i2c_dri模块的驱动时钟频率(CLK_FREQ) 56 .I2C_FREQ (I2C_FREQ ) // I2C的SCL时钟频率 57 ) u_i2c_dri( 58 //global clock 59 .clk (sys_clk ), // i2c_dri模块的驱动时钟(CLK_FREQ) 60 .rst_n (sys_rst_n ), // 复位信号 61 //i2c interface 62 .i2c_exec (i2c_exec ), // I2C触发执行信号 63 .bit_ctrl (BIT_CTRL ), // 器件地址位控制(16b/8b) 64 .i2c_rh_wl (i2c_rh_wl ), // I2C读写控制信号 65 .i2c_addr (i2c_addr ), // I2C器件内地址 66 .i2c_data_w (i2c_data_w), // I2C要写的数据 67 .i2c_data_r (i2c_data_r), // I2C读出的数据 68 .i2c_done (i2c_done ), // I 2C一次操作完成 69 .scl (scl ), // I2C的SCL时钟信号 70 .sda (sda ), // I2C的SDA信号 71 //user interface 72 .dri_clk (clk ) // I2C操作时钟 73 ); 74 75 //例化动态数码管显示模块 76 seg_led u_seg_led( 77 //module clock 78 .clk (sys_clk ), // 时钟信号 79 .rst_n (sys_rst_n), // 复位信号 80 //seg_led interface 81 .sel (sel ), // 位选 82 .seg_led (seg_led ), // 段选 83 //user interface 84 .data (num ), // 显示的数值 85 .point (POINT ), // 小数点具体显示的位置,从高到低,高电平有效 86 .en (1'd1 ), // 数码管使能信号 87 .sign (1'b0 ) // 符号位(高电平显示“-”号) 88 ); 89 90 endmodule 顶层模块中主要完成对其余模块的例化,其中I2C驱动模块(i2c_dri)程序与“EEPROM读 写实验”章节中的IIC驱动模块(i2c_dri)程序完全相同。有关IIC驱动模块的详细介绍请大 家参考“EEPROM读写实验”。 PCF8591 AD和DA转换模块模块的代码如下所示: 1 module pcf8591( 2 //clock and reset 3 input clk , // 时钟信号 4 input rst_n , // 复位信号 5 6 //i2c interface 7 output reg i2c_rh_wl , // I2C读写控制信号 8 output reg i2c_exec , // I2C触发执行信号 9 output reg [15:0] i2c_addr , // I2C器件内地址 10 output reg [ 7:0] i2c_data_w , // I2C要写的数据 11 input [ 7:0] i2c_data_r , // I2C读出的数据 12 input i2c_done , // I2C一次操作完成 13 14 //user interface 15 output reg [19:0] num // 数码管要显示的数据 16 ); 17 18 //parameter 19 parameter CONTORL_BYTE = 8'b0100_0000; // PCF8591的控制字 20 parameter V_REF = 12'd3300 ; // 3.3V放大1000倍,避免用小数 21 22 //reg define 23 reg [7:0] da_data ; // DA数据 24 reg [7:0] ad_data ; // AD数据 25 reg [3:0] flow_cnt ; // 状态流控制 26 reg [18:0] wait_cnt ; // 计数等待 27 28 //wire define 29 wire [19:0] num_t ; // 临时寄存的数据 30 31 //***************************************************** 32 //** main code 33 //***************************************************** 34 35 assign num_t = V_REF * ad_data ; 36 37 //DA输出数据 38 always @(posedge clk or negedge rst_n) begin 39 if(rst_n == 1'b0) begin 40 da_data <= 8'd0; 41 end 42 else if(i2c_rh_wl == 1'b0 & i2c_done == 1'b1)begin 43 if(da_data == 8'd255) 44 da_data<= 8'd0; 45 else 46 da_data<= da_data + 1'b1; 47 end 48 end 49 50 //AD输入数据处理 51 always @(posedge clk or negedge rst_n) begin 52 if(rst_n == 1'b0) begin 53 num <= 20'd0; 54 end 55 else 56 num <= num_t >> 4'd8; 57 end 58 59 //AD、DA控制及采样 60 always @(posedge clk or negedge rst_n) begin 61 if(rst_n == 1'b0) begin 62 i2c_exec <= 1'b0; 63 i2c_rh_wl<= 1'b0; 64 i2c_addr <= 8'd0; 65 i2c_data_w <= 8'd0; 66 flow_cnt <= 4'd0; 67 wait_cnt <= 17'd0; 68 end 69 else begin 70 i2c_exec <= 1'b0; 71 case(flow_cnt) 72 'd0: begin 73 if(wait_cnt == 17'd100) begin 74 wait_cnt<= 17'd0; 75 flow_cnt<= flow_cnt + 1'b1; 76 end 77 else 78 wait_cnt<= wait_cnt + 1'b1; 79 end 80 //DA转换输出 81 'd1: begin 82 i2c_exec <= 1'b1; 83 i2c_addr <= CONTORL_BYTE; 84 i2c_rh_wl <= 1'b0; 85 i2c_data_w<= da_data; 86 flow_cnt <= flow_cnt + 1'b1; 87 end 88 'd2: begin 89 if(i2c_done == 1'b1) begin 90 flow_cnt<= flow_cnt + 1'b1; 91 end 92 end 93 'd3: begin 94 //每1秒变化0.1V,需33秒变化完,共256【0~255】次变化,故每次变化计数为: 95 //(33/256)*10^6 = 128906 96 if(wait_cnt == 17'd128906) begin 97 wait_cnt<= 17'd0; 98 flow_cnt<= flow_cnt + 1'b1; 99 end 100 else 101 wait_cnt<= wait_cnt + 1'b1; 102 end 103 //AD转换输入 104 'd4: begin 105 i2c_exec <= 1'b1; 106 i2c_addr <= CONTORL_BYTE; 107 i2c_rh_wl <= 1'b1; 108 flow_cnt <= flow_cnt + 1'b1; 109 end 110 'd5: begin 111 if(i2c_done == 1'b1) begin 112 ad_data <= i2c_data_r; 113 flow_cnt<= 4'd0; 114 end 115 end 116 default: flow_cnt <= 4'd0; 117 endcase 118 end 119 end 120 121 endmodule 因 为 AD 采集到的 数 据 ad_data = VAIN = 256× 3. 3 ,所以输入的 模拟电压 VAIN = _×3.3×1000 256×1000 。程序中第35行我们对采集到的AD数据进行了处理,使其乘以V_REF(3300), 也就是对参考电压放大1000倍,然后在第58行以右移8位的方式除以256。结合顶层模块的第20 行可知我们使数码管的小数点显示在第4位数码管,相当于对输入给数码管显示的数值缩小 1000倍,这样我们送给数码管显示的值就是实际的模拟输入值。 图 31.4.3为读取过程中SignalTap抓取的波形图。可以看到寄存器地址为40h,也就是控 制字为0x40,选择的是通道0(AIN0),并使能模拟输出。当前读到的AD值为02h,写的DA值为 03h,可见传送的上次AD转换的值,传递给数码管显示的数值为25,表明当前输入的模拟电压 的值为0.025V, 图 31.4.3 SignalTap波形图 下载验证 首先我们打开PCF8591模数/数模转换实验工程,在工程所在的路径下打开adda_top/par文 件夹,在里面找到“adda_top.qpf”并双击打开。注意工程所在的路径名只能由字母、数字以 及下划线组成,不能出现中文、空格以及特殊字符等。工程打开后如图 31.5.1所示。 图 31.5.1 ADC模数DAC数模转换(PCF8591)工程 然后将下载器一端连电脑,另一端与开发板上对应端口连接,并将P4的AOUT引脚与AINO用 跳帽或者杜邦线连接起来,最后连接电源线并打开电源开关。 接下来我们下载程序,验证PCF859的ADC模数DAC数模转换功能。 工程打开后通过点击工具栏中的“Programmer”图标打开下载界面,通过“Add File”按 钮选择adda_top/par/output_files目录下的“adda_top.sof”文件。开发板电源打开后,在 程序下载界面点击“Hardware Setup”,在弹出的对话框中选择当前的硬件连接为“USB- Blaster[USB-0]”。然后点击“Start”将工程编译完成后得到的sof文件下载到开发板中,如 图 31.5.2所示。 图 31.5.2 程序下载界面 下载完成后观察到开发板上数码管显示的值从0增加到3.3V,说明ADC模数DAC数模转换 (PCF8591)实验程序下载验证成功。 图 31.5.3 数码管显示 |
|
相关推荐
|
|
矩阵4x4个按键,如何把识别结果按编号01-16(十进制)显示在两个七段数码管上?
867 浏览 0 评论
855 浏览 0 评论
1767 浏览 0 评论
465 浏览 0 评论
645 浏览 0 评论
1240 浏览 25 评论
5455 浏览 113 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-5 14:23 , Processed in 0.606026 second(s), Total 62, Slave 44 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号