赞
踩
uart通信协议简单理解为串转并和并转串的两个模块。同时必须保证数据的正确性。且输入输出端为串行。
此次实现uart协议通过回环来保证数据接收发送的正确。用状态机来理解(也不知道是不是状态机,觉得这样写比较好理解)。
两个接收,发送是对于开发板来说的。开发板的接收端连接的是pc机的发送端。反之。
串口发送接收的速度,一秒传输多少的数据,此次基于9600波特率。(比特率与波特率不同,比特率为在一个波特率内传输的bit数)
(代码仿真的时候,可以使用01010101和10101010这两个数分别来检验模块的正确性)
可以看出,数据为高为通常状况,当需要发送时,首先发送一个低电平,然后开始传输数据,等八位数据发送结束后,再发送一个结束位。这样,结束8bit数据的发送。从上面仿真图的可以很清楚的看出发送接收状况。当接收到起始位时,表示开始接收。具体接收会在下文代码里边具体展现。通过状态机很好的理解。
同时发送的时候,需要先发送最低位。具体在下图表示。
- module uart_rx(
- //-----------input
- clk,rst_n,rx_data,
- //-----------output
- po_data,po_flag
- );
- input clk;
- input rst_n;
- input rx_data;
- output [7:0] po_data;
- output po_flag;
- //------------------------------
- parameter idle = 2'd0,
- start = 2'd1,
- work = 2'd2,
- stop = 2'd3;
- //------------------------------
- /*这里得到5207的方法是系统时钟周期50_000_000/9600 可以改变两者数据实现不同的传输需求*/
- localparam baud_cnt_end = 5207 ;
- localparam baud_cnt_m = (baud_cnt_end + 1) / 2 - 1;
- //------------------------------
- reg rx1;
- reg rx2;
- reg rx_flag;
- reg [12:0] baud_cnt;
- reg bit_flag;
- reg [3:0] bit_cnt;
- reg po_flag;
- reg [7:0] po_data;
- reg [1:0] current_state;
- reg [1:0] next_state;
- //------------------------------
- //第一个进程,同步时序always块,形式固定
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- current_state <= idle;
- else
- current_state <= next_state;
- end
- //第二个always,组合逻辑模块,描述状态迁移条件判断
- always@(*)
- begin
- case(current_state)
- idle:/*空闲状态*/
- begin
- rx_flag = 1'b0;
- begin
- if(nedge)/*当接收到下降沿的时候开始进入发送起始位状态*/
- next_state = start;
- else
- next_state = idle;
- end
- end
- start:/*发送起始位*/
- begin
- rx_flag = 1'b1;
- begin
- if(bit_cnt == 4'b1)
- next_state = work;
- else
- next_state = start;
- end
- end
- work:/*发送数据位*/
- begin
- rx_flag = 1'b1;
- begin
- if(bit_cnt == 4'd9)
- next_state = stop;
- else
- next_state = work;
- end
- end
- stop:/*发送停止位*/
- begin
- rx_flag = 1'b1;
- begin
- if(bit_cnt == 4'd10)
- next_state = idle;
- else
- next_state = stop;
- end
- end
- default:
- begin
- next_state = idle;
- end
- endcase
- end
- //第三个进程,描述输出,同步时序always块
- //下降沿检测------------------------------
- /*跨时钟域处理!!?后文专门解释所有的跨时钟域问题
- 但bit打两拍*/
- //reg rx1;
- //reg rx2;
- //reg rx3;
- always@(posedge clk)
- begin
- rx1 <= rx_data;
- rx2 <= rx1;
- end
- wire nedge;
- assign nedge = rx2 && ~rx1;
- /*波特率计数,在发送状态计数*/
- //定义band_cnt------------------------------
- //reg [12:0] baud_cnt;
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- baud_cnt <= 13'd0;
- else
- if(baud_cnt == baud_cnt_end)
- baud_cnt <= 13'd0;
- else
- if(rx_flag)
- baud_cnt <= baud_cnt + 1'b1;
- else
- baud_cnt <= 13'd0;
- end
- /*发送接收位这里取数据是在接收中点处取数据,因为是串转并,所以在哪里接收数据都无所谓后面等会了
- 把小梅哥用数组接收总结下*/
- //define bit_flag------------------------------
- //reg bit_flag;
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- bit_flag <= 1'b0;
- else
- if(baud_cnt == baud_cnt_m)
- bit_flag <= 1'b1;
- else
- bit_flag <= 1'b0;
- end
- /*发送的是第多少位*/
- //define bit_cnt------------------------------
- //reg [3:0] bit_cnt;
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- bit_cnt <= 4'd0;
- else
- if(rx_flag == 0)
- bit_cnt <= 4'd0;
- else
- if(baud_cnt == baud_cnt_end)
- bit_cnt <= bit_cnt + 1'b1;
- end
- /*接收结束信号*/
- //define po_flag------------------------------
- //reg po_flag;
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- po_flag <= 1'b0;
- else
- if(bit_cnt == 4'd10)
- po_flag <= 1'b1;
- else
- po_flag <= 1'b0;
- end
- /*数据寄存*/
- reg [7:0] po_data_r;
- always@(posedge clk )
- begin
- if(bit_cnt == 4'd10)
- po_data <= po_data_r;
- else
- po_data <= 8'd0;
- end
- /*数据转换,串行转并行数据,在相应的数据接收数据,忽略起始位和结束位*/
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- po_data_r <= 8'b0;
- else
- if(rx_flag)
- if(bit_flag)
- case(bit_cnt)
- 4'd1 : po_data_r[0] <= rx2 ;
- 4'd2 : po_data_r[1] <= rx2 ;
- 4'd3 : po_data_r[2] <= rx2 ;
- 4'd4 : po_data_r[3] <= rx2 ;
- 4'd5 : po_data_r[4] <= rx2 ;
- 4'd6 : po_data_r[5] <= rx2 ;
- 4'd7 : po_data_r[6] <= rx2 ;
- 4'd8 : po_data_r[7] <= rx2 ;
- default:po_data_r <= po_data_r ;
- endcase
- else
- po_data_r <= po_data_r;
- else
- po_data_r <= 8'd0;
- end
- endmodule
A:修改前面一些时序的小问题(保证输出有效拉高点输出数据,可以不使用状态机,使用状态机纯粹当初是为了加深理解,这里我把代码还保留,只不过注释掉了,具体可以参考)
B:波特率可改变,改变前面的定义,可以实现任意波特率的发送与接收。可用于图片发送等等一系列模块(奇偶数可以改变,同时改变中点值即可)
查找表:
1,接收模块
- module uart_rx(
- //-----------input
- clk,rst_n,rx_data,
- //-----------output
- po_data,rx_done
- );
- input clk;
- input rst_n;
- input rx_data;
- output [7:0] po_data;
- output reg rx_done;
-
- localparam baud_cnt_end = 'd434 - 'd1 ;
- localparam baud_cnt_m = (baud_cnt_end ) / 'd2 ;
- // localparam baud_cnt_m = (baud_cnt_end + 'd1) / 'd2 - 'd1 ;
- //------------------------------
- reg rx1;
- reg rx2;
- reg rx_flag;
- reg [12:0] baud_cnt;
- reg bit_flag;
- reg [3:0] bit_cnt;
- reg [7:0] po_data;
-
-
- //第三个进程,描述输出,同步时序always块
- //下降沿检测------------------------------
- //reg rx1;
- //reg rx2;
- //reg rx3;
- always@(posedge clk)
- begin
- rx1 <= rx_data;
- rx2 <= rx1;
- end
- wire nedge;
- assign nedge = rx2 && ~rx1 && !rx_flag;
- // define rx_flag
- always @ (posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- rx_flag <= 1'b0;
- else
- if(nedge)
- rx_flag <= 1'b1;
- else
- if(bit_cnt == 'd8 && baud_cnt == baud_cnt_end)
- rx_flag <= 1'b0;
- end
- //定义band_cnt------------------------------
- //reg [12:0] baud_cnt;
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- baud_cnt <= 13'd0;
- else
- if(baud_cnt == baud_cnt_end)
- baud_cnt <= 13'd0;
- else
- if(rx_flag)
- baud_cnt <= baud_cnt + 1'b1;
- else
- baud_cnt <= 13'd0;
- end
- //define bit_flag------------------------------
- //reg bit_flag;
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- bit_flag <= 1'b0;
- else
- if(baud_cnt == baud_cnt_m)
- bit_flag <= 1'b1;
- else
- bit_flag <= 1'b0;
- end
- //define bit_cnt------------------------------
- //reg [3:0] bit_cnt;
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- bit_cnt <= 4'd0;
- else
- if(rx_flag == 0)
- bit_cnt <= 4'd0;
- else
- if(baud_cnt == baud_cnt_end && rx_flag)
- bit_cnt <= bit_cnt + 1'b1;
- end
- //define rx_done------------------------------
-
- //reg rx_done;
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- rx_done <= 1'b0;
- else
- if(bit_cnt == 'd8 && baud_cnt == baud_cnt_end - 'd1)
- rx_done <= 1'b1;
- else
- rx_done <= 1'b0;
- end
- reg [7:0] po_data_r;
- always@(posedge clk )
- begin
- if(bit_cnt == 'd8 && baud_cnt == baud_cnt_end - 'd1)
- po_data <= po_data_r;
- else
- po_data <= 8'd0;
- end
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- po_data_r <= 8'b0;
- else
- if(rx_flag)
- if(bit_flag)
- case(bit_cnt)
- 4'd1 : po_data_r[0] <= rx2 ;
- 4'd2 : po_data_r[1] <= rx2 ;
- 4'd3 : po_data_r[2] <= rx2 ;
- 4'd4 : po_data_r[3] <= rx2 ;
- 4'd5 : po_data_r[4] <= rx2 ;
- 4'd6 : po_data_r[5] <= rx2 ;
- 4'd7 : po_data_r[6] <= rx2 ;
- 4'd8 : po_data_r[7] <= rx2 ;
- default:po_data_r <= po_data_r ;
- endcase
- else
- po_data_r <= po_data_r;
- else
- po_data_r <= 8'd0;
- end
-
- endmodule
- // //------------------------------
- // parameter idle = 2'd0,
- // start = 2'd1,
- // work = 2'd2,
- // stop = 2'd3;
- // //------------------------------
- // reg [1:0] current_state;
- // always@(*)
- // begin
- // case(current_state)
- // idle:
- // if(nedge)
- // current_state = start;
- // else
- // current_state = idle;
- // start:
- // if(bit_cnt == 4'b1)
- // current_state = work;
- // else
- // current_state = start;
- // work:
- // if(bit_cnt == 'd7 && baud_cnt == baud_cnt_end)
- // current_state = stop;
- // else
- // current_state = work;
- // stop:
- // if(rx_done)
- // current_state = idle;
- // else
- // current_state = stop;
- // default:
- // current_state = idle;
- // endcase
- // end
2,发送模块
- module uart_tx(
- //-----------input
- clk,rst_n,pi_data,tx_en,
- //-----------output
- tx_data
- );
- input clk;
- input rst_n;
- input [7:0] pi_data;
- input tx_en;
- output reg tx_data;
-
- localparam baud_cnt_end = 'd434 - 'd1 ;
- // localparam baud_cnt_m = (baud_cnt_end ) / 'd2 ;
- // localparam baud_cnt_m = (baud_cnt_end + 'd1) / 'd2 - 'd1 ;
-
-
- reg tx_en1;
- reg tx_en2;
- reg tx_flag;
- reg [12:0] baud_cnt;
- reg [3:0] bit_cnt;
- always@(posedge clk )
- begin
- tx_en1 <= tx_en;
- tx_en2 <= tx_en1;
- end
-
- always @ (posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- tx_flag <= 1'b1;
- else
- if(tx_en2 && !tx_flag)
- tx_flag <= 1'b1;
- else
- if(bit_cnt == 'd8 && baud_cnt == baud_cnt_end)
- tx_flag <= 1'b0;
- end
- //reg [3:0] bit_cnt;
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- bit_cnt <= 4'd0;
- else
- if(!tx_flag)
- bit_cnt <= 4'd0;
- else
- if(baud_cnt == baud_cnt_end)
- bit_cnt <= bit_cnt + 1'b1;
- else
- bit_cnt <= bit_cnt;
- end
- //reg [12:0] baud_cnt;
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- baud_cnt <= 13'd0;
- else
- if(baud_cnt == baud_cnt_end)
- baud_cnt <= 13'd0;
- else
- if(tx_flag)
- baud_cnt <= baud_cnt + 1'b1;
- else
- baud_cnt <= 13'd0;
- end
- reg [7:0] pi_data_r;
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- pi_data_r <= 8'd0;
- else
- if(tx_en)
- pi_data_r <= pi_data;
- else
- pi_data_r <= pi_data_r;
- end
- //define tx_data
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- tx_data <= 1'b1;
- else
- if(tx_flag)
- case(bit_cnt)
- 4'd0 : tx_data <= 1'b0 ;
- 4'd1 : tx_data <= pi_data_r[0] ;
- 4'd2 : tx_data <= pi_data_r[1] ;
- 4'd3 : tx_data <= pi_data_r[2] ;
- 4'd4 : tx_data <= pi_data_r[3] ;
- 4'd5 : tx_data <= pi_data_r[4] ;
- 4'd6 : tx_data <= pi_data_r[5] ;
- 4'd7 : tx_data <= pi_data_r[6] ;
- 4'd8 : tx_data <= pi_data_r[7] ;
- 4'd9 : tx_data <= 1'b1 ;
- default:;
- endcase
- else
- tx_data <= 1'b1;
- end
-
- endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。