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;endalways @(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;endendmodule
分频模块的仿真波形如下,上面是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
沒有留言:
張貼留言