FPGA开发--串口发送模块
文章摘要:
串口应该是计算机最古老的通信接口之一,早期的应用无处不在,虽然目前有一点被USB、以太网以及无线接口等这些后浪拍死在沙滩上的苗头,但由于其协议的简单、简单、简单(重要的事说三遍)及可靠,所以在某些领域仍具有相当广泛的应用。
当然这都不重要,本文的重点是通过描述串口发送模块的方法,来简单说明一下基于FPGA的通信协议的实现,并对后续的其他接口通信达到抛砖引玉、触类旁通的效果;
知识重点: 状态机,边沿检测,串并转换;
硬件平台: EP4CE6F17C8
开发环境: Quartus II 13.1
时序图:
对于一个合格的老司机来说,串口时序图应该早已烂熟于心了,不过,子又曾经曰过:“有图有真相”,所以为了对照代码,看图说话,还是放上来充数罢;
/*
* 模块说明:串口发送模块(9600-N-8-1)
*/
module uart_send(
input clk, // 时钟
input rst_n, // 复位,低电平有效
output reg txd, // 发送引脚
input [7:0]data, // 发送数据
input en, // 发送使能,高电平有效
output busy // 发送忙标志(高电平表示忙)
);
// 发送时序状态机
parameter S_IDLE = 4'd0;
parameter S_START = 4'd1;
parameter S_BIT0 = 4'd2;
parameter S_BIT1 = 4'd3;
parameter S_BIT2 = 4'd4;
parameter S_BIT3 = 4'd5;
parameter S_BIT4 = 4'd6;
parameter S_BIT5 = 4'd7;
parameter S_BIT6 = 4'd8;
parameter S_BIT7 = 4'd9;
parameter S_STOP = 4'd10;
reg [3:0] status; // 当前状态
// 波特率计算 9600bps即每bit时间为1/9600 S
// 50MHz时钟下,计数值为50_000_000/9600 = 5208
parameter BAUD_CNT = 32'd5208;
reg [31:0]timer; // 波特率定时器
reg [7:0]rdata; // 数据寄存器,防止发送过程中修改了数据端口
// 发送忙标志位
assign busy = (status == S_IDLE)?1'b0:1'b1;
always @(posedge clk or negedge rst_n)
begin
if(~rst_n)
begin
status <= S_IDLE; // 复位时设置为空闲状态
txd <= 1'b1; // 复位时发送引脚为高电平
timer <= 0;
end
else
begin
timer <= timer + 1'b1; // 计时
// 发送状态机
case(status)
S_IDLE:
begin
if(en) // 只有空闲状态时,收到使能信号,才启动发送
begin
timer <= 0;
rdata <= data; // 保存待发数据
status <= S_START; // 启动发送
end
else
status <= S_IDLE; // 这里主要是设计风格
end
S_START:
begin
if(timer == BAUD_CNT) // 1bit起始位时间
begin
timer <= 0;
status = S_BIT0;
end
else
txd <= 1'b0; // 发送起始位
end
S_BIT0:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
status = S_BIT1;
end
else
txd <= rdata[0];
end
S_BIT1:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
status = S_BIT2;
end
else
txd <= rdata[1];
end
S_BIT2:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
status = S_BIT3;
end
else
txd <= rdata[2];
end
S_BIT3:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
status = S_BIT4;
end
else
txd <= rdata[3];
end
S_BIT4:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
status = S_BIT5;
end
else
txd <= rdata[4];
end
S_BIT5:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
status = S_BIT6;
end
else
txd <= rdata[5];
end
S_BIT6:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
status = S_BIT7;
end
else
txd <= rdata[6];
end
S_BIT7:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
status = S_STOP;
end
else
txd <= rdata[7];
end
S_STOP:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
status = S_IDLE;
end
else
txd <= 1'b1; // 发送停止位
end
default:
status <= S_IDLE;
endcase
end
end
endmodule