基于FPGA的uart串口实现过程

本文章旨在记录学习的过程,如有错误之处,请各位指正
UART在FPGA中的应用: 串口通讯对于嵌入式设备开发是一个必不可少的通讯接口,在fpga中串口多用于调试或外界设备与fpga之间传输控制命令。

概念解读:

uart的关心配置:

任意打开一款PC端的串口助手,去浏览它的配置选项都大同小异,我们的重点关心项为波特率、校验位、数据位、停止位以及流控制等相关信息。在fpga实现uart中我们也主要实现这些功能的配置。
在这里插入图片描述

图1.UART时序

配置分析:

接下来我们对上述中提到的关键配置信息一一讲解:

波特率: 波特率表示每秒钟传送的码元符号的个数,它是对符号传输速率的一种度量,它用单位时间内载波调制状态改变的次数来表示,1波特即指每秒传输1个符号。 波特(Baud,单位符号:Bd)。
上述为波特率的基本概念,现实中很多人将波特率比特率的概念混淆,认为波特率就是每秒传输多少bit。其区别请各位自行搜索理解。
校验位: 此处校验位使用的是常用的奇偶校验方式,奇偶校验方式可以对传输的数据正确性进行简单的保障,使接收方有判断数据正确与否的能力。

校验类型校验位校验方式
奇校验 (odd)1位(1bit)数据位中所有的“1”的个数加上校验位的个数总和需保持为奇数例如:数据为:01110100 其中有4个“1”,为满足奇校验要求需要将检验位置“1”;反之当数据为:01110110 其中有5个”1“,此时我们需要将校验位置为”0“。
偶校验 (even)1位(1bit)数据位中所有的“1”的个数加上校验位的个数总和需保持为偶数例如:数据为:01110100 其中有4个“1”,为满足奇校验要求需要将检验位置“0”;反之当数据为:01110110 其中有5个”1“,此时我们需要将校验位置为”1“。
1校验(mark)1位(1bit)校验位始终为:“1”,1校验属于固定校验的方式:其并不是对数据进行校验,而是对接收设备发送一个状态位,使接收设备判断收到的设备有没有受到噪声干扰或数据不同步。
0校验(space)1位(1bit)校验位始终为:“0”,0校验与1校验同属于固定校验方式,其方式与1校验也相同。
无校验(none)0位(0bit)无校验位

数据位: 每包数据中有效数据(payload)的宽度,常见配置有5bit、6bit、7bit、8bit,其中8bit最为常见使用最为频繁。
停止位: 停止位是单包的最后一位(标识位),其常见配置为1、1.5、2位,起作用不仅仅只有标志一包数据的结束,另外一个作用是“校准时钟”:由于UART之间无总线传输的时钟,主从机都是通过自己内部产生相同的时钟进行匹配,所以可通过停止位来对时钟进行校准,适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
流控制: 由于本次实验没有涉及到,所以在此并不做解释。有需要的小伙伴可以自行搜索。(后续会补充完整)。

波特率产生:

时钟是FPGA的心脏,所有时序逻辑都是需要在时钟下产生的,所以需要为UART收发时序生成所需要的参考时钟,也就是产生波特率时钟。UART中常使用的波特率有:9600、115200、38400……等频率,以波特率 115200 为例:
两相调制(单个调制状态对应1个二进制位)的比特率等于波特率;所以当波特率为115200buad时,比特率等于115200bit/s,由此我们可推算出其传输1bit所需要的时间,可通过logic实现时钟的倍频操作得出波特率的参考频率
在这里插入图片描述

图2.UART时序

参 考 时 钟 / 波 特 率 = 倍 频 关 系 参考时钟/波特率=倍频关系 /=
为了保证数据的的准确性可在buad_clk为high时的中点处采集数据,在buad_clk为low时的中点处改变数据;部分实现代码如下

