芯路恒电子技术论坛

 找回密码
 立即注册

微信扫码登录

手机号码,快捷登录

手机号码,快捷登录

热搜: 合集
查看: 392|回复: 2

请教一个DAC TLV5638的问题

[复制链接]

该用户从未签到

2

主题

1

回帖

15

积分

新手入门

Rank: 1

积分
15
发表于 2025-12-5 13:55:38 | 显示全部楼层 |阅读模式
我想使用  DOUT A输出最大电压4.095V, 但是测量到的总是2.04V,不知道为什么? 下面是代码,使用内部参考源2.04V


module tlv5638_chA_1hz (
    input  wire clk,         // 系统时钟(默认50MHz)
    input  wire rst_n,       // 异步复位(低有效)
    output reg [11:0] voltage_data,
    output reg  sclk,        // TLV5638 SPI时钟(1MHz)
    output reg  din,         // TLV5638 串行数据(MSB先行)
    output reg  cs_n         // TLV5638 片选(低有效)
);

// ===========================================================================
// 核心参数定义(基于手册规则,内部参考2.048V)
// ===========================================================================
localparam SYS_CLK_FREQ    = 50_000_000;  // 系统时钟频率(50MHz)
localparam SPI_CLK_FREQ    = 1_000_000;   // SPI目标时钟频率(1MHz)
localparam DAC_RESOLUTION  = 12;          // TLV5638为12位DAC
localparam UPDATE_FREQ     = 1;           // 电压更新频率(1Hz)
localparam INT_REF_VOLTAGE = 2048;        // 内部参考电压(2.048V,单位:毫伏)
localparam TARGET_STEP_V   = 500;         // 目标电压步进(0.5V,单位:毫伏)
localparam OUTPUT_GAIN     = 2;           // 输出缓冲固定×2增益

// 关键参数计算
localparam FULL_SCALE_V    = OUTPUT_GAIN * INT_REF_VOLTAGE; // 满量程输出(4096mV=4.096V)
localparam SPI_CLK_DIV     = SYS_CLK_FREQ / (2 * SPI_CLK_FREQ); // SCLK分频系数(25)
localparam UPDATE_CNT      = SYS_CLK_FREQ / UPDATE_FREQ;    // 1秒计数阈值(50_000_000)
localparam SHIFT_CYCLES    = 16;         // 需传输16位数据(4控制+12数据)

// 0.5V对应的12位数字量步进(手册公式:ΔD=(TARGET_STEP_V×4096)/FULL_SCALE_V)
localparam VOLTAGE_STEP    = (TARGET_STEP_V * 4096) / FULL_SCALE_V; // (500×4096)/4096=500

// ===========================================================================
// 控制字定义(根据手册修正 - D15:R1, D14:SPD, D13WR, D12:R0)
// ===========================================================================
// 配置控制寄存器:R1=1,R0=1→控制寄存器,SPD=0,PWR=0,REF1=1,REF0=1→内部2.048V参考
localparam [15:0] CONTROL_REF_CONFIG = 16'b1100_0000_0000_0010;

// 写入DAC A寄存器:R1=1,R0=0→DAC A,SPD=0,PWR=0
localparam [3:0]  CMD_DAC_A_WRITE    = 4'b1001;

// 状态机定义
localparam IDLE         = 3'd0;  // 空闲(复位初始化)
localparam CONFIG_REF   = 3'd1;  // 配置CONTROL寄存器(激活内部参考)
localparam SHIFT_START  = 3'd2;  // 拉低片选+准备DAC数据移位
localparam SHIFT_RUN    = 3'd3;  // 串行移位(SCLK翻转+数据输出)
localparam DELAY_1S     = 3'd4;  // 移位完成后1秒延时

