FPGA|CPLD|ASIC论坛
直播中

正点原子运营官

6年用户 1793经验值
擅长:嵌入式技术 模拟技术 控制/MCU
私信 关注
[资料]

正点原子开拓者FPGA开发板资料连载第三十一章 AD/DA实验

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 数码管显示





更多回帖

发帖
×
20
完善资料,
赚取积分