串口RS232的学习
RS232通信协议简介
1、RS232是UART的一种,没有时钟线,只有两根数据线,分别是rx和tx,这两根线都是1bit位宽的。其中rx是接受数据的线,tx是发送数据的线。
2、rx的位宽为1bit,PC机通过串口调试助手往FPGA发送8bit数据时,FPGA通过串口线rx一位一位的接收数据,从最低位到最高位依次接收,最后在FPGA里面位拼接成8bit数据。
3、tx 位宽为 1bit,FPGA 通过串口往 PC 机发 8bit 数据时,FPGA 把 8bit 数据通过 tx 线一位一位的传给 PC 机,从最低位到最高位依次发送,最后上位机通过串口助手按照 RS232 协议把这一位一位的数据位拼接成 8bit 数据。
4、串口数据的发送与接收是基于帧结构的,即一帧一帧的发送与接收数据。每一帧除了中间包含 8bit 有效数据外,还在每一帧的开头都必须有一个起始位,且固定为 0;在每一帧的结束时也必须有一个停止位,且固定为 1,即最基本的帧结构(不包括校验等)有 10bit。在不发送或者不接收数据的情况下,rx 和 tx 处于空闲状态,此时 rx 和 tx 线都保持 高电平,如果有数据帧传输时,首先会有一个起始位,然后是 8bit 的数据位,接着有 1bit 的停止位,然后 rx 和 tx 继续进入空闲状态,然后等待下一次的数据传输。如下图所示 为一个最基本的 RS232 帧结构。
PS:Rx:一共是发送十位数据,但是第一位和第十位分别是0和1,中间八位任意,只需要对中间八位进行传输即可
5、波特率:在信息传输通道中,携带数据信息的信号单元叫码元(因为串口是 1bit 进 行传输的,所以其码元就是代表一个二进制数),每秒钟通过信号传输的码元数称为码元 的传输速率,简称波特率,常用符号“Baud”表示,其单位为“波特每秒(Bps)”。串口常见的波特率有 4800、9600、115200 等,我们选用 9600 的波特率进行串口的编写与仿真。
6、比特率:每秒钟通信信道传输的信息量称为位传输速率,简称比特率,其单位为 “每秒比特数(bps)”。比特率可由波特率计算得出,公式为:比特率=波特率 * 单个调制状态对应的二进制位数。如果使用的是 9600 的波特率,其串口的比特率为:9600Bps * 1bit= 9600bps。
7、由计算得串口发送或者接收 1bit 数据的时间为一个波特,即 1/9600 秒,如果用 50MHz(周期为 20ns)的系统时钟来计数,需要计数的个数为 cnt = (1s * 10^9)ns / 9600bit)ns / 20ns ≈ 5208 个系统时钟周期,即每个 bit 数据之间的间隔要在 50MHz 的时钟 频率下计数 5208 次
8、上位机通过串口发 8bit 数据时,会自动在发 8 位有效数据前发一个波特时间的起始位,也会自动在发完 8 位有效数据后发一个停止位。同理,串口助手接收上位机发送的数据前,必须检测到一个波特时间的起始位才能开始接收数据,接收完 8bit 的数据后,再接收一个波特时间的停止位。
程序设计
1、顶层模块框图如下
可以看到,顶层的框图一共包含两个模块,分别是接收和发送模块。
2、接收模块
接收模块的作用是将从PC输入的串行数据转换为并行数据 。
一帧数据一共有10bit,其中起始位和停止位分别为0和1,接收模块只接收中间八位数据,将中间8位的串行数据转化为并行,然后把8位的并行数据输入到发送模块。
接收模块的框图如下图所示:
接收模块波形图如下:
对波形图的一些理解: 接收的Rx信号需要同步到系统时钟sys_clk下,即单比特信号从低速时钟域同步到快速时钟域,通常使用的方法都是打两拍。但为何要同步三次呢?因为start_flag需要提取一个下降沿,而第一个同步的时钟是一个不稳定的信号,所以需要用第二个和第三个同步后的来提取下降沿。
根据波形图写出代码如下:
module uart_rx
#(parameter uart_bps = 'd9600,parameter clk_freq = 'd50_000_000
)
(input wire sys_clk ,input wire sys_rst_n ,input wire rx ,output reg [7:0] po_data ,output reg po_flag );localparam boud_cnt_MAX = clk_freq/uart_bps ;reg rx_reg1 ;reg rx_reg2 ;reg rx_reg3 ;reg start_flag ;reg work_en ;reg [13:0] boud_cnt ;reg bit_flag ;reg [3:0] bit_cnt ;reg [7:0] rx_data ;reg rx_flag ;//regesiter delay, two clock time
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)rx_reg1<=1'b1;elserx_reg1<=rx;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)rx_reg2<=1'b1;elserx_reg2<=rx_reg1;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)rx_reg3<=1'b1;elserx_reg3<=rx_reg2;//start_flag
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)start_flag<=1'b0;else if((~rx_reg2)&&(rx_reg3))start_flag<=1'b1;elsestart_flag<=1'b0;//work_en
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)work_en<=1'b0;else if(bit_cnt==4'd8&&bit_flag==1'b1)work_en<=1'b0;else if(start_flag==1'b1)work_en<=1'b1;elsework_en<=work_en;//boud_cnt
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)boud_cnt<=14'd0;else if((boud_cnt==boud_cnt_MAX-1'b1)||(work_en==1'b0))boud_cnt<=14'd0;else if(work_en==1'b1)boud_cnt<=boud_cnt+1'b1;//bit_flag
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)bit_flag<=1'b0;else if(boud_cnt==boud_cnt_MAX/2-1'b1)bit_flag<=1'b1;elsebit_flag<=1'b0;//bit_cnt
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)bit_cnt<=1'b0;else if(bit_cnt==4'd8&&bit_flag==1'b1)bit_cnt<=1'b0;else if(bit_flag==1'b1)bit_cnt<=bit_cnt+1'b1;elsebit_cnt<=bit_cnt;//rx_data[7:0]
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)rx_data<=8'b0;else if((bit_cnt>=4'd1)&&(bit_cnt<=4'd8)&&(bit_flag==1'b1))rx_data<={rx_reg3,rx_data[7:1]};//rx_flag
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)rx_flag<=1'b0;else if(bit_flag==1'b1&&bit_cnt==4'd8)rx_flag<=1'b1;elserx_flag<=1'b0;//po_data
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)po_data<=8'd0;else if(rx_flag==1'b1)po_data<=rx_data;
//po_flag
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)po_flag<=1'b0;else if(rx_flag==1'b1)po_flag<=1'b1;elsepo_flag<=1'b0;endmodule
接收模块仿真代码如下:
`timescale 1ns / 1ns
module tb_uart_rx();reg sys_clk ;
reg sys_rst_n ;
reg rx ;wire [7:0] po_data ;
wire po_flag ;initialbeginsys_clk=1'b1;sys_rst_n<=1'b0;rx<=1;#20sys_rst_n<=1'b1;endalways #10 sys_clk=~sys_clk;initialbegin#200rx_bit(8'd0);rx_bit(8'd1);rx_bit(8'd2);rx_bit(8'd3);rx_bit(8'd4);rx_bit(8'd5);rx_bit(8'd6);rx_bit(8'd7);endtask rx_bit(input [7:0] data
);integer i;for(i=0;i<10;i=i+1)begincase(i)0:rx<=1'b0;1:rx<=data[0];2:rx<=data[1];3:rx<=data[2];4:rx<=data[3];5:rx<=data[4];6:rx<=data[5];7:rx<=data[6];8:rx<=data[7];9:rx<=1'b1;endcase#(5208*20);end
endtaskuart_rx inst
(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n) ,
.rx (rx) ,.po_data (po_data) ,
.po_flag (po_flag) );
endmodule
Vivado仿真波形图
整体波形如下
对仿真波形图的理解: 在空闲状态下,Rx保持高电平,当检测到低电平时,波特计数器开始计数,当波特计数器计数到最大值的时候,则开始传输数据,一个8bit的数据传输完成后,po_data才开始输出。
3、发送模块
发送模块的作用的接收来自接收模块的八位并行数据,然后将这八位并行数据转换位串行数据,一位一位的发送出去 。
一帧共有10bit,第一位和最后一位分别位0和1,来自接收模块的八位数据在中间。
模块设计
我们将串口接收模块取名为 uart_tx,根据功能简介我们对整个设计要求有了大致的了解,其中设计的关键点是如何将串并行数据转化为串行数据并发送出去,也就是按照顺序将并行数据发送至 PC 机上。FPGA 发送的串行数据同样没有时钟,所以要和 PC 机接约定好使用相同的波特率,一个一个地发送比特,为了后面做串口的回环测试我们仍选择使用 9600bps的波特率
模块的Visio框图如下:
发送模块的波形图如下:
ps:注意发送同样是10bit的数据,当第一个bit_flag=1的时候开始发送起始位0,当bit_flag=9的时候发送停止位1,其余时间Tx保持空闲状态。
根据波形图可以写出如下代码:
module uart_tx
#(parameter uart_bps = 'd9600,parameter clk_freq = 'd50_000_000
)
(input wire sys_clk ,input wire sys_rst_n ,input wire [7:0] pi_data ,input wire pi_data_flag ,output reg tx );localparam boud_cnt_max=clk_freq/uart_bps;reg work_en ;reg [12:0] boud_cnt ;reg bit_flag ;reg [3:0] bit_cnt ;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)work_en<=1'b0;else if((bit_cnt==4'd9)&&(bit_flag==1'b1)) work_en<=1'b0;else if(pi_data_flag==1'b1)work_en<=1'b1;elsework_en<=work_en;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)boud_cnt<=13'd0;else if((boud_cnt==boud_cnt_max-1'b1)||work_en==1'b0)boud_cnt<=13'd0;else if(work_en==1'b1)boud_cnt<=boud_cnt+1'b1;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)bit_flag<=1'b0;else if(boud_cnt==13'd1)bit_flag<=1'b1;elsebit_flag<=1'b0;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)bit_cnt<=4'd0;else if((bit_cnt==4'd9)&&(bit_flag==1'b1))bit_cnt<=4'd0;else if((bit_flag==1'b1)&&(work_en==1'b1))bit_cnt<=bit_cnt+1'b1;elsebit_cnt<=bit_cnt;
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)tx<=1'b1;else if(bit_flag==1'b1) case(bit_cnt)0:tx<=1'b0;1:tx<=pi_data[0];2:tx<=pi_data[1];3:tx<=pi_data[2];4:tx<=pi_data[3];5:tx<=pi_data[4];6:tx<=pi_data[5];7:tx<=pi_data[6];8:tx<=pi_data[7];9:tx<=1'b1;default:tx<=1'b1;endcaseendmodule
发送模块的仿真代码如下:
`timescale 1ns / 1nsmodule tb_uart_tx();reg sys_clk ;
reg sys_rst_n ;
reg [7:0] pi_data ;
reg pi_data_flag;
wire tx ;initialbeginsys_clk=1'b1;sys_rst_n<=1'b0;#20sys_rst_n<=1'b1;end
always #10 sys_clk=~sys_clk;initialbeginpi_data<=8'b0;pi_data_flag<=1'b0;#200
//transport the data 0pi_data<=8'd0;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);
//transport the data 1 pi_data<=8'd1;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);
//transport the data 2 pi_data<=8'd2;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);
//transport the data 3 pi_data<=8'd3;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);
//transport the data 4 pi_data<=8'd4;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);
//transport the data 5 pi_data<=8'd5;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);
//transport the data 6 pi_data<=8'd6;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);
//transport the data 7 pi_data<=8'd7;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);enduart_tx inst
(.sys_clk (sys_clk) ,.sys_rst_n (sys_rst_n) ,.pi_data (pi_data) ,.pi_data_flag (pi_data_flag) ,.tx (tx) );
endmodule
Vivado 仿真波形如下:
顶层模块
顶层模块代码如下:
module top_rs232(input wire sys_clk ,input wire sys_rst_n ,input wire rx ,output wire tx );parameter uart_bps = 14'd9600;parameter clk_freq = 26'd50_000_000;wire [7:0] po_data;wire po_flag;uart_rx
#(.uart_bps(uart_bps),.clk_freq(clk_freq)
)
uart_rx_inst
(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n) ,
.rx (rx) ,.po_data (po_data) ,
.po_flag (po_flag) );uart_tx
#(.uart_bps(uart_bps),.clk_freq(clk_freq)
)
uart_tx_inst1
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.pi_data (po_data),
.pi_data_flag (po_flag),.tx (tx)); endmodule
仿真测试代码如下:
`timescale 1ns / 1nsmodule tb_top_rs232();reg sys_clk ;
reg sys_rst_n ;
reg rx ;wire tx ;initialbeginsys_clk=1'b1;sys_rst_n<=1'b0;rx<=1'b1;#20sys_rst_n<=1'b1;end
always #10 sys_clk=~sys_clk;initialbegin#200rx_byte();endtask rx_byte();integer j;for(j=0;j<8;j=j+1)rx_bit(j);
endtasktask rx_bit(input [7:0]data
);integer i;for(i=0;i<10;i=i+1)begincase(i)0:rx<=1'b0;1:rx<=data[0];2:rx<=data[1];3:rx<=data[2];4:rx<=data[3];5:rx<=data[4];6:rx<=data[5];7:rx<=data[6];8:rx<=data[7];9:rx<=1'b1;endcase#(5208*20);end
endtasktop_rs232 inst(.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n) ,
.rx (rx) ,.tx (tx));endmodule
Vivado仿真波形图如下:
串口RS232的学习相关推荐
- 串口通信协议简介—学习笔记
串口通信协议简介-学习笔记 文章目录 串口通信协议简介-学习笔记 一.串口.COM口.UART口, TTL.RS-232.RS-485区别详解 1.物理接口形式 2.电平标准 2.1 **TTL** ...
- C#接收串口RS232的CD、CTS、DSR信号
通过串口RS232和工控机连接,工控机可以接收设备(光电传感器)的IO信号 SerialPortSwitch portSwitch = new SerialPortSwitch("COM3& ...
- 通过编写串口助手工具学习MFC过程——(三)Unicode字符集的宽字符和多字节字符转换...
通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...
- 串口RS232/485/UART转CANbus总线转换器网关CSM100模块CANCOM
串口RS232/485/UART转CANbus总线转换器网关CSM100模块CANCOM CANUART-100T系列智能双向UART转CAN模块具有一路TTL UART串口通道和一路CAN通道,实现 ...
- java 串口 dtr rts_串口(RS232 RS485等)通讯中RTS/CTS,DTR/DSR的含义详解
串口(RS232 RS485等)通讯中RTS/CTS,DTR/DSR的含义详解 先贴出缩写的含义: DTR – Data Terminal Ready DSR – Data Set Ready RTS ...
- 串口RS232 RS485最本质的区别!-!I2C通讯协议 最简单的总线通讯!-深入理解SPi通讯协议!
5分钟看懂!串口RS232 RS485最本质的区别!-4分钟看懂!I2C通讯协议 最简单的总线通讯!-深入理解SPi通讯协议,5分钟看懂! 一.5分钟看懂!串口RS232 RS485最本质的区别! 二 ...
- 串口/RS232/RS485 简解
前言 首先归一下类(借鉴网友的说法,还可深入了解): UART SPI IIC 这些是一类,一般只规定了高低电平的逻辑. RS232 RS485 RS422这些属于一类,规定了电平的电压范围,数据逻辑 ...
- 【整理】串口(RS232/RS485等)通讯中RTS/CTS,DTR/DSR的含义详解
[整理]串口(RS232/RS485等)通讯中RTS/CTS,DTR/DSR的含义详解 RS232 crifan 7年前 (2013-10-17) 14942浏览 0评论 [背景] 之前就折腾过很多关 ...
- 串口通讯助手学习交流
串口通讯助手学习交流 最近花了一些时间用C#写了一个串口通讯助手,因为工作中用到了串口通信,最开始是用C++中的MFC写的,后面觉得C#更方便一些,使用的都是SerialPort类:而且我的C#也是自 ...
- 通过JAVA与串口(RS232)通信实例
通过JAVA与串口(RS232)通信实例 博客分类: J2SE JavaVBthread 最近了解到的需求是需要需激光打刻机进行(RS232)串口通信, 这里使用的是RXTX开源包实现的. 之前 ...
最新文章
- C/C++中constkeyword
- rs485编程java_串行编程RS485
- apache2配置支持php7,Ubuntu14.04服务器环境下配置PHP7.0+Apache2+Mysql5.7的方法
- Selenium API-WebDriver 方法
- 推荐系统系列教程之十四:经典模型融合方法----线性模型和树模型的组合拳
- 真正的程序员(转贴)
- Bailian4147 汉诺塔问题(Hanoi)
- css3中的文字效果
- Session销毁方式
- wow模型修改器_wow模型修改器
- mysql5.6卸载干净_Mysql完全干净卸载教程
- rx590 黑苹果 无货_RX 590显卡在Mojave黑苹果系统中识别为RX 480问题的解决一例。...
- 机器人拉格朗日动力学应用公式详解
- IDEA的界面布局折腾(Main Menu主菜单栏消失的问题)
- 51单片机——人体红外感应报警
- dr优先级默认_OSPF路由器不能成为DR/BDR唯一的方法:DR优先级=0
- scikit-learn学习系列 - 广义线性模型
- Dpark的安装测试
- RabbitMQ学习笔记(一)
- PTA_2019春_电话聊天狂人
热门文章
- 清华同方服务器硬盘更换,清华同方bios设置硬盘教程
- 【Linux】Linux系统编程入门
- WooyunWifi路由器
- 台式计算机怎么查看网卡号,怎么查看台式机电脑有没有无线网卡?
- 【STM32Cube笔记】4-STM32Cube配置时钟设置
- 毕业实用统计模型(一)——时间序列
- 物联网需要php和嵌入式吗,物联网与嵌入式两者是什么关系
- 语义分割-FCNs in the wild: Pixel-level adversarial and constraint-based adaptation 对抗方法实现不同数据集语义分割
- 我在华为外包一年的经历分享。
- potato电脑版连接不上_potato chat正式版PC端安装教程