uart通信方式因其简单易于实现和成本低的特点,是工业现场经常用到的一种通信方式,大部分的数字仪表,模块和设备都能支持。常见的Rs232,Rs485,Rs422皆与uart格式相同;不同的是Uart一般是TTL电平的,RS232指的是RS232电平的串口,Rs485指的是Rs485电平(或电气特性)的。

本文实现的是通信参数固定的一种简单情况,参数可变模块后面再整理下。话不多说,直接上代码。
// 9600, N, 8, 1
module uart_st #
(
parameter BAUD_RATE = 115200
)
(
input clk50m,
input rst,
input [7:0] data,
input enable,
output reg tx,
output reg busy // 指示外部调用模块,在忙的时候,不接收新的数据
);
localparam BAUD_CNT = (BAUD_RATE == 9600) ? 5208 - 1 : (BAUD_RATE == 115200) ? 434 - 1 : 434 - 1 ;
// 缓存外部数据data
reg [7:0] data_r;
always @ (posedge clk50m or posedge rst)
if(rst)
data_r <= 0;
else if (enable && busy == 0)
data_r <= data;
else
data_r <= data_r;
// 用状态机来完成发送
reg [12:0] cnt;
reg [3:0] num;
// 定义状态空间
localparam FSM_IDLE = 0;
localparam FSM_START = 1;
localparam FSM_DATA = 2;
localparam FSM_PARITY = 3;
localparam FSM_STOP = 4;
// 定义状态变量
reg [2:0] cstate;
// 完成状态机,每一个状态里,状态转移和判断,根据状态输出
always @ (posedge clk50m or posedge rst)
if(rst)
begin
cstate <= FSM_IDLE;
busy <= 0;
tx <= 1;
cnt <= 0;
num <= 0;
end
else case (cstate)
FSM_IDLE:// 空闲状态
begin
tx <= 1; // 空闲发送 1
if(enable) // 需要发送数据
begin
cstate <= FSM_START;
busy <= 1;
cnt <= 0;
end
else cstate <= FSM_IDLE;
end
FSM_START:
begin
tx <= 0; // 发送起始位
if(cnt == BAUD_CNT)
begin
cnt <= 0;
cstate <= FSM_DATA;
end
else
begin
cnt <= cnt + 1;
cstate <= FSM_START;
end
end
FSM_DATA:
begin
tx <= data_r[num]; // 并串转换
if(cnt == BAUD_CNT && num == 8 - 1 )
begin
cstate <= FSM_STOP;
cnt <= 0;
num <= 0;
end
else if(cnt == BAUD_CNT && num < 8 - 1 )
begin
num <= num + 1;
cnt <= 0;
end
else
begin
cnt <= cnt + 1;
cstate <= FSM_DATA;
num <= num;
end
end
FSM_PARITY:
begin
cstate <= FSM_STOP;
end
FSM_STOP:
begin
tx <= 1;
if(cnt == BAUD_CNT )
begin
cnt <= 0;
cstate <= FSM_IDLE;
busy <= 0;
end
else
begin
cnt <= cnt + 1;
cstate <= FSM_STOP;
end
end
default:
begin
cstate <= FSM_IDLE;
busy <= 0;
tx <= 1;
cnt <= 0;
num <= 0;
end
endcase
endmodule
仿真程序:
module tb();
reg clk = 0;
always #10 clk = ~clk;
wire busy;
reg [7:0] data;
reg tx_en = 0;
reg rst = 0;
initial
begin
data = 0;
tx_en = 0;
rst = 1;
#20;
rst = 0;
#10;
@(posedge clk);
repeat(10)
begin
while(busy) @(posedge clk);
data = data + 1;
tx_en = 1;
@(posedge clk);
tx_en = 0;
@(posedge clk);
end
#200;
$stop;
end
wire tx;
uart_st uart_st_inst
(
.clk50m (clk),
.rst (rst),
.data (data),
.enable (tx_en),
.tx (tx),
.busy (busy) // 指示外部调用模块,在忙的时候,不接收新的数据
);
endmodule
仿真波形:

