IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> [FPGA系列] UART项目实战总结 -> 正文阅读

[嵌入式][FPGA系列] UART项目实战总结

一、UART基本概念

????????通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART。它将要传输的资料在串行通信并行通信之间加以转换,可用于单片机之间、单片机与PC之间数据通信,也就是我们常说的串口。

二、串口协议

? ? ? ? 这里所说的串口协议按照我的理解,是针对“帧”这个单位来说的,其结构如下图所示。

?????????在一帧中,其基本单位是“位”,也就是bit。一帧数据包含起始位、数据位、校验位(可有可无)和停止位。因为串口通信是异步通信,没有同步时钟协调,所以我们在接收数据时,要知道一帧的帧头是从哪开始的,所以需要一个起始位,在数据线空闲状态下,通信线电平始终为高,当一帧数据发来时,其起始位为‘0’,所以会产生一个下降沿,我们通过判断这个下降沿就可以知道一帧数据到来。

数据位为8位,也就是一个字节,发送顺序为从低位到高位依次发送。

然后是校验位(可有可无)。最后是停止位,停止位将重新拉回高电平,然后处于空闲状态,等待下一帧数据到来。

三、FPGA&Verilog实战

????????1、思路整理

? ? ? ? ? ? ? ? 整个模块可以分为两部分,接收部分和发送部分,如图所示。

?????????分为两部分后,后续使用灵活,每部分的功能又可以细分为波特率产生、发送/接收数据、产生标志位(方便仿真分析)。

?

????????2、代码编写

? ? ? ? ? ? ? ? 两个模块功能很相似,我们先写接收模块。

????????(1)接收模块

module uart_rx
#(
?? ?parameter BAT = 'd9600? ? ? ? //波特率9600
)
(
?? ?input wire ?? ??? ??? ?clk?? ??? ?,? ? ?//时钟50MHZ
?? ?
?? ?input wire ?? ??? ??? ?rst?? ??? ?,? ? ? //复位信号,低有效
?? ?
?? ?input wire ?? ??? ? ?? ?rx?? ??? ?,? ? ? //接收端
?? ?
?? ?output reg [9:0]?? ?rx_data,? ? ? //接收缓存
?? ??? ??? ??? ?
?? ?output reg ?? ??? ??? ?rx_flag? ? ? ?//接收完成标志
);

parameter?? ?BAT_CNT = 'd50_000_000 / BAT;? ? ? ? //根据波特率计算计数值
parameter?? ?IDLE? ? ?= 4'b0001,
?? ??? ??? ??? ?????START? = 4'b0010,
? ? ? ? ? ? ? ? ? ? DATA? ? = 4'b0100,
? ? ? ? ? ? ? ? ? ? STOP? ?= 4'b1000;
?? ??? ??? ??? ?
reg [3:0] ?? ?state,next_state;
reg [3:0] ?? ?rx_cnt;
reg ?? ??? ??? ?bat_flag;
reg [12:0] ??cnt;
reg ?? ??? ??? ?rx_dely1,
? ? ? ? ? ? ? ? ? rx_dely2;

//rx_dely1:rx打一拍
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?rx_dely1 <= 0;
?? ?else
?? ??? ?rx_dely1 <= rx;
?? ??? ?
//rx_dely2:rx打两拍
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?rx_dely2 <= 0;
?? ?else
?? ??? ?rx_dely2 <= rx_dely1;

//cnt:波特率频率计数
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?cnt <= 0;
?? ?else if(cnt == BAT_CNT-1 || state == IDLE)
?? ??? ?cnt <= 0;
?? ?else
?? ??? ?cnt <= cnt + 1'b1;

