fpga通过uart向上位机传输ad7606采集数据

作者:数字站

1. 概括

前文提供了ad7606的驱动程序,本文通过串口将8路adc采集的数据传输给上位机显示。
工程的总体框图如下图所示,ad7606_drive驱动模块采集ad7606八路数据,八个数据处理模块data_dispose把采集的八路数据转换为电压数据,并且转换为BCD编码。uart_byte模块选择对应通道的数据发送给串口发送模块uart_tx,传输给上位机显示。
图1 工程框图.JPG


注意本工程ad7606驱动虽然是200Ksps采样率一直在采集数据,但由于uart的传输速率过低,其实并不是采集的所有数据都会通过uart传输给上位机,本工程只能简单采集稳定的电压,做做电压表可以,并不能用来采集波形数据。

驱动模块直接查看前面即可,本文不再赘述。

2. 程序设计

2.1数据处理模块

AD7606模块输入电压范围是[-5,+5],本模块功能是将16位adc补码数据转换为电压值,最终转换为BCD码,便于后续传输给上位机显示。
如下所示,首先把补码数据转换为15位原码数据,且保存符号位。转换为15位数据ch_data后,对应的模拟电压值为[0,5V],符号位决定电压值的正负。
 always@(posedge clk or negedge rst_n)begin
        r_din_vld <= {r_din_vld[0],din_vld};
    end

    //对应通道数据有效时,把补码数据转换为原码且保留符号位;
    always@(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            r_ch_data <= 'd0;
            r_ch_sig <= 'd0;
        end
        else if(din_vld)begin
            r_ch_data <= din[15] ? (~din[14:0] + 1) : din[14:0];
            r_ch_sig <= din[15];
        end
    end

电压值y,ad7606采集的15位数据x的转换关系为y=x/(2^15)。因此通过下面的方式转换,为了保留计算精度,电压值ch_voltage为真实值的2^15*1000倍,单位mv。ch_voltage = ch_data * 1000的计算方式采用移位和加法计算。

//对应通道数据乘以5000,得到真实电压的2^15倍,单位mv;
    always@(posedge clk)begin
        if(r_din_vld[0])begin//5/32768≈0.15mv,ADC采集数据乘以0.15得电压,0.15*2^15*1000=5000
            r_ch_voltage <= {r_ch_data,12'd0} + {r_ch_data,9'd0} + {r_ch_data,8'd0} + {r_ch_data,7'd0} + {r_ch_data,3'd0};
        end
    end

舍弃ch_voltage的低15位数据,等效除以2^15,电压值r_voltage计算就是转换结果,单位mv。

always@(posedge clk)begin
        r_voltage <= r_din_vld[1] ? r_ch_voltage[27:15] : r_voltage;//除以2^15次方,得到mv电压;
        r_voltage_vld <= r_din_vld[1];
        voltage_sig <= r_ch_sig;
    end

最后通过hex2bcd模块把转换后的电压值转换为BCD码,该模块全部采用参数化设计,前面有一篇文章也讲了具体实现原理,有兴趣的可以点击查看。

//--###############################################################################################
//--#
//--# File Name    : data_dispose
//--# Designer    : 数字站
//--# Tool      : Quartus 2018.1
//--# Design Date  : 2024.10.10
//--# Description  : 
//--# Version    : 0.0
//--# Coding scheme  : UTF-8(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
module data_dispose (
    input                  clk                ,//系统时钟信号;
    input                  rst_n              ,//系统复位信号,低电平有效;

    input        [15 : 0]      din                ,//输入数据;
    input                        din_vld              ,//输入数据有效指示信号,高电平有效;
    output        [15 : 0]      voltage              ,//输出电压,单位mv
    output reg                      voltage_sig   = 'd0 ,//输出电压的正负,低电平表示正;
    output                voltage_vld          //输出数据有效指示信号,高电平有效;
);
    reg             [1 : 0]         r_din_vld     = 'd0 ;
    reg             [14 : 0]        r_ch_data           ;//
    reg             [27 : 0]        r_ch_voltage  = 'd0 ;
    reg                             r_ch_sig      = 'd0 ;
    reg              [12 : 0]       r_voltage     = 'd0 ;  
  reg                        r_voltage_vld = 'd0 ;

    always@(posedge clk or negedge rst_n)begin
        r_din_vld <= {r_din_vld[0],din_vld};
    end

    //对应通道数据有效时,把补码数据转换为原码且保留符号位;
    always@(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            r_ch_data <= 'd0;
            r_ch_sig <= 'd0;
        end
        else if(din_vld)begin
            r_ch_data <= din[15] ? (~din[14:0] + 1) : din[14:0];
            r_ch_sig <= din[15];
        end
    end

    //对应通道数据乘以5000,得到真实电压的2^15倍,单位mv;
    always@(posedge clk)begin
        if(r_din_vld[0])begin//5/32768≈0.15mv,ADC采集数据乘以0.15得电压,0.15*2^15*1000=5000
            r_ch_voltage <= {r_ch_data,12'd0} + {r_ch_data,9'd0} + {r_ch_data,8'd0} + {r_ch_data,7'd0} + {r_ch_data,3'd0};
        end
    end

    //产生输出数据;
    always@(posedge clk)begin
        r_voltage <= r_din_vld[1] ? r_ch_voltage[27:15] : r_voltage;//除以2^15次方,得到mv电压;
        r_voltage_vld <= r_din_vld[1];
        voltage_sig <= r_ch_sig;
    end

    //调用十六进制转BCD模块,将13位十六进制数据转换成16位BCD码;
    hex2bcd #(
        .IN_DATA_W  ( 13            )//输入数据位宽;
    )
    u_hex2bcd (
        .clk        ( clk           ),//系统时钟;
        .rst_n      ( rst_n         ),//系统复位,低电平有效;
        .din        ( r_voltage     ),//输入二进制数据;
        .din_vld    ( r_voltage_vld ),//输入数据有效指示信号,高电平有效;
        .rdy        (               ),//忙闲指示信号,该信号高电平时才能输入有效数据;
        .dout       ( voltage       ),//输出8421BCD码;
        .dout_vld   ( voltage_vld   ) //输出数据有效指示信号,高电平有效;
    );

endmodule

2.2 uart字节发送模块

首先通过存储器保存八路adc转换后的bcd数据,保存的时候还要将数据加48转换为ascii编码,因为0对应的ascii码对应数值为48。

 always@(posedge clk)begin
        if(i_voltage_vld[0])begin
            r_voltage_sig[0] <=  i_voltage_sig[0];//保存通道0的符号位;
            r_voltage_g[0] <= i_voltage0[15 : 12] + 8'd48;//将通道0的个位数据转换为ASCII对应字符;
            r_voltage_s[0] <= i_voltage0[11 : 8] + 8'd48;//将通道0的十分位数据转换为ASCII对应字符;
            r_voltage_b[0] <= i_voltage0[7 : 4] + 8'd48;//将通道0的百分位数据转换为ASCII对应字符;
            r_voltage_q[0] <= i_voltage0[3 : 0] + 8'd48;//将通道0的千分位数据转换为ASCII对应字符;
        end
    end

    always@(posedge clk)begin
        if(i_voltage_vld[1])begin
            r_voltage_sig[1] <=  i_voltage_sig[1];//保存通道1的符号位;
            r_voltage_g[1] <= i_voltage1[15 : 12] + 8'd48;//将通道1的个位数据转换为ASCII对应字符;
            r_voltage_s[1] <= i_voltage1[11 : 8] + 8'd48;//将通道1的十分位数据转换为ASCII对应字符;
            r_voltage_b[1] <= i_voltage1[7 : 4] + 8'd48;//将通道1的百分位数据转换为ASCII对应字符;
            r_voltage_q[1] <= i_voltage1[3 : 0] + 8'd48;//将通道1的千分位数据转换为ASCII对应字符;
        end
    end

    always@(posedge clk)begin
        if(i_voltage_vld[2])begin
            r_voltage_sig[2] <=  i_voltage_sig[2];//保存通道2的符号位;
            r_voltage_g[2] <= i_voltage2[15 : 12] + 8'd48;//将通道2的个位数据转换为ASCII对应字符;
            r_voltage_s[2] <= i_voltage2[11 : 8] + 8'd48;//将通道2的十分位数据转换为ASCII对应字符;
            r_voltage_b[2] <= i_voltage2[7 : 4] + 8'd48;//将通道2的百分位数据转换为ASCII对应字符;
            r_voltage_q[2] <= i_voltage2[3 : 0] + 8'd48;//将通道2的千分位数据转换为ASCII对应字符;
        end
    end

    always@(posedge clk)begin
        if(i_voltage_vld[3])begin
            r_voltage_sig[3] <=  i_voltage_sig[3];//保存通道3的符号位;
            r_voltage_g[3] <= i_voltage3[15 : 12] + 8'd48;//将通道3的个位数据转换为ASCII对应字符;
            r_voltage_s[3] <= i_voltage3[11 : 8] + 8'd48;//将通道3的十分位数据转换为ASCII对应字符;
            r_voltage_b[3] <= i_voltage3[7 : 4] + 8'd48;//将通道3的百分位数据转换为ASCII对应字符;
            r_voltage_q[3] <= i_voltage3[3 : 0] + 8'd48;//将通道3的千分位数据转换为ASCII对应字符;
        end
    end

    always@(posedge clk)begin
        if(i_voltage_vld[4])begin
            r_voltage_sig[4] <=  i_voltage_sig[4];//保存通道4的符号位;
            r_voltage_g[4] <= i_voltage4[15 : 12] + 8'd48;//将通道4的个位数据转换为ASCII对应字符;
            r_voltage_s[4] <= i_voltage4[11 : 8] + 8'd48;//将通道4的十分位数据转换为ASCII对应字符;
            r_voltage_b[4] <= i_voltage4[7 : 4] + 8'd48;//将通道4的百分位数据转换为ASCII对应字符;
            r_voltage_q[4] <= i_voltage4[3 : 0] + 8'd48;//将通道4的千分位数据转换为ASCII对应字符;
        end
    end

    always@(posedge clk)begin
        if(i_voltage_vld[5])begin
            r_voltage_sig[5] <=  i_voltage_sig[5];//保存通道5的符号位;
            r_voltage_g[5] <= i_voltage5[15 : 12] + 8'd48;//将通道5的个位数据转换为ASCII对应字符;
            r_voltage_s[5] <= i_voltage5[11 : 8] + 8'd48;//将通道5的十分位数据转换为ASCII对应字符;
            r_voltage_b[5] <= i_voltage5[7 : 4] + 8'd48;//将通道5的百分位数据转换为ASCII对应字符;
            r_voltage_q[5] <= i_voltage5[3 : 0] + 8'd48;//将通道5的千分位数据转换为ASCII对应字符;
        end
    end

    always@(posedge clk)begin
        if(i_voltage_vld[6])begin
            r_voltage_sig[6] <=  i_voltage_sig[6];//保存通道6的符号位;
            r_voltage_g[6] <= i_voltage6[15 : 12] + 8'd48;//将通道6的个位数据转换为ASCII对应字符;
            r_voltage_s[6] <= i_voltage6[11 : 8] + 8'd48;//将通道6的十分位数据转换为ASCII对应字符;
            r_voltage_b[6] <= i_voltage6[7 : 4] + 8'd48;//将通道6的百分位数据转换为ASCII对应字符;
            r_voltage_q[6] <= i_voltage6[3 : 0] + 8'd48;//将通道6的千分位数据转换为ASCII对应字符;
        end
    end

    always@(posedge clk)begin
        if(i_voltage_vld[7])begin
            r_voltage_sig[7] <=  i_voltage_sig[7];//保存通道7的符号位;
            r_voltage_g[7] <= i_voltage7[15 : 12] + 8'd48;//将通道7的个位数据转换为ASCII对应字符;
            r_voltage_s[7] <= i_voltage7[11 : 8] + 8'd48;//将通道7的十分位数据转换为ASCII对应字符;
            r_voltage_b[7] <= i_voltage7[7 : 4] + 8'd48;//将通道7的百分位数据转换为ASCII对应字符;
            r_voltage_q[7] <= i_voltage7[3 : 0] + 8'd48;//将通道7的千分位数据转换为ASCII对应字符;
        end
    end


上位机显示的格式为:ADX:±3.426V,上位机显示的每个通道的数据需要发送11字节数据,并且后续还要跟两个空格或者回车,因此每个通道需要发送13字节数据。
为了便于数据接收端识别数据,当串口发送一个字节数据后,需要暂停一段时间后在发送下字节数据。因此使用一个空闲计数器对空闲时间计数,停留发送2位数据才开始下字节数据传输。
always@(posedge clk or negedge rst_n)begin
        if(~rst_n)begin//初始值为0;
            r_rdy_cnt <= 'd0;
        end
        else if(i_uart_tx_rdy)begin
            if(r_end_rdy_cnt)
                r_rdy_cnt <= 'd0;
            else
                r_rdy_cnt <= r_rdy_cnt + 'd1;
        end
    end

    always@(posedge clk)begin
        r_end_rdy_cnt <= i_uart_tx_rdy && (r_rdy_cnt == 2*BPS_CNT-2);
    end

通过两个计数器分别记录发送当前通道的第几个数据、发送的数据属于第几个通道。

always@(posedge clk or negedge rst_n)begin
        if(~rst_n)begin//初始值为0;
            r_byte_cnt <= 'd0;
        end
        else if(r_end_rdy_cnt)begin
            if(r_byte_cnt == 12)
                r_byte_cnt <= 'd0;
            else
                r_byte_cnt <= r_byte_cnt + 'd1;
        end
    end

    //一轮需要传输8个通道的数据到PC端,使用一个8进制计数器对传输数据的通道数计数;
    //当一个通道数据传输结束时加1,计数器采用溢出清零;
    always@(posedge clk or negedge rst_n)begin
        if(~rst_n)begin//初始值为0;
            r_ch_cnt <= 'd0;
        end
        else if(r_end_rdy_cnt && (r_byte_cnt == 12))begin
            r_ch_cnt <= r_ch_cnt + 'd1;
        end
    end

通过译码器转换数据,根据字节计数器的值发送对应的数据,有部分数据根据通道不同发送不同数据。比如通道7数据发送结束后,需要发送回车和换行字符,不需要发送空格字符。

always@(posedge clk)begin
        if(r_end_rdy_cnt)begin
            case(r_byte_cnt)
                4'd0    : o_uart_txdata <= 8'd65;//发送字符A对应的ASCCI码值;
                4'd1    : o_uart_txdata <= 8'd68;//发送字符D对应的ASCCI码值;
                4'd2    : o_uart_txdata <= r_ch_cnt + 'd49;//发送通道r_ch_cnt对应的ASCCI码值;
                4'd3    : o_uart_txdata <= 8'd58;//发送字符:对应的ASCCI码值;
                //电压的正负值对应的ASCII码,r_voltage_sig为高电平表示对应通道电压为负数。
                4'd4    : o_uart_txdata <= r_voltage_sig[r_ch_cnt] ? 8'd45 : 8'd43;
                4'd5    : o_uart_txdata <= r_voltage_g[r_ch_cnt];//发送个位电压对应的ASCCI码值;
                4'd6    : o_uart_txdata <= 8'd46;//发送字符.对应的ASCCI码值;
                4'd7    : o_uart_txdata <= r_voltage_s[r_ch_cnt];//发送十分位对应的ASCCI码值;
                4'd8    : o_uart_txdata <= r_voltage_b[r_ch_cnt];//发送百分位对应的ASCCI码值;
                4'd9    : o_uart_txdata <= r_voltage_q[r_ch_cnt];//发送千分位对应的ASCCI码值;
                4'd10   : o_uart_txdata <= 8'd86 ;//发送字符V对应的ASCCI码值;
                4'd11   : o_uart_txdata <= (&r_ch_cnt) ? 8'd10 : 8'd32;//如果是发送最后一个通道的数据,则发送换行,否则发送空格;
                4'd12   : o_uart_txdata <= (&r_ch_cnt) ? 8'd13 : 8'd32;//如果是发送最后一个通道的数据,则发送回车,否则发送空格;
                default : o_uart_txdata <= 8'hff;
            endcase
        end
    end

    always@(posedge clk)begin
        o_uart_txdata_vld <= r_end_rdy_cnt;//生成并行数据有效指示信号;
    end

该模块参考代码如下所示:

//--###############################################################################################
//--#
//--# File Name    : uart_byte
//--# Designer    : 数字站
//--# Tool      : Quartus 2018.1
//--# Design Date  : 2024.10.10
//--# Description  : 
//--# Version    : 0.0
//--# Coding scheme  : UTF-8(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
module uart_byte #(
    parameter       FLCK    =       50_000_000  ,//系统时钟频率,默认50MHZ;
    parameter       BPS     =       9600         //串口波特率;
)(
    input              clk                    ,//系统时钟信号;
    input              rst_n                  ,//系统复位信号,低电平有效;

    input      [15 : 0]      i_voltage0              ,//输入数据;
    input      [15 : 0]      i_voltage1              ,//输入数据;
    input      [15 : 0]      i_voltage2              ,//输入数据;
    input      [15 : 0]      i_voltage3              ,//输入数据;
    input      [15 : 0]      i_voltage4              ,//输入数据;
    input      [15 : 0]      i_voltage5              ,//输入数据;
    input      [15 : 0]      i_voltage6              ,//输入数据;
    input      [15 : 0]      i_voltage7              ,//输入数据;
    input      [7 : 0]      i_voltage_sig           ,//各通道数据的正负数据指示信号;
    input      [7 : 0]      i_voltage_vld           ,//输入数据有效指示信号,高电平有效;
    input                       i_uart_tx_rdy           ,//uart发送模块空闲指示信号;
    output reg  [7 : 0]          o_uart_txdata = 'd0     ,//需要uart发送的并行数据;
    output reg          o_uart_txdata_vld = 'd0  //需要的发送的并行数据有效指示信号;
);
    localparam  BPS_CNT   =     FLCK/BPS                ;//波特率为9600bit/s,当波特率为115200bit/s时,DATA_115200==434;
    localparam  BPS_CNT_W =     $clog2(2*BPS_CNT-1)     ;//根据BPS_CNT调用函数自动计算计数器bps_cnt位宽;
    reg         [7 : 0]         r_voltage_sig = 'd0     ;
    reg         [BPS_CNT_W-1:0] r_rdy_cnt               ;
    reg                         r_end_rdy_cnt           ;
    reg [7 : 0] r_voltage_g     [7 : 0]                 ;
    reg [7 : 0] r_voltage_s     [7 : 0]                 ;
    reg [7 : 0] r_voltage_b     [7 : 0]                 ;
    reg [7 : 0] r_voltage_q     [7 : 0]                 ;
    reg [3 : 0]                 r_byte_cnt              ;//
    reg [2 : 0]                 r_ch_cnt                ;

    /********** 存储数据 *************/
    always@(posedge clk)begin
        if(i_voltage_vld[0])begin
            r_voltage_sig[0] <=  i_voltage_sig[0];//保存通道0的符号位;
            r_voltage_g[0] <= i_voltage0[15 : 12] + 8'd48;//将通道0的个位数据转换为ASCII对应字符;
            r_voltage_s[0] <= i_voltage0[11 : 8] + 8'd48;//将通道0的十分位数据转换为ASCII对应字符;
            r_voltage_b[0] <= i_voltage0[7 : 4] + 8'd48;//将通道0的百分位数据转换为ASCII对应字符;
            r_voltage_q[0] <= i_voltage0[3 : 0] + 8'd48;//将通道0的千分位数据转换为ASCII对应字符;
        end
    end

    always@(posedge clk)begin
        if(i_voltage_vld[1])begin
            r_voltage_sig[1] <=  i_voltage_sig[1];//保存通道1的符号位;
            r_voltage_g[1] <= i_voltage1[15 : 12] + 8'd48;//将通道1的个位数据转换为ASCII对应字符;
            r_voltage_s[1] <= i_voltage1[11 : 8] + 8'd48;//将通道1的十分位数据转换为ASCII对应字符;
            r_voltage_b[1] <= i_voltage1[7 : 4] + 8'd48;//将通道1的百分位数据转换为ASCII对应字符;
            r_voltage_q[1] <= i_voltage1[3 : 0] + 8'd48;//将通道1的千分位数据转换为ASCII对应字符;
        end
    end

    always@(posedge clk)begin
        if(i_voltage_vld[2])begin
            r_voltage_sig[2] <=  i_voltage_sig[2];//保存通道2的符号位;
            r_voltage_g[2] <= i_voltage2[15 : 12] + 8'd48;//将通道2的个位数据转换为ASCII对应字符;
            r_voltage_s[2] <= i_voltage2[11 : 8] + 8'd48;//将通道2的十分位数据转换为ASCII对应字符;
            r_voltage_b[2] <= i_voltage2[7 : 4] + 8'd48;//将通道2的百分位数据转换为ASCII对应字符;
            r_voltage_q[2] <= i_voltage2[3 : 0] + 8'd48;//将通道2的千分位数据转换为ASCII对应字符;
        end
    end

    always@(posedge clk)begin
        if(i_voltage_vld[3])begin
            r_voltage_sig[3] <=  i_voltage_sig[3];//保存通道3的符号位;
            r_voltage_g[3] <= i_voltage3[15 : 12] + 8'd48;//将通道3的个位数据转换为ASCII对应字符;
            r_voltage_s[3] <= i_voltage3[11 : 8] + 8'd48;//将通道3的十分位数据转换为ASCII对应字符;
            r_voltage_b[3] <= i_voltage3[7 : 4] + 8'd48;//将通道3的百分位数据转换为ASCII对应字符;
            r_voltage_q[3] <= i_voltage3[3 : 0] + 8'd48;//将通道3的千分位数据转换为ASCII对应字符;
        end
    end

    always@(posedge clk)begin
        if(i_voltage_vld[4])begin
            r_voltage_sig[4] <=  i_voltage_sig[4];//保存通道4的符号位;
            r_voltage_g[4] <= i_voltage4[15 : 12] + 8'd48;//将通道4的个位数据转换为ASCII对应字符;
            r_voltage_s[4] <= i_voltage4[11 : 8] + 8'd48;//将通道4的十分位数据转换为ASCII对应字符;
            r_voltage_b[4] <= i_voltage4[7 : 4] + 8'd48;//将通道4的百分位数据转换为ASCII对应字符;
            r_voltage_q[4] <= i_voltage4[3 : 0] + 8'd48;//将通道4的千分位数据转换为ASCII对应字符;
        end
    end

    always@(posedge clk)begin
        if(i_voltage_vld[5])begin
            r_voltage_sig[5] <=  i_voltage_sig[5];//保存通道5的符号位;
            r_voltage_g[5] <= i_voltage5[15 : 12] + 8'd48;//将通道5的个位数据转换为ASCII对应字符;
            r_voltage_s[5] <= i_voltage5[11 : 8] + 8'd48;//将通道5的十分位数据转换为ASCII对应字符;
            r_voltage_b[5] <= i_voltage5[7 : 4] + 8'd48;//将通道5的百分位数据转换为ASCII对应字符;
            r_voltage_q[5] <= i_voltage5[3 : 0] + 8'd48;//将通道5的千分位数据转换为ASCII对应字符;
        end
    end

    always@(posedge clk)begin
        if(i_voltage_vld[6])begin
            r_voltage_sig[6] <=  i_voltage_sig[6];//保存通道6的符号位;
            r_voltage_g[6] <= i_voltage6[15 : 12] + 8'd48;//将通道6的个位数据转换为ASCII对应字符;
            r_voltage_s[6] <= i_voltage6[11 : 8] + 8'd48;//将通道6的十分位数据转换为ASCII对应字符;
            r_voltage_b[6] <= i_voltage6[7 : 4] + 8'd48;//将通道6的百分位数据转换为ASCII对应字符;
            r_voltage_q[6] <= i_voltage6[3 : 0] + 8'd48;//将通道6的千分位数据转换为ASCII对应字符;
        end
    end

    always@(posedge clk)begin
        if(i_voltage_vld[7])begin
            r_voltage_sig[7] <=  i_voltage_sig[7];//保存通道7的符号位;
            r_voltage_g[7] <= i_voltage7[15 : 12] + 8'd48;//将通道7的个位数据转换为ASCII对应字符;
            r_voltage_s[7] <= i_voltage7[11 : 8] + 8'd48;//将通道7的十分位数据转换为ASCII对应字符;
            r_voltage_b[7] <= i_voltage7[7 : 4] + 8'd48;//将通道7的百分位数据转换为ASCII对应字符;
            r_voltage_q[7] <= i_voltage7[3 : 0] + 8'd48;//将通道7的千分位数据转换为ASCII对应字符;
        end
    end

    /********* 发送数据 ************/
    //空闲计数器,发送一字节数据后,暂停一段时间在发送下字节数据;
    always@(posedge clk or negedge rst_n)begin
        if(~rst_n)begin//初始值为0;
            r_rdy_cnt <= 'd0;
        end
        else if(i_uart_tx_rdy)begin
            if(r_end_rdy_cnt)
                r_rdy_cnt <= 'd0;
            else
                r_rdy_cnt <= r_rdy_cnt + 'd1;
        end
    end

    always@(posedge clk)begin
        r_end_rdy_cnt <= i_uart_tx_rdy && (r_rdy_cnt == 2*BPS_CNT-2);
    end

    //每个通道需要发送13字节串口数据到PC端,使用一个13进制计数器对发送数据计数;
    //当下游模块空闲时表示发送完成1字节数据,计数器加1.
    always@(posedge clk or negedge rst_n)begin
        if(~rst_n)begin//初始值为0;
            r_byte_cnt <= 'd0;
        end
        else if(r_end_rdy_cnt)begin
            if(r_byte_cnt == 12)
                r_byte_cnt <= 'd0;
            else
                r_byte_cnt <= r_byte_cnt + 'd1;
        end
    end

    //一轮需要传输8个通道的数据到PC端,使用一个8进制计数器对传输数据的通道数计数;
    //当一个通道数据传输结束时加1,计数器采用溢出清零;
    always@(posedge clk or negedge rst_n)begin
        if(~rst_n)begin//初始值为0;
            r_ch_cnt <= 'd0;
        end
        else if(r_end_rdy_cnt && (r_byte_cnt == 12))begin
            r_ch_cnt <= r_ch_cnt + 'd1;
        end
    end

    //产生下游uart模块需要发送的并行数据;
    always@(posedge clk)begin
        if(r_end_rdy_cnt)begin
            case(r_byte_cnt)
                4'd0    : o_uart_txdata <= 8'd65;//发送字符A对应的ASCCI码值;
                4'd1    : o_uart_txdata <= 8'd68;//发送字符D对应的ASCCI码值;
                4'd2    : o_uart_txdata <= r_ch_cnt + 'd49;//发送通道r_ch_cnt对应的ASCCI码值;
                4'd3    : o_uart_txdata <= 8'd58;//发送字符:对应的ASCCI码值;
                //电压的正负值对应的ASCII码,r_voltage_sig为高电平表示对应通道电压为负数。
                4'd4    : o_uart_txdata <= r_voltage_sig[r_ch_cnt] ? 8'd45 : 8'd43;
                4'd5    : o_uart_txdata <= r_voltage_g[r_ch_cnt];//发送个位电压对应的ASCCI码值;
                4'd6    : o_uart_txdata <= 8'd46;//发送字符.对应的ASCCI码值;
                4'd7    : o_uart_txdata <= r_voltage_s[r_ch_cnt];//发送十分位对应的ASCCI码值;
                4'd8    : o_uart_txdata <= r_voltage_b[r_ch_cnt];//发送百分位对应的ASCCI码值;
                4'd9    : o_uart_txdata <= r_voltage_q[r_ch_cnt];//发送千分位对应的ASCCI码值;
                4'd10   : o_uart_txdata <= 8'd86 ;//发送字符V对应的ASCCI码值;
                4'd11   : o_uart_txdata <= (&r_ch_cnt) ? 8'd10 : 8'd32;//如果是发送最后一个通道的数据,则发送换行,否则发送空格;
                4'd12   : o_uart_txdata <= (&r_ch_cnt) ? 8'd13 : 8'd32;//如果是发送最后一个通道的数据,则发送回车,否则发送空格;
                default : o_uart_txdata <= 8'hff;
            endcase
        end
    end

    always@(posedge clk)begin
        o_uart_txdata_vld <= r_end_rdy_cnt;//生成并行数据有效指示信号;
    end

endmodule

uart发送模块依旧使用以前模块,前文详细讲解过uart接收模块全模式设计方式,本文就不再赘述,参考代码如下所示:

//--###############################################################################################
//--#
//--# File Name    : uart_tx
//--# Designer    : 数字站
//--# Tool      : Quartus 2018.1
//--# Design Date  : 2024.10.10
//--# Description  : 
//--# Version    : 0.0
//--# Coding scheme  : UTF-8(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
module uart_tx #(
    parameter       FLCK    =       50_000_000  ,//系统时钟频率,默认50MHZ;
    parameter       BPS     =       9600        ,//串口波特率;
    parameter       DATA_W  =       8           ,//发送数据位数以及输出数据位宽;
    parameter       START_W =       1           ,//1位起始位;
    parameter       CHECK_W =       2'b00       ,//校验位,2'b00代表无校验位,2'b01表示奇校验,2'b10表示偶校验,2'b11按无校验处理。
    parameter       STOP_W  =       2'b01        //停止位,2'b01表示1位停止位,2'b10表示2位停止位,2'b11表示1.5位停止位;
)(
    input                           clk         ,//系统工作时钟50MHZ
    input                           rst_n       ,//系统复位信号,低电平有效

    input           [DATA_W-1:0]    tx_data     ,//数据输入信号。
    input                           tx_data_vld ,//数据有效指示信号,高电平有效。

    output reg                      uart_tx     ,//uart接口数据输出信号。
    output reg                      tx_rdy       //模块忙闲指示信号;
);
    localparam      BPS_CNT   =     FLCK/BPS        ;//波特率为9600bit/s,当波特率为115200bit/s时,DATA_115200==434;
    localparam      BPS_CNT_W =     $clog2(BPS_CNT-1);//根据BPS_CNT调用函数自动计算计数器bps_cnt位宽;
    localparam      DATA_CNT  =     START_W + DATA_W + (^CHECK_W) + ((STOP_W==2'b11) ? 2 : STOP_W);//计数器计数值;
    localparam      DATA_CNT_W=     $clog2(DATA_CNT-1);//根据计数器cnt的值,利用函数自动计算此计数器的位宽;

    reg                             flag            ;
    reg                             tx_rdy_ff0      ;//
    reg             [DATA_CNT-1:0]  tx_data_tmp     ;
    reg             [BPS_CNT_W-1:0] bps_cnt         ;
    reg             [DATA_CNT_W-1:0]data_cnt        ;

    wire                            add_bps_cnt     ;
    wire                            end_bps_cnt     ;
    wire                            end_data_cnt    ;

    /*发送一位数据所需要的时间*/
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            bps_cnt <= {{BPS_CNT_W}{1'b0}};
        end
        else if(add_bps_cnt)begin
            if(end_bps_cnt || end_data_cnt)
                bps_cnt <= {{BPS_CNT_W}{1'b0}};
            else
                bps_cnt <= bps_cnt + {{{BPS_CNT_W-1}{1'b0}},1'b1};
        end
    end

    assign add_bps_cnt = flag;
    assign end_bps_cnt = add_bps_cnt && bps_cnt == BPS_CNT-1;

    /*发送一组数据所用时间*/
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin//
            data_cnt <= {{DATA_CNT}{1'b0}};
        end
        else if(end_data_cnt)begin
            data_cnt <= {{DATA_CNT}{1'b0}};
        end
        else if(end_bps_cnt)begin
            data_cnt <= data_cnt + {{{DATA_CNT-1}{1'b0}},1'b1};
        end
    end

    //根据停止位的不同生成不同的计数器结束条件;
    generate
        if(STOP_W == 2'b11)//1.5位停止位,因为CNT_NUM没有包含起始位,所以要等计数器计数到CNT_NUM时清零;
            assign end_data_cnt = data_cnt == DATA_CNT-1 && add_bps_cnt && bps_cnt == BPS_CNT/2-1;
        else//停止位为1位或者2位;
            assign end_data_cnt = end_bps_cnt && data_cnt == DATA_CNT-1;
    endgenerate

     //这个期间UART在发送数据;
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            flag <= 1'b0;
        end
        else if(tx_data_vld)begin
            flag <= 1'b1;
        end
        else if(end_data_cnt)begin
            flag <= 1'b0;
        end
    end

    //UART模块处于忙时期,收到上游模块数据或者正在处理上游模块所发数据;
    always@(*)begin
        if(tx_data_vld || flag)begin
            tx_rdy = 1'b0;
        end
        else begin
            tx_rdy = 1'b1;
        end
    end

    always@(posedge clk)begin
        tx_rdy_ff0 <= tx_rdy;
    end

    //将上游模块所发并行数据转化为串行数据;
    generate
        if(CHECK_W == 2'b01)begin//奇校验;
            always@(posedge clk or negedge rst_n)begin
                if(rst_n==1'b0)begin
                    tx_data_tmp <= {{DATA_CNT+1}{1'b1}};
                end
                else if(tx_rdy_ff0 && tx_data_vld)begin
                    tx_data_tmp <= {{{STOP_W}{1'b1}},~(^tx_data),tx_data,{{START_W}{1'b0}}};
                end
            end
        end
        else if(CHECK_W == 2'b10)begin//偶校验
            always@(posedge clk or negedge rst_n)begin
                if(rst_n==1'b0)begin
                    tx_data_tmp <= {{DATA_CNT+1}{1'b1}};
                end
                else if(tx_rdy_ff0 && tx_data_vld)begin
                    tx_data_tmp <= {{{STOP_W}{1'b1}},(^tx_data),tx_data,{{START_W}{1'b0}}};
                end
            end
        end
        else begin//无校验
            always@(posedge clk or negedge rst_n)begin
                if(rst_n==1'b0)begin
                    tx_data_tmp <= {{DATA_CNT+1}{1'b1}};
                end
                else if(tx_rdy_ff0 && tx_data_vld)begin
                    tx_data_tmp <= {{{STOP_W}{1'b1}},tx_data,{{START_W}{1'b0}}};
                end
            end
        end
    endgenerate

    //将串行数据按9600波特率送出,先发低位;
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            uart_tx <= 1'b1;
        end
        else if(add_bps_cnt && bps_cnt==0)begin
            uart_tx <= tx_data_tmp[data_cnt];
        end
    end

    endmodule

2.3 顶层模块

顶层模块如下所示,单独列出来是因为代码使用for循环例化了8个通道的数据处理模块。参考代码如下所示:

//--###############################################################################################
//--#
//--# File Name    : top
//--# Designer    : 
//--# Tool      : Quartus 2018.1
//--# Design Date  : 2024.10.10
//--# Description  : 
//--# Version    : 0.0
//--# Coding scheme  : UTF-8(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
module top #(
    parameter       FLCK        =   100_000_000     ,//系统时钟频率,默认50MHZ;
    parameter       BPS         =   115200          ,//串口波特率;
    parameter       UART_DATA_W =   8               ,//发送数据位数以及输出数据位宽;
    parameter       START_W     =   1               ,//1位起始位;
    parameter       CHECK_W     =   2'b00           ,//校验位,2'b00代表无校验位,2'b01表示奇校验,2'b10表示偶校验,2'b11按无校验处理。
    parameter       STOP_W      =   2'b01            //停止位,2'b01表示1位停止位,2'b10表示2位停止位,2'b11表示1.5位停止位;
)(
    input                           clk             ,//系统时钟,50MHz;
    input                           rst_n           ,//系统复位,低电平有效;

    input                           busy            ,//转换完成指示信号,下降沿有效;
    input                           frstdata        ,//指示采集到的第一个数据;
    input       [15 : 0]            adc_din         ,//AD7606所采集到的十六位数据信号;

    input                           uart_rx         ,
    output                          cs              ,//AD7606片选信号,读数据时拉低;
    output                          rd              ,//AD7606读使能信号,读数据时拉低,下降沿时AD7606将数据发送到数据线上,上升沿时可以读出数据;
    output                          reset           ,//AD7606复位信号,高电平有效,每次复位至少拉高50ns;
    output      [2 : 0]             os              ,//AD7606过采样模式信号,默认不使用过采样;
    output                          convst          ,//AD7606采样启动信号,无效时高电平,采样计数器完成时拉低两个时钟;
    output                          uart_tx          //uart接口数据输出信号。
);
    wire                            clk_100m        ;//锁相环输出时钟;
    wire        [15 : 0]            w_7606_data     ;//AD7606通道采集的补码数据;
    wire        [7 : 0]             w_7606_data_vld ;//指示AD7606输出的数据来自哪个数据通道;
    wire        [15 : 0] w_voltage  [7 : 0]         ;//输出电压,单位mv
    wire        [7 : 0]             w_voltage_sig   ;//输出电压的正负,低电平表示正;
    wire        [7 : 0]             w_voltage_vld   ;//输出数据有效指示信号,高电平有效;
    wire        [UART_DATA_W-1:0]   w_tx_data       ;//数据输入信号。
    wire                            w_tx_data_vld   ;//数据有效指示信号,高电平有效。
    wire                            w_tx_rdy        ;//模块忙闲指示信号;

    //例化锁相环
    pll u_pll (
        .areset ( ~rst_n    ),
        .inclk0 ( clk       ),
        .c0     ( clk_100m  )
    );

    //例化ad7606驱动模块
    ad7606_drive #(
        .FCLK       ( FLCK              ),//系统时钟频率,单位Hz,默认100MHz;
        .SMAPLE     ( 200_000           ) //AD7606采样频率,单位Hz,默认200KHz;
    )
    u_ad7606_drive (
        .clk        ( clk_100m          ),//系统时钟,100MHz;
        .rst_n      ( rst_n             ),//系统复位,低电平有效;
        .busy       ( busy              ),//转换完成指示信号,下降沿有效;
        .frstdata   ( frstdata          ),//指示采集到的第一个数据;
        .adc_din    ( adc_din           ),//AD7606所采集到的十六位数据信号;
        .cs         ( cs                ),//AD7606片选信号,读数据时拉低;
        .rd         ( rd                ),//AD7606读使能信号,读数据时拉低,下降沿时AD7606将数据发送到数据线上,上升沿时可以读出数据;
        .reset      ( reset             ),//AD7606复位信号,高电平有效,每次复位至少拉高50ns;
        .os         ( os                ),//AD7606过采样模式信号,默认不使用过采样;
        .convst     ( convst            ),//AD7606采样启动信号,无效时高电平,采样计数器完成时拉低两个时钟;
        .data       ( w_7606_data       ),//AD7606采集到的数据.数据均为补码;
        .data_vld   ( w_7606_data_vld   ) //指示AD7606输出的数据来自哪个数据通道;
    );

    genvar i;
    //使用for循环例化8个数据处理模块,将ad7606采集的数据转换为bcd码的mv电压;
    generate
        for(i=0 ; i<8 ; i=i+1)begin : DATA
            data_dispose u_data_dispose(
                .clk        ( clk_100m          ),//系统时钟信号;
                .rst_n          ( rst_n              ),//系统复位信号,低电平有效;
                .din        ( w_7606_data       ),//输入数据;
                .din_vld      ( w_7606_data_vld[i]),//输入数据有效指示信号,高电平有效;
                .voltage      ( w_voltage[i]      ),//输出电压,单位mv
                .voltage_sig    ( w_voltage_sig[i]  ),//输出电压的正负,低电平表示正;
                .voltage_vld    ( w_voltage_vld[i]  ) //输出数据有效指示信号,高电平有效;
            );
        end
    endgenerate

    //例化数据处理模块
    uart_byte #(
        .FLCK               ( FLCK          ),//系统时钟频率,默认50MHZ;
        .BPS                ( BPS           ) //串口波特率;
    )
    u_uart_byte(
        .clk            ( clk_100m      ),//系统时钟信号;
        .rst_n              ( rst_n          ),//系统复位信号,低电平有效;
        .i_voltage0         ( w_voltage[0]  ),//输入数据;
        .i_voltage1         ( w_voltage[1]  ),//输入数据;
        .i_voltage2         ( w_voltage[2]  ),//输入数据;
        .i_voltage3         ( w_voltage[3]  ),//输入数据;
        .i_voltage4         ( w_voltage[4]  ),//输入数据;
        .i_voltage5         ( w_voltage[5]  ),//输入数据;
        .i_voltage6         ( w_voltage[6]  ),//输入数据;
        .i_voltage7         ( w_voltage[7]  ),//输入数据;
        .i_voltage_sig      ( w_voltage_sig ),//各通道数据的正负数据指示信号;
        .i_voltage_vld      ( w_voltage_vld ),//输入数据有效指示信号,高电平有效;
        .i_uart_tx_rdy      ( w_tx_rdy      ),//uart发送模块空闲指示信号;
        .o_uart_txdata      ( w_tx_data     ),//需要uart发送的并行数据;
        .o_uart_txdata_vld  ( w_tx_data_vld ) //需要的发送的并行数据有效指示信号;
    );

    //例化串口发送模块;
    uart_tx #(
        .FLCK       ( FLCK          ),//系统时钟频率,默认50MHZ;
        .BPS        ( BPS           ),//串口波特率;
        .DATA_W     ( UART_DATA_W   ),//发送数据位数以及输出数据位宽;
        .START_W    ( START_W       ),//1位起始位;
        .CHECK_W    ( CHECK_W       ),//校验位,2'b00代表无校验位,2'b01表示奇校验,2'b10表示偶校验,2'b11按无校验处理。
        .STOP_W     ( STOP_W        ) //停止位,2'b01表示1位停止位,2'b10表示2位停止位,2'b11表示1.5位停止位;
    )
    u_uart_tx (
        .clk        ( clk_100m      ),//系统工作时钟50MHZ
        .rst_n      ( rst_n         ),//系统复位信号,低电平有效
        .tx_data    ( w_tx_data     ),//数据输入信号。
        .tx_data_vld( w_tx_data_vld ),//数据有效指示信号,高电平有效。
        .uart_tx    ( uart_tx       ),//uart接口数据输出信号。
        .tx_rdy     ( w_tx_rdy      ) //模块忙闲指示信号;
    );

endmodule

3. 工程仿真

仿真的数据来源依旧使用前文ad7606驱动模块的测试数据,对应参考代码如下所示。

`timescale 1 ns/1 ns
//--###############################################################################################
//--#
//--# File Name    : test
//--# Designer    : 数字站
//--# Tool      : Quartus 2018.1
//--# Design Date  : 2024.11.3
//--# Description  : 
//--# Version    : 0.0
//--# Coding scheme  : GBK(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
module test();
    localparam          CYCLE    =   20    ;//系统时钟周期,单位ns,默认10ns;
    localparam          RST_TIME  =   10    ;//系统复位持续时间,默认10个系统时钟周期;

    reg                 clk         =   'd1     ;//系统时钟,100MHz;
    reg                 rst_n       =   'd1     ;//系统复位,高电平有效;
    reg                 busy        =   'd0     ;//转换完成指示信号,下降沿有效;
    reg                 frstdata    =   'd0     ;//指示采集到的第一个数据;
    reg     [15 : 0]    adc_din     =   'd0     ;//AD7606所采集到的十六位数据信号;

    wire                cs                      ;//AD7606片选信号,读数据时拉低;
    wire                rd                      ;//AD7606读使能信号,读数据时拉低,下降沿时AD7606将数据发送到数据线上,上升沿时可以读出数据;
    wire                reset                   ;//AD7606复位信号,高电平有效,每次复位至少拉高50ns;
    wire    [2 : 0]     os                      ;//AD7606过采样模式信号,默认不使用过采样;
    wire                convst                  ;//AD7606采样启动信号,无效时高电平,采样计数器完成时拉低两个时钟;
    wire                uart_tx                 ;//uart接口数据输出信号。

    //待测试的模块例化
    top u_top (
        .clk        ( clk       ),//系统时钟,50MHz;
        .rst_n      ( rst_n     ),//系统复位,低电平有效;
        .busy       ( busy      ),//转换完成指示信号,下降沿有效;
        .frstdata   ( frstdata  ),//指示采集到的第一个数据;
        .adc_din    ( adc_din   ),//AD7606所采集到的十六位数据信号;
        .cs         ( cs        ),//AD7606片选信号,读数据时拉低;
        .rd         ( rd        ),//AD7606读使能信号,读数据时拉低,下降沿时AD7606将数据发送到数据线上,上升沿时可以读出数据;
        .reset      ( reset     ),//AD7606复位信号,高电平有效,每次复位至少拉高50ns;
        .os         ( os        ),//AD7606过采样模式信号,默认不使用过采样;
        .convst     ( convst    ),//AD7606采样启动信号,无效时高电平,采样计数器完成时拉低两个时钟;
        .uart_tx    ( uart_tx   ) //uart接口数据输出信号。
    );

    initial begin
        forever #(CYCLE/2) clk = ~clk;//生成本地时钟50M 
    end

    //产生复位信号
    localparam DATA_NUM = 400;
    reg [15:0] stimulus[1:DATA_NUM];
    integer Pattern;
    initial begin
        $readmemb("waveform_bit.txt",stimulus);//从外部TX文件(waveform_bit.txt)读入数据作为测试激励;
        Pattern = 1;
        #1;
        rst_n = 1'b0;
        #(CYCLE*RST_TIME);
        rst_n = 1'b1;
        repeat(20)@(posedge clk);
        repeat(DATA_NUM)begin
            @(posedge convst);
            busy <= 1'b1;
            repeat(30)@(posedge clk);
            busy <= 1'b0;
            @(negedge rd);
            adc_din <= stimulus[Pattern];
            Pattern <= Pattern + 1;
            @(posedge clk);
        end
        repeat(20)@(posedge clk);//延迟20个时钟周期;
        $stop;
    end

endmodule

采用Quartus 18.1和modelsim联合仿真,打开modelsim仿真界面wave后,先删除该界面自动添加的信号。然后如下图所示,添加我提前设置好的波形文件wave.do。

图2 添加波形文件.JPG

对应结果如下所示,相关模块的所有信号已经全部添加且分类。


图3 波形界面.JPG

之后重新开始仿真,点击run -all即可,仿真文件运行到$stop位置自动停止仿真。

仿真.JPG

由于TestBench给ad7606八个通道赋值相同数据,因此最终采集的数据如下图所示。

图4 八个通道采集数据.JPG

通道0的数据处理模块如下图所示,首先采集到的16位补码数据为16’d4107,对应原码为15’d4107,然后计算对应的模拟电压((4107) / (2^15)) * 5V = 0.6266V = 626mv,最终将计算结果转换为BCD码,与下图结果完全一致。

图5 数据处理模块仿真.JPG

而uart_byte模块把八个通道计算结果根据含义保存到对应存储器中,同时根据计数器的值向下游模块传输数据。下图所示,每当上游模块完成一次数据转换,均会将存储器的数据刷新。图片

图6 存储数据.JPG

传输数据相关的计数器如下所示,uart_tx_rdy为高电平表示uart发送模块处于空闲状态。为了防止uart接收端反应不过来,这里会让空闲状态多持续一段时间,才开始发送下字节数据。使用计数器rdy_cnt来计数这段时间。

图7 数据传输时序.JPG

上图中ch_cnt用来表示当前传输第几个通道的数据,而byte_cnt用来计数当前发送该通道第几字节数据。
上图中表示传输通道0第0字节数据为8’d65,对应字符A,然后发送第1字节数据8’d68对应D,之后传输通道对应数值,一般给用户显示都是从1开始的,因此8’d49表示通道1。上述三个数据显示结果为AD1,后续仿真类似,不再赘述。
最后就是uart发送模块,如下图所示,发送8’h41(与8’d65相等),先发送低位数据,起始位会占用一位,结果正确。图片

图8 uart发送模块仿真.JPG

由于工程比较简单,因此仿真这块做得都比较简洁,文末会提供工程,拿到后可以自行仿真。

4. 上板实测

由于手里这块板子的设计问题,导致最终没办法上板,具体问题如下所示。板子上有一个uart的公头,如下所示。图片

图9 板载uart接口.JPG

对应原理图如下所示,公头的2脚接的是板子的uart发送引脚。

图10 uart原理图.JPG


下图是淘宝上该接口的线材接口信息,母头线材2脚也是发送引脚?没找到2脚用于接收的母头线材,不知道设计这块板子的工程师是如何想的,黑金自家也没有卖这种线材。


图11 淘宝线材.JPG

图片在网上找了一张功能类似的截图,上板后的结果应该与下图一致。

图12 串口调试助手显示.JPG

虽然不能使用串口显示数据,但是也可以通过signal tap抓一下实测数据,如果时序没有问题,依旧可以保证工程没有问题。

如下图所示,ad7606的八个通道全部悬空,然后下载程序到板子中。

图13 悬空ad7606八个通道输入引脚.JPG


使用signal tap抓取通道0的数据,如下所示,对应数据为16’d11857,转换为模拟电压为(16’d11857 / (2^15)) * 5V = 1.809V = 1809mv,与抓取的bcd码转换结果一致。

图14 转换时序.JPG

由于uart每发送一个字节数据中间会间隔很长时间,为了看到依次发送的数据对不对,则需要抓取很多数据,signal tap采用如下设置,只抓取o_uart_tx_vld为高电平的数据,舍弃其余无用数据。图片

图15 signal tap设置.JPG


抓取数据如下所示,65对应的字符A,54对应的是通道6,由此可知第一帧数据为通道6的数据,第二帧为通道7的数据。图片


图16 抓取发送的uart帧数据.JPG


与下图译码器部分代码对比,可以验证上图的正确性。

图17 对应译码.JPG

最后修改signal tap抓取uart发送模块时序,如下图所示,发送数据为8’h2e,先发送低位数据。

图18 抓取uart发送模块时序.JPG


相关时序验证结束,整体时序是没有问题的,如果换个正常的uart硬件接口可以直接使用,在公众号后台回复“uart传输ad7606数据”获取对应代码。
文章来源:数字站

最新文章

最新文章