文章目录
前言
本篇博客的实验内容是实现uart串口通信发送数据和接收数据,并加入按键模块调整波特率,数码管模块显示波特率,实现不同波特率下的PC端与FPGA板子的数据回环,并加入FIFO IP核对数据进行缓存,当数据大于等于8个的时候,一次性发送8个数据出去。
实验环境:quartus 18.1 modelsim Cyclone IV开发板
一、UART通信协议
UART 即通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),是一种串行、异步、全双工的通信协议。特点是通信线路简单,适用于远距离通信,但传输速度慢。它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。
1.1 通信格式

它的一帧数据由4部分组成:
起始位(1bit)
数据位(6\7\8 bit)
奇偶校验位(1bit)
停止位(1bit\1.5bit\2bit)
注意:它在空闲状态下为高电平,起始位为低电平,停止位为高电平。它的数据位甚至可以为5位或者4位,但是不能为9位及以上。
为什么起始位是低电平?
因为它的空闲位为高电平,必须有一个由高到低的下降沿变化才能知道后面要传输数据了,不然如果起始位为高电平那它怎么知道你何时传数据呢?
2.2 MSB或LSB
它的数据位传输顺序可以采用MSB也可以采用LSB,由通信双方决定。
MSB:数据高位先发送
LSB:数据低位先发送
在本次实验的工程里面采用的是LSB的格式传输。
2.3 奇偶校验位
奇校验:当实际数据中“1”的个数为奇数的时候,这个校验位就是“0”,否则这个校验位就是“1”;
偶校验:当实际数据中“1”的个数为偶数的时候,这个校验位就是“0”,否则这个校验位就是“1”。
注意:奇偶校验只能检验奇数个数据的错误,对于偶数个数据的错误无法检测。
本次实验没有用到奇偶校验,因此在这里单独进行说明:
在接收模块中接收串行数据的校验方法:
奇校验实现方式:校验位默认为高电平,每检测到1,则状态翻转
偶校验实现方式:校验位默认为低电平,每检测到1,则状态翻转

核心代码:

在接收模块接收并行数据进行校验:
偶校验:将输入数据按位异或
奇校验:将输入数据按位异或再取反(与偶校验相反)
核心代码:

2.4 UART传输速率
UART的数据传输速率用波特率(baud)表示,常见的波特率有9600、19200、38400、57600、115200,最常用的是9600和115200。
通信双方必须保持一致的波特率,不然数据就会出错,这个在最后我们的上板验证会体现。
以9600为例,它代表我们传输1bit数据所需要的时间是1/9600=104160ns,我们的时钟周期为20ns,所以传输1bit数据需要104160/20 = 5208个时钟周期。
注意:波特率和比特率的区别
比特率:是指每秒传送的比特(bit)数。单位为 bps(Bit Per Second),比特率越高,每秒传送数据就越多。
波特率:在电子通信领域,波特(Baud)即调制速率,指的是有效数据信号调制载波的速率,即单位时间内载波调制状态变化的次数。它是对符号传输速率的一种度量,1波特即指每秒传输1个符号,而通过不同的调制方式,可以在一个码元符号上负载多个bit位信息。
在信息传输通道中,携带数据信息的信号单元叫码元,每秒钟通过信道传输的码元数称为码元传输速率,简称波特率。波特率是传输通道频宽的指标。
比特率=波特率x单个调制状态对应的二进制位数。
二、UART通信回环
2.1 系统架构设计

