FPGA与stm32实现串口通信(stm32发,FPGA收)

要使用FPGA与STM32实现串口通信,以下是一般的步骤:

  1. 配置STM32的串口模块:使用STM32的库函数或者寄存器配置,选择一个可用的串口模块(例如USART1、USART2等)并设置相应的波特率、数据位、停止位等参数。确保STM32串口的引脚与FPGA连接正确。

  2. 配置FPGA与STM32之间的通信接口:确定FPGA与STM32之间的通信接口,通常使用串口通信需要至少两个信号线:一个用于发送数据(TX),一个用于接收数据(RX)。确保FPGA的引脚与STM32的串口引脚相连。

  3. 在FPGA中实现串口通信接口:使用HDL(硬件描述语言,如Verilog或VHDL)编写FPGA逻辑,实现串口通信接口。这包括发送和接收数据的逻辑,以及与STM32的串口模块进行数据交换的逻辑。

  4. 在STM32中编写串口通信代码:使用STM32的开发环境(如Keil或STM32CubeIDE)编写串口通信的代码。这包括配置STM32串口模块、发送和接收数据的代码。

  5. 在FPGA中进行数据的发送和接收:通过FPGA的串口通信接口将数据发送到STM32,或从STM32接收数据。确保数据的格式和协议在FPGA和STM32之间匹配。

  6. 在STM32中进行数据的发送和接收:使用STM32的串口通信代码,接收来自FPGA的数据或将数据发送到FPGA。确保STM32的串口配置与FPGA的串口配置匹配。

  7. 测试与调试:使用示波器或串口调试工具监视串口数据的发送和接收。检查数据的准确性和完整性,并进行必要的调试和修复。

需要注意的是,FPGA和STM32之间的串口通信需要确保数据格式、波特率等参数的一致性。另外,还需要关注引脚连接和信号电平的匹配,以确保正确的数据传输。具体的实现细节和代码编写可能因具体的FPGA型号和STM32型号而有所不同,你可以参考相关的开发文档和例程来帮助你完成串口通信的实现。

       以下为本人做无人船时使用的FPGA与stm32串口通信过程。

       首先为FPGA输出PWM波控制两个电机的过程:

//FPGA 控制舵机程序(verilog)
module pwm_gen(

input nreset,
input clk,
input en,//1

input [31:0] period,
input [31:0] h_time,

output reg pwm

);

reg [31:0] CNT;

always @ (posedge clk)
begin
	if(nreset)
		CNT <= 0;
	else if(CNT >= period - 1 )
		CNT <= 0;
	else
		CNT <= CNT + 1;
end

always @ (posedge clk)
begin
	if(nreset)
		pwm <= 0;
    else    //nreset = 1
    begin
        if(en == 0)
            pwm <= 0;
        else    //en = 1
        begin
            if(CNT <= h_time - 1)
                pwm <= 1;
            else
                pwm <= 0;
        end
    end
end

endmodule 
//125M 20M
//例化可控制三个舵机
module PWM1(clk,nreset,uart_rxd,pwm3,pwm4);
	input clk;
	input nreset;
	input uart_rxd;
	output pwm3;//[2:0]
	output pwm4;//[2:0]
	//reg pwm2;
	
	wire [31:0]left0;
	wire [31:0]right0;
	wire clkp0;
wire uart_rx_done;		
wire uart_rx_data;
wire locked;

 pwm_gen a1(
 .nreset(nreset),
 .clk(clk),
 .en(1),
 .period(2500000),
 .h_time(left0),
 .pwm(pwm3)
 );  
  pwm_gen a2(
 .nreset(nreset),
 .clk(clk),
 .en(1),
 .period(2500000),
 .h_time(right0),
 .pwm(pwm4)
 );  
//  pwm_gen a2(nreset,clk,1,2500000,right0,pwm4);  //125000  250000
// pwm_gen a2(clk,nreset,1,2500000,1500_000,pwm3[1]);  
// pwm_gen a3(clk,nreset,1,2500000,2000_000,pwm3[2]);  

 
//例化被测试的接收模块
//uart_rx #(
//	.BPS			(9600),		//波特率9600
//	.SYS_CLK_FRE	(50_000_000)//时钟频率50M	
//)

