写在前面
从本文开始,将连载fpga开发基础知识,将这几年浅显的fpga开发经验整理出来,一是梳理一下这几年给别人做fpga的经历,同时也是分享给大家,也希望大牛批评指正。
一、UART串口通信基本概念
串口通信是非常基本且应用十分广泛的低速通信接口,无论是在dsp、单片机、arm还是在fpga中,编写uart串口通信程序是必备的基础。
首先要先了解UART串口通信的基本概念,UART串口通信是全双工的,支持发送和接收通信同时进行。硬件上UART串口只需要两条线tx和rx,分别进行发送和接收。UART串口通信没有同步时钟线,这就需要引入一个概念波特率来区分两位数据实现串行通信,波特率是指每秒传输的位数。常见的有9600bps、115200bps、460800bps等等。在fpga实现中,只需要理解每一位的时间长度是1/波特率,计数长度是时钟频率/波特率就可以了。
UART串口通信基本协议,一次UART串口发送和接收一般包含10bit或11bit,包含校验位是11bit,不包含则是10bit。有效数据位一般是8bit。依次为起始位、数据位、校验位(可选)、停止位。
起始位:首先发送的第一位,必须为0,提示接收方开始接收有效数据。
数据位:一般为8位,有效数据位。
校验位:可选位,一般为奇校验、偶校验、固定校验。校验位由数据位中1的个数来决定
停止位:最后发送的一位,必须为1,提示接收方结束接收。
包含以上所有位为一帧数据,经过一个间隔后发送或接收第二帧。由以上的内容我们可以知道如何发送和接收一帧数据了。接下来就是用fpga实现。
二、UART串口通信FPGA实现
首先编写发送模块,先将发送数据缓存,状态机由空闲状态转为发送状态,空闲状态指示信号拉低,通知上层模块发送正在进行不要更新待发送字节。根据波特率进行计数,依次发送一帧数据的所有位,发送完成后状态机转入空闲拉高空闲状态,一次发送就完成了。
//
// //
// //
// This source file is part of uart module. //
// module features: 1.config bits number of one byte //
// 2.config bitrate //
// module function: transmit uart bits //
// copyright of this source file belongs to mayidianzi in CSDN blog //
// contact the author by e-mail 1061165280@qq.com //
// //
//
//================================================================================
// Revision History:
// Date By Revision Change Description
//--------------------------------------------------------------------------------
// 2023/2/26 mayidianzi 1.0 Original
//*******************************************************************************/
module uart_tx
#(
parameter CLK_FRE = 50, //module clock frequency(MHz)
parameter BAUD_RATE = 115200, //serial baud rate
parameter TRANSMIT_BITS = 9 //bits number per byte
)
(
input clk, //clock input
input rst_n, //asynchronous reset input, low active
input[TRANSMIT_BITS-1:0] tx_data, //data to send
input tx_data_valid, //data to be sent is valid
output reg tx_data_ready, //send ready
output tx_pin //serial data output
);
/ parameter config \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//calculates the clock cycle for baud rate
localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE;
//state machine code
localparam C_IDLE = 1;//wait for transmit
localparam C_START = 2;//start bit
localparam C_SEND_BYTE = 3;//data bits
localparam C_STOP = 4;//stop bit
localparam C_WAIT = 5;//blank between two bytes transmit
// register define \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
reg[2:0] state;
reg[15:0] cycle_cnt; //baud counter
reg[3:0] bit_cnt;//bit counter
reg[TRANSMIT_BITS-1:0] tx_data_latch; //latch data to send
reg tx_reg; //serial data output
assign tx_pin = tx_reg;
// main code \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//state machine
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
state <= C_IDLE;
cycle_cnt <= 0;
bit_cnt <= 0;
end
else
case(state)
C_IDLE:
if(tx_data_valid == 1'b1)
state <= C_START;
else
state <= C_IDLE;
C_START:
if(cycle_cnt == CYCLE - 1)
begin
cycle_cnt <= 0;
state <= C_SEND_BYTE;
end
else
cycle_cnt <= cycle_cnt + 1;
C_SEND_BYTE:
if(cycle_cnt == CYCLE - 1 && bit_cnt == TRANSMIT_BITS-1)
begin
state <= C_STOP;
cycle_cnt <= 0;
bit_cnt <= 0;
end
else if(cycle_cnt == CYCLE - 1)
begin
cycle_cnt <= 0;
bit_cnt <= bit_cnt + 1;
end
else
cycle_cnt <= cycle_cnt + 1;
C_STOP:
if(cycle_cnt == CYCLE - 1)
begin
state <= C_WAIT;
cycle_cnt <= 0;
end
else
cycle_cnt <= cycle_cnt + 1;
C_WAIT:
if(cycle_cnt == 10*CYCLE - 1)
begin
state <= C_IDLE;
cycle_cnt <= 0;
end
else
cycle_cnt <= cycle_cnt + 1;
default:
state <= C_IDLE;
endcase
end
//data output
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
tx_data_ready <= 1'b0;
else if(state == C_IDLE)
if(tx_data_valid == 1'b1)
tx_data_ready <= 1'b0;
else
tx_data_ready <= 1'b1;
else if(state == C_WAIT && cycle_cnt == 10*CYCLE - 1)
tx_data_ready <= 1'b1;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
tx_data_latch <= 0;
end
else if(state == C_IDLE && tx_data_valid == 1'b1)
tx_data_latch <= tx_data;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
tx_reg <= 1'b1;
else
case(state)
C_IDLE,C_STOP:
tx_reg <= 1'b1;
C_START:
tx_reg <= 1'b0;
C_SEND_BYTE:
tx_reg <= tx_data_latch[bit_cnt];
default:
tx_reg <= 1'b1;
endcase
end
endmodule
然后再来编写接收模块,接收模块状态机要检测起始位,发现信号拉低后进入接收状态,按照波特率进行计数依次接收所有位,之后转入空闲状态等待下一个起始位,同时拉高数据接收完毕信号提示上层锁存接收数据。
//
// //
// //
// This source file is part of uart module. //
// module features: 1.config bits number of one byte //
// 2.config bitrate //
// module function: receive uart bits //
// copyright of this source file belongs to mayidianzi in CSDN blog //
// contact the author by e-mail 1061165280@qq.com //
// //
//
//================================================================================
// Revision History:
// Date By Revision Change Description
//--------------------------------------------------------------------------------
// 2023/2/26 mayidianzi 1.0 Original
//*******************************************************************************/
module uart_rx
#(
parameter CLK_FRE = 50, //module clock frequency(MHz)
parameter BAUD_RATE = 115200, //serial baud rate
parameter RECEIVE_BITS = 9 //bits number per byte
)
(
input clk, //clock input
input rst_n, //asynchronous reset input, low active
output reg[RECEIVE_BITS-1:0] rx_data, //received serial data
output reg rx_data_valid, //received serial data is valid
input rx_pin //serial data input
);
/ parameter config \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//calculates the clock cycle for baud rate
localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE;
//state machine
localparam C_WAIT = 0; //blank between two bytes
localparam C_IDLE = 1; //wait for start bit
localparam C_START = 2; //start bit
localparam C_REC_BYTE = 3; //data bits
localparam C_STOP = 4; //stop bit
localparam C_DATA = 5; //data pack
// register define \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
reg[2:0] state;
reg[RECEIVE_BITS-1:0] rx_bits; //temporary storage of received data
reg[15:0] cycle_cnt; //baud counter
reg[3:0] bit_cnt; //bit counter
// main code \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//state machine
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
state <= C_WAIT;
cycle_cnt <= 0;
bit_cnt <= 0;
end
else
case(state)
C_WAIT: //wait for blank between two bytes
if(cycle_cnt > CYCLE & rx_pin == 1)
begin
cycle_cnt <= 0;
state <= C_IDLE;
bit_cnt <= 0;
end
else if(rx_pin == 1)
cycle_cnt <= cycle_cnt + 1;
else
cycle_cnt <= 0;
C_IDLE: //wait for first start bit
if(rx_pin == 0)
state <= C_START;
else
state <= C_IDLE;
C_START: //start bit
if(cycle_cnt == CYCLE - 1) //one data cycle
begin
state <= C_REC_BYTE;
cycle_cnt <= 0;
end
else
cycle_cnt <= cycle_cnt + 1;
C_REC_BYTE:
if(cycle_cnt == CYCLE - 1 && bit_cnt == RECEIVE_BITS-1) //receive bits data
begin
state <= C_STOP;
bit_cnt <= 0;
cycle_cnt <= 0;
end
else if(cycle_cnt == CYCLE - 1)
begin
cycle_cnt <= 0;
bit_cnt <= bit_cnt + 1;
end
else
cycle_cnt <= cycle_cnt + 1;
C_STOP:
if(cycle_cnt == CYCLE - 1)
begin
state <= C_DATA;
cycle_cnt <= 0;
end
else
cycle_cnt <= cycle_cnt + 1;
C_DATA: //data receive complete
state <= C_IDLE;
default:
state <= C_WAIT;
endcase
end
//data ready signal
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_data_valid <= 1'b0;
else if(state == C_STOP)
rx_data_valid <= 1'b1;
else if(state == C_DATA)
rx_data_valid <= 1'b0;
end
//data output
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_data <= 8'd0;
else if(state == C_STOP)
rx_data <= rx_bits;//latch received data
end
//receive serial data bit data
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_bits <= 8'd0;
else if(state == C_REC_BYTE && cycle_cnt == CYCLE/2 - 1)
rx_bits[bit_cnt] <= rx_pin;
else
rx_bits <= rx_bits;
end
endmodule
三、后记
以上所有内容均记载在我的串口通用模块内,可以直接下载测试工程了解,或根据自己的工程需要进行改编。如果不想在串口通信方面耽误时间,集中力量开发高级功能,可直接调用我编写的通用串口模块,可直接进行例化实现串口通信功能。通用串口模块可分别自定义收发波特率、收发校验种类,具备串口通信常用的全部功能,可根据实际需求设置,减少您的开发周期,本通用串口模块经过仿真模拟和硬件测试,附有测试报告和开发说明,方便使用,传送门:文章来源:https://www.toymoban.com/news/detail-787912.html
通用串口模块文章来源地址https://www.toymoban.com/news/detail-787912.html
到了这里,关于FPGA开发基础篇之一(接口篇)UART串口的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!