UART原理
通用异步收发传输器(Universal Asynchronous Receiver / Transmitter),通常称作UART,是一种异步收发传输器,是电脑硬件的一部分。将资料由串行通信与并行通信间作传输转换,作为并行输入成为串行输出的芯片,通常集成于其他通讯接口的连结上。
1、UART发送
数据的发送实际上就是按照帧格式将寄存器中的并行数据转为串行数据,为其加上起始位和停止位,以一定的波特率进行传输。波特率可以有多种选择,如9600bits/s,14400bits/s,19200bits/s,38400bits/s等
2、UART接收
由于传输中有可能会产生毛刺,接收端极有可能将毛刺误认为是起始位,所以要对检测到的下降沿进行判别。一般采用如下的方法:取接收端的时钟频率是发送频率的16倍频,当检测到一个下降沿后,在接下来的16个周期内检测数据线上“0”的个数,若“0”的个数超过8个或者10(根据具体情况设置),则认为是起始位到来,否则认为起始位没有到来,继续检测传输线,等待起始位。
3、发送主要代码
//==============================================================================
//开始发送使能
//==============================================================================
reg start_trig_reg1 ;//延迟一拍触发信号
reg start_en ;//开始发送使能信号
always @ (posedge i_100m_clk or negedge i_rst_n)if(!i_rst_n)start_trig_reg1 <=1'b0;elsestart_trig_reg1 <= i_start_trig ;always @ (posedge i_100m_clk or negedge i_rst_n)if(!i_rst_n)start_en <= 1'b0 ;else if(!start_trig_reg1&&i_start_trig)//检测触发信号上升沿start_en <= 1'b1 ;elsestart_en <= 1'b0 ;//==============================================================================
//波特率计算
//==============================================================================always @ (posedge i_100m_clk or negedge i_rst_n)if(!i_rst_n)time_cnt <= 10'h0 ;else if(crt_state==IDLE)time_cnt <= 10'b0 ;else if(time_cnt==10'd867) // 波特率率为115200 ,每一位的周期是8.68us=868个周期time_cnt <= 10'b0 ;elsetime_cnt <=time_cnt + 1'b1 ;//===============================================================================
//并串转换,数据移位
//===============================================================================
always @ (posedge i_100m_clk or negedge i_rst_n)if(!i_rst_n)shift_data <= 10'h3ff;else if((time_cnt == 9'h0) && (next_state == SEND_START))shift_data <= {1'b1,i_tx_data[7:0],1'b0}; //time_cnt 每一次为0时,就需要移出一位数据到TX线上else if(time_cnt == 9'h0)shift_data <= {1'b1,shift_data[9:1]};elseshift_data <= shift_data;//==============================================================================
// 奇偶校验
//==============================================================================
// 奇偶校验位的产生,parity_cnt 为1 时,数据中1的个数为奇数,该位将在发送校验位时
// 发送出去。计算过程是独立的,属于并行的流水线架构。
always @ (posedge i_100m_clk or negedge i_rst_n)if(!i_rst_n)parity_cnt <= 1'h0;else if(crt_state == IDLE)parity_cnt <= 1'h0;else if((time_cnt == 9'd10) && (crt_state == SEND_DATA))parity_cnt <= parity_cnt + shift_data[0];elseparity_cnt = parity_cnt; //===============================================================================
//检测发送的位顺序,位计数器
//===============================================================================
always @ (posedge i_100m_clk or negedge i_rst_n)if(!i_rst_n)bit_cnt <= 4'h0;else if(crt_state != next_state)bit_cnt<= 4'h0 ;else if(time_cnt == 10'd867)bit_cnt<= bit_cnt + 4'h1;elsebit_cnt <=bit_cnt;//------------------------------------------------------------------------------
//发送控制状态机
//------------------------------------------------------------------------------
always @ (posedge i_100m_clk or negedge i_rst_n)
beginif(!i_rst_n)crt_state <= IDLE;elsecrt_state <= next_state;
endalways @ ( *)case(crt_state)IDLE :if( start_en )next_state = SEND_START;//进入发送起始位elsenext_state = crt_state;SEND_START :if((bit_cnt == 4'h0) && (time_cnt == 10'd867))//发送状态必须保持完整的计数周期,每一位的时间严格保证next_state = SEND_DATA ;elsenext_state = crt_state; SEND_DATA :if((bit_cnt == 4'h7) && (time_cnt == 10'd867))//发送8位数据next_state = SEND_PARITY;elsenext_state = crt_state;SEND_PARITY :if((bit_cnt == 4'h0) && (time_cnt == 10'd867))//奇偶校验位next_state = SEND_STOP;elsenext_state = crt_state;SEND_STOP :if((bit_cnt == 4'h0) && (time_cnt == 10'd400))//结束位的持续时间不是867个周期next_state = IDLE;elsenext_state = crt_state;default : next_state = IDLE;endcase//==========================================================================
//数据发送
//============================================================================
always @ (posedge i_100m_clk or negedge i_rst_n)if(!i_rst_n)o_uart_tx_bit <= 1'b1;else if(crt_state[1] || crt_state[2]) //发送数据位和起始位o_uart_tx_bit <= shift_data[0]; else if(crt_state == SEND_PARITY) //发送奇偶校验位o_uart_tx_bit <= parity_cnt; else o_uart_tx_bit <= 1'b1; //==============================================================================
//发送完指示
//==============================================================================always @ (posedge i_100m_clk or negedge i_rst_n) if(!i_rst_n) o_tx_done<= 1'b0 ;else if(crt_state [4]) o_tx_done<= 1'b1 ; elseo_tx_done<= 1'b0 ; endmodule
4、接收主要代码
// 计时
//===============================================================
// 波特率为115200,每一位串行数据持续的周期数=1/115200s=8.68us=868*10ns,因此计数868个周期
reg [9:0] time_cnt ;
reg [3:0] bit_cnt ;always@(posedge i_100m_clk or negedge i_rst_n) if(!i_rst_n) time_cnt <= 10'b0 ;else if(crt_state==idle)time_cnt <= 10'b0 ;else if(time_cnt==10'd867) time_cnt <=10'b0 ;elsetime_cnt <=time_cnt+1'b1 ; always@(posedge i_100m_clk or negedge i_rst_n)//位计数器 if(!i_rst_n) bit_cnt <= 4'b0 ;else if(crt_state==idle)bit_cnt <=4'b0 ;else if(time_cnt==10'd867)bit_cnt <=bit_cnt+1'b1 ;elsebit_cnt <=bit_cnt ;//================================================================
//状态机
//================================================================ always@(posedge i_100m_clk or negedge i_rst_n) if(!i_rst_n) crt_state <= idle ;elsecrt_state <=next_state ;always@( * ) begincase(crt_state)idle : if( fall_edge ) next_state = start ;elsenext_state = crt_state ;start: if(bit_cnt==0&&time_cnt==10'd867) //起始位 next_state =data ;elsenext_state = crt_state ;data: if(bit_cnt==4'd9) //八位数据位next_state = stop ;elsenext_state =crt_state ;
// parity: if(time_cnt==10'd867)next_state = stop ;elsenext_state = crt_state ;stop : if(time_cnt==10'd867)next_state = idle ;elsenext_state =crt_state ; default: next_state = idle ;endcaseend //====================================================================//串并转换,移位输出//====================================================================
reg [7:0] rx_data_reg1 ;always@(posedge i_100m_clk or negedge i_rst_n) if(!i_rst_n)rx_data_reg1 <= 8'b0 ;else if(crt_state[2]&&time_cnt==10'd435) rx_data_reg1 <= {serial_data_r2,rx_data_reg1[7:1]} ;else rx_data_reg1 <=rx_data_reg1 ;always@(posedge i_100m_clk or negedge i_rst_n) if(!i_rst_n) o_rx_data <=8'b 0 ;else if(bit_cnt==4'd8&&time_cnt==10'd700) o_rx_data <=rx_data_reg1 ;elseo_rx_data <=o_rx_data ; ///=======数据输出指示always@(posedge i_100m_clk or negedge i_rst_n) if(!i_rst_n) o_rx_finish <=1'b0 ;else if(bit_cnt==4'd9&&time_cnt==10'd700) o_rx_finish <=1'b1 ;elseo_rx_finish <=1'b0 ; endmodule