该系统分为了6个模块,分别是按键消抖模块(fsm_key)、波特率设置模块(baud)、数码管显示模块(sel_seg)、接收模块(uart_rx)、FIFO数据缓存模块(fifo)以及发送模块(uart_tx)。
首先是通过按键调整波特率:输入的按键信号进入按键消抖模块,然后将消抖后的按键信号传给波特率设置模块,通过按键信号可以设置不同的波特率,有常用的波特率选择:9600、19200、38400、57600、115200。默认情况下是9600。然后把设置好的波特率传给数码管显示模块进行波特率的显示。同时将设置好的波特率传给接收和发送模块,两个模块的波特率应该保持一致。同时接收模块接收了外界传入的串行数据后,将串行数据转成并行数据传给FIFO模块进行数据缓存,当FIFO里面缓存的数据大于等于8个数据时,FIFO模块将开启数据发送使能信号,然后把读取到的一个数据传给发送模块,然后发送模块再把并行数据转成串行数据一个一个的发送出去。每发送完一个数据后都拉高一个数据发送完成的使能信号传给FIFO模块,FIFO在根据这个使能信号再读一个数据传给发送模块进行数据发送,重复该步骤直到发送出8个数据为止。
2.2 fsm_key
//三段式状态机实现按键消抖
module fsm_key #(parameter CNT_20MS = 26'd1_000_000,
KEY_NUM = 3'd1)(//20MS计数器和按键的数量
input clk ,//时钟信号
input rst_n ,//复位信号
input [KEY_NUM-1:0] key_in ,//按键输入信号
output reg [KEY_NUM-1:0] key_out //按键输出信号
);
parameter IDLE = 4'b0001, //空闲状态
DOWN = 4'b0010, //按键按下抖动状态
HOLD = 4'b0100, //消抖后保持低电平有效状态
UP = 4'b1000; //释放抖动状态
reg [3:0] state_c;//现态
reg [3:0] state_n;//次态
reg [25:0] cnt_20ms;//20ms计数寄存器
wire add_cnt_20ms;//开始计数
wire end_cnt_20ms;//结束计数
reg [KEY_NUM-1:0] key_r0;//同步
reg [KEY_NUM-1:0] key_r1;//打拍
wire [KEY_NUM-1:0] nedge;//下降沿
wire [KEY_NUM-1:0] podge;//上升沿
//第一段状态机:时序逻辑,描述状态空间的切换
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
//第二段状态机:组合逻辑,描述状态转移条件
always @(*) begin
case (state_c)
IDLE:begin
if(nedge)begin //检测到下降沿进入抖动状态
state_n = DOWN;
end
else begin
state_n = state_c;
end
end
DOWN:begin
if(podge)begin //检测到上升沿证明是一个抖动回到空闲状态
state_n = IDLE;
end
else if(end_cnt_20ms && podge == 1'b0)begin//计时结束并且没有出现上升沿就证明是一个有效按下,进入下一个状态
state_n = HOLD;
end
else begin
state_n = state_c;
end
end
HOLD:begin
if(podge)begin//出现上升沿,进入释放抖动状态
state_n = UP;
end
else begin
state_n = state_c;
end
end
UP :begin
if(end_cnt_20ms)begin//延时结束进入空闲状态
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
default: state_n = state_c;
endcase
end
//第三段状态机,组合时序都可以,描述输出
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
key_out <= {
KEY_NUM{
1'b0}};
end
else if(state_c == DOWN && end_cnt_20ms)begin
key_out <= ~key_r1;
end
else begin
key_out <= {
KEY_NUM{
1'b0}};
end
end
//打拍寄存
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
key_r0 <= {
KEY_NUM{
1'b1}}; //{3{1'b1}} -> 3'b111 {
}拼接符号
key_r1 <= {
KEY_NUM{
1'b1}};
end
else begin
key_r0 <= key_in;
key_r1 <= key_r0;
end
end
// always @(posedge Clk or negedge Rst_n)begin
// if(!Rst_n)begin
// key_r <= 10'b11;
// end
// else begin
// key_r <= {key_r[0],&key_in}; //打拍寄存
// end
// end
assign nedge = ~key_r0 & key_r1;//要用按位与,因为不止一个按键信号
assign podge = ~key_r1 & key_r0;
//20MS计数器
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt_20ms <= 26'd0;
end
else if(add_cnt_20ms)begin
if(end_cnt_20ms)begin
cnt_20ms <= CNT_20MS;
end
else begin
cnt_20ms <= cnt_20ms + 1'd1;
end
end
else begin
cnt_20ms <= 26'd0;
end
end
assign add_cnt_20ms = state_c == DOWN || state_c == UP;
assign end_cnt_20ms = add_cnt_20ms && (cnt_20ms == CNT_20MS - 1'd1);
endmodule
2.3 baud
module baud(
input clk ,
input rst_n ,
input key ,//按键输入信号
output reg [16:0] baud //波特率
);
reg [2:0] cnt;//设置波特率的计数器:0为9600,1为19200,2为38400,3为57600,4为115200
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt <= 3'd0;
end
else if(key)begin
if(cnt == 3'd4)begin
cnt <= 3'd0;
end
else begin
cnt <= cnt + 1'd1;
end
end
el

本文介绍了在FPGA上实现UART串口通信回环的实验。阐述了UART通信协议,包括通信格式、数据传输顺序、奇偶校验位和传输速率等。详细说明了系统架构设计,包含多个模块及工作流程。进行了仿真分析,并通过上板验证,展示了不同波特率下的通信情况。

6424

被折叠的 条评论
为什么被折叠?



