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
总结
串口通信收发数据。
本文作者:cloud-sword
本文链接:https://www.cnblogs.com/cloud-sword/p/16407492.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步