原创 三十而立学FPGA之UART

2022-5-20 07:14 2320 16 4 分类: FPGA/CPLD 文集: FPGA
UART介绍 简介

通用异步收发器(Universal Asynchronous Receiver/Transmitter),既UART

时序

 

根据时序图可以了解到:

1. uart在空闲的时候是高电平
2. 当突变为低电平或者有一个下降沿,则是告知接收方uart要传数据了
3. 这里实现为8bit数据传输,当数据传输完成,在1或1.5或2个时钟周期内将传输线拉高,表示停止传输

UART之RX实现 状态转移图

 

源代码 
状态转移实现

always @(*)
  • begin
  • case (state)
  • IDLE:begin
  • if(rx_pin==1'b0)
  • nextstate <= START;
  • else
  • nextstate <= IDLE;
  • end
  • START:begin
  • if(cycle_cnt == CYCLE_CNT_MAX-1)//当一个BIT周期后,接收数据
  • nextstate <= REC_BYTE;
  • else
  • nextstate <= START;
  • end
  • REC_BYTE:begin
  • if(cycle_cnt == CYCLE_CNT_MAX-1&&bit_cnt==4'd7)//8位数据接收完成,跳转到检测停止位
  • nextstate <= STOP;
  • else
  • nextstate <= REC_BYTE;
  • end
  • STOP:begin
  • if(cycle_cnt == CYCLE_CNT_MAX/2-1)//半个bit周期,检测到停止位,将接收的数据发送到其他模块
  • nextstate <= DATA;
  • else
  • nextstate <= STOP;
  • end
  • DATA:begin //一个时钟周期后模块进入空闲态
  • nextstate <= IDLE;
  • end
  • endcase
  • end
  • 复制代码

    时序描述

     

    //周期计数,既系统时钟内每个BIT所需要的周期数,通过波特率可计算出最大计数值</p><p style="margin-top: 0; margin-bottom: 0;">
  • always@(posedge clk or negedge rst_n)
  • begin
  • if(rst_n == 1'b0)
  • cycle_cnt <= 16'd0;
  • else if(state == REC_BYTE && (cycle_cnt == CYCLE_CNT_MAX-1||nextstate != state))//只需要在开始接收数据时开始计数,所以前提条件是状态在REC_BYTE,而且如果计数值达到最大值或者在状态跳转的时候都需要将计数值清零
  • cycle_cnt <= 16'd0;
  • else
  • cycle_cnt <= cycle_cnt + 16'd1;
  • end
  • //位计数,以确认接收的位数
  • always@(posedge clk or negedge rst_n)
  • begin
  • if(rst_n == 1'b0)
  • bit_cnt <= 4'd0;
  • else if(state == REC_BYTE) //仅在接收数据状态时进行位计数
  • if(cycle_cnt == CYCLE_CNT_MAX-1)
  • bit_cnt <= bit_cnt + 4'd1;
  • else
  • bit_cnt <= bit_cnt;
  • else
  • bit_cnt <= 4'd0;
  • end
  • //数据接收
  • always@(posedge clk or negedge rst_n)
  • begin
  • if(rst_n == 1'b0)
  • rx_data_r <= 8'd0;
  • else if(state == REC_BYTE && cycle_cnt == CYCLE_CNT_MAX/2-1)
  • rx_data_r[bit_cnt] <= tx_pin;
  • else
  • rx_data_r <= rx_data_r;
  • end
  • //将数据传输给其他模块
  • always@(posedge clk or negedge rst_n)
  • begin
  • if(rst_n == 1'b0)
  • rx_data <= 8'd0;
  • else if(state == STOP || nextstate != state)
  • rx_data <= rx_data_r;
  • end
  • //接收完成标志
  • always@(posedge clk or negedge rst_n)
  • begin
  • if(rst_n == 1'b0)
  • rx_done <= 1'b0;
  • else if(state == STOP )
  • rx_done <= 1'b1;
  • else
  • rx_done <= 1'b0;
  • end
  • 复制代码

     

    UART之TX实现

    实现TX就不用三段式状态机这么麻烦了,直接用序列机完全就可以了

    模块使能或者说发送请求

    always@(posedge clk or negedge rst_n)
  • begin
  • if(rst_n == 1'b0)
  • tx_en <= 1'b0;
  • else if(tx_req_pos == 1'b1)
  • tx_en <= 1'b1;
  • else if(bit_cnt == 4'd11)
  • tx_en <= 1'b0;
  • else
  • tx_en <= tx_en;
  • end
  • 复制代码

     

    周期计数、位计数

    //周期计数,既系统时钟内每个BIT所需要的周期数,通过波特率可计算出最大计数值
  • always@(posedge clk or negedge rst_n)
  • begin
  • if(rst_n == 1'b0)
  • cycle_cnt <= 16'd0;
  • else if(tx_en)
  • if(cycle_cnt == CYCLE_CNT_MAX-1)//当模块使能时,开始计数,计数到最大值再从零开始
  • cycle_cnt <= 16'd0;
  • else
  • cycle_cnt <= cycle_cnt + 16'd1;
  • else
  • cycle_cnt <= 16'd0;
  • end
  • //位计数
  • always@(posedge clk or negedge rst_n)
  • begin
  • if(rst_n == 1'b0)
  • bit_cnt <= 4'd0;
  • else if(cycle_cnt == 16'd1) //如果计数到最大值bit_cnt累加的话,那么数据发送需要多等一个bit周期
  • bit_cnt <= bit_cnt + 4'd1;
  • else if(bit_cnt == 4'd11)
  • bit_cnt <= 4'd0;
  • else
  • bit_cnt <= bit_cnt;
  • end
  • 复制代码

    发送数据

    always@(posedge clk or negedge rst_n)
  • begin
  • if(rst_n == 1'b0)
  • tx_pin_r <= 1'b1;
  • else
  • case(bit_cnt)
  • 0:tx_pin_r <= 1'b1; //这里需要避免bit_cnt=0的时候发送起始位,因为当复位的时候bit_cnt的值是零的,会在复位时就已经发出了起始位,而导致接收端的误判和发送的时序紊乱
  • 1:tx_pin_r <= START_BIT;
  • 2:tx_pin_r <= send_data[0];
  • 3:tx_pin_r <= send_data[1];
  • 4:tx_pin_r <= send_data[2];
  • 5:tx_pin_r <= send_data[3];
  • 6:tx_pin_r <= send_data[4];
  • 7:tx_pin_r <= send_data[5];
  • 8:tx_pin_r <= send_data[6];
  • 9:tx_pin_r <= send_data[7];
  • 10:tx_pin_r <= STOP_BIT;
  • default: tx_pin_r <= 1'b1;
  • end
  • 复制代码

     

     

    作者: 二月半, 来源:面包板社区

    链接: https://mbb.eet-china.com/blog/uid-me-1862109.html

    版权声明:本文为博主原创,未经本人允许,禁止转载!

    PARTNER CONTENT

    文章评论2条评论)

    登录后参与讨论

    雏羽 2022-6-16 17:17

    luckyzy2000: 现在的三十还是算年轻人:)
    不年轻了吧,再过十年就没啥精力了

    luckyzy2000 2022-6-16 09:58

    现在的三十还是算年轻人:)
    相关推荐阅读
    二月半 2025-06-17 16:39
    13. ESP32开发之定时器中断
    概述相关API函数举例:定时发送一个事件总结概述ESP32有一组外设--定时器组。它可以选择不同的时钟源和分配系数。该定时器应用灵活,超时报警可以自动更新计数值。相关API函数1.定时器配置结构体ty...
    二月半 2025-06-12 14:32
    【拆解】一款远程控制开关
    七年前买了个远程控制开关,想想那个时候应该物联网才兴起的时候吧。如今因为控制麻烦且经常出现连接掉线问题,于是给淘汰了。这个设备我是拿来控制吊灯,特别麻烦的是,当晚上关灯后,会有一点灯点亮着,掉线的时候...
    二月半 2025-06-12 10:11
    ESP32开发之GPIO中断
    电路图GPIO的中断类型相关API函数应用举例总结电路图在ESP32中内部有完整的控制电路,比如上下拉以及滤波器等,所以我们这里可以直接用一个微动开关连接到地。GPIO的中断类型GPIO_INTR_D...
    二月半 2025-06-09 22:37
    ESP32开发之WS2812B控制
    WS2812B数据手册重点摘录硬件电路Remote Control Transceive(RMT)概念RMT的相关API函数一段简单的控制WS2812B的应用举例总结WS2812B数据手册重点摘录WS...
    二月半 2025-06-04 09:07
    10. ESP32开发之LED闪烁和呼吸的实现
    硬件电路介绍GPIO输出模式GPIO配置过程闪烁灯的源码LED PWM的控制器(LEDC)概述LEDC配置过程及现象整体流程硬件电路介绍电路图如下:只要有硬件基础的应该都知道上图中,当GPIO4的输出...
    二月半 2025-06-02 21:24
    【Milk-V Duo 开发板】+初用体验
    许久未在面包板社区申请板卡评测了。这一次偶然最近的这款Milk-V Duo开发板正在评测。首次看到如此简单切功能强大的嵌入式平台:· 支持linux、rtos· 可接一路Camera,做人脸检测、目标...
    我要评论
    2
    16
    关闭 站长推荐上一条 /4 下一条