目录
串口基础知识
一、什么是串口?有哪些特点?
二、常见的串口通信协议有哪些?他们有什么区别?
1.硬件接口类型:
2.工作方式:
3.通信方式:
4.逻辑特性不同
5.抗干扰性、传输距离和传输速率也不同
三、常见的接口类型有哪些?
UART物理层
UART协议层
一、数据构成:
实验设计
一、接口定义与整体设计
二、时序分析
三、代码实现
小结
篇幅有限,本实验工程源文件已打包至个人主页资源处,需要请自取......
串口基础知识
一、什么是串口?有哪些特点?
串口(UART)全称通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),主要用于数据间的串行传递,是一种全双工传输模式。
特点:1.数据是一位一位顺序发送,节省I/O资源
2.异步通信,收发双方可以有不同的时钟,通过数据的起始位和停止位实现同步
3.全双工通信模式:即有发和收有两根线,可以同时进行
二、常见的串口通信协议有哪些?他们有什么区别?
常见协议有RS232、RS422、RS485,区别如下:
1.硬件接口类型:
2.工作方式:
RS232和RS422是全双工,而RS485是半双工
3.通信方式:
RS232: 只能实现点对点通信
RS485:能实现点对多主从通信
RS422:也能实现点对多主从通信
4.逻辑特性不同
RS232: 逻辑”1” : -3V ~ -15 V;逻辑”0” : +3V ~+15 V
RS485: 逻辑”1” : +2V ~ +6 V; 逻辑”0” : -2V ~ -6 V
RS422: 逻辑”1” : +2V ~ +6 V; 逻辑”0” : -2V ~ -6 V
5.抗干扰性、传输距离和传输速率也不同
三、常见的接口类型有哪些?
DB9、DB25、USB转串口
下图为DB9标准串口通讯接口:
UART物理层
以FPGA为例,片间通信时,FPGA与PC端通过两根线连接,一条发送数据,一条接收数据,交叉相连。由于RS-232 电平标准的信号不能直接被控制器直接识别,需要将RS-232电平标准转换为TTL电平标准,用到电平转换芯片MA3232.当使用USB转串口接口时用到CH340芯片完成转换。
UART协议层
一、数据构成:
UART 在发送或接收过程中的一帧数据由4部分组成,起始位、数据位、奇偶校验位和停止位,如图所示。
起始位: 起始位必须是持续一个比特时间的逻辑0电平,标志传输一个字符的开始,接收方可用起始位使自己的接收时钟与发送方的数据同步。
数据位: 数据位紧跟在起始位之后,是通信中的真正有效信息。数据位的位数可以由通信双方共同约定。传输数据时先传送字符的低位,后传送字符的高位,即LSB。
奇偶校验位: 奇偶校验位仅占一位,用于进行奇校验或偶校验,奇偶检验位不是必须有的。如果是奇校验,需要保证传输的数据总共有奇数个逻辑高位(数据中有偶数个1,则校验位为1);如果是偶校验,需要保证传输的数据总共有偶数个逻辑高位(数据中有奇数个1,则校验位为1)。
停止位: 停止位可以是是1位、1.5位或2位,可以由软件设定。它一定是逻辑1电平,标志着传输一个字符的结束。
空闲位: 空闲位是指从一个字符的停止位结束到下一个字符的起始位开始,表示线路处于空闲状态,必须由高电平来填充。
二、波特率与比特率
波特率:每秒传输的码元个数,单位波特(Baud);
比特率:每秒传输的信息总量,单位每秒比特数(bps)
公式:比特率x单个调制状态对应的二进制数,例如,波特率4800*1bit=4800bps
实验设计
一、接口定义与整体设计
发送模块整体框图、输入输出信号如下所示:
接收模块整体框图、输入输出信号如下所示:
二、时序分析
发送模块时序如下所示:
这里使用work_en发送使能信号指示完整发送周期,以9600波特率为例,系统时钟为50MHZ,设定cnt_baud,从0-5207(sys_clk/9600)计数,用bit_flag指示发送的第几个bit,同时计数cnt_bit,这里设置cnt_byte指示发送了多少字节,可为后续使用。当pi_flag拉高时work_en拉高,开始发送数据。tx严格按照rs232标准发送数据。
接收模块时序如下所示:
由于UART是异步通信总线,在数据传输时存在跨时钟域处理问题。从慢时钟域(PC机的波特率)到快时钟域(sys_clk),采用电平同步方法,即对rx打两拍,取下降沿(再对rx_r2打一拍),作为接收完成信号的标志。
三、代码实现
module uart_tx
#(//parameter UART_BPS = 'd9600 ,//串口波特率//parameter CLK_FREQ = 'd50_000_000 //系统时钟频率parameter BAUT_MAX = 13'd5208 ,parameter TIME_1S = 26'd49_999_999 )
(input wire sys_clk ,//系统时钟 50MHzinput wire sys_rst_n ,//全局复位input wire [7:0]pi_data ,//模块输入的 8bit 数据input wire pi_flag ,//并行数据有效标志信号output reg tx ,output reg finish);//parameter BAUT_MAX = CLK_FREQ / UART_BPS ;/**************define*****************/
reg work_en ;//发送使能
reg [12:0]cnt_baut ;//
reg bit_flag ;//
reg [3:0]bit_cnt ;//
reg [2:0]cnt_byte ;//计数发送的字节数
reg [25:0] cnt_1s ;
reg cnt_1s_flag ;always@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n)cnt_1s_flag <= 1'd0 ;else if(cnt_1s == TIME_1S)cnt_1s_flag <= 1'd0 ;else if((cnt_byte == 3'd7) && (bit_cnt == 4'd10) && (cnt_baut == BAUT_MAX - 1'd1))cnt_1s_flag <= 1'd1 ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n)cnt_1s <= 26'd0 ;else if(cnt_1s == TIME_1S)cnt_1s <= 26'd0 ;else if(cnt_1s_flag)cnt_1s <= cnt_1s + 1'd1 ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n) work_en <= 1'd0 ;else if(((bit_cnt == 4'd10) && (cnt_baut == BAUT_MAX - 1'd1)) || (cnt_1s_flag))work_en <= 1'd0 ;else if(pi_flag)work_en <= 1'd1 ;else work_en <= work_en ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n) cnt_baut <= 13'd0 ;else if((cnt_baut == BAUT_MAX - 1'd1) || (~work_en))cnt_baut <= 13'd0 ; else if(work_en)cnt_baut <= cnt_baut + 1'd1 ;elsecnt_baut <= cnt_baut ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n) bit_flag <= 1'd0 ;else if(cnt_baut == 13'd1)bit_flag <= 1'd1 ; elsebit_flag <= 1'd0 ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n) bit_cnt <= 4'd0 ;else if((bit_flag == 1'd1) && (bit_cnt == 4'd10))bit_cnt <= 4'd0 ; else if((bit_flag == 1'd1) && (work_en))bit_cnt <= bit_cnt + 1'd1 ;elsebit_cnt <= bit_cnt ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n) finish <= 1'd0 ;else if(((bit_cnt == 4'd10) && (cnt_baut == BAUT_MAX - 1'd1)&& (cnt_byte <3'd7)) || (cnt_1s == TIME_1S - 1'd1))finish <= 1'd1 ; else if((bit_cnt == 4'd10) && (cnt_baut == BAUT_MAX - 1'd1) && (cnt_byte == 3'd7)) finish <= 1'd0 ;else finish <= 1'd0 ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n) cnt_byte <= 3'd0 ; else if((cnt_1s == TIME_1S - 1'd1)&& (cnt_byte ==3'd7))cnt_byte <= 3'd0 ; else if((finish) && (~cnt_1s_flag))cnt_byte <= cnt_byte + 1'd1 ;else if(cnt_1s == TIME_1S - 1'd1) cnt_byte <= 3'd0 ;else cnt_byte <= cnt_byte ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n) tx <= 1'd1 ;else case(bit_cnt)4'd0 : tx <= 1'd1 ;4'd1 : tx <= 1'd0 ;4'd2 : tx <= pi_data[0] ;4'd3 : tx <= pi_data[1] ;4'd4 : tx <= pi_data[2] ;4'd5 : tx <= pi_data[3] ;4'd6 : tx <= pi_data[4] ;4'd7 : tx <= pi_data[5] ;4'd8 : tx <= pi_data[6] ;4'd9 : tx <= pi_data[7] ;4'd10 : tx <= 1'd1 ;default : tx <= 1'd1 ;endcaseendendmodule
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 po_flag ,output reg [7:0]po_data );localparam BAUD_MAX = CLK_FREQ / UART_BPS ;//对rx打两拍reg rx_r1 ;reg rx_r2 ;reg rx_r3 ;reg start_nedge ;reg work_en ;reg [13:0]baud_cnt;reg bit_flag;reg [3:0]bit_cnt ;reg [7:0]rx_data ;reg rx_flag ;always@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n)rx_r1 <= 1'd1 ;else rx_r1 <= rx ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n)rx_r2 <= 1'd1 ;else rx_r2 <= rx_r1 ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n)rx_r3 <= 1'd1 ;else rx_r3 <= rx_r2 ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n)start_nedge <= 1'd0 ;else if((~rx_r2) && (rx_r3))start_nedge <= 1'd1 ;elsestart_nedge <= 1'd0 ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n)work_en <= 1'd0 ;else if(start_nedge)work_en <= 1'd1 ;else if((bit_cnt == 4'd8) && (bit_flag))work_en <= 1'd0 ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n)baud_cnt <= 13'd0 ;else if((baud_cnt == BAUD_MAX - 1'd1) || (~work_en))baud_cnt <= 13'd0 ;else if(work_en)baud_cnt <= baud_cnt + 1'd1 ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n)bit_flag <= 1'd0 ;else if(baud_cnt == BAUD_MAX / 2 - 1'd1)bit_flag <= 1'd1 ;elsebit_flag <= 1'd0 ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n)bit_cnt <= 4'd0 ;else if((bit_cnt == 4'd8) && (bit_flag))bit_cnt <= 4'd0 ;else if(bit_flag)bit_cnt <= bit_cnt + 1'd1 ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n)rx_data <= 8'd0 ;else if(((bit_cnt >= 4'd1) && (bit_cnt <= 4'd8)) && (bit_flag) && (work_en))rx_data <= {rx_r3,rx_data[7:1]} ;end always@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n)rx_flag <= 1'd0 ;else if((bit_cnt == 4'd8) && (bit_flag))rx_flag <= 1'd1 ;elserx_flag <= 1'd0 ;end always@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n)po_data <= 8'd0 ;else if(rx_flag)po_data <= rx_data ;endalways@(posedge sys_clk or negedge sys_rst_n)beginif(~sys_rst_n)po_flag <= 1'd0 ;else if(rx_flag)po_flag <= 1'd1 ;elsepo_flag <= 1'd0 ;endendmodule
小结
通过工程练习,可以很轻松地完成串口的收发模块编写,此时可以继续完成串口回环实验检测,这两个模块的编写是否正确。仿真结果如下:
篇幅有限,本实验工程源文件已打包至个人主页资源处,需要请自取......