uart串口通信
Posted liujiahong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了uart串口通信相关的知识,希望对你有一定的参考价值。
以下知识是通过学习小梅哥FPGA后所总结的思路
USB转TTL电路图:
UART发送端发送一个字节数据时序图:
对于其中的每一位进行采样,一般情况下每一位数据的中间点是最稳定的,因此一般应用中,采集中间时刻时的数据即可,如下图所示:
但是在工业应用中,往往有非常强的电磁干扰,只采样一次就作为该数据的电平判定,是不保险的,有可能恰好采集到被干扰的信号而导致结果出错,因此需要使用多次采样求概率的方式进行。以下为改进型的单bit数据接收方式示意图:
在这张图中,将每一位数据又平均分成了16小段,对于Bit_x这一位数据,考虑到数据在刚刚发生变化和即将发生变化的这一时期,数据极有可能不稳定的(用红色标出的两段),在这两个时间段采集数据,很有可能得到错误的结果,因此这两段时间的电平无效,采集时直接忽略。而中间这一时间段(用绿色标出),数据本身是比较稳定的,一般都代表了正确的结果。但是也不排除该段数据受强电磁干扰而出现错误的电平脉冲,因此对这一段电平,进行多次采样,并求高低电平发生的概率,6次采集结果中,取出现次数多的电平作为采样结果。例如,采样6次的结果分别为1/1/1/1/0/1/,则取电平结果为1,若为0/0/1/0/0/0,,则取电平结果为0,当6次采样结果中1和0各占一半(各3次),则可判断当前通信线路环境非常恶劣,数据不具有可靠性。
串口发送模块包含两个主要组件:
1、 起始位检测进程
2、 波特率产生模块
3、 数据接收进程
串口接收模块整体结构体:
串口发送模块详细结构图
波特率时钟计算:
系统时钟周期为System_clk_period
9600 |
104167ns |
104167/ System_clk_period/16 |
325-1 |
19200 |
52083ns |
52083/ System_clk_period/16 |
163-1 |
38400 |
26041ns |
26041/ System_clk_period/16 |
81-1 |
57600 |
17361ns |
17361/ System_clk_period/16 |
54-1 |
115200 |
8680ns |
8680/ System_clk_period/16 |
27-1 |
//串口接收模块 module uart_byte_tx( Clk, Rst_n, data_byte, send_en, baud_set, Rs232_Tx, Tx_Done, 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; reg bps_clk; //波特率时钟周期 reg [15:0]div_cnt;//分频计数器:每计数满一轮,即计数满一个波特率周期, // 即div_cnt = bps_DR 时该寄存器则清零重新计数 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 <= 1‘b0; else if(send_en) uart_state <= 1‘b1; else if(bps_cnt == 4‘d11) 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; else r_data_byte <= r_data_byte; always@(posedge Clk or negedge Rst_n) if(!Rst_n) bps_DR <= 16‘d5207; else begin case(baud_set) //不同波特率所对应的分频计数最大值 0:bps_DR <= 16‘d5207; 1:bps_DR <= 16‘d2603; 2:bps_DR <= 16‘d1301; 3:bps_DR <= 16‘d867; 4:bps_DR <= 16‘d433; default:bps_DR <= 16‘d5207; endcase end //counter 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; // bps_clk gen always@(posedge Clk or negedge Rst_n) if(!Rst_n) bps_clk <= 1‘b0; else if(div_cnt == 16‘d1) bps_clk <= 1‘b1; else bps_clk <= 1‘b0; //bps counter always@(posedge Clk or negedge Rst_n) if(!Rst_n) bps_cnt <= 4‘d0; else if(bps_cnt == 4‘d11) bps_cnt <= 4‘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) 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; //bps_cnt的值会持续一个波特率周期才会改变 1:Rs232_Tx <= START_BIT; //一个波特率周期 = 发送一个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
//串口接收模块 module uart_byte_rx( Clk, Rst_n, baud_set, Rs232_Rx, data_byte, Rx_Done ); input Clk; input Rst_n; input [2:0]baud_set; input Rs232_Rx; output reg [7:0]data_byte; output reg Rx_Done; reg s0_Rs232_Rx,s1_Rs232_Rx;//同步寄存器 reg tmp0_Rs232_Rx,tmp1_Rs232_Rx;//数据寄存器 reg [15:0]bps_DR;//分频计数器计数最大值 reg [15:0]div_cnt;//分频计数器 reg bps_clk;// reg [7:0]bps_cnt; reg uart_state; reg [2:0] r_data_byte [7:0]; //定义8个位宽为3的寄存器 reg [7:0] tmp_data_byte; reg [2:0] START_BIT,STOP_BIT; //起始位和停止位 wire nedege; //同步寄存器,消除亚稳态 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; always@(posedge Clk or negedge Rst_n) if(!Rst_n) bps_DR <= 16‘d324; else begin case(baud_set) 0:bps_DR <= 16‘d324; 1:bps_DR <= 16‘d162; 2:bps_DR <= 16‘d80; 3:bps_DR <= 16‘d53; 4:bps_DR <= 16‘d26; default:bps_DR <= 16‘d324; endcase end //counter 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; // bps_clk gen always@(posedge Clk or negedge Rst_n) if(!Rst_n) bps_clk <= 1‘b0; else if(div_cnt == 16‘d1) bps_clk <= 1‘b1; else bps_clk <= 1‘b0; //bps counter always@(posedge Clk or negedge Rst_n) if(!Rst_n) bps_cnt <= 8‘d0; else if(bps_cnt == 8‘d159 | (bps_cnt == 8‘d12 && (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) // data_byte <= 8‘d0; // else if(bps_cnt == 8‘d159) // data_byte <= tmp_data_byte; // else // data_byte <= data_byte; always@(posedge Clk or negedge Rst_n) if(!Rst_n) data_byte <= 8‘d0; else if(bps_cnt == 8‘d159)begin data_byte[0] <= r_data_byte[0][2]; //r_data_byte[0]寄存器位宽为3, 000=0 , 001=1 ,010=2 ,011=3 data_byte[1] <= r_data_byte[1][2]; // 100=4 , 101=5 ,110=6 ,111=7 data_byte[2] <= r_data_byte[2][2]; //可以发现当r_data_byte[0]寄存器的值>3时,data_byte[0]寄存器所接收的值为1, data_byte[3] <= r_data_byte[3][2]; //而r_data_byte[0][2]的值恰好都=1。所以此处取r_data_byte[0]寄存器的的二位值作为接收到的有效数据 data_byte[4] <= r_data_byte[4][2]; data_byte[5] <= r_data_byte[5][2]; data_byte[6] <= r_data_byte[6][2]; data_byte[7] <= r_data_byte[7][2]; end 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次数据的值累加存在r_data_byte寄存器 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; //采样6次数据,如果6次数据的和大于3,则表示所采集的该bit位的数据位1, 38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rs232_Rx; //如果如果6次数据的和小于3,则表示所采集的该bit位的数据位0, 54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rs232_Rx; //如果如果6次数据的和等于3,则表示所采集的该bit位的数据位无效, 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]; STOP_BIT = STOP_BIT; end endcase end always@(posedge Clk or negedge Rst_n) if(!Rst_n) uart_state <= 1‘b0; else if(nedege) uart_state <= 1‘b1; else if(Rx_Done || (bps_cnt == 8‘d12 && (START_BIT > 2))) uart_state <= 1‘b0; else uart_state <= uart_state; endmodule
以上是关于uart串口通信的主要内容,如果未能解决你的问题,请参考以下文章
arduino(esp32c3) uart通信问题 Serial.write发送到哪了?