其他分享
首页 > 其他分享> > FPGA与MSP430G2553通信 UART串口操作

FPGA与MSP430G2553通信 UART串口操作

作者:互联网

个人感觉,FPGA对数据的处理相对麻烦。在之前的等精度测频法中,明明是简简单单的一些除法,却需要引入IP核来进行一步一步的浮点数运算,当时为此死了很多脑细胞,比较详细的内容见我早一些时间的吐槽博客:Verilog设计练习 基于FPGA的等精度频率计_Krism0912的博客-CSDN博客_用verilog设计等精度频率计

所以最好的是FPGA采集一些基本数据量,然后直接传到电脑或者其他地方进行处理,免得浪费一大把好时光。。

这次是对FPGA串口操作进行的一些调试,FPGA的书写代码有参考网上的资源,不过进行了大幅度的更改,参考的代码太过久远突然找不到了、、

由于FPGA比较常用给别的MCU发数据而不是收数据,所以这次仅仅写了FPGA的串口发送功能。

UART常见的是一次发送10位数据,包括一位起始位,8位数据位和一位停止位,其原理网上有很多,这里不说太多。。下图给一个时序的例子。

所以一般给出的代码是一次传输8位数据,若要传输更大的数据则需要分开发送,其实也就是连续发几次的问题。

以下代码给出的是FPGA发出一个40位数据的例子。

module uart_tx_1(

    input clk, //9600Hz         
    input [39:0]data_in,
    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 [5: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<40) 
                                    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_in;  
        else if (state == SEND_DATA)
            data_o_tmp[38:0] <= data_o_tmp[39:1];         //将要发送的数据位放在data_o_tmp[0]
             
    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 == 40)
              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

 

按照实际的情况进行代码书写即可,其中红色加粗部分是代码的核心部分。

逻辑结构中,通过 send_start_flag 这个信号来控制一次完整的40位数据的发送。 在发送完8位数据后会对发送的数据量进行判断(紫色部分),如果未发送完则继续下一次的发送,直到发够为止。如果只是发送8位数据,且不需要连续发送的话,直接让SEND_END转入IDLE状态,等待下一个send_start_flag到来即可。发送其他位数的数据也是同样的道理。

顺带一提,40位数据包含32位有效数据和我自定义的8位的识别码(比如在电脑上获取数据后依据这八位数据是多少来将决定将这32位数据赋值给谁之类)

当然按上述程序的写法,如果恰好传输中途关掉了串口那数据传输就会出错。

剩下的只要根据需要的波特率生成一个输入的时钟即可控制UART的时序。

module main(clk,rst,txd);
input clk,rst;
output txd;
localparam baud_rate = 9600;  
localparam div_num='d100_000_000/baud_rate; 

localparam sendtime_control_num = 'd9600;
reg [13:0] num=0;
reg [26:0] num1=0;

wire uart_clk;
reg send_start_flag;

reg [31:0]data_in = 32'b10101010_11110000_11110000_11110000;
reg [7:0]identify_num = 8'b10100000;
wire [39:0] data;

assign data={identify_num,data_in};

always @(posedge clk or negedge rst)
    begin
    if(!rst)
        num<=0;
    else if(num==div_num)
        num<=0;
    else
        num<=num+1;
    end
 
assign uart_clk = (num==div_num);

 always @(posedge uart_clk or negedge rst)
    begin
    if(!rst)
    begin
         num1<=0;
         send_start_flag<=0;
    end
    else if(num1==sendtime_control_num)
    begin
         num1<=0;
         send_start_flag<=1;
    end
    else
    begin
         num1<=num1+1;
         send_start_flag<=0;
    end
    end   

uart_tx_1 U1(.clk(uart_clk),.data_in(data),.rst(rst),
    .send_start_flag(send_start_flag),.txd(txd));

endmodule

我直接定义了常数的data进行测试,实际工程中换掉data改成获取的数据即可。

由于计划的传输速度不需要很快,波特率选用为9600,以此生成一个9600Hz的uart_clk供UART操作。(见橙色部分,仅仅是很简单的分频而已)当然,如果你需要一秒钟发送成百上千个数据,就得好好考虑一下波特率够不够了。

预计传输数据为1s左右以此,于是send_start_flag的脉冲间隔也应该是1s左右。代码中每隔9600个uart_clk将send_start_flag置高一次。

仿真时序见下(对定义的常数值进行更改,不然很久都出不来结果)

串口调试器调试结果:

 

输出的txd信号从扩展IO口引出,如果只需要和电脑通信,那只要定义EGO1上专用的UART引脚,直接接上USB线即可。

剩下的是MSP430的RX接收操作,相对于FPGA简单了很多。

#include <msp430.h> 

/**
 * main.c
 */

void UART_Init();
unsigned int a=0,b=0,c=0,d=0,e=0;
int i=1;
unsigned long int data=0;
double cal_result;
void main(void)
{
    WDTCTL = WDTPW | WDTHOLD;    // stop watchdog timer
    UART_Init();
    for(;;)
       {
       }

}


void UART_Init()
{

      P1SEL = BIT1 + BIT2 ;                     // P1.1 = RXD, P1.2=TXD
      P1SEL2 = BIT1 + BIT2 ;                    // P1.1 = RXD, P1.2=TXD
      UCA0CTL1 |= UCSWRST;
      UCA0CTL1 |= UCSSEL_2;                     // SMCLK
      UCA0BR0 = 104;                            // 1MHz 9600
      UCA0BR1 = 0;                              // 1MHz 9600
      UCA0MCTL = UCBRS0;                       // Modulation UCBRSx = 1

      UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
      IE2=UCA0RXIE;

      _bis_SR_register(CPUOFF+GIE);  

}

#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;

      data =a+b*256+(unsigned long) c*256*256+(unsigned long) d*256*256*256;
      cal_result =data/24.34;
      IE2|=UCA0RXIE;

      i=1;
    }
}

 

430需要连续读取5次8位数据,在MSP430的RX中断中采用了以上看起来非常蠢的写法,不过还有没有什么更好的写法。。暂时还没想到。

还原数据data后,运算就可以轻松搞定了(这里弄了一个cal_result测试测试),再也不用引用什么IP核了去算Nx除以Nb了,谢。。

复杂计算之前我关掉了UART接收中断,怕出些问题,不加也可。

 

当然,单片机的计算能力有时候还是看不过去,也可以尝试一下Matlab的串口操作,也非常的简单。

标签:FPGA,MSP430G2553,send,else,state,num,串口,rst,data
来源: https://blog.csdn.net/Krism0912/article/details/123618071