send_en:发送使能信号
data_byte:待发送字符串
baud_set:波特率设置
rs232_tx:发送信号线
tx_done:发送结束标志
uart_state:模块状态 空闲为0,工作为1
照图施工,按照一直模块,编写各模块代码。
module uart_tybe_tx(
Clk,
Rst_n,
data_byte,
send_en,
buad_set,
Rs232_Tx,
Tx_done,
uart_state
);
input Clk;
input Rst_n;
input [ 7:0]data_byte;
input send_en;
input [2:0]baud_set;
output reg Rs232_Ttx;
output reg Tx_done;
output reg uart_state;
逻辑设计//
reg dps_clk;//波特率时钟
reg [15:0]div_cnt;//16位分频计数器
reg [15:0]dsp_DR;//分频计数器最大值
reg [3:0]dps_cnt;//波特率时钟计数器,计数到11,四位计数器
reg [7:0]r_data_byte;//data_byte寄存器(防止中途数据改变)
localparam STATE_BIT = 1'B0;//局部参数localparam 定义起始位
localparam STOP_BIT = 1'B1;//定义结束位
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
uart_state <= 1'b0;
else if(send_en)
uart_state <= 1'b1;
else if(dps_cnt == 4'd11)
uart_state <= 1'b0;
else
uart_state <= uart_state;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
r_data_byte <= 8'd0;
else if(send_en)
r_data_byte <= data_byte;
else
r_data_byte <= r_data_byte;
//该过程能保证该数据在使用过程中是稳定的
//查找表设计
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
dps_DR <= 16'd5207;//默认对应9600波特率
else begin
case(baud_set)//总共三位,最多八种
0:dps_DR <= 16'd5207;
1: dps_DR <= 16'2602;//19200
2: dps_DR <= 16'1301;//38400
3: dps_DR <= 16'd868;//57600
4: dps_DR <= 16'd433;//115200
//还有三种
default:dps_DR <= 16'd5207;
endcase
end
//counter
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state)begin //计数使能前提
if(div_cnt == dps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt +1'b1;//自加
end
else
div_cnt <= 16'd0;
//单周期脉冲波特率时钟的产生dps_clk
always@(posedge Cclk or negedge Rst_n)
if(!Rst_n)
dps_clk <= 1'b0;
else if(div_cnt == 16'd1) //刚开始计数的时候就产生一个高脉冲
dps_clk <= 1'b1;
else
dps_clk <= 1'b0;
//dps counter
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
dps_cnt <= 4'd0;
else if(dps_cnt == 4'd11)//清零
dps_cnt <= 4'd0;
else if(dps_clk)
dps_cnt <= dps_cnt +1'b1;
else //dps_clk为低电平的时候
dps_cnt <= dps_cnt;
//发送结束标志位
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Tx_done <= 1'b0;
else if(dps_cnt == 4'd11)
Tx_done <= 1'b1;
else
Tx_done <= 1'b0;
//十选一多路器
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Rs232_Tx <= 1'b1;//默认状态为高电平
else begin
case(dps_cnt)
0:Rs232_Tx <= STATE_BIT;//起始位
1:Rs232_Tx <= r_data_byte[0];
2:Rs232_Tx <= r_data_byte[1];
3:Rs232_Tx <= r_data_byte[2];
4:Rs232_Tx <= r_data_byte[3];
6:Rs232_Tx <= r_data_byte[4];
7:Rs232_Tx <= r_data_byte[5];
8:Rs232_Tx <= r_data_byte[6];
9:Rs232_Tx <= r_data_byte[7];
10:Rs232_Tx <= STOP_BIT;
default:Rs232_Tx <= 1'b1;
endcase
end
endmodule
接下来为仿真程序:(uart_tybe_tx_tb)
`timescale 1ns/1ns
`define clk_period 20
module uart_tybe_tx_tb;
reg Clk;
reg Rrst_n;
reg [7:0]data_byte;
reg send_en;
reg [2:0]baud_set;
wire Rs232_Tx;
wire Tx_done;
wire yart_state;
uart_tybe_tx uart_tybe_tx0(
.Clk(Clk),
.Rst_n(Rst_n),
.data_byte(data_byte),
.send_en(send_en),
.baud_set(baud_set),
.Rs232_Tx(Rs232_Tx),
.Tx_done(Tx_done),
.uart_state(uart_state)
);
//初始化
initial Clk = 1;
always #(`clk_period/2)Clk = ~Clk;
initial begin
Rst_n = 1'b0;
data_byte = 8'd0;
send_en = 1'b0;
baud_set = 3'd4;//115200
#(`clk_period*20+1)//+1为了与系统时钟避开,能更好看清仿真图像
Rst_n = 1'b1;
#(`clk_period*50);
data_byte = 8'haa;
send_en = 1'b1;
#`clk_period;
send_en = 1'b0;
@(posedge Tx_done)//等待信号上升沿
#(`clk_period*5000);//重新发送
data_byte = 8'h55;
send_en = 1'b1;
#`clk_period;
send_en = 1'b0;
@(posedge Tx_done)
#(`clk_period*5000);
$stop;
end
endmodule
仿真图象如下:觉得有点问题,之后再修改来看看
之后为板机调试,结合按键和led,会再建一个顶层模块和按键模块。
uart_tx_top:
//板机调试,新建一个顶层模块
module uart_tx_top(Clk,Rst_n,Rs232_Tx,key_in0,led);
input Clk;
input key_in0;
input Rst_n;
output Rs232_Tx;
output led;
wire send_en;
wire [7:0]data_byte;
wire key_state0;
wire key_flag0;
assign send_en = key_flag0 & !key_state0;//按键检测信号与按键状态为低电平
uart_tybe_tx uart_tybe_tx0(
.Clk(Clk),
.Rst_n(Rst_n),
.data_byte(data_byte),
.send_en(send_en),
.baud_set(3'd4),//对应115200,虽说是端口,但是可以给定值
.Rs232_Tx(Rs232_Tx),
.Tx_done(),//不需要设计逻辑去控制,即可不使用
.uart_state(led)//将串口的状态连接到led上,直接通过led显示串口状态
);
key_filter key_filter0(
.Clk(Clk),
.Rst_n(Rst_n),
.key_in(key_in0),
.key_state(key_state0),//key_state0和key_flag0作为控制信号
.key_flag(key_flag0)
);
issp issp(
.probe(),//探针不需要使用,就不连接,被综合掉
.source(data_byte)
);
//一般不在顶层写代码
endmodule
按键模块(key_filter)
module key_filter(Clk,Rst_n,key_in,key_state,key_flag);//filter:滤波
input Clk;
input Rst_n;
input key_in;
output reg key_state;//按键稳定状态
output reg key_flag;//按键检测成功标志信号
localparam//定义状态
IDLE = 4'b0001,//空闲状态
FILTER0 = 4'b0010,//按下滤波状态
DOWN = 4'b0100,//按下稳定状态
FILTER1 = 4'b1000;//释放滤波状态
//在quartus中,寄存器放在哪里都行,但在modelsim中不行
reg[3:0]state;//状态寄存器
reg key_temp0,key_temp1;//定义两个寄存器
wire pedge,negdge;//定义一个上升沿和一个下降沿
reg cnt_full;//定义计数满寄存器(计数器满标志信号)
reg [19:0]cnt;//定义计数器20位宽,需要计数20ms
//给出使能计数信号后,才开始计数,其余时候为清零状态
reg en_cnt;//使能计数寄存器
reg key_in_s0,key_in_s1;//两个同步寄存器
//同步逻辑(异步信号的同步处理)
always@(posedge Clk or negedge Rst_n)//复位后寄存器有初始值
if(!Rst_n)begin
key_in_s0 <= 1'b0;
key_in_s1 <= 1'b0;
end
else begin
key_in_s0 <= key_in;
key_in_s1 <= key_in_s0;//key_in_s1就是已经同步到系统时钟
end
//使用D触发器存储两个相邻时钟上升沿时外部输入信号(已同步到系统时钟域中)的电平状态
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
key_temp0 <= 1'b0;
key_temp1 <= 1'b0;
end
else begin
key_temp0 <= key_in_s1; //这里对于temp0使用两级D触发器,提高稳定性
key_temp1 <= key_temp0;
end
//检测上升沿与下降沿(边沿检测)
assign negdge = !key_temp0 & key_temp1;//组合逻辑输出值为1,则检测到下降沿
assign pedge = key_temp0 & (!key_temp1);//!为取非 ~为按位取反
//eg:0110~1001 !(0110)=0 !(0000)=1
//状态机主程序
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin//所有赋值的值,再复位的时候都要赋个初值
key_flag <= 1'b0;
key_state <= 1'b1;
state <= IDLE;
en_cnt <= 1'b0;
end
else begin
case(state)
IDLE:
begin
key_flag <= 1'b0;//因为在FILTER1里拉高了,在这里要清零
if(negdge)begin
state <= FILTER0;
en_cnt <= 1'b1;
end
else
state <= IDLE;
end
FILTER0:
if(cnt_full)begin
key_flag <= 1'b1;
key_state <= 1'b0;
en_cnt <= 1'b0;//计数满了之后清零
state <= DOWN;
end
else if(pedge)begin//如果还没有计数完,检测到上升沿
state <= IDLE;
en_cnt <= 1'b0;//表示这只是抖动,不用计数,等待下降沿的到来才开始计数
end
else
state <= FILTER0;
DOWN:
begin
key_flag <= 1'b0;//该语句与之后的if语句并行的
if(pedge)begin
state <= FILTER1;
en_cnt <= 1'b1;
end
else
state <= DOWN;
end
FILTER1:
if(cnt_full)begin
key_flag <= 1'b1;//可忽略,也可表示再次检测到按键,按键释放
key_state <= 1'b1;
//en_cnt <= 1'b0;//计数满了之后清零
state <= IDLE;
end
else if(negdge)begin//如果还没有计数完,检测到上升沿
state <= DOWN;
en_cnt <= 1'b0;//表示这只是抖动,不用计数,等待下降沿的到来才开始计数
end
else
state <= FILTER1;
default:
begin
en_cnt <= 1'b0;//给一个回到正常状态的值
key_flag <= 1'b0;//默认为没有按键的时候的一个状态
key_state <= 1'b1;
state <= IDLE;
end
endcase
end
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt <= 20'd0;
else if(en_cnt)//有使能信号下
cnt <= cnt + 1'b1;
else //没有使能
cnt <= 20'd0;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt_full <= 1'b0;
else if(cnt == 999999)//1000000-1
cnt_full <= 1'b1;
else
cnt_full <= 1'b0;
endmodule