//bat_flag:波特率计数标志
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?bat_flag <= 0;
?? ?else if(cnt == BAT_CNT/2-1'b1)? ? ? ? //在每bit周期中间位置产生标志信号
?? ??? ?bat_flag <= 1'b1;? ? ? ? ? ? ? ? ? ? ? ? ? ?//此时采集的数据可靠性高
?? ?else
?? ??? ?bat_flag <= 0;

//rx_cnt:输入数据计数?? ??? ?
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?rx_cnt <= 0;
?? ?else if((rx_cnt == 'd9 && cnt == BAT_CNT/2) || state == IDLE)
?? ??? ?rx_cnt <= 0;
?? ?else if(cnt == BAT_CNT-1'b1)? ? ? ? ? ? ?
?? ??? ?rx_cnt <= rx_cnt + 1'b1;
?? ??? ?
//state:状态改变
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?state <= IDLE;
?? ?else
?? ??? ?state <= next_state;
?? ??? ?
//next_state:次态改变
always@(*)
?? ?case(state)
?? ??? ?IDLE ?: next_state <= (~rx_dely1 & rx_dely2)?START:IDLE;
?? ??? ?START : next_state <= DATA;
?? ??? ?DATA ?: next_state <= (rx_cnt == 'd8 && cnt == BAT_CNT-1'b1)?STOP:DATA;
?? ??? ?STOP ?: next_state <= (cnt == BAT_CNT/2)?IDLE:STOP;
?? ??? ?default : next_state <= IDLE;
?? ?endcase
?? ?
//rx,rx_data:输入数据
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?rx_data <= 0;
?? ?else if(bat_flag == 1'b1)
?? ??? ?rx_data[rx_cnt] <= rx_dely2;? ? ? ??//保存起始位、数据位、停止位共10bit数据
?? ??? ?
//rx_flag:输出完成标志
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?rx_flag <= 0;
?? ?else if(state == STOP && cnt == BAT_CNT/2)
?? ??? ?rx_flag <= 1'b1;
?? ?else
?? ??? ?rx_flag <= 0;? ? ? ??
?? ??? ?
?? ??? ?
endmodule

????????(2)发送模块? ? ? ??

module uart_tx
#(
?? ?parameter BAT = 'd9600
)
(
?? ?input wire ?? ??? ??? ?clk?? ??? ?,
?? ?
?? ?input wire ?? ??? ??? ?rst?? ??? ?,
?? ?
?? ?input wire?? ??? ??? ?rx_flag?? ?,
?? ?
?? ?input wire [9:0] ?? ?rx_data?? ?,
?? ?
?? ?output reg ?? ??? ??? ?tx?? ??? ??? ?,
?? ??? ??? ??? ?
?? ?output reg ?? ??? ??? ?tx_flag
);

parameter?? ?BAT_CNT = 'd50_000_000 / BAT;
parameter?? ?IDLE? ? ?= 4'b0001,
? ? ? ? ? ? ? ? ? ? START? = 4'b0010,
? ? ? ? ? ? ? ? ? ? DATA? ? = 4'b0100,
? ? ? ? ? ? ? ? ? ? STOP? ?= 4'b1000;
?? ??? ??? ??? ?
reg [3:0] ?? ?state,next_state;
reg [3:0] ?? ?in_cnt;
reg ?? ??? ??? ?bat_flag;
reg [12:0]? ?cnt;
reg [9:0]? ? ?data;

//data:数据缓存
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?data <= 0;
?? ?else if(rx_flag == 1'b1)? ? ? ? //接收到rx模块发来的接收完成标志
?? ??? ?data <= rx_data;? ? ? ? ? ? ?//将数据从数据缓存读出
?? ?else
?? ??? ?data <= data;
?? ??? ?
//cnt:波特率频率计数
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?cnt <= 0;
?? ?else if(cnt == BAT_CNT-1 || state == IDLE)
?? ??? ?cnt <= 0;
?? ?else
?? ??? ?cnt <= cnt + 1'b1;

//bat_flag:波特率计数标志
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?bat_flag <= 0;
?? ?else if(cnt == BAT_CNT-2)
?? ??? ?bat_flag <= 1'b1;
?? ?else
?? ??? ?bat_flag <= 0;

//in_cnt:输出数据计数?? ??? ?
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?in_cnt <= 0;
?? ?else if((in_cnt == 'd9 && cnt == BAT_CNT - 1'b1) || state == IDLE)
?? ??? ?in_cnt <= 0;
?? ?else if(bat_flag == 1'b1)
?? ??? ?in_cnt <= in_cnt + 1'b1;
?? ??? ?
//state:状态改变
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?state <= IDLE;
?? ?else
?? ??? ?state <= next_state;
?? ??? ?
//next_state:次态改变
always@(*)
?? ?case(state)
?? ??? ?IDLE ?: next_state <= (rx_flag)?START:IDLE;
?? ??? ?START : next_state <= DATA;
?? ??? ?DATA ?: next_state <= (in_cnt == 'd8 && cnt == BAT_CNT-1'b1)?STOP:DATA;
?? ??? ?STOP ?: next_state <= (cnt == BAT_CNT/2)?IDLE:STOP;
?? ??? ?default : next_state <= IDLE;
?? ?endcase
?? ?
//tx,rx_data:输出数据
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?tx <= 1'b1;
?? ?else if(state == STOP && cnt == BAT_CNT/2)
?? ??? ?tx <= 1'b1;
?? ?else if(state != IDLE)
?? ??? ?tx <= data[in_cnt];? ? ? ? //发送数据,将采集到的数据原封不动返回
?? ??? ?
//tx_flag:输出完成标志
always@(posedge clk or negedge rst)
?? ?if(!rst)
?? ??? ?tx_flag <= 0;
?? ?else if(state == STOP && cnt == BAT_CNT/2)
?? ??? ?tx_flag <= 1'b1;
?? ?else
?? ??? ?tx_flag <= 0;

endmodule
?

????????(3)顶层模块

module uart
(
?? ?input wire ?? ?clk?? ??? ?,
?? ?
?? ?input wire ?? ?rst?? ??? ?,
?? ?
?? ?input wire ?? ?rx?? ??? ??,
?? ?
?? ?output? ? ? ? ? ?tx? ? ? ? ?,
?? ?
?? ?output? ? ? ? ? ?rx_flag?,? ? ? ? //放到端口里是为了方便仿真
?? ?
?? ?output? ? ? ? ? ?tx_flag
);

wire [9:0] ?? ?rx_data;

uart_rx?
#(9600)
uart_rx_inst
(
?? ?.clk? ? ? ? ?(clk? ? ? ? ?),
?? ?.rst? ? ? ? ?(rst? ? ? ? ? ),
?? ?.rx? ? ? ? ? (rx? ? ? ? ? ?),?? ?
?? ?
?? ?.rx_data??(rx_data?),
?? ?.rx_flag?? (rx_flag?? )
);
?? ??
uart_tx?
#(9600)
uart_tx_inst
(
?? ?.clk? ? ? ? ? ?(clk? ? ? ? ),
?? ?.rst? ? ? ? ? ?(rst? ? ? ? ?),
?? ?.rx_flag? ? (rx_flag??),
?? ?.rx_data?? (rx_data?),
?? ?
?? ?.tx? ? ? ? ? ? (tx? ? ? ? ? ?),
?? ?.tx_flag?? ?(tx_flag???)
);

endmodule

????????3、仿真分析

????????(1)编写Testbench文件进行仿真

`timescale 1ns/1ns

module tb_uart();

reg ?? ?clk;
reg ?? ?rst;
reg ?? ?rx?? ?;

wire ?? ?tx;
wire ?? ?rx_flag;
wire ?? ?tx_flag;

initial?
?? ?begin
?? ??? ?clk <= 0;
?? ??? ?rst <= 0;
?? ??? ?rx <= 1'b1;
?? ??? ?#20
?? ??? ?rst <= 1;?? ?
?? ?end

always #10 clk <= ~clk;

initial
?? ?begin
?? ??? ?#80
?? ??? ??? ?begin
?? ??? ??? ??? ?rx <= 0;? ? ? ? ? ?//起始位
?? ??? ??? ??? ?#104166;? ? ? ? //对应波特率9600的单bit周期时间
?? ??? ??? ??? ?rx <= 0;
?? ??? ??? ??? ?#104166;
?? ??? ??? ??? ?rx <= 1;
?? ??? ??? ??? ?#104166;
?? ??? ??? ??? ?rx <= 1;
?? ??? ??? ??? ?#104166;
?? ??? ??? ??? ?rx <= 0;
?? ??? ??? ??? ?#104166;
?? ??? ??? ??? ?rx <= 0;
?? ??? ??? ??? ?#104166;
?? ??? ??? ??? ?rx <= 1;
?? ??? ??? ??? ?#104166;
?? ??? ??? ??? ?rx <= 1;
?? ??? ??? ??? ?#104166;
?? ??? ??? ??? ?rx <= 0;
?? ??? ??? ??? ?#104166;
?? ??? ??? ??? ?rx <= 1;? ? ? ? ? ?//停止位
?? ??? ??? ??? ?#105000;?? ?
?? ??? ??? ?end
?? ?end
uart uart_inst
(
?? ?.clk? ? ? ? ?(clk?? ??? ?),
?? ?.rst? ? ? ? ?(rst?? ??? ?),?? ?
?? ?.rx? ? ? ? ? (rx? ? ? ? ?),
?? ?
?? ?.tx? ? ? ? ? ?(tx? ? ? ? ?),
?? ?.rx_flag?? (rx_flag?),
?? ?.tx_flag?? (tx_flag??)
);

endmodule
?

????????(2)Modelsim仿真

????????可以看到,仿真结果和预期的效果相同,实验成功。

?四、总结

? ? ? ? 总体来说,这个项目不是很难,只要理清楚其逻辑和时序,写程序就很方便了,万变不离其宗。需要注意的是,在接收模块执行时,接收到停止位后不必等待停止位周期完全结束就可以停止接收了,直接返回空闲状态等待下一帧数据到来,这样在多字节连续发送时可以避免数据遗漏,读到错码。

? ? ? ? PS:作者是FPGA初学者,水平有限,写的不是很详细,仅作为一个简单的总结,有问题的地方还请大家多多指出,谢谢~

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-03 12:04:50  更:2021-09-03 12:06:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 0:32:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码