2020年2月16日 星期日

RS232串口通信实验Verilog

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这两个参数。
公式里的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

沒有留言:

張貼留言

Messaging API作為替代方案

  LINE超好用功能要沒了!LINE Notify明年3月底終止服務,有什麼替代方案? LINE Notify將於2025年3月31日結束服務,官方建議改用Messaging API作為替代方案。 //CHANNEL_ACCESS_TOKEN = 'Messaging ...