串口作为电子世界中最简单的通信协议,再单片机,fpga,各种嵌入式系统中发挥了举足轻重的作用,串口通信的协议非常简单,最经典的就是RS232标准,但是当前大多数电脑主机因为RS232接口体积很大,逐渐的放弃了这种接口,为了能实现串口通信,常常在开发板上用芯片进行转接,构成USB转TTL电路,作为一个串口使用。
串口通信的协议一般是具有一个开始位,一个停止位,8个数据位,Verilog的实现就是根据串口通信的标准进行设置。
模块端口如下
module uart_byte_tx(
Clk,
Rst_n,
data_byte,
send_en,
Baud_Set,
uart_tx,
Tx_Done,
uart_state
);
input Clk ; //模块全局时钟输入,50M
input Rst_n; //复位信号输入,低有效
input [7:0]data_byte; //待传输8bit数据
input send_en; //发送使能
input [2:0]Baud_Set; //波特率设置
output reg uart_tx; //串口输出信号
output reg Tx_Done; //1byte数据发送完成标志
output reg uart_state; //发送数据状态
上面就是串口发送模块的端口,下面简单介绍下实现过程
首先是串口发送状态信号,标志着串口发送模块正在工作,此时不应该让模块进行其他工作
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
uart_state <= 1'b0;
else if(send_en)
uart_state <= 1'b1;
else if(bps_cnt == 4'd11)
uart_state <= 1'b0;
else
uart_state <= uart_state;
再就是串口发送数据缓存,防止再发送过程中数据被改变,导致串口发出的数据出错
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
data_byte_reg <= 8'd0;
else if(send_en)
data_byte_reg <= data_byte;
else
data_byte_reg <= data_byte_reg;
因为串口是异步通信,通信双方要提前约定通信速率(波特率)
这里采用多路选择器对通信速率进行选择
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_DR <= 16'd5207;
else begin
case(Baud_Set)
0:bps_DR <= 16'd5207;
1:bps_DR <= 16'd2603;
2:bps_DR <= 16'd1301;
3:bps_DR <= 16'd867;
4:bps_DR <= 16'd433;
default:bps_DR <= 16'd5207;
endcase
end
例如,工作时钟是50M,波特率是9600,则上述选择应该是0(50M/9600≈5207)
定义一个计数器,进行分频,生成串口的通信速率
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state)begin
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
串口通信时钟生成,根据上面的分频,生成一个对应的串口时钟,供发送时使用
// bps_clk gen
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
定义一个bit计数器,表明发送状态中每一时刻应该处在什么状态(发送启停位还是数据)
//bps counter
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_cnt <= 4'd0;
else if(bps_cnt == 4'd11)
bps_cnt <= 4'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
生成一个发送完成信号,告诉其他模块已经发送完成,可以进行下次发送
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Tx_Done <= 1'b0;
else if(bps_cnt == 4'd11)
Tx_Done <= 1'b1;
else
Tx_Done <= 1'b0;
根据bit计数器,生成最终的串口接口数据
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
uart_tx <= 1'b1;
else begin
case(bps_cnt)
0:uart_tx <= 1'b1;
1:uart_tx <= START_BIT;
2:uart_tx <= data_byte_reg[0];
3:uart_tx <= data_byte_reg[1];
4:uart_tx <= data_byte_reg[2];
5:uart_tx <= data_byte_reg[3];
6:uart_tx <= data_byte_reg[4];
7:uart_tx <= data_byte_reg[5];
8:uart_tx <= data_byte_reg[6];
9:uart_tx <= data_byte_reg[7];
10:uart_tx <= STOP_BIT;
default:uart_tx <= 1'b1;
endcase
end
当bps_cnt==0时,在空闲状态,串口线为高电平
当bps_cnt==1时,发送起始位,串口线为高电平
当bps_cnt==2~9时,发送需要发送的数据,低位先发送
当bps_cnt==10时,发送停止位,串口线为高电平