AD-DA

1. PCF8591简介

PCF8591是一个单片集成、单电源供电、低功耗的8位CMOS数据采集转换(AD/DA)器件,具有4个模拟输入、1个模拟输出和1个串行IIC总线接口。

1.1 PCF8591框图

在这里插入图片描述

1.2 PCF8591器件地址

A0/A1/A2用于开发板上多快PCF8591的地址配置;读写控制位(0:写 -DA 1:读-AD)
在这里插入图片描述

1.3 PCF8591状态寄存器

在这里插入图片描述
bit[6]:模拟输出使能(1:使能 0:不使能)
bit[5:4]:模拟输入编程,00:4个AIN通道都是单端输入
bit[2]:自增标志 0:不自增 1:自增
bit[1:0]:00:AIN0通道 01:AIN1通道 10:AIN2通道 11:AIN3通道

1.4 PCF8591写数据(DA转换)

在这里插入图片描述
S:Start
A:应答
ADDRESS:器件地址7’b1001000=7’h48
CONTROL BYTE:控制字
DATA BYTE:DA转换数据
在这里插入图片描述
上图为写入数据与输出电压之间的关系及公式,在本次实验过程中,VAGND=0,VREF=3.3V,VAOUT=3.3/256*Data

1.5 PCF8591读数据(AD转换)

在这里插入图片描述
S:Start
A:应答
ADDRESS:器件地址7’b1001000=7’h48
DATA BYTE:AD转换后的数据
在这里插入图片描述
读出数据和模拟电压之间的转换关系:
Visb=3.3V/256=0.012890625V
AD_DATA=0
VIN=0Visb=0V
AD_DATA=255
VIN=255
Visb=3.287109375V

2. 程序设计

使用FPGA开发板上的PCF8591模块实现数模、模数转换。FPGA输出从0~255变化的数字信号,经DAC转换后得到模拟信号。然后利用ADC采集该模拟信号,并将采集的电压值显示在数码管上。
即0-255——>DA——>AOUT——>AIN——>AD——>0~255

2.1 系统框图

在这里插入图片描述

2.2 源码

其中IIC设计源码参考EEPROM读写–IIC协议

扫描二维码关注公众号,回复: 13144997 查看本文章
module adda_top(
    //system clock
    input                sys_clk    ,    // 系统时钟
    input                sys_rst_n  ,    // 系统复位

    //PCF8591 interface
	output               i2c_ack    ,    // I2C应答标志 0:应答 1:未应答
    output               scl        ,    // i2c时钟线
    inout                sda        ,    // i2c数据线

    //user interface
    output        [5:0]  sel        ,    // 数码管位选
    output        [7:0]  seg_led         // 数码管段选
);

//parameter define
parameter    SLAVE_ADDR =  7'h48        ; // 器件地址(SLAVE_ADDR)
parameter    BIT_CTRL   =  1'b0         ; // 字地址位控制参数(16b/8b)
parameter    CLK_FREQ   = 26'd50_000_000; // i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter    I2C_FREQ   = 18'd250_000   ; // I2C的SCL时钟频率
parameter    POINT      = 6'b00_1000    ; // 控制点亮数码管小数点的位置

//wire define
wire           clk       ;                // I2C操作时钟
wire           i2c_exec  ;                // i2c触发控制
wire   [15:0]  i2c_addr  ;                // i2c操作地址
wire   [ 7:0]  i2c_data_w;                // i2c写入的数据
wire           i2c_done  ;                // i2c操作结束标志
wire           i2c_rh_wl ;                // i2c读写控制
wire   [ 7:0]  i2c_data_r;                // i2c读出的数据
wire   [19:0]  num       ;                // 数码管要显示的数据

//*****************************************************
//**                    main code
//*****************************************************

//例化AD/DA模块
pcf8591 u_pcf8591(
    //global clock
    .clk         (clk       ),            // 时钟信号
    .rst_n       (sys_rst_n ),            // 复位信号
    //i2c interface
    .i2c_exec    (i2c_exec  ),            // I2C触发执行信号
    .i2c_rh_wl   (i2c_rh_wl ),            // I2C读写控制信号
    .i2c_addr    (i2c_addr  ),            // I2C器件内地址
    .i2c_data_w  (i2c_data_w),            // I2C要写的数据
    .i2c_data_r  (i2c_data_r),            // I2C读出的数据
    .i2c_done    (i2c_done  ),            // I2C一次操作完成
    //user interface
    .num         (num       )             // 采集到的电压
);

