原创 fpga学习日记22,代码阅读之实现UART

2013-9-2 19:01 1251 16 16 分类: FPGA/CPLD 文集: 数电,FPGA学习

UART的实现代码阅读和学习

说明本文代码来自 《深入浅出玩转FPGA》书籍的光盘资料  仅作交流学习之用 请勿用于其他用途或直接与原书作者联系  如果代码原作者对本博文有意见请及时联系我

网上其实有不少verilog实现uart的例子

但是觉得特权的这个例程很有借鉴意义  主要归纳为以下几点

1,代码和注释的风格比较规范

2,模块化设计  使程序体力更清晰 便于理解

3,变量命名合理 值得借鉴

4,数据的收发方案比前面博客提到的数据发送方法更优秀 上次我们采用的是通过计数器卡时间节点的方法 判断语句众多 执行效率低下 

5,注释详细

所以例程中有很多值得我们学习的地方

代码已经通过测试没有问题

 

该实例主要实现了通过uart接收数据并把收到的数据原样返回

通信参数:9600    N   8  1

 

首先,整个收发器采用模块化设计  各个构成很直观

我们先看下模块的组成

模块分为 波特率产生模块 串口发送模块  串口接收模块 顶层模块

111.jpg

顶层模块:负责声明输入输出和变量 以及 实例化波特率产生模块 串口发送模块  串口接收模块    

在这里学习变量命名技巧以及模块实例化的方法(部分代码)

module my_uart_top(
               clk,rst_n,
               rs232_rx,rs232_tx
               );

input clk;            // 50MHz主时钟
input rst_n;        //低电平复位信号

input rs232_rx;        // RS232接收数据信号
output rs232_tx;    //    RS232发送数据信号

wire bps_start1,bps_start2;    //接收到数据后,波特率时钟启动信号置位
wire clk_bps1,clk_bps2;        // clk_bps_r高电平为接收数据位的中间采样点,同时也作为发送数据的数据改变点
wire[7:0] rx_data;    //接收数据寄存器,保存直至下一个数据来到
wire rx_int;        //接收数据中断信号,接收到数据期间始终为高电平
//----------------------------------------------------
//下面的四个模块中,speed_rx和speed_tx是两个完全独立的硬件模块,可称之为逻辑复制
//(不是资源共享,和软件中的同一个子程序调用不能混为一谈)
////////////////////////////////////////////
speed_select        speed_rx(    
                           .clk(clk),    //波特率选择模块
                           .rst_n(rst_n),
                           .bps_start(bps_start1),
                           .clk_bps(clk_bps1)
                       );

my_uart_rx            my_uart_rx(        
                           .clk(clk),    //接收数据模块
                           .rst_n(rst_n),
                           .rs232_rx(rs232_rx),
                           .rx_data(rx_data),
                           .rx_int(rx_int),
                           .clk_bps(clk_bps1),
                           .bps_start(bps_start1)
                       );

endmodule

波特率产生模块:用于产生接受和发送的时钟以及捕捉数据的时钟信号

这段主要学习新时钟的生成方法(部分代码)

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

串口接收模块:用于侦听接收引脚的信号并存入接收寄存器

主要学习接收信号的判断方式

串口发送模块:负责将发送寄存器中的数据发送出去

这里学习下8位数据的发送方法

verilog语言:
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'd11) num <= 4'd0;    //复位
       end
end
 

最后附上四个verilog代码文件 所有权归原作者所有

 

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
16
关闭 站长推荐上一条 /1 下一条