uart_rx u_uart_rx (
	.sys_clk		(clkp0),			
	.sys_rst_n		(sys_rst_n),			
	.uart_rxd		(uart_rxd),			
	.uart_rx_done	(uart_rx_done),		
	.uart_rx_data	(uart_rx_data)	,
	.left(left0),
	.right(right0)
);
 clk_wiz_0 uu0
 (
  // Clock out ports
.clk_out1(clkp0),
 .reset(nreset),
.locked(),
 // Clock in ports
 .clk_in1(clk)
 );
 // 1000 2000
//1000_000  1500_000 2000_000
endmodule

      其次建立FPGA的uart模块:

//`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/16 18:08:02
// Design Name: 
// Module Name: uart
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module uart_rx(
	input 			sys_clk,			//50M系统时钟
	input 			sys_rst_n,			//系统复位
	input 			uart_rxd,			//接收数据线
	output reg 		uart_rx_done,		//数据接收完成标志
	output reg [7:0]uart_rx_data,		//接收到的数据
	output reg 	[31:0]	left,		//数据接收完成标志
	output reg 	[31:0]	right		//数据接收完成标志
);
//常量化波特率,可更改
parameter	BPS=115200;					//波特率9600bps,可更改
parameter	SYS_CLK_FRE=50_000_000;		//50M系统时钟
localparam	BPS_CNT=SYS_CLK_FRE/BPS;	//传输一位数据所需要的时钟个数
 
reg 			uart_rx_d0;		//寄存1拍
reg 			uart_rx_d1;		//寄存2拍
reg [15:0]		clk_cnt;				//时钟计数器
reg [3:0]		rx_cnt;					//接收计数器
reg 			rx_flag;				//接收标志位
reg [7:0]		uart_rx_data_reg;		//数据寄存
reg [3:0] cnt_dj;//电机PWM值计数	
reg [31:0] count0 = 10'd100;
wire 			neg_uart_rx_data;		//数据的下降沿
reg [31:0] lefft; 
reg [31:0] lefft1; 
assign	neg_uart_rx_data=uart_rx_d1 & (~uart_rx_d0);  //捕获数据线的下降沿,用来标志数据传输开始
//将数据线打两拍,作用1:同步不同时钟域信号,防止亚稳态;作用2:用以捕获下降沿
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(sys_rst_n)begin
		uart_rx_d0<=1'b0;
		uart_rx_d1<=1'b0;
	end
	else begin
		uart_rx_d0<=uart_rxd;
		uart_rx_d1<=uart_rx_d0;
	end		
