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
enduart_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 = 1UCA0CTL1 &= ~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