【FPGA】串口通信讲解-状态机判断数据值

news/2025/2/19 13:05:52/

🎉欢迎来到FPGA专栏~串口通信讲解


  • ☆* o(≧▽≦)o *☆~我是小夏与酒🍹
  • 博客主页:小夏与酒的博客
  • 🎈该系列文章专栏:FPGA学习之旅
  • 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
  • 📜 欢迎大家关注! ❤️
    FPGQ2

CSDN

🎉 目录-串口通信讲解

  • 一、效果演示
  • 二、串口通信基础知识
  • 三、基本通信代码编写
    • 3.1 异步信号分析
    • 3.2 USB
    • 3.3 RS232
    • 3.4 串口接收模块
    • 3.5 串口发送模块
  • 四、串口通信控制led亮灭

遇见未来

一、效果演示

🥝视频演示:

FPGA串口通信控制led点亮与熄灭

🥝RTL视图:
顶层模块:
顶层模块

状态机判断数据值:
FSM
状态转移

二、串口通信基础知识

参考【小月电子】大佬的博客:【小月电子】ALTERA FPGA开发板系统学习教程-LESSON7串口通信。

UART 是一种采用异步串行通信方式的通用异步收发传输器(universal asynchronous receiver-transmitter),它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。

UART 串口通信需要两根信号线来实现,一根用于串口发送,另外一根负责串口接收。UART 在发送或接收过程中的一帧数据由 4 部分组成,起始位、数据位、奇偶校验位和停止位。其中,起始位标志着一帧数据的开始,停止位标志着一帧数据的结束,数据位是一帧数据中的有效数据。校验位分为奇校验和偶校验,用于检验数据在传输过程中是否出错。奇校验时,发送方应使数据位中 1 的个数与校验位中 1 的个数之和为奇数;接收方在接收数据时,对 1 的个数进行检查,若不为奇数,则说明数据在传输过程中出了差错。同样,偶校验则检查 1 的个数是否为偶数。

串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,单位是 bps( 位/秒),常用的波特率有 9600、19200、38400、57600 以及 115200 等。

在本篇文章中,串口通信所使用的波特率为9600,计算过程:
1、板载晶振为50MHz,时钟周期为20ns
2、波特率9600bps表示每秒传输9600bit
3、传输1bit的时间为:1/9600(秒)=104167ns
4、104167/20=5208.35,也就是5208个时钟周期,需要采集1次

在异步串口时序图中,空闲时为高电平,起始位为低电平,停止为高电平:
异步串口时序图
串口接收时序图:
串口接收时序图

串口发送时序图:
串口发送时序图

三、基本通信代码编写

3.1 异步信号分析

参考链接:高级FPGA设计技巧!多时钟域和异步信号处理解决方案。

亚稳态
触发器的建立时间和保持时间在时钟上升沿左右定义了一个时间窗口,如果触发器的数据输入端口上数据在这个时间窗口内发生变化(或者数据更新),那么就会产生时序违规。存在这个时序违规是因为建立时间要求和保持时间要求被违反了,此时触发器内部的一个节点(一个内部节点或者要输出到外部节点)可能会在一个电压范围内浮动,无法稳定在逻辑0或者逻辑1状态。换句话说,如果数据在上述窗口中被采集,触发器中的晶体管不能可靠地设置为逻辑0或者逻辑1对应的电平上。所以此时的晶体管并未处于饱和区对应的高或者低电平,而是在稳定到一个确定电平之前,徘徊在一个中间电平状态(这个中间电平或许是一个正确值,也许不是)。如下图所示,这就是所谓的亚稳态:
亚稳态

解决亚稳态的方法之一就是打两拍处理
打两拍
处理实现代码:

reg	rxd_ff1;
reg	rxd_ff2;
//打两拍操作-第一拍
always@(posedge clk or negedge rst_n)beginif(!rst_n)rxd_ff1 <= 1'b0;elserxd_ff1 <= rxd;
end
//打两拍操作-第二拍
always@(posedge clk or negedge rst_n)beginif(!rst_n)rxd_ff2 <= 1'b0;elserxd_ff2 <= rxd_ff1;
end

