RS232串口通信实验Verilog
独孤求真 posted @ 2010年9月21日 20:24 in CPLD,FPGA学习 with tags RS232;串口;Verilog; , 15090 阅读
RS232串口通信实验几乎是入门必做实验,这是本人学习过程中做的练习。首先是分频模块,分频模块是学习了OpenCore上的uart2bus项目的。http://opencores.org/project,uart2bus
该模块接收任意频率的输入频率(clk_i),输出频率(记为clk_o)由baud_freq_i和baud_limit_i根据以下公式计算,使用时首先需要根据输入频率和输出频率计算出baud_freq_i和baud_limit_i这两个参数。
该模块接收任意频率的输入频率(clk_i),输出频率(记为clk_o)由baud_freq_i和baud_limit_i根据以下公式计算,使用时首先需要根据输入频率和输出频率计算出baud_freq_i和baud_limit_i这两个参数。
公式里的GCD(Greatest Common Divisor)表示取2个数的最大公约数。
这里系统时钟频率为50MHz,串口频率为9600,通常我们是在中间进行采样。因此分频模块分出来的频率为16*9600。模块如下:
/*
* this module has been changed to receive the baud rate
* dividing counter from registers.
* the two registers should be calculated as follows:
* first register:
* baud_freq = 16*baud_rate / gcd(global_clock_freq, 16*baud_rate)
* second register:
* baud_limit = (global_clock_freq /
* gcd(global_clock_freq, 16*baud_rate)) - baud_freq
*/
module
baud_gen(clk_i, rst_i, ce16_o, baud_freq_i, baud_limit_i);
input
clk_i;
input
rst_i;
output
ce16_o;
input
[11:0] baud_freq_i;
input
[15:0] baud_limit_i;
reg
ce16_o;
reg
[15:0] count;
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
count <=
16'h0
;
else
if
(count >= baud_limit_i)
count <= count - baud_limit_i;
else
count <= count + baud_freq_i;
end
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
ce16_o <=
1'b0
;
else
if
(count >= baud_limit_i)
ce16_o <=
1'b1
;
else
ce16_o <=
1'b0
;
end
endmodule
分频模块的仿真波形如下,上面是50M的输入时钟,下面是分频后的,需要注意的是,分频后的时钟的占空比不是1:1的,每一个9600Hz的周期中产生16个高电平,高电平维持一个20ns(50MHz的一个周期)
计算baud_freq和baud_limitC程序如下,程序中的输入频率clk_i为50MHZ,输出频率为9600*16。
#include <stdio.h>
int
GCD(
int
a,
int
b)
{
if
(b == 0)
return
a;
else
return
GCD(b, a%b);
}
int
main()
{
int
clk_i = 50*1000*1000;
int
clk_o = 9600*16;
int
gcd = GCD(clk_i, clk_o);
int
baud_freq = clk_o/gcd;
int
baud_limit = (clk_i/gcd) - baud_freq;
printf
(
"baud_freq: 0x%x, baud_limit: 0x%x\n"
, baud_freq, baud_limit);
return
0;
}
module
uart_tx(clk_i,
rst_i,
ce16_i,
ser_o,
tx_data_i,
new_tx_i,
tx_int_o,
tx_busy_o);
input
clk_i;
input
rst_i;
input
ce16_i;
output
ser_o;
input
[7:0] tx_data_i;
input
new_tx_i;
output
tx_int_o;
output
tx_busy_o;
reg
ser_o;
reg
tx_int_o;
reg
tx_busy_o;
reg
[3:0] count;
reg
[3:0] bit_count;
reg
[8:0] tx_data;
wire
ce1_end;
assign
ce1_end = (count ==
4'b1111
) & ce16_i;
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
count <=
4'h0
;
else
if
(tx_busy_o & ce16_i)
count <= count +
1'b1
;
else
if
(!tx_busy_o & ce16_i)
count <=
1'b0
;
end
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
tx_busy_o <=
1'b0
;
else
if
(!tx_busy_o & new_tx_i)
tx_busy_o <=
1'b1
;
else
if
(tx_busy_o & ce1_end & (bit_count ==
4'd9
))
tx_busy_o <=
1'b0
;
end
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
bit_count <=
4'h0
;
else
if
(tx_busy_o & ce1_end)
bit_count <= bit_count +
1'b1
;
else
if
(!tx_busy_o)
bit_count <=
4'h0
;
end
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
tx_data <=
9'h0
;
else
if
(!tx_busy_o)
tx_data <= {tx_data_i,
1'b0
};
else
if
(tx_busy_o & ce1_end)
tx_data <= {
1'b1
, tx_data[8:1]};
end
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
ser_o <=
1'b1
;
else
if
(tx_busy_o)
ser_o <= tx_data[0];
else
ser_o <=
1'b1
;
end
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
tx_int_o <=
1'b0
;
else
if
(tx_busy_o & ce1_end & (bit_count ==
4'd9
))
tx_int_o <=
1'b1
;
else
tx_int_o <=
1'b0
;
end
endmodule
module
uart_rx(clk_i,
rst_i,
ce16_i,
ser_i,
rx_data_o,
rx_int_o);
input
clk_i;
input
rst_i;
input
ce16_i;
input
ser_i;
output
[7:0] rx_data_o;
output
rx_int_o;
reg
[7:0] rx_data_o;
reg
rx_int_o;
reg
[7:0] rx_data;
reg
[1:0] ser_in;
reg
[3:0] count;
reg
[3:0] bit_count;
reg
rx_busy;
wire
ce1_mid;
wire
ce1_end;
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
ser_in <=
2'b11
;
else
ser_in <= {ser_in[0], ser_i};
end
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
count <=
4'h0
;
else
if
((rx_busy | (ser_in[1] ==
1'b0
)) & ce16_i)
count <= count +
1'b1
;
else
if
(!rx_busy & ce16_i)
count <=
4'h0
;
end
assign
ce1_mid = ((count ==
4'b0111
) & ce16_i);
assign
ce1_end = ((count ==
4'b1111
) & ce16_i);
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
rx_busy <=
1'b0
;
else
if
(!rx_busy & ce1_mid)
rx_busy <=
1'b1
;
else
if
(rx_busy & ce1_end & (bit_count ==
4'd9
))
rx_busy <=
1'b0
;
end
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
bit_count <=
4'h0
;
else
if
(rx_busy & ce1_mid)
bit_count <= bit_count +
1'b1
;
else
if
(!rx_busy)
bit_count <=
4'h0
;
end
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
rx_data <=
8'h0
;
else
if
(ce1_mid)
rx_data <= {ser_in[1], rx_data[7:1]};
end
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
begin
rx_data_o <=
8'h0
;
rx_int_o <=
1'b0
;
end
else
if
(ce1_end & (bit_count ==
4'd8
))
begin
rx_data_o <= rx_data;
rx_int_o <=
1'b1
;
end
else
rx_int_o <=
1'b0
;
end
endmodule
module
uart_loop_top(clk_i, rst_i, ser_i, ser_o);
input
clk_i;
input
rst_i;
input
ser_i;
output
ser_o;
// baud rate generator parameters for 9600 baud on 50MHz clock
`define D_BAUD_FREQ
12'h30
`define D_BAUD_LIMIT
16'h3CD9
wire
ce16;
wire
[11:0] baud_freq;
wire
[15:0] baud_limit;
wire
[7:0] rx_data;
reg
[7:0] rx_reg;
reg
start_tx;
wire
tx_int;
wire
tx_busy;
assign
baud_freq = `D_BAUD_FREQ;
assign
baud_limit = `D_BAUD_LIMIT;
baud_gen baud_gen1(.clk_i(clk_i),
.rst_i(rst_i),
.ce16_o(ce16),
.baud_freq_i(baud_freq),
.baud_limit_i(baud_limit));
uart_rx uart_rx1(.clk_i(clk_i),
.rst_i(rst_i),
.ce16_i(ce16),
.ser_i(ser_i),
.rx_data_o(rx_data),
.rx_int_o(rx_int));
uart_tx uart_tx1(.clk_i(clk_i),
.rst_i(rst_i),
.ce16_i(ce16),
.ser_o(ser_o),
.tx_data_i(rx_reg),
.new_tx_i(start_tx),
.tx_int_o(tx_int),
.tx_busy_o(tx_busy));
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
rx_reg <=
8'h0
;
else
if
(rx_int & ~tx_busy)
rx_reg <= rx_data;
end
always
@(
posedge
clk_i
or
negedge
rst_i)
begin
if
(!rst_i)
start_tx <=
1'b0
;
else
if
(rx_int & ~tx_busy)
start_tx <=
1'b1
;
else
start_tx <=
1'b0
;
end
endmodule
沒有留言:
張貼留言