UART的实现代码阅读和学习
说明本文代码来自 《深入浅出玩转FPGA》书籍的光盘资料 仅作交流学习之用 请勿用于其他用途或直接与原书作者联系 如果代码原作者对本博文有意见请及时联系我
网上其实有不少verilog实现uart的例子
但是觉得特权的这个例程很有借鉴意义 主要归纳为以下几点
1,代码和注释的风格比较规范
2,模块化设计 使程序体力更清晰 便于理解
3,变量命名合理 值得借鉴
4,数据的收发方案比前面博客提到的数据发送方法更优秀 上次我们采用的是通过计数器卡时间节点的方法 判断语句众多 执行效率低下
5,注释详细
所以例程中有很多值得我们学习的地方
代码已经通过测试没有问题
该实例主要实现了通过uart接收数据并把收到的数据原样返回
通信参数:9600 N 8 1
首先,整个收发器采用模块化设计 各个构成很直观
我们先看下模块的组成
模块分为 波特率产生模块 串口发送模块 串口接收模块 顶层模块
顶层模块:负责声明输入输出和变量 以及 实例化波特率产生模块 串口发送模块 串口接收模块
在这里学习变量命名技巧以及模块实例化的方法(部分代码)
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代码文件 所有权归原作者所有
文章评论(0条评论)
登录后参与讨论