FPGA&MSP430实现等精度频率计


前段时间依次实现了FPGA的等精度频率计和FPGA与430的通信测试。

FPGA频率计:Verilog设计练习 基于FPGA的等精度频率计_Krism0912的博客-CSDN博客_用verilog设计等精度频率计

FPGA与430通信测试:

FPGA与MSP430G2553通信 UART串口操作_Krism0912的博客-CSDN博客

由于FPGA中对数据运算过于麻烦,所以此次将上述两个成果进行了整合,将FPGA的数据通过串口传至MSP430后再进行运算。

在整合过程中碰到的一个百思不得其解的问题:串口测试中发现传输过去的Nx的值总是理论值的2倍。花了几个小时才最终弄清楚原因,下面稍做记录。

错误出现在FPGA的uart_tx原代码的改写。

改写的代码如下:

module uart_tx_1(

    input clk, //9600Hz         
    input [39:0]data_Nx,
    input [39:0]data_Nb,
    input rst,
    input send_start_flag,
    output reg txd
    );
//串口发送状态机分为四个状态:等待、发送起始位、发送数据、发送完成
    
    localparam IDLE = 0,
               SEND_START = 1,
               SEND_DATA = 2,
               SEND_END = 3;
    
    reg [3:0] state = IDLE;
    reg [4:0] count = 0;
    reg [39:0] data_o_tmp=0;
    
    reg send_flag=0;

    reg [6:0] send_data_num =0;

 always @(posedge clk or negedge rst)  
    begin   
        if(!rst)
            state <= IDLE;
        else
            begin
            case (state)
                IDLE: begin
                        if (send_start_flag) state<= SEND_START; 
                        else state <= IDLE;
                      end
                
                SEND_START: state <= SEND_DATA;
                
                SEND_DATA: begin
                              if (count == 7) state <= SEND_END; 
                              else state <= SEND_DATA;
                          end
                
                SEND_END: begin
                             if (send_data_num>0 && send_data_num<80) 
                                    state <= SEND_START;
                             else     state <= IDLE;
                            end
                default: state <= IDLE;
            endcase
        end
    end
    
    always @(posedge clk or negedge rst)
    begin
        if(!rst)
            count<=0;  
        else if (state == SEND_DATA)
            count <= count + 1;
        else if (state == IDLE | state == SEND_END)
            count <= 0;
    end
    
    always @(posedge clk or negedge rst)
        if(!rst)
            data_o_tmp <=0;
        else if (send_start_flag)
            data_o_tmp <= data_Nb;  //data_o_tmp是某个数字

        else if (send_data_num==40)
            data_o_tmp <= data_Nx;

        else if (state == SEND_DATA)
            data_o_tmp[38:0] <= data_o_tmp[39:1];         //将要发送的数据位放在 data_o_tmp[0]

        else
            data_o_tmp <= data_o_tmp;
             


    always @(posedge clk or negedge rst) 
        begin
            if(!rst)
                send_data_num <=0;
            else if(state == SEND_DATA)
              send_data_num = send_data_num+1;
            else if(send_data_num == 80)
              send_data_num <= 0;
            else
             send_data_num <=  send_data_num;
        end      
    
    always @(posedge clk or negedge rst)
        if (!rst)
            txd <=1;
        else if (state == SEND_START)
            txd <= 0;                                   //发送起始位
        else if (state == SEND_DATA)
            txd <= data_o_tmp[0];                       //发送数据低位在先,高位在后
        else if (state == SEND_END)
            txd <= 1;                                    //发送停止位
endmodule

原程序仅发送一个40位的数据进行测试,而根据实际结果,FPGA需要将Nx,Nb两个40位数据接连发送给MSP430。

我理所当然地想到只需把send_data_num由原来的40改为80,并且在 send_data_num=40时将待发送的数据由Nb改为Nx即可。

然而测试了很久,Nx的值一直是理论值的两倍,而Nb没什么问题。

