FPGA ise项目 实现点亮led灯,串口收发


前言

FPGA verilog项目学习


一、FPGA开发板使用

1.D1闪烁 间隔1s

代码如下:

module D1
#(
 parameter CNT_MAX = 26'd49_999_999
)
(
input wire sys_clk , //系统时钟 50Mh
input wire sys_rst_n , //全局复位
 
output reg led1_out //输出控制 led 灯
);

//reg define
reg [25:0] cnt ; //经计算得需要 26 位宽的寄存器才够 1s
reg cnt_flag;

//cnt:计数器计数,当计数到 CNT_MAX 的值时清零
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 26'b0;
else if(cnt == CNT_MAX)
cnt <= 26'b0;
else
 cnt <= cnt + 1'b1;
 
//cnt_flag:计数到最大值产生的标志信号,每当计数满标志信号有效时取反
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_flag <= 1'b0;
else if(cnt == CNT_MAX)
cnt_flag <= 1'b1;
 else
 cnt_flag <= 1'b0;
 
//led_out:输出控制一个 LED 灯
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 led1_out <= 1'b0;
 else if(cnt_flag == 1'b1)
 led1_out <= ~led1_out;
 
 endmodule

2.D2闪烁,间隔1s,按下key1,key2,实现D2的闪烁时间加长/减少

代码如下(示例):

module D2
(
	 output reg led1_out, //输出控制 led 灯
	 output reg led2_out, //输出控制 led 灯
    input wire sys_clk , //系统时钟 50Mh
    input wire sys_rst_n , //全局复位
	 input   key1_flag,
	 input   key2_flag

);


//reg define
reg [26:0] cnt; //经计算得需要 27 位宽的寄存器才够 1s,29位宽10s
reg [25:0] cnt1; //经计算得需要 27 位宽的寄存器才够 1s,29位宽10s
reg [26:0] CNT_MAX ; //计数到一秒
reg [26:0] CNT1_MAX ;

initial 
begin
CNT_MAX <= 27'd50_000_000; //计数到一秒
CNT1_MAX <= 27'd50_000_000; //计数到一秒
end


 
//cnt:计数器计数,当计数到 CNT_MAX 的值时清零
always@(posedge sys_clk or negedge sys_rst_n)
begin
    if(sys_rst_n == 1'b0)
    cnt <= 27'd0;
    else if(cnt == CNT_MAX)
    cnt <= 27'd0;
    else
    cnt <= cnt + 27'd1;
 end
 
//cnt1:计数器计数,当计数到 CNT1_MAX 的值时清零
always@(posedge sys_clk or negedge sys_rst_n)
begin
    if(sys_rst_n == 1'b0)
    cnt1 <= 0;
    else if(cnt1 == CNT1_MAX)
    cnt1 <= 0;
    else
    cnt1 <= cnt1 + 26'd1;
 end
 
 
 //led1_out:输出控制一个 LED 灯的闪烁
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
 led1_out <= 1'b1;
else if(cnt1 == CNT1_MAX )
led1_out <= ~led1_out;
//led1_out <= 1;
end



always@(posedge sys_clk )
begin
    if(key1_flag==1 )
	 begin
	      
         CNT_MAX <= CNT_MAX + 27'd25_000_000;//key1按下一次,闪烁时间加长0.5s
	 end
	 else if(CNT_MAX >= 27'd49_000_001)
	 begin
	       if(key2_flag==1)
			 begin
			      CNT_MAX <= CNT_MAX - 27'd25_000_000;//key2按下一次,闪烁时间减小0.5s
			 end
	 end
end


//led2_out:输出控制一个 LED 灯的闪烁
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
 led2_out <= 1'b1;
else if(cnt == CNT_MAX )
led2_out <= ~led2_out;
//led2_out <= 1;
//else if(key1_flag==1 )
//led2_out <= ~led2_out;
end

 
endmodule

二、串口通信

key3作为串口驱动信号,向mcu发送数据,反复发送,key4按下,mcu发送信号给FPGA,FPGA发送反馈数据给mcu

发送代码如下(示例):

module uart(
    input  sys_clk,
    input  sys_rst_n,
    output  reg uart_txd,
    input  key3_flag,//key3_flag 为 1 时表示消抖后检测到按键被按下
	 input  key4_flag,
    input  [7:0] uart_data,
	 input [7:0] uart_data1,
	 input uart_done,
	  output reg led3_out

    );


			 
/*//............按键检测............//
//按键消抖
reg [19:0] cnt_20ms ; //计数器
parameter CNT_MAX = 20'd999_999; //计数器计数最大值
//cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_20ms <= 20'b0;
else if(key3_in == 1'b1)
cnt_20ms <= 20'b0;
else if(cnt_20ms == CNT_MAX && key3_in == 1'b0)
cnt_20ms <= cnt_20ms;
else
cnt_20ms <= cnt_20ms + 1'b1;
 
//key3_flag:当计数满 20ms 后产生按键有效标志位
//且 key3_flag 在 999_999 时拉高,维持一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
  key3_flag <= 1'b0;
else if(cnt_20ms == CNT_MAX - 1'b1)
  key3_flag <= 1'b1;
else
  key3_flag <= 1'b0;*/



//.........串口...........//
parameter BPS = 5208; 		
reg [7:0] data_buf;
reg tx_start;
reg tx1_start;
reg[3:0] tx_cnt;
reg[12:0] clk_cnt;
//reg[3:0] state;
reg tx_flag;
reg tx1_flag;

always @ (posedge sys_clk or negedge sys_rst_n)
		if(!sys_rst_n)
		begin
         tx_start <= 1'b0;
			tx1_start <= 1'b0;
		end
		else begin
		if(key3_flag==1)
		  begin tx_start <= ~tx_start;end
		if(key4_flag==1)
         begin tx1_start <= ~tx1_start;end
      end
//发送55 aa 66 99 01 23 45 67//
always @ (posedge sys_clk or negedge sys_rst_n)
		if(!sys_rst_n)
			tx_flag <=0;
		else if(tx_start)
			tx_flag <=1'b1;
		else if((tx_cnt == 4'd9)&&(clk_cnt ==BPS/2))
			      tx_flag <= 1'b0;	
		
//发送mcu的反馈数据的标志位//
always @ (posedge sys_clk or negedge sys_rst_n)
		if(!sys_rst_n)
			tx1_flag <=0;
		else if(tx1_start)
			tx1_flag <=1'b1;
		else if((tx_cnt == 4'd9)&&(clk_cnt ==BPS/2))
			      tx1_flag <= 1'b0;
		
always @ (posedge sys_clk or negedge sys_rst_n)
		begin
			if(!sys_rst_n)
				begin
				clk_cnt<=13'd0;tx_cnt <= 4'd0;
				end
			else if(tx_flag||tx1_flag)
			begin
				if(clk_cnt == BPS-1)
				begin
				   clk_cnt <= 13'd0;
					tx_cnt <= tx_cnt +1'b1;
					if(tx_cnt == 4'd9)tx_cnt <=0;
				end
				else
				begin
				clk_cnt <= clk_cnt +1'b1;tx_cnt<=tx_cnt;		
            end				
			end
		end

	
		


 
       /*
always @ (posedge sys_clk or negedge sys_rst_n)
		begin
			if(!sys_rst_n)
				begin
				state<=IDLE;
	         //data_buf=8'h55;
				end
			else 
				case(state)
				IDLE:if(key3_flag==1)
					begin
					tx_cnt<=0;
					clk_cnt<=0;
						if(tx_start==1 )
							state<=SEND;
					end
				SEND:
					begin
					if(tx_cnt==4'd9&&clk_cnt==BPS/2)
						begin
						tx_cnt<=4'd10;
						state<=END;
						end
					else if(clk_cnt==BPS-1)
						begin
						clk_cnt<=0;
						tx_cnt<=tx_cnt+1'b1;
						end
					else
						begin
						clk_cnt<=clk_cnt+1'b1;
						tx_cnt<=tx_cnt;
						end
					end
				END:
				begin
				if(tx_start==1)
					begin
					state<=END;
					end
				else
					state<=IDLE;
				end
				default:state<=IDLE;
			endcase
		end
*/		
		//2s发送一次数据
		reg [26:0] cnt_2s;
		always @ (posedge sys_clk or negedge sys_rst_n)
		if(!sys_rst_n)			
			cnt_2s<=0;
		else if(cnt_2s==27'd100_000_000)//2s
			cnt_2s<=0;
		else
			cnt_2s<=cnt_2s+27'd1;

		//1.5s发送一次数据
		reg [27:0] cnt_15s;
		always @ (posedge sys_clk or negedge sys_rst_n)
		if(!sys_rst_n)			
			cnt_15s<=0;
		else if(cnt_15s==28'd75_000_000-1)//1.5s
			cnt_15s<=0;
		else
			cnt_15s<=cnt_15s+28'd1;
      //八位数据轮流
		reg [3:0] cnt_8;
		always @ (posedge sys_clk or negedge sys_rst_n)
		if(!sys_rst_n)
			cnt_8<=4'd0;
		else if(cnt_2s==27'd100_000_000)//2s
			cnt_8<=cnt_8+4'd1;
		else if(cnt_8 == 4'd8)
			cnt_8<=4'd0;
			
		//两位数据轮流	
		reg [3:0] cnt1_2;
		always @ (posedge sys_clk or negedge sys_rst_n)
		if(!sys_rst_n)
			cnt1_2<=4'd0;
		else if(cnt_15s==27'd75_000_000-1)//1.5s
			cnt1_2<=cnt1_2+4'd1;
		else if(cnt1_2 == 4'd2)//
			cnt1_2<=4'd0;
//两位数据轮流	
		reg [3:0] cnt1_8;
		always @ (posedge sys_clk or negedge sys_rst_n)
		if(!sys_rst_n)
			cnt1_8<=4'd0;
		else if(cnt_15s==27'd75_000_000-1)//1.5s
			cnt1_8<=cnt1_8+4'd1;
		else if(cnt1_8 == 4'd8)//
			cnt1_8<=4'd0;
		
		always @ (posedge sys_clk or negedge sys_rst_n)
		begin
		if(!sys_rst_n)
			data_buf<=8'h00;
//		else if(tx1_flag == 1) begin  
//		   data_buf <= 8'h11;

      else if(tx1_flag ==1)begin//key4按下,发送数据11
//		   else if(uart_done==1)//数据传输完成的标志位,发送反馈数据
		     /*if(uart_data == 8'h89)
			  data_buf <= uart_data;
           if(uart_data == 8'hAB)
			  data_buf <= uart_data;
           if(uart_data == 8'hCD)
			  data_buf <= uart_data;*/
			 if(uart_data == 8'h11)
			  data_buf <= uart_data;
//          //if(uart_done==1)begin
//			 if(cnt_15s<=28'd75_000_000 && uart_done==1)
//			  data_buf <=  uart_data;
//			 if(cnt_15s>28'd75_000_000 && uart_done==1)
//			  data_buf <= ~uart_data;	   
//			  end 
			  //end 
			  if(uart_data == uart_data )
            case(cnt1_2)
				4'd0:data_buf <= uart_data;
				4'd1:data_buf <= uart_data1;
				default:;
			   endcase	
//            case(cnt1_8)
//				4'd0:data_buf <= array[0];//55
//				4'd1:data_buf <=~array[0];//aa
//				4'd2:data_buf <= array[1];//66
//				4'd3:data_buf <=~array[1];//99
//				4'd4:data_buf <= array[2];//01
//				4'd5:data_buf <=~array[2];//23
//				4'd6:data_buf <= array[3];//45
//				4'd7:data_buf <=~array[3];//67
//				default:;//00
//			endcase 
		end	
		else if(tx_flag ==1)//发送的两帧数据
			case(cnt_8)
				4'd0:data_buf <= 8'h55;//55
				4'd1:data_buf <= 8'hAA;//aa
				4'd2:data_buf <= 8'h66;//66
				4'd3:data_buf <= 8'h99;//99
				4'd4:data_buf <= 8'h01;//01
				4'd5:data_buf <= 8'h23;//23
				4'd6:data_buf <= 8'h45;//45
				4'd7:data_buf <= 8'h67;//67
				default:data_buf <= 8'h00;//00
			endcase
		end
		
		always @ (posedge sys_clk or negedge sys_rst_n)
		begin
			if(!sys_rst_n)
				uart_txd <= 8'b1;
			else 
				case(tx_cnt)
					4'd0:uart_txd <= 8'b0;
					4'd1:uart_txd <= data_buf[0];
					4'd2:uart_txd <= data_buf[1];
					4'd3:uart_txd <= data_buf[2];
					4'd4:uart_txd <= data_buf[3];
					4'd5:uart_txd <= data_buf[4];
					4'd6:uart_txd <= data_buf[5];
					4'd7:uart_txd <= data_buf[6];
					4'd8:uart_txd <= data_buf[7];
					4'd9:uart_txd <= 8'b1;
					default:uart_txd <= 8'b1;
				endcase

				
		end
reg [7:0] array [0:3];
reg [3:0]	i;	
//检测是否接收到数据uart_data == 8'h89 || uart_data == 8'hAB || uart_data == 8'hCD || uart_data == 8'hEF
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
    begin
        led3_out<=1;
    end
else if(uart_done == 1'b1)
    begin i<=0;array[i]<=uart_data;i<=i+1;end
else if(uart_data1 == 8'h32)
    begin led3_out <= 0 ; end	
else if(uart_data1 == 8'h10)
    begin led3_out <= 1 ; end
else if(uart_data1 == 8'h76)
    begin led3_out <= 0 ; end	
else if(uart_data1 == 8'h54)
    begin led3_out <= 1 ; end	 
endmodule

接收代码如下(示例):

module rx(
    input  sys_clk,
    input  sys_rst_n,
	 input  key4_flag,
	 input  uart_rxd,
    output   reg[7:0] uart_data,
	 output   reg[7:0] uart_data1,
	 output   reg      uart_done
//	 output reg led3_out

    );

//.........串口...........//
parameter BPS = 5208;

reg         rx_start;
reg         rx_flag;//数据有效段的标志,拉高为有效
reg [3:0]   rx_cnt;//波特率周期的计数器
reg [12:0]  clk_cnt;//系统时钟的计数器
reg [7:0]   rxdata;//输出数据的寄存器
reg         uart_rxd_reg0;
reg         uart_rxd_reg1;
wire        start_flag;
initial uart_data <= 8'h11;

//initial begin uart_data <= 8'h11; end
//边缘检测的功能,检测数据的低电平,开始位,数据传输开始就拉高start_flag
always@(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin
        uart_rxd_reg0 <= 1'b1;
        uart_rxd_reg1 <= 1'b1;
    end
    else begin
        uart_rxd_reg0 <= uart_rxd;
        uart_rxd_reg1 <= uart_rxd_reg0;     
    end
end
assign start_flag = (uart_rxd_reg1)&(~uart_rxd_reg0);

/*//key4按下,mcu开始发送数据,FPGA接收数据传输开始位拉高,数据接收标志位rx_start就拉高
always @ (posedge sys_clk or negedge sys_rst_n)
		if(!sys_rst_n)
         rx_start <= 1'b0;
		else if(key4_flag && start_flag)
         rx_start <= 1;*/

//根据rx_start传输开始的标志,设置传输的有效段的范围, 即从开始位到结束位都拉高rx_flag,至于为甚后面是波特率周期的一办,是为了给一帧数据留一个缓冲的时间;
always@(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        rx_flag <= 0;
    else 
	 begin
        //if(rx_start)
		  if(start_flag)
            rx_flag <= 1'b1;
        else if(rx_cnt == 4'd9 && clk_cnt == BPS/2)
            rx_flag <= 1'b0;
        else
            rx_flag <= rx_flag;        
    end 
end
//系统时钟的计数器
always@(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        clk_cnt <= 0;
    else begin
        if(rx_flag)begin
            if(clk_cnt < BPS - 1)
                clk_cnt <= clk_cnt + 1'b1;
            else
                clk_cnt <= 0;    
        end
        else
            clk_cnt <= 0;    
    end 
end
//波特率周期计数器,每当系统时钟计数器计数到一个波特率周期时,波特率周期计数器就加一
always@(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        rx_cnt <= 0;
    else begin
        if(rx_flag)begin
            if(clk_cnt == BPS - 1)
                rx_cnt <= rx_cnt + 1'b1;
            else
                rx_cnt <= rx_cnt;    
        end
        else
            rx_cnt <= 0;
    end 
end
//串行数据转换为并行数据,至于为什么是计数到波特率周期的一半,是为了采样中间的值,这样采样的正确性高
always@(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        rxdata <= 0;
    else begin
        if(rx_flag)begin
            if(clk_cnt == BPS/2)begin
                case(rx_cnt)
                    4'd1:   rxdata[0] <= uart_rxd;      
                    4'd2:   rxdata[1] <= uart_rxd; 
                    4'd3:   rxdata[2] <= uart_rxd; 
                    4'd4:   rxdata[3] <= uart_rxd; 
                    4'd5:   rxdata[4] <= uart_rxd; 
                    4'd6:   rxdata[5] <= uart_rxd; 
                    4'd7:   rxdata[6] <= uart_rxd; 
                    4'd8:   rxdata[7] <= uart_rxd; 
                    default:;
                endcase       
            end
            else
                rxdata <= rxdata;    
        end
        else
            rxdata <= 0;
    end
end
//最后将寄存的输出数据传给输出,并拉高输出完的标志位uart_done
always@(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin
        uart_data <= uart_data;
		  uart_data1 <= 0;
        uart_done <= 0;
    end
    else begin
        if(rx_cnt == 4'd9)begin
            uart_data <= rxdata;
				uart_data1 <= ~rxdata;
            uart_done <= 1'b1; 
        end
        else begin
//            uart_data <= 0;
//				uart_data1 <= 0;
            uart_done <= 0;
        end
    end
end

检测是否接收到数据
//always@(posedge sys_clk or negedge sys_rst_n)
//if(!sys_rst_n)
//    begin
//        led3_out<=1;
//    end
else if(uart_done == 1'b1)
//else if(uart_data == 8'h89)
//    begin led3_out <= ~led3_out ; end

	
endmodule




 

按键检测模块

module key1_filter
#(
parameter CNT_MAX = 20'd999_999 //计数器计数最大值
)
(
input wire sys_clk , //系统时钟 50MHz
input wire sys_rst_n , //全局复位
input wire key1_in , //按键输入信号

output reg key1_flag //key1_flag 为 1 时表示消抖后检测到按键被按下
                       //key1_flag 为 0 时表示没有检测到按键被按下

 );


//按键消抖
reg [19:0] cnt_20ms ; //计数器

//cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_20ms <= 20'b0;
else if(key1_in == 1'b1)
cnt_20ms <= 20'b0;
else if(cnt_20ms == CNT_MAX && key1_in == 1'b0)
cnt_20ms <= cnt_20ms;
else
cnt_20ms <= cnt_20ms + 1'b1;
 
//key1_flag:当计数满 20ms 后产生按键有效标志位
//且 key1_flag 在 999_999 时拉高,维持一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
  key1_flag <= 1'b0;
else if(cnt_20ms == CNT_MAX - 1'b1)
  key1_flag <= 1'b1;
else
  key1_flag <= 1'b0;


按键按的次数
//always@(posedge sys_clk or negedge sys_rst_n)
//if(sys_rst_n == 1'b0)
//  key1_value <= 4'd0;
//else if(key1_flag == 1'b1)
//  key1_value <= key1_value + 4'd1;
//else
//  key1_value <= 4'd0;
 
endmodule

总结

串口通信收发数据。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值