//例化i2c_dri
i2c_dri #(
    .SLAVE_ADDR  (SLAVE_ADDR),            // slave address从机地址,放此处方便参数传递
    .CLK_FREQ    (CLK_FREQ  ),            // i2c_dri模块的驱动时钟频率(CLK_FREQ)
    .I2C_FREQ    (I2C_FREQ  )             // I2C的SCL时钟频率
) u_i2c_dri(
    //global clock
    .clk         (sys_clk   ),            // i2c_dri模块的驱动时钟(CLK_FREQ)
    .rst_n       (sys_rst_n ),            // 复位信号
    //i2c interface
    .i2c_exec    (i2c_exec  ),            // I2C触发执行信号
    .bit_ctrl    (BIT_CTRL  ),            // 器件地址位控制(16b/8b)
    .i2c_rh_wl   (i2c_rh_wl ),            // I2C读写控制信号
    .i2c_addr    (i2c_addr  ),            // I2C器件内地址
    .i2c_data_w  (i2c_data_w),            // I2C要写的数据
    .i2c_data_r  (i2c_data_r),            // I2C读出的数据
    .i2c_done    (i2c_done  ),            // I 2C一次操作完成
	.i2c_ack     (i2c_ack   ),            // I2C应答标志 0:应答 1:未应答
    .scl         (scl       ),            // I2C的SCL时钟信号
    .sda         (sda       ),            // I2C的SDA信号
    //user interface
    .dri_clk     (clk       )             // I2C操作时钟
);

//例化动态数码管显示模块
seg_led u_seg_led(
    //module clock
    .clk           (sys_clk  ),           // 时钟信号
    .rst_n         (sys_rst_n),           // 复位信号
    //seg_led interface
    .seg_sel       (sel      ),           // 位选
    .seg_led       (seg_led  ),           // 段选
    //user interface
    .data          (num      ),           // 显示的数值
    .point         (POINT    ),           // 小数点具体显示的位置,从高到低,高电平有效
    .en            (1'd1     ),           // 数码管使能信号
    .sign          (1'b0     )            // 符号位(高电平显示“-”号)
);

endmodule
module pcf8591(
    //clock and reset
    input                 clk        ,    // 时钟信号
    input                 rst_n      ,    // 复位信号

    //i2c interface
    output   reg          i2c_rh_wl  ,    // I2C读写控制信号
    output   reg          i2c_exec   ,    // I2C触发执行信号
    output   reg  [15:0]  i2c_addr   ,    // I2C器件内地址
    output   reg  [ 7:0]  i2c_data_w ,    // I2C要写的数据
    input         [ 7:0]  i2c_data_r ,    // I2C读出的数据
    input                 i2c_done   ,    // I2C一次操作完成

    //user interface
    output   reg  [19:0]  num             // 数码管要显示的数据
);

//parameter
parameter    CONTORL_BYTE = 8'b0100_0000; // PCF8591的控制字
parameter    V_REF        = 12'd3300    ; // 3.3V放大1000倍,避免用小数

//reg define
reg    [7:0]    da_data   ;               // DA数据
reg    [7:0]    ad_data   ;               // AD数据
reg    [3:0]    flow_cnt  ;               // 状态流控制
reg    [18:0]   wait_cnt  ;               // 计数等待

//wire define
wire   [19:0]   num_t     ;               // 临时寄存的数据

//*****************************************************
//**                    main code
//*****************************************************

assign num_t = V_REF * ad_data ;

//DA输出数据
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0) begin
        da_data  <= 8'd0;
    end
    else if(i2c_rh_wl == 1'b0 && i2c_done == 1'b1)begin
        if(da_data == 8'd255)
            da_data<= 8'd0;
        else
            da_data<= da_data + 1'b1;
    end
end

//AD输入数据处理
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0) begin
        num <= 20'd0;
    end
    else
        num <= num_t >> 4'd8;
end

//AD、DA控制及采样
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0) begin
        i2c_exec <= 1'b0;
        i2c_rh_wl<= 1'b0;
        i2c_addr <= 8'd0;
        i2c_data_w <=  8'd0;
        flow_cnt   <=  4'd0;
        wait_cnt   <= 17'd0;
    end
    else begin
        i2c_exec <= 1'b0;
        case(flow_cnt)
            'd0: begin
                if(wait_cnt == 17'd100) begin
                    wait_cnt<= 17'd0;
                    flow_cnt<= flow_cnt + 1'b1;
                end
                else
                    wait_cnt<= wait_cnt + 1'b1;
            end
            //DA转换输出
            'd1: begin
                i2c_exec  <= 1'b1;
                i2c_addr  <= CONTORL_BYTE;
                i2c_rh_wl <= 1'b0;
                i2c_data_w<= da_data;
                flow_cnt  <= flow_cnt + 1'b1;
            end
            'd2: begin
                if(i2c_done == 1'b1) begin
                    flow_cnt<= flow_cnt + 1'b1;
                end
            end
            'd3: begin
            //每1秒变化0.1V,需33秒变化完,共256【0~255】次变化,故每次变化计数为:
            //(33/256)*10^6 = 128906
                if(wait_cnt == 17'd128906) begin
                    wait_cnt<= 17'd0;
                    flow_cnt<= flow_cnt + 1'b1;
                end
                else
                    wait_cnt<= wait_cnt + 1'b1;
            end
            //AD转换输入
            'd4: begin
                i2c_exec  <= 1'b1;
                i2c_addr  <= CONTORL_BYTE;
                i2c_rh_wl <= 1'b1;
                flow_cnt  <= flow_cnt + 1'b1;
            end
            'd5: begin
                if(i2c_done == 1'b1) begin
                    ad_data <= i2c_data_r;
                    flow_cnt<= 4'd0;
                end
            end
            default: flow_cnt <= 4'd0;
        endcase
    end
end

endmodule

猜你喜欢

转载自blog.csdn.net/gemengxia/article/details/115461519
da
ad