ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

FPGA与MSP430G2553通信 UART串口操作

2022-03-20 19:04:56  阅读:228  来源: 互联网

标签:FPGA MSP430G2553 send else state num 串口 rst data


个人感觉,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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有