uart通讯协议
Posted noticeable
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了uart通讯协议相关的知识,希望对你有一定的参考价值。
本次设计的源码在http://download.csdn.net/detail/noticeable/9912383 下载
实验目的:通过uart通讯协议的编写,了解FPGA的通讯协议编写的方法。
实验现象:FPAG可以通过USB转TTL工具向电脑收发数据。
相关知识点:1、uart通讯协议是什么及其内容。2、in system surce and probes(editor)调试工具的使用。
关于串行通讯:串口通讯协议是一种主机之间常用的通讯协议,通过模块按位发送和接收字节,可以达到通讯的目的,其通讯只需要三根根数据线(GND,RXD,TXD)即可完成。其中串口通讯最重要的参数是波特率,数据位,停止位和奇偶校验位,下面来一一讲解这 些参数的作用。
(1)波特率:波特率是串口的通讯速率,常见的比特率为1200bps、4800bps、9600bps、38400bps、115200bps、256000bps、500000bps,这里bps的意思是bit/s,因此可以知道,波特率实际上是每秒可以传输的bit的个数,由此可知波特率与时钟是直接挂钩的,比如波特率位9600bps,那么时钟就是9600hz,即每秒内产生9600个时钟,每个时钟周期发送1bit的数据。(这里注意:波特率越高,传输距离越短)
波特率分频计数器的方法:
(2)数据位:数据位可以在通讯过程中告知另一个主机,完成一次通讯过程中真正有效的数据是多少位,完成对传输数据的位数的校验。
(3)起始位、停止位:起始位位于单个数据包的第一位,停止位位于单个包的最后一位,每个包都是一个字节, 另一台主机接收到停止位时就知道信号发送已经完成了。
(4)奇偶校验位:奇偶校验是通讯协议中的一种简单的检错方法,其有时钟检错方式,:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位为1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。
项目设计:
本次设计发送模块的整体结构体如下 发送时序图
首先按照系统框图编写tx项目源码:
发生时序图
源码程序如下
module uart_tx( clk, rst_n, send_en, baud_set, tx_done, rs232_tx, data_byte, uart_state ); input clk; input rst_n; input [7:0]data_byte;//数据发送寄存器 input send_en; input [2:0]baud_set; output reg rs232_tx; //输出引脚 output reg tx_done; //传输完成的标志位 output reg uart_state; //uart_state传送状态 reg [15:0]div_cnt; //分频计数器 reg bps_clk; //波特率时钟 reg [15:0]bps_dr; //分频计数最大值 reg [3:0]bps_cnt; //波特率计数时钟 reg [7:0]r_data_byte;//发送缓存区 localparam start_bit=1\'b0; localparam stop_bit=1\'b1; //具有优先级的信号控制,产生控制信号 always@(posedge clk or negedge rst_n) if(!rst_n) uart_state<=0; else if(send_en==1\'b1) uart_state<=1; else if(bps_cnt == 4\'d11) //假设穿的是一组8位的数据+起始位+停止位 uart_state <= 1\'b0; else uart_state<=uart_state; //发送过程中需要保证数据是稳定的,选用一个寄存器将数据先缓存起来 always@(posedge clk or negedge rst_n) if(!rst_n) r_data_byte<=8\'d0; else if(send_en) r_data_byte<=data_byte; //设计查找表DR_LUT,通过查找表设置波特率
//1/波特率/20ns
always@(posedge clk or negedge rst_n) if(!rst_n) bps_dr<=16\'d5207; else begin case (baud_set) 0:bps_dr<=16\'d5207;//9600 1:bps_dr<=16\'d2603;//19200 2:bps_dr<=16\'d1301;//28400 3:bps_dr<=16\'d867;//57600 4:bps_dr<=16\'d433;//115200 // 5:bps_dr<=16\'d5207; // 6:bps_dr<=16\'d5207; // 7:bps_dr<=16\'d5207; default bps_dr<=16\'d5207;//9600 endcase end //分频计数器 always@(posedge clk or negedge rst_n) if(!rst_n) div_cnt<=16\'d0; else if(uart_state)begin if(div_cnt==bps_dr)//到达计数最大值时清零 div_cnt<=16\'d0; else div_cnt<=div_cnt+1\'b1; end else div_cnt<=16\'d0; //单周期的波特率时钟产生 always@(posedge clk or negedge rst_n) if(!rst_n) bps_clk<=1\'b0; else if(div_cnt==16\'d1) bps_clk<=1; else bps_clk<=0; //设计波特率计数器 always@(posedge clk or negedge rst_n) if(!rst_n) bps_cnt<=4\'b0; else if (bps_cnt == 4\'d11) bps_cnt<=4\'b0; else if (bps_clk) bps_cnt<=bps_cnt+1\'b1; else bps_cnt<=bps_cnt; //发送完成信号 always@(posedge clk or negedge rst_n) if(!rst_n) tx_done<=1\'b0; else if(bps_cnt==4\'d11) tx_done<=1\'b1; else tx_done <=1\'b0; //数据发送,即一个十选一的多路器 always@(posedge clk or negedge rst_n) if(!rst_n) rs232_tx<=1\'b1; else begin case(bps_cnt) 0:rs232_tx<=1\'b1; 1:rs232_tx<=start_bit;//起始位 2:rs232_tx<=r_data_byte[0]; 3:rs232_tx<=r_data_byte[1]; 4:rs232_tx<=r_data_byte[2]; 5:rs232_tx<=r_data_byte[3]; 6:rs232_tx<=r_data_byte[4]; 7:rs232_tx<=r_data_byte[5]; 8:rs232_tx<=r_data_byte[6]; 9:rs232_tx<=r_data_byte[7]; 10:rs232_tx<=stop_bit;//结束位,本次设计不设奇偶校验位 default rs232_tx<=1\'b1; endcase end endmodule
编写testbench文件
`timescale 1ns/1ns `define clock_period 20 module uart_tx_tb; reg clk; reg rst_n; reg [7:0]data_byte; reg send_en; reg [2:0]baud_set; wire rs232_tx; wire tx_done; wire uart_state; uart_tx uart_tx0( .clk(clk), .rst_n(rst_n), .send_en(send_en), .baud_set(baud_set), .tx_done(tx_done), .rs232_tx(rs232_tx), .data_byte(data_byte), .uart_state(uart_state) ); initial clk=1; always#(`clock_period/2) clk=~clk; initial begin rst_n<=1\'b0; data_byte<=8\'d0; send_en<=1\'d0; baud_set=3\'d4; #(`clock_period*20+1) rst_n<=1\'b1; #(`clock_period*50+1) data_byte<=8\'haa; send_en<=1\'d1; #(`clock_period) send_en<=1\'d0; @(posedge tx_done)//等待传输完成的上升沿 #(`clock_period*500)//重新发送 data_byte<=8\'h55; send_en<=1\'d1; #(`clock_period) send_en<=1\'d0; #(`clock_period*500) $stop; end endmodule
仿真结果如下,可以看到,每当有一个send_en 时,databyte都会将串口数据输出出来。
将之前用到的key_filter文件添加进来,作为传输的控制信号。
添加IP核以便使用in system sources and probe editor 工具
将.v文件添加到file中,编写uart_top
module uart_top(clk ,rst_n,rs232_tx,key_in,led); input key_in; input clk; input rst_n; output rs232_tx; wire send_en; wire [7:0]data_byte; output led; wire key_flag,key_state; assign send_en=key_flag&!key_state;//按键检测成功且为低电平时,发生使能 uart_tx uart_tx1( .clk(clk), .rst_n(rst_n), .send_en(send_en), .baud_set(3\'d0), .tx_done(), .rs232_tx(rs232_tx), .data_byte(data_byte), .uart_state(led)//将串口状态用LED显示出来 ); key_filter key_filter0( .clk(clk), .rst_n(rst_n), .key_in(key_in), .key_flag(key_flag), .key_state(key_state) ); ISSP ISSP0( .probe(), .source(data_byte) ); endmodule
进行引脚分配,进行板级验证。
由于DE1-SOC上并未板载USB转TTL模块,这里需要自备一个,然后将模块的RXD口连接到开发板上的GPIO_0D1口上,打开串口调试软件并通过USB转TTL模块连接到FPGA上(波特率设为9600),按下KEY1后,可以看到开发板上的LEDR0快速闪烁一下,串口调试助手接收到00两个数据
打开in system sources and probes editor,
修改格式为hex格式,然后输入任意数据,我这里输入66,
然后再按一下key1,可以看到串口调试软件显示出来66,即我们给的source值,这里发送端的设计就完成了,下面继续按相同的方法设计接收端的协议。
UART数据接收部分:
之前已经讲过了uart 发送端的时序图,对应的,理论上接收端的时序图如下,一般采样在每一位数据的中点是最稳定的。
但是在工业等复杂的环境中,电磁场的干扰是很强的,所以在这里需要进行抗干扰处理,需要多次采样求概率来进行接收,进行改进后的单bit数据接收方式示意图
同样编写Verilog代码:
module uart_rx(clk, rs232_rx, baud_set, rst_n, data_byte, rx_done ); input clk; input rs232_rx; input [2:0]baud_set; input rst_n; output reg [7:0]data_byte; output reg rx_done; reg s0_rs232_rx,s1_rs232_rx;//两个同步寄存器 reg tmp0_rs232_rx,tmp1_rs232_rx;//数据寄存器 wire nedege; reg [15:0]bps_dr;//分频计数器计数最大值 reg [15:0]div_cnt;//分频计数器 reg uart_state; reg bps_clk; reg [7:0]bps_cnt; reg [2:0]r_data_byte [7:0];//前面[2:0]是每一位数据的存储宽度,[7:0]指位宽 reg [2:0] start_bit,stop_bit; //异步信号同步处理,消除亚稳态,有疑惑的看之前的按键消抖部分 always@(posedge clk or negedge rst_n) if(!rst_n) begin s0_rs232_rx<=1\'b0; s1_rs232_rx<=1\'b0; end else begin s0_rs232_rx<=rs232_rx; s1_rs232_rx<=s0_rs232_rx; end //数据寄存 always@(posedge clk or negedge rst_n) if(!rst_n) begin tmp0_rs232_rx<=1\'b0; tmp1_rs232_rx<=1\'b0; end else begin tmp0_rs232_rx<=s1_rs232_rx; tmp1_rs232_rx<=tmp0_rs232_rx; end assign nedege=tmp0_rs232_rx&tmp1_rs232_rx;//下降沿检测 //波特率设置模块 //10^9/波特率/20ns/这里为了稳定1bit数据会采16次所以还要除16 always@(posedge clk or negedge rst_n) if(!rst_n) bps_dr<=16\'d324; else begin case (baud_set) 0:bps_dr<=16\'d324;//9600 1:bps_dr<=16\'d162;//19200 2:bps_dr<=16\'d80;//28400 3:bps_dr<=16\'d53;//57600 4:bps_dr<=16\'d26;//115200 default bps_dr<=16\'d324;//9600 endcase end //分频计数器 always@(posedge clk or negedge rst_n) if(!rst_n) div_cnt<=16\'d0; else if(uart_state)begin if(div_cnt==bps_dr)//到达计数最大值时清零 div_cnt<=16\'d0; else div_cnt<=div_cnt+1\'b1; end else div_cnt<=16\'d0; //单周期的波特率时钟产生 always@(posedge clk or negedge rst_n) if(!rst_n) bps_clk<=1\'b0; else if(div_cnt==16\'d1) bps_clk<=1; else bps_clk<=0; //设计波特率计数器 always@(posedge clk or negedge rst_n) if(!rst_n) bps_cnt<=8\'b0; else if(bps_cnt == 8\'d159 | (bps_cnt == 8\'d12 && (start_bit > 2)))//到12位的时候,start_bit>2说明接收错误,起始位不对 bps_cnt <= 8\'d0;//接收完成或者开始检测到错误信号,波特率时钟停止 else if (bps_clk) bps_cnt<=bps_cnt+1\'b1; else bps_cnt<=bps_cnt; //接收完成信号 always@(posedge clk or negedge rst_n) if(!rst_n) rx_done<=1\'b0; else if(bps_cnt==8\'d159) rx_done<=1\'b1; else rx_done <=1\'b0; //数据读取,对每次采样进行求和值 always@(posedge clk or negedge rst_n) if(!rst_n)begin start_bit=3\'d0; r_data_byte[0]<=3\'d0; r_data_byte[1]<=3\'d0; r_data_byte[2]<=3\'d0; r_data_byte[3]<=3\'d0; r_data_byte[4]<=3\'d0; r_data_byte[5]<=3\'d0; r_data_byte[6]<=3\'d0; r_data_byte[7]<=3\'d0; stop_bit<=3\'d0; end else if(bps_clk)begin case(bps_cnt) 0:begin start_bit = 3\'d0; r_data_byte[0] <= 3\'d0; r_data_byte[1] <= 3\'d0; r_data_byte[2] <= 3\'d0; r_data_byte[3] <= 3\'d0; r_data_byte[4] <= 3\'d0; r_data_byte[5] <= 3\'d0; r_data_byte[6] <= 3\'d0; r_data_byte[7] <= 3\'d0; stop_bit = 3\'d0; end 6,7,8,9,10,11:start_bit <= start_bit + s1_rs232_rx; 22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_rs232_rx; 38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_rs232_rx; 54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_rs232_rx; 70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_rs232_rx; 86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_rs232_rx; 102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_rs232_rx; 118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_rs232_rx; 134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_rs232_rx; 150,151,152,153,154,155:stop_bit <= stop_bit + s1_rs232_rx; default: begin start_bit = start_bit; r_data_byte[0] <= r_data_byte[0]; r_data_byte[1] <= r_data_byte[1]; r_data_byte[2] <= r_data_byte[2]; r_data_byte[3] <= r_data_byte[3]; r_data_byte[4] <= r_data_byte[4]; r_data_byte[5] <= r_data_byte[5]; r_data_byte[6] <= r_data_byte[6]; r_data_byte[7] <= r_data_byte[7串口通信UART