赞
踩
异步收发传输器(Universal Asynchronous Receiver/Transmitter)简称UART。
在发送数据时,将并行数据转换为串行数据来传输;在接收数据时,将接收的串行数据转换为并行数据。
UART和SPI、IIC不同,UART是异步通信接口,异步通信的接收方不知道数据什么时候送达,因此发送方和接收方要约定传输的波特率。
波特率:每秒钟传输码元的数量,用符号“Baud”表示,单位“波特每秒(Bps)”。串口常见的波特率有4800、9600、115200等,本节实验选用9600的波特率。
比特率:每秒钟传输比特的数量,单位“每秒比特数(bps)”。
比特率=波特率 * 单个调制状态对应的二进制位数,串口通信是采用二进制调制方式,因此串口的比特率为=9600Bps *1bit= 9600bps
串口发送、接收1bit数据的时间为一个波特,即1/9600秒,如果用FPGA的50Mhz(周期为20ns)的系统时钟来计数,需要计数cnt=50 000 000 / 9600 ≈ 5208个系统时钟周期,即每个bit数据之间在50Mhz的时钟频率下计数5208次。
1.RS232是UART的一种,没有时钟线,只有两根数据线rx和tx,两根线都是1bit位宽。
例:发送十进制数198,它的二进制为11000110,串行通信发送方只需要一根数据线就可以传输11000110。
2.rx位宽1bit,PC机通过串口调试助手往FPGA发数据,rx从低位往高位一次接收,最后在FPGA里拼接成8bit数据。
3.tx位宽1bit,FPGA通过串口调试助手往PC机发8bit,FPGA把8bit数据通过tx从最低位到最高位依次发送,最后上位机通过串口助手把一位一位的数据拼接成8bit数据。
4.帧结构,即一帧一帧发送和接收数据,每一帧除了8bit数据还有1bit起始位和1bit停止位。
module uart_rx #( parameter UART_BPS = 'd9600, //串口波特率 parameter CLK_FREQ = 'd50_000_000 //时钟频率 ) ( input wire sys_clk , //系统时钟50MHz input wire sys_rst_n , //全局复位 input wire rx , //串口接收数据 output reg [7:0] po_data , //串转并后的8bit数据 output reg po_flag //串转并后的数据有效标志信号 ); //********************************************************************// //****************** Parameter and Internal Signal *******************// //********************************************************************// //localparam define localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ; //5208 //reg define reg rx_reg1 ; reg rx_reg2 ; reg rx_reg3 ; reg start_nedge ; reg work_en ; reg [12:0] baud_cnt ; reg bit_flag ; reg [3:0] bit_cnt ; reg [7:0] rx_data ; reg rx_flag ; //********************************************************************// //***************************** Main Code ****************************// //********************************************************************// //插入两级寄存器进行数据同步,用来消除亚稳态 //rx_reg1:第一级寄存器,寄存器空闲状态复位为1 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rx_reg1 <= 1'b1; else rx_reg1 <= rx; //第一个寄存器用于系统同步,表示数据位于系统时钟下 //rx_reg2:第二级寄存器,寄存器空闲状态复位为1 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rx_reg2 <= 1'b1; else rx_reg2 <= rx_reg1; //rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rx_reg3 <= 1'b1; else rx_reg3 <= rx_reg2; //start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) start_nedge <= 1'b0; else if((~rx_reg2) && (rx_reg3)) //rx_reg2低电平 && rx_reg3高电平 start_nedge <= 1'b1; else start_nedge <= 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(start_nedge == 1'b1) work_en <= 1'b1; else if((bit_cnt == 4'd8) && (bit_flag == 1'b1)) work_en <= 1'b0; //baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) baud_cnt <= 13'b0; else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0)) baud_cnt <= 13'b0; else if(work_en == 1'b1) baud_cnt <= baud_cnt + 1'b1; //bit_flag:当baud_cnt计数器计数到中间数时采样的数据最稳定, //此时拉高一个标志信号表示数据可以被取走 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) bit_flag <= 1'b0; else if(baud_cnt == BAUD_CNT_MAX/2 - 1) bit_flag <= 1'b1; else bit_flag <= 1'b0; //bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位) //都接收完成后计数器清零 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) bit_cnt <= 4'b0; else if((bit_cnt == 4'd8) && (bit_flag == 1'b1)) bit_cnt <= 4'b0; else if(bit_flag ==1'b1) bit_cnt <= bit_cnt + 1'b1; //rx_data:输入数据进行移位 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:输入数据移位完成时rx_flag拉高一个时钟的高电平 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rx_flag <= 1'b0; else if((bit_cnt == 4'd8) && (bit_flag == 1'b1)) rx_flag <= 1'b1; else rx_flag <= 1'b0; //po_data:输出完整的8位有效数据 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) po_data <= 8'b0; else if(rx_flag == 1'b1) po_data <= rx_data; //po_flag:输出数据有效标志(比rx_flag延后一个时钟周期,为了和po_data同步) always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) po_flag <= 1'b0; else po_flag <= rx_flag; endmodule
module uart_tx #( parameter UART_BPS = 'd9600, //串口波特率 parameter CLK_FREQ = 'd50_000_000 //时钟频率 ) ( input wire sys_clk , //系统时钟50MHz input wire sys_rst_n , //全局复位 input wire [7:0] pi_data , //模块输入的8bit数据 input wire pi_flag , //并行数据有效标志信号 output reg tx //串转并后的1bit数据 ); //********************************************************************// //****************** Parameter and Internal Signal *******************// //********************************************************************// //localparam define localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ; //reg define reg [12:0] baud_cnt; reg bit_flag; reg [3:0] bit_cnt ; reg work_en ; //********************************************************************// //***************************** Main Code ****************************// //********************************************************************// //work_en:接收数据工作使能信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) work_en <= 1'b0; else if(pi_flag == 1'b1) work_en <= 1'b1; else if((bit_flag == 1'b1) && (bit_cnt == 4'd9)) work_en <= 1'b0; //baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) baud_cnt <= 13'b0; else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0)) baud_cnt <= 13'b0; else if(work_en == 1'b1) baud_cnt <= baud_cnt + 1'b1; //bit_flag:当baud_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) bit_flag <= 1'b0; else if(baud_cnt == 13'd1) bit_flag <= 1'b1; else bit_flag <= 1'b0; //bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) bit_cnt <= 4'b0; else if((bit_flag == 1'b1) && (bit_cnt == 4'd9)) bit_cnt <= 4'b0; else if((bit_flag == 1'b1) && (work_en == 1'b1)) bit_cnt <= bit_cnt + 1'b1; //tx:输出数据在满足rs232协议(起始位为0,停止位为1)的情况下一位一位输出 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; endcase endmodule
module rs232 ( input wire sys_clk , //系统时钟50MHz input wire sys_rst_n , //全局复位 input wire rx , //串口接收数据 output wire tx //串口发送数据 ); //********************************************************************// //****************** Parameter and Internal Signal *******************// //********************************************************************// //parameter define parameter UART_BPS = 20'd9600 , //比特9600 CLK_FREQ = 26'd50_000_000 ; //时钟频率 //wire define wire [7:0] po_data; wire po_flag; //********************************************************************// //*************************** Instantiation **************************// //********************************************************************// //------------------------ uart_rx_inst ------------------------ uart_rx #( .UART_BPS (UART_BPS ), //串口波特率 .CLK_FREQ (CLK_FREQ ) //时钟频率 ) uart_rx_inst ( .sys_clk (sys_clk ), //input sys_clk .sys_rst_n (sys_rst_n ), //input sys_rst_n .rx (rx ), //input rx .po_data (po_data ), //output [7:0] po_data .po_flag (po_flag ) //output po_flag ); //------------------------ uart_tx_inst ------------------------ uart_tx #( .UART_BPS (UART_BPS ), //串口波特率 .CLK_FREQ (CLK_FREQ ) //时钟频率 ) uart_tx_inst ( .sys_clk (sys_clk ), //input sys_clk .sys_rst_n (sys_rst_n ), //input sys_rst_n .pi_data (po_data ), //input [7:0] pi_data .pi_flag (po_flag ), //input pi_flag .tx (tx ) //output tx ); endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。