// ===========================================================================
// 内部信号定义
// ===========================================================================
reg [2:0]  curr_state;                   // 当前状态
reg [2:0]  next_state;                   // 下一状态
reg [15:0] dac_data;                     // 16位串行控制字(4控制+12数据)
reg [3:0]  shift_cnt;                    // 移位计数器(0~15)
reg [31:0] sclk_cnt;                     // SCLK分频计数器
reg [31:0] delay_cnt;                    // 1秒延时计数器
reg ref_config_done;                     // 参考配置完成标志

// ===========================================================================
// 1. 状态机时序逻辑
// ===========================================================================
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        curr_state <= IDLE;
    end else begin
        curr_state <= next_state;
    end
end

// ===========================================================================
// 2. 状态机组合逻辑
// ===========================================================================
always @(*) begin
    next_state = curr_state;
    case (curr_state)
        IDLE: begin
            next_state = CONFIG_REF;  // 复位后首先配置内部参考
        end
        CONFIG_REF: begin
            // 参考配置完成 → 进入延时准备第一次DAC输出
            if (ref_config_done) begin
                next_state = DELAY_1S;
            end
        end
        SHIFT_START: begin
            next_state = SHIFT_RUN;    // 准备完成后立即进入移位
        end
        SHIFT_RUN: begin
            // 16位移位完成 → 进入1秒延时
            if (shift_cnt == SHIFT_CYCLES - 1'b1 && sclk_cnt == SPI_CLK_DIV - 1'b1) begin
                next_state = DELAY_1S;
            end
        end
        DELAY_1S: begin
            // 1秒延时完成 → 回到DAC数据准备(循环输出)
            if (delay_cnt == UPDATE_CNT - 1'b1) begin
                next_state = SHIFT_START;
            end
        end
        default: begin
            next_state = IDLE;
        end
    endcase
end

// ===========================================================================
// 3. SCLK生成逻辑(符合手册时序:空闲高电平,占空比50%)
// ===========================================================================
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        sclk_cnt <= 32'd0;
        sclk <= 1'b1;  // 手册:SCLK空闲状态为高电平
    end else begin
        // 仅在"参考配置"和"DAC移位"阶段生成SCLK
        if (curr_state == CONFIG_REF || curr_state == SHIFT_RUN) begin
            if (sclk_cnt == SPI_CLK_DIV - 1'b1) begin
                sclk_cnt <= 32'd0;
                sclk <= ~sclk;  // 计数到阈值翻转,确保占空比50%
            end else begin
                sclk_cnt <= sclk_cnt + 1'b1;
            end
        end else begin
            sclk_cnt <= 32'd0;
            sclk <= 1'b1;  // 非移位阶段保持高电平
        end
    end
end

// ===========================================================================
// 4. 核心控制逻辑
// ===========================================================================
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cs_n           <= 1'b1;          // 复位时片选拉高
        din            <= 1'b0;          // 数据默认低电平
        shift_cnt      <= 4'd0;          // 移位计数器清零
        voltage_data   <= 12'd0;         // 初始DAC数据(0V)
        delay_cnt      <= 32'd0;         // 延时计数器清零
        dac_data       <= 16'd0;         // 控制字初始化
        ref_config_done <= 1'b0;         // 参考配置标志清零
    end else begin
        case (curr_state)
            IDLE: begin
                cs_n <= 1'b1;
                din <= 1'b0;
                ref_config_done <= 1'b0;
            end

            // --------------------------
            // 状态1:配置CONTROL寄存器(激活内部2.048V参考)
            // D15-D12: 1100 (R1=1,R0=1→控制寄存器, SPD=0, PWR=0)
            // D1-D0:   11   (REF1=1,REF0=1→内部2.048V参考)
            // --------------------------
            CONFIG_REF: begin
                cs_n <= 1'b0;  // 拉低片选
                dac_data <= CONTROL_REF_CONFIG;  // 16'b1100_0000_0000_0011
               
                // SCLK上升沿:输出当前位数据(MSB先行)
                if (sclk_cnt == 32'd0 && sclk == 1'b0) begin
                    din <= dac_data[15 - shift_cnt];
                end
               
                // SCLK下降沿:移位计数
                if (sclk_cnt == SPI_CLK_DIV - 1'b1 && sclk == 1'b1) begin
                    if (shift_cnt < SHIFT_CYCLES - 1'b1) begin
                        shift_cnt <= shift_cnt + 1'b1;
                    end else begin
                        // 16位传输完成,置位配置标志
                        ref_config_done <= 1'b1;
                        shift_cnt <= 4'd0;  // 为下一次传输准备
                    end
                end
            end

            // --------------------------
            // 状态2:准备DAC数据(通道A)
            // D15-D12: 1000 (R1=1,R0=0→DAC A, SPD=0, PWR=0)
            // D11-D0:  12位电压数据
            // --------------------------
            SHIFT_START: begin
                cs_n <= 1'b0;  // 拉低片选
                shift_cnt <= 4'd0;    // 复位移位计数器
               
                // 16位控制字:修正的控制位 + 12位电压数据
                dac_data <= {CMD_DAC_A_WRITE, voltage_data};  // {4'b1000, 12位电压数据}
               
                // 提前输出最高位(确保第一个SCLK上升沿时有有效数据)
                if (sclk_cnt == 32'd0) begin
                    din <= dac_data[15];
                end
            end

            // --------------------------
            // 状态3:串行移位(传输DAC数据)
            // --------------------------
            SHIFT_RUN: begin
                cs_n <= 1'b0;  // 保持片选有效
               
                // SCLK上升沿:输出当前位数据
                if (sclk_cnt == 32'd0 && sclk == 1'b0) begin
                    din <= dac_data[15 - shift_cnt];
                end
               
                // SCLK下降沿:移位计数器递增
                if (sclk_cnt == SPI_CLK_DIV - 1'b1 && sclk == 1'b1) begin
                    if (shift_cnt < SHIFT_CYCLES - 1'b1) begin
                        shift_cnt <= shift_cnt + 1'b1;
                    end
                end
            end

            // --------------------------
            // 状态4:1秒延时+电压数据递增
            // --------------------------
            DELAY_1S: begin
                cs_n <= 1'b1;  // 拉高片选(结束本次移位)
               
                if (delay_cnt == UPDATE_CNT - 1'b1) begin
                    delay_cnt <= 32'd0;
                    // 电压数据递增:每次+500(对应0.5V),溢出后循环
                    if (voltage_data + VOLTAGE_STEP >= 4096) begin
                        voltage_data <= 12'd0;
                    end else begin
                        voltage_data <= voltage_data + VOLTAGE_STEP;
                    end
                end else begin
                    delay_cnt <= delay_cnt + 1'b1;
                end
            end
            
            default: begin
                cs_n <= 1'b1;
                din <= 1'b0;
                shift_cnt <= 4'd0;
            end
        endcase
    end
end

endmodule




回复

使用道具 举报

  • TA的每日心情
    慵懒
    2021-2-24 10:16
  • 456

    主题

    403

    回帖

    1万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    16595
    QQ
    发表于 2025-12-5 15:19:32 | 显示全部楼层
    这个建议看波形,是不是数据位错位了一位,也可以使用我们tlv5618的驱动测了看看
    回复 支持 1 反对 0

    使用道具 举报

    该用户从未签到

    2

    主题

    1

    回帖

    15

    积分

    新手入门

    Rank: 1

    积分
    15
     楼主| 发表于 2025-12-5 21:26:32 | 显示全部楼层
    谢谢,手上没有示波器,是要仿真一下吗? 新手上路,太难了
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|小黑屋|Archiver|芯路恒电子技术论坛 |鄂ICP备2021003648号

    GMT+8, 2026-1-10 15:43 , Processed in 0.056925 second(s), 38 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc. Template By 【未来科技】【 www.wekei.cn 】

    快速回复 返回顶部 返回列表