end
//捕获到数据下降沿(起始位0)后,拉高传输开始标志位,并在第9个数据(终止位)的传输过程正中(数据比较稳定)再将传输开始标志位拉低,标志传输结束
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(sys_rst_n)
		rx_flag<=1'b0;
	else begin 
		if(neg_uart_rx_data)
			rx_flag<=1'b1;
		else if((rx_cnt==4'd9)&&(clk_cnt==BPS_CNT/2))//在第9个数据(终止位)的传输过程正中(数据比较稳定)再将传输开始标志位拉低,标志传输结束
			rx_flag<=1'b0;
		else 
			rx_flag<=rx_flag;			
	end
end
//时钟每计数一个BPS_CNT(传输一位数据所需要的时钟个数),即将数据计数器加1,并清零时钟计数器
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(sys_rst_n)begin
		rx_cnt<=4'd0;
		clk_cnt<=16'd0;
	end
	else if(rx_flag)begin
		if(clk_cnt<BPS_CNT-1'b1)begin
			clk_cnt<=clk_cnt+1'b1;
			rx_cnt<=rx_cnt;
		end
		else begin
			clk_cnt<=16'd0;
			rx_cnt<=rx_cnt+1'b1;
		end
	end
	else begin
		rx_cnt<=4'd0;
		clk_cnt<=16'd0;
	end		
end
//在每个数据的传输过程正中(数据比较稳定)将数据线上的数据赋值给数据寄存器
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(sys_rst_n)
		uart_rx_data_reg<=8'd0;
	else if(rx_flag)
		if(clk_cnt==BPS_CNT/2) begin
			case(rx_cnt)			
				4'd1:uart_rx_data_reg[0]<=uart_rxd;
				4'd2:uart_rx_data_reg[1]<=uart_rxd;
				4'd3:uart_rx_data_reg[2]<=uart_rxd;
				4'd4:uart_rx_data_reg[3]<=uart_rxd;
				4'd5:uart_rx_data_reg[4]<=uart_rxd;
				4'd6:uart_rx_data_reg[5]<=uart_rxd;
				4'd7:uart_rx_data_reg[6]<=uart_rxd;
				4'd8:uart_rx_data_reg[7]<=uart_rxd;
				default:;
			endcase
		end
		else
			uart_rx_data_reg<=uart_rx_data_reg;
	else
		uart_rx_data_reg<=8'd0;
end	
//当数据传输到终止位时,拉高传输完成标志位,并将数据输出
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n)
begin
		uart_rx_done=1'b0;
		left=32'd125000;
		lefft=32'd0;
		lefft1=32'd0;
		right=32'd0;
		cnt_dj=4'd0;
		uart_rx_data=8'd0;
end	
else if(rx_cnt==4'd9)
begin//接收完判断
		uart_rx_done=1'b1;
		uart_rx_data=uart_rx_data_reg;
//		rx_cnt<=4'd0;

		if(uart_rx_data==16'hFF &&cnt_dj==4'd0)
		begin
		  cnt_dj=4'd1;
		  uart_rx_data=8'd0;
		  left=32'd0;
		  right=32'd0;
		end
        else if(uart_rx_data!=16'hFF &&cnt_dj==4'd1)
        begin
              left=uart_rx_data*count0;
              cnt_dj=4'd2;
		end
		else if(uart_rx_data==16'hFE &&cnt_dj==4'd2)
		begin
		  cnt_dj=4'd3;
		  uart_rx_data=8'd0;
		end
		else if(uart_rx_data!=16'hFE &&cnt_dj==4'd3)
        begin
              left=left+uart_rx_data;
              left=left*10'd125;
              cnt_dj=4'd4;
		end
		else if(uart_rx_data==16'hFD &&cnt_dj==4'd4)
        begin
		  cnt_dj=4'd5;
		  uart_rx_data=8'd0;
		end
		else if(uart_rx_data!=16'hFD &&cnt_dj==4'd5)
        begin
              right=uart_rx_data*count0;
              cnt_dj=4'd6;
		end
		else if(uart_rx_data==16'hFC &&cnt_dj==4'd6)
        begin
		  cnt_dj=4'd7;
		  uart_rx_data=8'd0;
		end	
		else if(uart_rx_data!=16'hFC &&cnt_dj==4'd7)
        begin
              right=right+uart_rx_data;
              right=right*10'd125;
              cnt_dj=4'd0;
		end
end		
else 
begin
		uart_rx_done<=1'b0;
		uart_rx_data=8'd0;
end
end
endmodule

//	if(uart_rx_data==16'hFF &&cnt_dj==4'd0)
//		begin
//		  cnt_dj=4'd1;
//		  uart_rx_data=8'd0;
//		  left=32'd0;
//		  right=32'd0;
//		end
//        else if(uart_rx_data!=16'hFF)
//        begin
//            if(cnt_dj==4'd4)
//            begin
//              right=right+uart_rx_data;
//              cnt_dj=4'd0;
//            end
//            if(cnt_dj==4'd3)
//            begin
//              right=uart_rx_data*100;
//              cnt_dj=4'd4;
//            end
//            if(cnt_dj==4'd2)
//            begin
//              left=left+uart_rx_data;
//              lefft1=left;
//              cnt_dj=4'd3;
//            end
//            if(cnt_dj==4'd1)
//            begin
//              left=uart_rx_data*count0;
//              lefft<=left;
//              cnt_dj=4'd2;
//            end
//		end

       由于两模块间需要进行分频处理,将125MHZ分成50MHZ,我们采用vivado自带的IP核进行分频处理,具体步骤如下图所示:

       仿真模块如下所示:

`timescale 1ns/1ps
//module tb;
//  reg sys_clk;
//  reg sys_rst_n;
//  wire PWMA_H, PWMA_L, PWMB_H, PWMB_L, PWMC_H, PWMC_L;

//  // Reset generation
//  initial begin
//    sys_rst_n = 0;
//    sys_clk=0;
//    #10;
//    sys_rst_n = 1;
//    #100;
//    $finish; // End simulation after some time
//  end

//  // Clock generation
//  always #5 sys_clk = ~sys_clk;

//  // Testbench logic
//  reg clk_counter = 0;

//  // Instantiate the PWM module
//PWM1 a4(sys_clk,sys_rst_n,PWMA_H);

//  always @(posedge sys_clk) begin
//    if (sys_rst_n) begin
//      clk_counter <= clk_counter + 1;

//      // Read PWM output signals
//      $display("Time: %t, PWMA_H: %b, PWMA_L: %b, PWMB_H: %b, PWMB_L: %b, PWMC_H: %b, PWMC_L: %b",
//        $time, PWMA_H, PWMA_L, PWMB_H, PWMB_L, PWMC_H, PWMC_L);

//      // Add your testbench assertions or other test logic here

//      // Stop simulation after a certain number of clock cycles
//      if (clk_counter == 1000)
//        $finish;
//    end
//  end

//endmodule


//模块、接口定义
module uart_rx_tb();
reg sys_clk;			
reg sys_rst_n;			
reg uart_rxd;
wire locked;
wire clkp0;
  wire PWMA_H;
wire uart_rx_done;		
wire uart_rx_data;
wire pwm3;		
wire pwm4;
wire [31:0] left0;		
wire [31:0] right0;
 pwm_gen a1(sys_rst_n,sys_clk,1,2000000,left0,pwm3);  
  pwm_gen a2(sys_rst_n,sys_clk,1,2000000,right0,pwm4);  
//例化被测试的接收模块
uart_rx #(
	.BPS			(9600),		//波特率9600
	.SYS_CLK_FRE	(50_000_000)//时钟频率50M	
)
u_uart_rx(
	.sys_clk		(clkp0),			
	.sys_rst_n		(sys_rst_n),			
	.uart_rxd		(uart_rxd),			
	.uart_rx_done	(uart_rx_done),		
	.uart_rx_data	(uart_rx_data),
		.left(left0),
	.right(right0)
);
 clk_wiz_1 uu1
 (
  // Clock out ports
.clk_out1(clkp0),
 .reset(sys_rst_n),
.locked(locked),
 // Clock in ports
 .clk_in1(sys_clk)
 );
 
// PWM1 a4(sys_clk,sys_rst_n,PWMA_H);
localparam	CNT=50_000_000/9600*20;	//计算出传输每个时钟所需要的时间
initial begin	//传输8位数据	8'b01010101
	//初始时刻定义
		sys_clk	<=1'b0;	
		sys_rst_n<=1'b0;		
		uart_rxd<=1'b1;
	#20 //系统开始工作
		sys_rst_n<=1'b1;
			#20 //系统开始工作
		sys_rst_n<=1'b0;
	#(CNT/2)	
		uart_rxd<=1'b0;//开始传输起始位
	#CNT		
		uart_rxd<=1'b1;//传输最低位,第1位
	#CNT		
		uart_rxd<=1'b0;//传输第2位
	#CNT		
		uart_rxd<=1'b1;//传输第3位
	#CNT		
		uart_rxd<=1'b0;	//传输第4位
	#CNT		
		uart_rxd<=1'b1;//传输第5位
	#CNT		
		uart_rxd<=1'b0;//传输第6位
	#CNT		
		uart_rxd<=1'b1;//传输第7位
	#CNT		
		uart_rxd<=1'b0;	//传输最高位,第8位
	#CNT		
		uart_rxd<=1'b1;	//传输终止位
	//FF	
	#(CNT/2)	
		uart_rxd<=1'b1;//开始传输起始位
	#(CNT/2)	
		uart_rxd<=1'b0;//开始传输起始位
	#CNT		
		uart_rxd<=1'b1;//传输最低位,第1位
	#CNT		
		uart_rxd<=1'b1;//传输第2位
	#CNT		
		uart_rxd<=1'b1;//传输第3位
	#CNT		
		uart_rxd<=1'b1;	//传输第4位
	#CNT		
		uart_rxd<=1'b1;//传输第5位
	#CNT		
		uart_rxd<=1'b1;//传输第6位
	#CNT		
		uart_rxd<=1'b1;//传输第7位
	#CNT		
		uart_rxd<=1'b1;	//传输最高位,第8位
	#CNT		
		uart_rxd<=1'b1;	//传输终止位
		// 左右电机的数
	#(CNT/2)	
		uart_rxd<=1'b1;//开始传输起始位
	#(CNT/2)	
		uart_rxd<=1'b0;//开始传输起始位
	#CNT		
		uart_rxd<=1'b0;//传输最低位,第1位
	#CNT		
		uart_rxd<=1'b1;//传输第2位
	#CNT		
		uart_rxd<=1'b0;//传输第3位
	#CNT		
		uart_rxd<=1'b1;	//传输第4位
	#CNT		
		uart_rxd<=1'b0;//传输第5位
	#CNT		
		uart_rxd<=1'b0;//传输第6位
	#CNT		
		uart_rxd<=1'b0;//传输第7位
	#CNT		
		uart_rxd<=1'b0;	//传输最高位,第8位
	#CNT		
		uart_rxd<=1'b1;	//传输终止位
		//FE	
	#(CNT/2)	
		uart_rxd<=1'b1;//开始传输起始位
	#(CNT/2)	
		uart_rxd<=1'b0;//开始传输起始位
	#CNT		
		uart_rxd<=1'b0;//传输最低位,第1位
	#CNT		
		uart_rxd<=1'b1;//传输第2位
	#CNT		
		uart_rxd<=1'b1;//传输第3位
	#CNT		
		uart_rxd<=1'b1;	//传输第4位
	#CNT		
		uart_rxd<=1'b1;//传输第5位
	#CNT		
		uart_rxd<=1'b1;//传输第6位
	#CNT		
		uart_rxd<=1'b1;//传输第7位
	#CNT		
		uart_rxd<=1'b1;	//传输最高位,第8位
	#CNT		
		uart_rxd<=1'b1;	//传输终止位
			#(CNT)	//00
	#(CNT/2)	
		uart_rxd<=1'b1;//开始传输起始位
	#(CNT/2)	
		uart_rxd<=1'b0;//开始传输起始位
	#CNT		
		uart_rxd<=1'b0;//传输最低位,第1位
	#CNT		
		uart_rxd<=1'b0;//传输第2位
	#CNT		
		uart_rxd<=1'b0;//传输第3位
	#CNT		
		uart_rxd<=1'b0;	//传输第4位
	#CNT		
		uart_rxd<=1'b0;//传输第5位
	#CNT		
		uart_rxd<=1'b0;//传输第6位
	#CNT		
		uart_rxd<=1'b0;//传输第7位
	#CNT		
		uart_rxd<=1'b0;	//传输最高位,第8位
	#CNT		
		uart_rxd<=1'b1;	//传输终止位
	//FD	
	#(CNT/2)	
		uart_rxd<=1'b1;//开始传输起始位
	#(CNT/2)	
		uart_rxd<=1'b0;//开始传输起始位
	#CNT		
		uart_rxd<=1'b1;//传输最低位,第1位
	#CNT		
		uart_rxd<=1'b0;//传输第2位
	#CNT		
		uart_rxd<=1'b1;//传输第3位
	#CNT		
		uart_rxd<=1'b1;	//传输第4位
	#CNT		
		uart_rxd<=1'b1;//传输第5位
	#CNT		
		uart_rxd<=1'b1;//传输第6位
	#CNT		
		uart_rxd<=1'b1;//传输第7位
	#CNT		
		uart_rxd<=1'b1;	//传输最高位,第8位
	#CNT		
		uart_rxd<=1'b1;	//传输终止位
		//右
	#(CNT/2)	
		uart_rxd<=1'b1;//开始传输起始位
	#(CNT/2)	
		uart_rxd<=1'b0;//开始传输起始位
	#CNT		
		uart_rxd<=1'b0;//传输最低位,第1位
	#CNT		
		uart_rxd<=1'b0;//传输第2位
	#CNT		
		uart_rxd<=1'b1;//传输第3位
	#CNT		
		uart_rxd<=1'b0;	//传输第4位
	#CNT		
		uart_rxd<=1'b1;//传输第5位
	#CNT		
		uart_rxd<=1'b0;//传输第6位
	#CNT		
		uart_rxd<=1'b0;//传输第7位
	#CNT		
		uart_rxd<=1'b0;	//传输最高位,第8位
	#CNT		
		uart_rxd<=1'b1;	//传输终止位
	//FC	
	#(CNT/2)	
		uart_rxd<=1'b1;//开始传输起始位
	#(CNT/2)	
		uart_rxd<=1'b0;//开始传输起始位
	#CNT		
		uart_rxd<=1'b0;//传输最低位,第1位
	#CNT		
		uart_rxd<=1'b0;//传输第2位
	#CNT		
		uart_rxd<=1'b1;//传输第3位
	#CNT		
		uart_rxd<=1'b1;	//传输第4位
	#CNT		
		uart_rxd<=1'b1;//传输第5位
	#CNT		
		uart_rxd<=1'b1;//传输第6位
	#CNT		
		uart_rxd<=1'b1;//传输第7位
	#CNT		
		uart_rxd<=1'b1;	//传输最高位,第8位
	#CNT		
		uart_rxd<=1'b1;	//传输终止位
		//00
	#(CNT/2)	
		uart_rxd<=1'b1;//开始传输起始位
	#(CNT/2)	
		uart_rxd<=1'b0;//开始传输起始位
	#CNT		
		uart_rxd<=1'b0;//传输最低位,第1位
	#CNT		
		uart_rxd<=1'b0;//传输第2位
	#CNT		
		uart_rxd<=1'b0;//传输第3位
	#CNT		
		uart_rxd<=1'b0;	//传输第4位
	#CNT		
		uart_rxd<=1'b0;//传输第5位
	#CNT		
		uart_rxd<=1'b0;//传输第6位
	#CNT		
		uart_rxd<=1'b0;//传输第7位
	#CNT		
		uart_rxd<=1'b0;	//传输最高位,第8位
	#CNT		
		uart_rxd<=1'b1;	//传输终止位
end
 
always begin
	#5	sys_clk=~sys_clk;	//时钟20ns,100M
end
// always begin
//	#10	sys_clk=~sys_clk;	//时钟20ns,50M
//end
endmodule 

        以上便实现了FPGA的接收功能,此时FPGA可以接收FF–FE–FD–FC–的信号,其中–代表16进制数,FF后的16进制数乘100加FE后面的16进制数及为左电机的电机值,同理,FD后面的乘100加FC后面的为右电机的电机值,我们只需要在单片机上实现发送FF–FE–FD–FC–的信号的功能即可完成全部过程。 

       仿真图如下所示:

      从仿真可以看出实验非常成功,对应项目的无人船而言也十分成功。欢迎大家积极讨论留言!!!

版权声明:本文为博主作者:小论同学原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/hsldlh/article/details/131295001

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2024年5月6日
下一篇 2024年5月6日

相关推荐