一个关于特权同学的串口通信代码的问题
时间:10-02
整理:3721RD
点击:
新手刚学FPGA遇到的一个疑惑,如下文橙色代码的所示,发送模块和接受模块都在中间采样点收数或者发数,收数我倒是好理解,因为中间时刻的值最稳定,所以在中间时刻进行收数采样,但是发数为什么也得在中间时刻到达后才进行呢?而且我实验了一下,发现去掉always @ (posedge clk or negedge rst_n)
if(!rst_n) clk_bps_r <= 1'b0;
else if(cnt == `BPS_PARA_2) clk_bps_r <= 1'b1;
else clk_bps_r <= 1'b0;这个语句(即只要达到tx_en=1就执行发数,不必达到cnt == `BPS_PARA_2),但是这样串口就不好使了,但是我不知道, else if(cnt == `BPS_PARA_2) clk_bps_r <= 1'b1; 然后再 执行
else if(tx_en) begin
if(clk_bps) begin
num <= num+1'b1;
case (num)
这样的作用是什么?
希望能有人给我耐心解决一下,感激不尽代码如下
波特率设置模块:
/////////////////////////////////////////////////////////////
module speed_select(
clk,rst_n,
bps_start,clk_bps
);
input clk; // 50MHz主时钟
input rst_n; //低电平复位信号
input bps_start; //接收到数据后,波特率时钟启动信号置位
output clk_bps; // clk_bps的高电平为接收或者发送数据位的中间采样点
`define BPS_PARA 5207 //波特率为9600时的分频计数值
`define BPS_PARA_2 2603 //波特率为9600时的分频计数值的一半,用于数据采样
reg[12:0] cnt; //分频计数
reg clk_bps_r; //波特率时钟寄存器
//----------------------------------------------------------
reg[2:0] uart_ctrl; // uart波特率选择寄存器
//----------------------------------------------------------
always @ (posedge clk or negedge rst_n)
if(!rst_n) cnt <= 13'd0;
else if((cnt == `BPS_PARA) || !bps_start) cnt <= 13'd0; //波特率计数清零
else cnt <= cnt+1'b1; //波特率时钟计数启动
always @ (posedge clk or negedge rst_n)
if(!rst_n) clk_bps_r <= 1'b0;
else if(cnt == `BPS_PARA_2) clk_bps_r <= 1'b1; // clk_bps_r高电平为接收数据位的中间采样点,同时也作为发送数据的数据改变点
else clk_bps_r <= 1'b0;
assign clk_bps = clk_bps_r;
endmodule
//////////////////////////////////////////////////////////////////////////
串口发送模块:
////////////////////////////////////////////////////////////////////////////
module my_uart_tx(
clk,rst_n,
rx_data,rx_int,rs232_tx,key,
clk_bps,bps_start
);
input clk; // 50MHz主时钟
input rst_n; //低电平复位信号
input clk_bps; // clk_bps_r高电平为接收数据位的中间采样点,同时也作为发送数据的数据改变点
input[7:0] rx_data; //接收数据寄存器
input rx_int; //接收数据中断信号,接收到数据期间始终为高电平,在该模块中利用它的下降沿来启动串口发送数据
input key;
output rs232_tx; // RS232发送数据信号
output bps_start; //接收或者要发送数据,波特率时钟启动信号置位
//---------------------------------------------------------
reg rx_int0,rx_int1,rx_int2; //rx_int信号寄存器,捕捉下降沿滤波用
wire neg_rx_int; // rx_int下降沿标志位
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
rx_int0 <= 1'b0;
rx_int1 <= 1'b0;
rx_int2 <= 1'b0;
end
else begin
rx_int0 <= rx_int;
rx_int1 <= rx_int0;
rx_int2 <= rx_int1;
end
end
assign neg_rx_int = ~rx_int1 & rx_int2; //捕捉到下降沿后,neg_rx_int拉高保持一个主时钟周期
//---------------------------------------------------------
reg[7:0] tx_data; //待发送数据的寄存器
//---------------------------------------------------------
reg bps_start_r;
reg tx_en; //发送数据使能信号,高有效
reg[3:0] num;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
bps_start_r <= 1'b0;
tx_en <= 1'b0;
tx_data <= 8'd0;
end
else if(neg_rx_int) begin //接收数据完毕,准备把接收到的数据发回去
bps_start_r <= 1'b1;
tx_data <= rx_data; //把接收到的数据存入发送数据寄存器
tx_en <= 1'b1; //进入发送数据状态中
end
else if(num==4'd10) begin //数据发送完成,复位
bps_start_r <= 1'b0;
tx_en <= 1'b0;
end
end
assign bps_start = bps_start_r;//接收或者要发送数据,波特率时钟启动信号置位
//---------------------------------------------------------
reg rs232_tx_r;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
num <= 4'd0;
rs232_tx_r <= 1'b1;
end
else if(tx_en) begin
if(clk_bps) begin
num <= num+1'b1;//对发送进行计数
case (num)
4'd0: rs232_tx_r <= 1'b0; //发送起始位
4'd1: rs232_tx_r <= tx_data[0]; //发送bit0
4'd2: rs232_tx_r <= tx_data[1]; //发送bit1
4'd3: rs232_tx_r <= tx_data[2]; //发送bit2
4'd4: rs232_tx_r <= tx_data[3]; //发送bit3
4'd5: rs232_tx_r <= tx_data[4]; //发送bit4
4'd6: rs232_tx_r <= tx_data[5]; //发送bit5
4'd7: rs232_tx_r <= tx_data[6]; //发送bit6
4'd8: rs232_tx_r <= tx_data[7]; //发送bit7
4'd9: rs232_tx_r <= 1'b1; //发送结束位
default: rs232_tx_r <= 1'b1;
endcase
end
else if(num==4'd10) num <= 4'd0; //复位
end
end
if(!rst_n) clk_bps_r <= 1'b0;
else if(cnt == `BPS_PARA_2) clk_bps_r <= 1'b1;
else clk_bps_r <= 1'b0;这个语句(即只要达到tx_en=1就执行发数,不必达到cnt == `BPS_PARA_2),但是这样串口就不好使了,但是我不知道, else if(cnt == `BPS_PARA_2) clk_bps_r <= 1'b1; 然后再 执行
else if(tx_en) begin
if(clk_bps) begin
num <= num+1'b1;
case (num)
这样的作用是什么?
希望能有人给我耐心解决一下,感激不尽代码如下
波特率设置模块:
/////////////////////////////////////////////////////////////
module speed_select(
clk,rst_n,
bps_start,clk_bps
);
input clk; // 50MHz主时钟
input rst_n; //低电平复位信号
input bps_start; //接收到数据后,波特率时钟启动信号置位
output clk_bps; // clk_bps的高电平为接收或者发送数据位的中间采样点
`define BPS_PARA 5207 //波特率为9600时的分频计数值
`define BPS_PARA_2 2603 //波特率为9600时的分频计数值的一半,用于数据采样
reg[12:0] cnt; //分频计数
reg clk_bps_r; //波特率时钟寄存器
//----------------------------------------------------------
reg[2:0] uart_ctrl; // uart波特率选择寄存器
//----------------------------------------------------------
always @ (posedge clk or negedge rst_n)
if(!rst_n) cnt <= 13'd0;
else if((cnt == `BPS_PARA) || !bps_start) cnt <= 13'd0; //波特率计数清零
else cnt <= cnt+1'b1; //波特率时钟计数启动
always @ (posedge clk or negedge rst_n)
if(!rst_n) clk_bps_r <= 1'b0;
else if(cnt == `BPS_PARA_2) clk_bps_r <= 1'b1; // clk_bps_r高电平为接收数据位的中间采样点,同时也作为发送数据的数据改变点
else clk_bps_r <= 1'b0;
assign clk_bps = clk_bps_r;
endmodule
//////////////////////////////////////////////////////////////////////////
串口发送模块:
////////////////////////////////////////////////////////////////////////////
module my_uart_tx(
clk,rst_n,
rx_data,rx_int,rs232_tx,key,
clk_bps,bps_start
);
input clk; // 50MHz主时钟
input rst_n; //低电平复位信号
input clk_bps; // clk_bps_r高电平为接收数据位的中间采样点,同时也作为发送数据的数据改变点
input[7:0] rx_data; //接收数据寄存器
input rx_int; //接收数据中断信号,接收到数据期间始终为高电平,在该模块中利用它的下降沿来启动串口发送数据
input key;
output rs232_tx; // RS232发送数据信号
output bps_start; //接收或者要发送数据,波特率时钟启动信号置位
//---------------------------------------------------------
reg rx_int0,rx_int1,rx_int2; //rx_int信号寄存器,捕捉下降沿滤波用
wire neg_rx_int; // rx_int下降沿标志位
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
rx_int0 <= 1'b0;
rx_int1 <= 1'b0;
rx_int2 <= 1'b0;
end
else begin
rx_int0 <= rx_int;
rx_int1 <= rx_int0;
rx_int2 <= rx_int1;
end
end
assign neg_rx_int = ~rx_int1 & rx_int2; //捕捉到下降沿后,neg_rx_int拉高保持一个主时钟周期
//---------------------------------------------------------
reg[7:0] tx_data; //待发送数据的寄存器
//---------------------------------------------------------
reg bps_start_r;
reg tx_en; //发送数据使能信号,高有效
reg[3:0] num;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
bps_start_r <= 1'b0;
tx_en <= 1'b0;
tx_data <= 8'd0;
end
else if(neg_rx_int) begin //接收数据完毕,准备把接收到的数据发回去
bps_start_r <= 1'b1;
tx_data <= rx_data; //把接收到的数据存入发送数据寄存器
tx_en <= 1'b1; //进入发送数据状态中
end
else if(num==4'd10) begin //数据发送完成,复位
bps_start_r <= 1'b0;
tx_en <= 1'b0;
end
end
assign bps_start = bps_start_r;//接收或者要发送数据,波特率时钟启动信号置位
//---------------------------------------------------------
reg rs232_tx_r;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
num <= 4'd0;
rs232_tx_r <= 1'b1;
end
else if(tx_en) begin
if(clk_bps) begin
num <= num+1'b1;//对发送进行计数
case (num)
4'd0: rs232_tx_r <= 1'b0; //发送起始位
4'd1: rs232_tx_r <= tx_data[0]; //发送bit0
4'd2: rs232_tx_r <= tx_data[1]; //发送bit1
4'd3: rs232_tx_r <= tx_data[2]; //发送bit2
4'd4: rs232_tx_r <= tx_data[3]; //发送bit3
4'd5: rs232_tx_r <= tx_data[4]; //发送bit4
4'd6: rs232_tx_r <= tx_data[5]; //发送bit5
4'd7: rs232_tx_r <= tx_data[6]; //发送bit6
4'd8: rs232_tx_r <= tx_data[7]; //发送bit7
4'd9: rs232_tx_r <= 1'b1; //发送结束位
default: rs232_tx_r <= 1'b1;
endcase
end
else if(num==4'd10) num <= 4'd0; //复位
end
end
我也刚开始学,也看特权同学的书,谈下我的理解,我存在错误理解也不一定。在这个串口例子中用的是脉冲边沿检测,仅仅是cnt == `BPS_PARA_2(中间部分)时一个脉冲的高电平用于读取状态和计数器加1,如果你纯粹tx_en=1就相当于检测到脉冲后的9个时钟节拍就已经把数据发完了,那当然不行了。主要注意的是原程序是一个时钟的高电平,而你改后可能就是持续高电平,你仿真一下就会发现计数器出问题了。
看看!
大厦 是是是是是大厦是是发
clk_bps是波特率时钟,收发数据实质上都是按照这个速率执行的,每一个clk_bps时钟周期收一个或者发一个bit数据,tx_en拉高意思是数据已经放到发寄存器了,可以准备发送,而这个待发送数据有8bit,加上起始位和终止位总共10bit,每一次tx_en变高时,每当clk_bps变高一次发一个bit,直到发完10bit也就是clk_bps走过10个周期才算发送完一个数据,当然不能只看tx_en啊,所以一个tx_en对应的是10个clk_bps周期