always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        Buad_gen_cnt[0] <= 17'd0;
	else if (!t_baud_en)
		Buad_gen_cnt[0] <= 17'd0;
    else if (Buad_gen_cnt[0] == (Buad_gen_flag - 17'd1))
        Buad_gen_cnt[0] <= 17'd0;
    else
        Buad_gen_cnt[0] <= Buad_gen_cnt[0] + 17'd1;  
end
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        t_Baud_clk <= 1'b1;
	else if (!t_baud_en)
		t_Baud_clk <= 1'b1;
    else if (Buad_gen_cnt[0] == (Buad_gen_flag - 17'd1))
        t_Baud_clk <= ~t_Baud_clk;
    else
        t_Baud_clk <= t_Baud_clk ;
end

时序整理:

想要实现一个总线,在解读完概念后就要对时序进行分析整理,判断如何实现:
uart_timing

图3.UART时序

根据uart时序图可以分析出uart信号线在空闲状态时钟为 “1” ,当有数据需要发送时,首先将信号线置 “0” 发送起始标志(start),然后以次又低位到高位发送数据(8bit DATA),后续是校验位(parity),检验位根据配置的校验方式可为:0/1bit;后将信号线置 “1” 发送结束标志(stop)。由此可根据时序设计发送和接收的状态机:

IDLE
START
DATA
PARITY
STOP
图4.UART状态机
状态机状态可粗略分为5个状态:
状态描述
IDLE数据信号线空闲状态
START数据传输开始状态
DATA有效数据传输状态
PARITY校验位判断状态
STOP数据传输结束状态

状态机部分实现代码(运用三段式状态机描述)
发送数据

always@(posedge clk or negedge rst_n) 
begin
	if(!rst_n)
		cstate <= IDLE   ;
	else
		cstate <= nstate ;
end
 
always@(*)
begin
    case(cstate)
        IDLE  : begin
//            if(state_switch) begin
                if(tren_en_ctrl & !fifo_empty)
                    nstate = START;
				else
					nstate = IDLE ;            
//            end else
//				nstate = IDLE ;                         
        end                  
        START : begin
			if(state_switch)
				nstate = TDATA	;
			else
				nstate = START	;
		end
		TDATA : begin
			if(state_switch & cstate_cnt == 8'd7)
				if(parity_rule != no_parity)
					nstate = PARITY ;
				else
					nstate = STOP 	;
			else
				nstate = TDATA	;
		end
		PARITY: begin
			if(state_switch)
				nstate = STOP 	;
			else
				nstate = PARITY ;
		end
		STOP  : begin	
			if(state_switch) begin
                if(tren_en_ctrl & !fifo_empty)
                    nstate = START;
				else
					nstate = IDLE ; 
			end else
				nstate = STOP 	;
		end
		default : nstate = IDLE ;
	endcase
end

接收数据

always@(posedge clk or negedge rst_n) 
begin
	if(!rst_n)
		cstate <= IDLE   ;
	else
		cstate <= nstate ;
end
 
always@(*)
begin
    case(cstate)
        IDLE  : begin
            if(rece_en & !uart_rx) begin
                nstate = START;        
            end else
				nstate = IDLE ;                         
        end                  
        START : begin
			if(state_switch)
				nstate = RDATA	;
			else
				nstate = START	;
		end
		RDATA : begin
			if(state_switch & cstate_cnt == 8'd7)
				if(parity_rule != no_parity)
					nstate = PARITY ;
				else
					nstate = STOP 	;
			else
				nstate = RDATA	;
		end
		PARITY: begin
			if(state_switch)
				nstate = STOP 	;
			else
				nstate = PARITY ;
		end
		STOP  : begin	
			if(state_switch) begin
                if(rece_en & !uart_rx)
                    nstate = START;
				else
					nstate = IDLE ; 
			end else
				nstate = STOP 	;
		end
		default : nstate = IDLE ;
	endcase
end

到此笔记结束,若有错误的地方,敬请各位指正。由于概念部分内容参考了网络上的部分文章,如有侵权请联系笔者删除!!!

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值