之后写了一大堆仿真程序观察Nx和Nb的值,发现没有任何不对。

后来脑子一抽随手改了自定义的标识符:

assign data_Nx={8'b0,Nx};
assign data_Nb={8'b1,Nb};

改为: 

assign data_Nx={8'd2,Nx};
assign data_Nb={8'b1,Nb}
;

发现UART传过去的标识符也不对,最后的是04而不是02。

于是恍然大悟,觉得是UART的改写部分存在问题。

于是又花了一大把时间仿真UART。仿真中给定Nx和Nb的值为:

Nx=32'b10101010_11110000_11110000_10101010;
Nb=32'b01100000_11111111_11110000_00001111;

波形图不仔细看还真的看不出有什么问题、、

但确确实实有问题。。

 

以send_data_num从33到40位为例(上图左方),对应的txd是:10000000(发送的数据是Nb的标识符8'b1,由低位发送至高位) 

send_data_num=40共占有3个uart_clk周期(当然 send_data_num=32时也同样如此)。

根据时序,第一个T对应的txd是0,是Nb的最后一位数据;

第二个T对应的txd是1,是发完Nb的第40位数据之后的停止位;

第三个T对应的txd是0,是发送新一个数据前的起始位。

如此,send_data_num从41到48位对应的txd应该是:01010101(Nx的低8位)

但是txd的波形上是0010101...(多了个0)

所以最后发送的数据差了一个倍数。如果不是把标识符从0换掉还发现不了。

正常情况下(上图),在SEND_DATA状态时,data_o_tmp[0](也就是待发送的那一位数据)的波形应该提前txd波形一个uart_clk周期。

按照时序,发送完一位数据后(比如第24位)data_o_tmp[0]的值应该马上替换为下一位数据(第25位)以在下一个时钟周期时被发送。 代码的棕色加粗部分体现了这一逻辑。

然而在40位时多发了一个0,因为上述的移位过程并没有发生。在黄线后(num=41的周期),data_o_tmp[0]应该从0变为1。(Nx=32'b10101010_11110000_11110000_10101010

--->Nx=32'b10101010_11110000_11110000_10101010)

原因清楚了,在黄线处的uart_clk的posedge_clk上升沿执行代码时,send_num_data=40(请参考同步时序和异步时序的区别,弄清楚代码里的变量值到底是多少),于是红色加粗部分的代码被执行,所以未发生移位。

红色加粗部分原意是在发送完40个数据时将data及时更换(波形图中也可以看出确实已经正确更换),但是却因此出错。

至此,出错原因已经确定。参考的改错方法:将红色加粗处的else if的判断条件变为:

        else if (send_data_num==40&&state==SEND_END)

MSP430中则比较好解决,代码很简单。

#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
    if(i==1)
    {
    a=UCA0RXBUF; i++;
    }
    else if(i==2)
    {
    b=UCA0RXBUF; i++;
    }
    else if(i==3)
    {
    c=UCA0RXBUF; i++;
    }
    else if(i==4)
    {
    d=UCA0RXBUF; i++;
    }
    else if(i==5)
    {
      e=UCA0RXBUF;
      IE2&=~UCA0RXIE;
      if(e==1)
          Nb = a+b*256+(unsigned long) c*256*256+(unsigned long) d*256*256*256;
      else if(e==2)
      {
          Nx = a+b*256+(unsigned long) c*256*256+(unsigned long) d*256*256*256;

          frequency = (double)Nx/(double)Nb*fb;
      }
      IE2|=UCA0RXIE;

      i=1;
    }
}

参考的测量结果:(来自硬木口袋信号发生器)

50kHz

 5kHz

 2Hz

 低频的测量还不是很理想,还需要进一步改进。

END

更新

SIGENT示波器测量结果

2Hz 

100Hz

1kHz

20kHz

100kHz

1MHz

10MHz

 40MHz

再往上就出不来结果了QAQ。。

等精度测频的基准时钟clk是由FPGA的PLL模块生成的400MHz时钟