前言
上次我们学习了FIFO ip核的使用,这次我们学习不同的设备通信之间最常用的一种通信方式 UART(通用串行异步通信收发器)。
一、UART是什么?
UART 是一种采用异步串行通信方式的通用异步收发传输器,在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。
UART 串口通信需要两根信号线来实现, 一根用于串口发送,另外一根负责串口接收。
UART 在发送或接收过程中的一帧数据由 4 部分组成, 起始位、 数据位、 奇偶校验位和停止位。
起始位标志着一帧数据的开始,停止位标志着一帧数据的结束, 数据位是一帧数据中的有效数据。 校验位分为奇校验和偶校验, 用于检验数据在传输过程中是否出错。 奇校验时, 发送方应使数据位中 1 的个数与校验位中 1 的个数之和为奇数;接收方在接收数据时, 对 1 的个数进行检查,若不为奇数,则说明数据在传输过程中出了差错。 同样,偶校验则检查 1 的个数是否为偶数。
本次实验使用的通信格式为8位数据位,奇校验,1位停止位,所以本次实验一帧总位数为起始位(1)+数据位(8)+校验位(1)+停止位(1) = 11位。
二、代码部分
1.串口发送
首先对使能信号进行上升沿检测,然后如果检测到上升沿,状态机从空闲状态跳转到起始位,然后时数据位,再其次是奇偶效验,最后是停止位,最主要的是核心状态机的实现过程,在此粘贴我的代码用于大家参考学习。
代码如下:
//该串口发送为 任意波特率(最低1200) 八位数据位 奇数校验 一位停止位
module uart_tx(
input wire sys_clk, //系统时钟
input wire sys_rst_n, //系统复位
input wire [7:0] uart_send_data, //待发送数据
input wire uart_send_en, //使能发送信号
output reg uart_txd, //串口输出
output reg uart_busy //串口发送繁忙信号
);
parameter Uart_Baud = 115200; //波特率
parameter Sys_Clk = 50_000_000; //系统时钟
parameter MAX_CNT = (Sys_Clk/Uart_Baud) - 1'b1;//计数器最大值
parameter IDLE = 3'd0; //空闲
parameter S1 = 3'd1; //起始位
parameter S2 = 3'd2; //数据位
parameter S3 = 3'd3; //校验位
parameter S4 = 3'd4; //停止位
reg [2:0] tx_state; //发送状态寄存器
reg [7:0] uart_data; //待发送数据暂存
reg [3:0] tx_data_wei; //已经发送的数据位数
reg data_check; //串口奇偶校验
reg tx_en_reg0; //同步tx使能信号
reg tx_en_reg1; //tx使能信号节拍
wire tx_en_up; //tx使能信号上升沿
reg [15:0] cnt; //计数器 计数器位宽限制波特率最低为1200
reg cnt_en ; //计数器使能
wire add_cnt; //计数器计数条件
wire end_cnt; //计数器重载条件
assign tx_en_up = tx_en_reg0 && (~tx_en_reg1); //上升沿检测
assign add_cnt = cnt_en; //计数器计数条件为计数器使能
assign end_cnt = add_cnt && (cnt == MAX_CNT); //计数器结束条件为计数器记到最大值并且计数器计数条件成立
//同步tx使能信号 并且打一次节拍 从而提取出使能信号变化的上升沿
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
tx_en_reg0 <= 1'b0;
tx_en_reg1 <= 1'b0;
end
else begin
tx_en_reg0 <= uart_send_en;
tx_en_reg1 <= tx_en_reg0;
end
end
//状态机 空闲 起始位 数据 校验位 停止位
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
tx_state <= IDLE;
uart_data <= 1'b0;
cnt_en <= 1'b0;
tx_data_wei <= 1'b0;
data_check <= 1'b0;
uart_busy <= 1'b0;
end
else begin
case(tx_state)
//空闲状态 所有变量复位 如果检测到使能信号上升沿 使能计数器 并且跳转到起始位状态
//将需要发送数据寄存
IDLE: begin
uart_data <= 1'b0;
cnt_en <= 1'b0;
tx_data_wei <= 1'b0