UART实现-黑金fpga开发板案例解析
uart 异步串口通讯 无需时钟线,俩根线一跟复制发数据一根负责收数据。
具体的时序如下图1:
可以看到 当数据线由高位变为地位时,即遇到一个下降沿时刻,表示开始这一次数据开始发送/接受,然后的高低电平表示数据位,一般是7-8个数据位,一般为8个,即一个字节,并可以添加校验位与停止位。
波特率介绍,由于uart
通讯,使用单线单独进行数据传输,所有需要保证发送端与接收端能正常通讯,需要对信号进行约束,简单来说,就是对信号的采样频率进行设定。
当设置波特率为 115200 的时候,如果时钟信号时 50Mhz,这里,数据的一个位的高低电平持续时间应该时 50_000_000 / 115200 这么多个时钟,这样就可以约定发送与接收端,相隔一定的时间对信号线进行采样,得到的每位的高低电平表示这位的数据。
程序设计
包括收和发的设计,使用状态机的思想进行程序设计
接收模块设计
整体的设计思路保持在一个always
语句块里,只对一个reg变量进行赋值。
-
构造状态机的状态常量
localparam S_IDLE = 1; localparam S_START = 2; //start bit localparam S_REC_BYTE = 3; //data bits localparam S_STOP = 4; //stop bit localparam S_DATA = 5;
-
构造状态机的状态变量进行状态切换
reg[2:0] state; reg[2:0] next_state; always@(posedge clk or negedge rst_n) beginif(rst_n == 1'b0)state <= S_IDLE;elsestate <= next_state; end
这里,state是当前时钟的变量,next_state是下一时钟的变量
-
对输入信号获得俩个节拍
reg rx_d0; //delay 1 clock for rx_pin reg rx_d1; //delay 1 clock for rx_d0 always@(posedge clk or negedge rst_n) beginif(rst_n == 1'b0)beginrx_d0 <= 1'b0;rx_d1 <= 1'b0; endelsebeginrx_d0 <= rx_pin;rx_d1 <= rx_d0;end end
这样 可以通过比较d0和d1去判断输入信号的上升沿还是下降沿
assign rx_negedge = rx_d1 && ~rx_d0;
-
状态机的具体实现
S_IDLE
在这里检测下降沿 如果检测到下降沿 则切换到S_START
状态S_START
检测波特率计数器是否计数满 如果计数满了 则跳转到S_REC_BYTE
数据接受状态,如果没满,则保持这个状态,等待计数满,其实也就是一个下降沿之后要保持一个bit的低电平时间之后才开始发送数据S_REC_BYTE
接受数据状态,在这个状态逐个接受数据,并检测是否接受完毕,完毕则跳转下一个S_STOP
状态,没有则保持这个状态S_STOP
等待半个bit时间,跳转到下一个S_DATA
状态S_DATA
always@(*) begincase(state)S_IDLE:if(rx_negedge)next_state <= S_START;elsenext_state <= S_IDLE;S_START:if(cycle_cnt == CYCLE - 1)//one data cycle next_state <= S_REC_BYTE;elsenext_state <= S_START;S_REC_BYTE:if(cycle_cnt == CYCLE - 1 && bit_cnt == 3'd7) //receive 8bit datanext_state <= S_STOP;elsenext_state <= S_REC_BYTE;S_STOP:if(cycle_cnt == CYCLE/2 - 1)//half bit cycle,to avoid missing the next byte receivernext_state <= S_DATA;elsenext_state <= S_STOP;S_DATA:if(rx_data_ready) //data receive completenext_state <= S_IDLE;elsenext_state <= S_DATA;default:next_state <= S_IDLE;endcase end
-
接受数据状态时进行接受数据
当在结束收据状态,且bit计数器到一半的时候,进行一次采样,也就是在中点进行采样,获取数据的高低电平,共采集八次,将八次的数据保存到rx_bits
中
reg[7:0] rx_bits; //temporary storage of received data
always@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)rx_bits <= 8'd0;else if(state == S_REC_BYTE && cycle_cnt == CYCLE/2 - 1)rx_bits[bit_cnt] <= rx_pin;elserx_bits <= rx_bits;
end
-
bit_cnt
的计数循环reg[2:0] bit_cnt; //bit counter always@(posedge clk or negedge rst_n) beginif(rst_n == 1'b0)beginbit_cnt <= 3'd0;endelse if(state == S_REC_BYTE)if(cycle_cnt == CYCLE - 1)bit_cnt <= bit_cnt + 3'd1;elsebit_cnt <= bit_cnt;elsebit_cnt <= 3'd0; end
-
cycle_cnt
计数循环reg[15:0] cycle_cnt; //baud counter always@(posedge clk or negedge rst_n) beginif(rst_n == 1'b0)cycle_cnt <= 16'd0;else if((state == S_REC_BYTE && cycle_cnt == CYCLE - 1) || next_state != state)cycle_cnt <= 16'd0;elsecycle_cnt <= cycle_cnt + 16'd1; end
-
将接受的数据输出 当状态机的状态时为stop状态且下一个状态为
S_DATA
时,将rx_bits
赋值到rx_data
中 进行输出。always@(posedge clk or negedge rst_n) beginif(rst_n == 1'b0)rx_data <= 8'd0;else if(state == S_STOP && next_state != state)rx_data <= rx_bits;//latch received data end
-
赋值接受数据完成标志位
状态机的状态时为stop状态且下一个状态为
S_DATA
时,其实也就是这一次的接受完成了,将rx_data_valid
赋值为1.always@(posedge clk or negedge rst_n) beginif(rst_n == 1'b0)rx_data_valid <= 1'b0;else if(state == S_STOP && next_state != state)rx_data_valid <= 1'b1;else if(state == S_DATA && rx_data_ready)rx_data_valid <= 1'b0; end
整体的程序
//
// //
// //
// Author: meisq //
// msq@qq.com //
// ALINX(shanghai) Technology Co.,Ltd //
// heijin //
// WEB: http://www.alinx.cn/ //
// BBS: http://www.heijin.org/ //
// //
//
// //
// Copyright (c) 2017,ALINX(shanghai) Technology Co.,Ltd //
// All rights reserved //
// //
// This source file may be used and distributed without restriction provided //
// that this copyright statement is not removed from the file and that any //
// derivative work contains the original copyright notice and the associated //
// disclaimer. //
// //
////================================================================================
// Revision History:
// Date By Revision Change Description
//--------------------------------------------------------------------------------
//2017/8/1 1.0 Original
//*******************************************************************************/
module uart_rx
#(parameter CLK_FRE = 50, //clock frequency(Mhz)parameter BAUD_RATE = 115200 //serial baud rate
)
(input clk, //clock inputinput rst_n, //asynchronous reset input, low active output reg[7:0] rx_data, //received serial dataoutput reg rx_data_valid, //received serial data is validinput rx_data_ready, //data receiver module readyinput rx_pin //serial data input
);
//calculates the clock cycle for baud rate
localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE;
//state machine code
localparam S_IDLE = 1;
localparam S_START = 2; //start bit
localparam S_REC_BYTE = 3; //data bits
localparam S_STOP = 4; //stop bit
localparam S_DATA = 5;reg[2:0] state;
reg[2:0] next_state;
reg rx_d0; //delay 1 clock for rx_pin
reg rx_d1; //delay 1 clock for rx_d0
wire rx_negedge; //negedge of rx_pin
reg[7:0] rx_bits; //temporary storage of received data
reg[15:0] cycle_cnt; //baud counter
reg[2:0] bit_cnt; //bit counterassign rx_negedge = rx_d1 && ~rx_d0;always@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)beginrx_d0 <= 1'b0;rx_d1 <= 1'b0; endelsebeginrx_d0 <= rx_pin;rx_d1 <= rx_d0;end
endalways@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)state <= S_IDLE;elsestate <= next_state;
endalways@(*)
begincase(state)S_IDLE:if(rx_negedge)next_state <= S_START;elsenext_state <= S_IDLE;S_START:if(cycle_cnt == CYCLE - 1)//one data cycle next_state <= S_REC_BYTE;elsenext_state <= S_START;S_REC_BYTE:if(cycle_cnt == CYCLE - 1 && bit_cnt == 3'd7) //receive 8bit datanext_state <= S_STOP;elsenext_state <= S_REC_BYTE;S_STOP:if(cycle_cnt == CYCLE/2 - 1)//half bit cycle,to avoid missing the next byte receivernext_state <= S_DATA;elsenext_state <= S_STOP;S_DATA:if(rx_data_ready) //data receive completenext_state <= S_IDLE;elsenext_state <= S_DATA;default:next_state <= S_IDLE;endcase
endalways@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)rx_data_valid <= 1'b0;else if(state == S_STOP && next_state != state)rx_data_valid <= 1'b1;else if(state == S_DATA && rx_data_ready)rx_data_valid <= 1'b0;
endalways@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)rx_data <= 8'd0;else if(state == S_STOP && next_state != state)rx_data <= rx_bits;//latch received data
endalways@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)beginbit_cnt <= 3'd0;endelse if(state == S_REC_BYTE)if(cycle_cnt == CYCLE - 1)bit_cnt <= bit_cnt + 3'd1;elsebit_cnt <= bit_cnt;elsebit_cnt <= 3'd0;
endalways@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)cycle_cnt <= 16'd0;else if((state == S_REC_BYTE && cycle_cnt == CYCLE - 1) || next_state != state)cycle_cnt <= 16'd0;elsecycle_cnt <= cycle_cnt + 16'd1;
end//receive serial data bit data
always@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)rx_bits <= 8'd0;else if(state == S_REC_BYTE && cycle_cnt == CYCLE/2 - 1)rx_bits[bit_cnt] <= rx_pin;elserx_bits <= rx_bits;
end
endmodule
模块总览
如果只需要使用这个模块的话,只看输入输出就可以了、
clk
时钟信号rst_n
复位信号rx_pin
数据接受引脚rx_data_ready
接受使能引脚 置一的时候进行接受,为0的时候则不接受数据rx_data
接受到的八位数据
发送模块设计
程序设计思路
整体的程序设计思路是和接受端没区别的;
代码
//
// //
// //
// Author: meisq //
// msq@qq.com //
// ALINX(shanghai) Technology Co.,Ltd //
// heijin //
// WEB: http://www.alinx.cn/ //
// BBS: http://www.heijin.org/ //
// //
//
// //
// Copyright (c) 2017,ALINX(shanghai) Technology Co.,Ltd //
// All rights reserved //
// //
// This source file may be used and distributed without restriction provided //
// that this copyright statement is not removed from the file and that any //
// derivative work contains the original copyright notice and the associated //
// disclaimer. //
// //
////================================================================================
// Revision History:
// Date By Revision Change Description
//--------------------------------------------------------------------------------
//2017/8/1 1.0 Original
//*******************************************************************************/
module uart_tx
#(parameter CLK_FRE = 50, //clock frequency(Mhz)parameter BAUD_RATE = 115200 //serial baud rate
)
(input clk, //clock inputinput rst_n, //asynchronous reset input, low active input[7:0] tx_data, //data to sendinput tx_data_valid, //data to be sent is validoutput reg tx_data_ready, //send readyoutput tx_pin //serial data output
);
//calculates the clock cycle for baud rate
localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE;
//state machine code
localparam S_IDLE = 1;
localparam S_START = 2;//start bit
localparam S_SEND_BYTE = 3;//data bits
localparam S_STOP = 4;//stop bit
reg[2:0] state;
reg[2:0] next_state;
reg[15:0] cycle_cnt; //baud counter
reg[2:0] bit_cnt;//bit counter
reg[7:0] tx_data_latch; //latch data to send
reg tx_reg; //serial data output
assign tx_pin = tx_reg;
always@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)state <= S_IDLE;elsestate <= next_state;
endalways@(*)
begincase(state)S_IDLE:if(tx_data_valid == 1'b1)next_state <= S_START;elsenext_state <= S_IDLE;S_START:if(cycle_cnt == CYCLE - 1)next_state <= S_SEND_BYTE;elsenext_state <= S_START;S_SEND_BYTE:if(cycle_cnt == CYCLE - 1 && bit_cnt == 3'd7)next_state <= S_STOP;elsenext_state <= S_SEND_BYTE;S_STOP:if(cycle_cnt == CYCLE - 1)next_state <= S_IDLE;elsenext_state <= S_STOP;default:next_state <= S_IDLE;endcase
end
always@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)begintx_data_ready <= 1'b0;endelse if(state == S_IDLE)if(tx_data_valid == 1'b1)tx_data_ready <= 1'b0;elsetx_data_ready <= 1'b1;else if(state == S_STOP && cycle_cnt == CYCLE - 1)tx_data_ready <= 1'b1;
endalways@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)begintx_data_latch <= 8'd0;endelse if(state == S_IDLE && tx_data_valid == 1'b1)tx_data_latch <= tx_data;endalways@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)beginbit_cnt <= 3'd0;endelse if(state == S_SEND_BYTE)if(cycle_cnt == CYCLE - 1)bit_cnt <= bit_cnt + 3'd1;elsebit_cnt <= bit_cnt;elsebit_cnt <= 3'd0;
endalways@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)cycle_cnt <= 16'd0;else if((state == S_SEND_BYTE && cycle_cnt == CYCLE - 1) || next_state != state)cycle_cnt <= 16'd0;elsecycle_cnt <= cycle_cnt + 16'd1;
endalways@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)tx_reg <= 1'b1;elsecase(state)S_IDLE,S_STOP:tx_reg <= 1'b1; S_START:tx_reg <= 1'b0; S_SEND_BYTE:tx_reg <= tx_data_latch[bit_cnt];default:tx_reg <= 1'b1; endcase
endendmodule
输入输出解析
clk
时钟信号rst_n
复位信号rx_data
需要发送的八位数据tx_data_valid
发送端使能tx_pin
数据发送引脚rx_data_ready
发送的过程中为0,发送结束为1,也可以表示为,为1 的时候准备开始下一次的发送
输出输出解析
clk
时钟信号rst_n
复位信号rx_data
需要发送的八位数据tx_data_valid
发送端使能tx_pin
数据发送引脚rx_data_ready
发送的过程中为0,发送结束为1,也可以表示为,为1 的时候准备开始下一次的发送