3.2 USB

硬件电路:
TTL

3.3 RS232

硬件电路:
RS232

3.4 串口接收模块

async_uart_rev.v:

///
//模块介绍:实现异步串口接收功能
///
module async_uart_rev(input				rst_n	,//复位信号,低电平有效input				clk,//时钟信号,50MHZinput				rxd,//串行接收数据output	reg	[7:0]	rev_data,//并行数据output	reg			rev_dvld //并行数据有效标志);parameter	baud_num	=5207;//1/9600*1000000000/20parameter	IDLE		=4'd0;parameter	START_ST    =4'd1;parameter	STOP_ST     =4'd2;reg	[12:0]	baud_cnt;reg			baud_cnt_en;wire		sample_en;reg	[3:0]	sample_num;reg			rxd_ff1;reg			rxd_ff2;reg	[3:0]	curr_st;//打两拍操作-第一拍always@(posedge clk or negedge rst_n)beginif(!rst_n)rxd_ff1 <= 1'b0;elserxd_ff1 <= rxd;end//打两拍操作-第二拍always@(posedge clk or negedge rst_n)beginif(!rst_n)rxd_ff2 <= 1'b0;elserxd_ff2 <= rxd_ff1;end//sample_en判断计数器是否到达计数值assign sample_en = (baud_cnt == baud_num[12:1])?1'b1:1'b0;//状态机跳转程序always@(posedge clk or negedge rst_n)beginif(!rst_n)curr_st<=IDLE;else case(curr_st)IDLE:beginif(rxd_ff2==0)curr_st<=START_ST;else;endSTART_ST:beginif(sample_num==8&&sample_en)curr_st<=STOP_ST;else;endSTOP_ST:beginif(rxd_ff2==1&&sample_en)curr_st<=IDLE;else;enddefault:;endcaseend//baud_cnt波特率计数器always@(posedge clk or negedge rst_n)beginif(!rst_n)baud_cnt<=0;else if(curr_st==START_ST||curr_st==STOP_ST)beginif(baud_cnt==baud_num)baud_cnt<=0;else baud_cnt<=baud_cnt+1;end elsebaud_cnt<=0;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)	sample_num<=0;else if(sample_en&&sample_num==9)sample_num<=0;else if(sample_en)sample_num<=sample_num+1;else;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)rev_data<=0;else if(sample_en)case(sample_num)1:rev_data[0]<=rxd_ff2;2:rev_data[1]<=rxd_ff2;3:rev_data[2]<=rxd_ff2;4:rev_data[3]<=rxd_ff2;5:rev_data[4]<=rxd_ff2;6:rev_data[5]<=rxd_ff2;7:rev_data[6]<=rxd_ff2;8:rev_data[7]<=rxd_ff2;default:;endcaseendalways@(posedge clk or negedge rst_n)beginif(!rst_n)	rev_dvld<=0;else if(sample_num==9&&sample_en)rev_dvld<=1;elserev_dvld<=0;endendmodule

3.5 串口发送模块

async_uart_tran.v:

///
//模块介绍:实现异步串口发送功能
///
module async_uart_tran(input				rst_n	,//复位信号,低电平有效input				clk,//时钟,50MHZinput		[7:0]	tran_data,//输入的并行数据input				tran_dvld,//输入的并行数据有效标志output		reg		txd      //串行输出数据);parameter	baud_num	=5207;//1/9600*1000000000/20parameter	IDLE		=4'd0;parameter	DATA_ST    	=4'd1;parameter	START_ST    =4'd2;parameter	STOP_ST     =4'd3;reg	[12:0]		baud_cnt;reg				baud_cnt_en;reg	[3:0]		sample_num;reg	[3:0]		curr_st;wire		sample_en;assign	sample_en = (baud_cnt==baud_num)?1'b1:1'b0;always@(posedge clk or negedge rst_n)beginif(!rst_n)curr_st<=IDLE;else case(curr_st)IDLE:beginif(tran_dvld==1)curr_st<=START_ST;else;endSTART_ST:beginif(sample_en==1)curr_st<=DATA_ST;endDATA_ST:beginif(sample_en&&sample_num==8)curr_st<=STOP_ST;else;endSTOP_ST:beginif(sample_en==1)curr_st<=IDLE;else;enddefault:;endcaseendalways@(posedge clk or negedge rst_n)beginif(!rst_n)baud_cnt<=0;else if(curr_st==START_ST||curr_st==DATA_ST||curr_st==STOP_ST)beginif(baud_cnt==baud_num)baud_cnt<=0;else baud_cnt<=baud_cnt+1;end elsebaud_cnt<=0;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)	sample_num<=0;else if(curr_st==IDLE)sample_num<=0;else if(sample_en)sample_num<=sample_num+1;else;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)txd<=1;else if(sample_en)case(sample_num)0:txd<=1'b0;1:txd<=tran_data[0];2:txd<=tran_data[1];3:txd<=tran_data[2];4:txd<=tran_data[3];5:txd<=tran_data[4];6:txd<=tran_data[5];7:txd<=tran_data[6];8:txd<=tran_data[7];9:txd<=1'b1;default:txd<=1;endcaseendendmodule

四、串口通信控制led亮灭

使用状态机判断数据并控制led亮灭

状态机学习参考链接:【FPGA零基础学习之旅#9】状态机基础知识。

主要根据状态跳转来编写代码:
状态转移

data.v:

module data(input 				clk,input 				rst_n,input 		[7:0]	rev_data,output reg 	[3:0]	led
);//定义LED的亮灭parameter led_light_all = 8'h11;//全部点亮parameter led_close_all = 8'h00;//全部熄灭parameter led_light_one = 8'hAA;//点亮led1parameter led_close_one = 8'hFF;//关闭led1localparam IDLE 	 = 4'b0001,LIGHTALL = 4'b0010,ONELIGHT = 4'b0100,ONECLOSE = 4'b1000;reg [3:0]curr_st;//状态机主程序always@(posedge clk or negedge rst_n)beginif(!rst_n)curr_st <= IDLE;else begincase(curr_st)IDLE:beginif(rev_data == led_light_all)curr_st <= LIGHTALL;else if(rev_data == led_light_one)curr_st <= ONELIGHT;else;endLIGHTALL:beginif(rev_data == led_close_all)curr_st <= IDLE;else;endONELIGHT:beginif(rev_data == led_close_one)curr_st <= ONECLOSE;else;endONECLOSE:curr_st <= IDLE;default:curr_st <= IDLE;endcaseendendalways@(posedge clk or negedge rst_n)beginif(!rst_n)led <= 4'b1111;else begincase(curr_st)IDLE:		led <= 4'b1111;LIGHTALL:led <= 4'b0000;ONELIGHT:led <= 4'b1110;ONECLOSE:led <= 4'b0000;default:led <= led;endcaseendendendmodule

data.v的RTL视图:

FSM

本篇文章实现的顶层模块:

///
//模块介绍:顶层模块,例化接收和发送模块,控制LED状态
///
module async_uart_top(input			clk		,//时钟,50MHZinput			rst_n		,//复位信号,低电平有效input			rxd		,//串行接收数据output			txd		,//串行发送数据output	[3:0] 	led);wire	[7:0]	rev_data;wire			rev_dvld;async_uart_rev Uasync_uart_rev(.rst_n(rst_n),.clk(clk),.rxd(rxd),.rev_data(rev_data),.rev_dvld(rev_dvld));async_uart_tran Uasync_uart_tran(.rst_n(rst_n),.clk(clk),.tran_data(rev_data),.tran_dvld(rev_dvld),.txd(txd));data Udata(.clk(clk),.rst_n(rst_n),.rev_data(rev_data),.led(led));endmodule

顶层模块的RTL视图:

顶层模块

csdn

🧸结尾


  • ❤️ 感谢您的支持和鼓励! 😊🙏
  • 📜您可能感兴趣的内容:
  • 【Python】Python实现串口通信(Python+Stm32)
  • 【Verilog HDL】FPGA-testbench基础知识
  • 【Arduino TinyGo】【最新】使用Go语言编写Arduino-环境搭建和点亮LED灯
  • 【全网首发开源教程】【Labview机器人仿真与控制】Labview与Solidworks多路支配关系-四足爬行机器人仿真与控制
    遇见未来

http://www.ppmy.cn/news/973736.html

相关文章

ChatGPT常用的指令(prompts)系列八-写作、UI设计、招聘、网路安全

系列文章目录 内容翻译自&#xff1a;https://github.com/f/awesome-chatgpt-prompts&#xff0c;并加入自己的实践内容 1、 ChatGPT常用的提示语&#xff08;prompts&#xff09;系列一 2、 ChatGPT常用的提示语&#xff08;prompts&#xff09;系列二 3、 ChatGPT常用的提示语…

ChatGPT背后的指令学习是什么?PSU最新首篇《指令学习》技术全面综述,详述指令学习关键问题

来源: 专知 任务语义可以用一组输入到输出的例子或一条文本指令来表示。传统的自然语言处理(NLP)机器学习方法主要依赖于大规模特定任务样本集的可用性。出现了两个问题: 首先&#xff0c;收集特定于任务的标记示例&#xff0c;不适用于任务可能太复杂或太昂贵而无法注释&#…

记录一下trackformer的安装过程

项目地址 1、创建python环境&#xff0c;并激活 conda create -n TF python3.8 -y conda activate TF2、进入trackformer中&#xff0c;并且运行setup.py cd Desktop/MOT/trackformer/ python setup.py install3、下载pytorch pytorch官方安装法 # CUDA 11.1 pip install …

springboot mybatis-plus 多数据源配置(HikariCP)

1.导入依赖jar <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.postgresql</groupId><artifactId>postgres…

史上AI最高分!谷歌大模型创美国医师执照试题新纪录,科学常识水平媲美人类医生...

杨净 羿阁 发自 凹非寺量子位 | 公众号 QbitAI 史上AI最高分&#xff0c;谷歌新模型刚刚通过美国医师执照试题验证&#xff01; 而且在科学常识、理解、检索和推理能力等任务中&#xff0c;直接与人类医生水平相匹敌。在一些临床问答表现中&#xff0c;最高超原SOTA模型17%以上…

首位华人女数学家获拉马努金奖!北大数院校友,陶哲轩恽之玮曾得过此奖

白交 发自 凹非寺量子位 | 公众号 QbitAI 又一位北大数院校友——唐云清&#xff0c;斩获拉马努金奖。 此前&#xff0c;00级校友张伟、恽神恽之玮&#xff0c;03级校友、浙大教授刘一峰曾获此奖。 △图源&#xff1a;George BergmanUC伯克利 除此之外&#xff0c;还有像陶哲轩…

chatgpt赋能Python-python_hanoi

Python Hanoi - 轻松解决汉诺塔问题 如果你是一位对算法有一定了解的程序员&#xff0c;那么你一定听说过汉诺塔问题。这个问题是计算机科学中最常见和最有趣的问题之一。这个问题听起来似乎很简单&#xff0c;但实际上很多程序员也会在这个问题上遇到困难。在这篇文章中&…

python try/except/finally

稍微总结一下&#xff0c;否则总是忘。 x abc def fetcher(obj, index): return obj[index] fetcher(x, 4) 输出&#xff1a; File "test.py", line 6, in <module> fetcher(x, 4) File "test.py", line 4, in fetcher return obj[index] …