串口通信原理
并行通信是指數據的各個位用多條數據線同時進行傳輸?
優點:傳輸速度快
缺點:占用引腳資源多
?串行通信是將數據分成一位一位的形式在一條傳輸線上逐個傳輸
?優點:通信線路簡單、占用引腳資源少
缺點:傳輸速度慢
同步通信:帶時鐘同步信號的數據傳輸;發送方和接收方在同一時鐘的控制下,同步傳輸數據。
異步通信:不帶時鐘同步信號的數據傳輸。發送方與接收方使用各自的時鐘控制數據的發送和接收過程。
串行通信的傳輸方向:
單工 ? :數據只能沿一個方向傳輸
半雙工:數據傳輸可以沿兩個方向,但需要分時進行
全雙工:數據可以同時進行雙向傳輸
下面是常見的串行通信接口
?UART (universal asynchronous receiver-transmitter) 是一種采用異步串行通信方式的通用異步收發傳輸器。它在發送數據時將并行數據轉換成串行數據來傳輸,在接收數據時將接收到的串行數據轉換成并行數據。協議層: ? 通信協議(包括數據格式、傳輸速率等) 物理層:接口類型、電平標準等。
?UART串口通信需要兩根信號線來實現,一根用于串口發送,另外一根負責串口接收。
串口通信的速率用波特率表示,它表示每秒傳輸二進制數據的位數,單位是bps(位/秒) 常用的波特率有9600、19200、38400、57600以及115200等。
針對異步串行通信的接口標準有RS23、RS422、RS485等
1.通信實驗:開發板與上位機通過串口通信,完成數據環回實驗
2.程序設計:
首先是分頻時鐘,用pll去設計ip核,輸入時鐘是50MHZ,輸出一個時鐘為1MHz,用于仿真。
具體ip核設計可以去看前面一篇文章:pll鎖相環(可以根據系統時鐘進行倍頻、分頻、相位偏移等等,而普通的計數器只能分頻)_小泡芙?的博客-CSDN博客
接收模塊:
?
module uart_recv(input sys_clk, //系統時鐘input sys_rst_n, //系統復位,低電平有效input uart_rxd, //UART接收端口output reg uart_done, //接收一幀數據完成標志信號output reg [7:0] uart_data //接收的數據);//parameter define parameter CLK_FREQ = 50000000; //系統時鐘頻率 parameter UART_BPS = 9600; //串口波特率 localparam BPS_CNT = CLK_FREQ/UART_BPS; //為得到指定波特率,//需要對系統時鐘計數BPS_CNT次 //reg define reg uart_rxd_d0; reg uart_rxd_d1; reg [15:0] clk_cnt; //系統時鐘計數器 reg [ 3:0] rx_cnt; //接收數據計數器 reg rx_flag; //接收過程標志信號 reg [ 7:0] rxdata; //接收數據寄存器//wire define wire start_flag;//***************************************************** //** main code //***************************************************** //捕獲接收端口下降沿(起始位),得到一個時鐘周期的脈沖信號 assign start_flag = uart_rxd_d1 & (~uart_rxd_d0); //對UART接收端口的數據延遲兩個時鐘周期 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) begin uart_rxd_d0 <= 1'b0;uart_rxd_d1 <= 1'b0; endelse beginuart_rxd_d0 <= uart_rxd; uart_rxd_d1 <= uart_rxd_d0;end end//當脈沖信號start_flag到達時,進入接收過程 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) rx_flag <= 1'b0;else beginif(start_flag) //檢測到起始位rx_flag <= 1'b1; //進入接收過程,標志位rx_flag拉高else if((rx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))rx_flag <= 1'b0; //計數到停止位中間時,停止接收過程elserx_flag <= rx_flag;end end//進入接收過程后,啟動系統時鐘計數器與接收數據計數器 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) begin clk_cnt <= 16'd0; rx_cnt <= 4'd0;end else if ( rx_flag ) begin //處于接收過程if (clk_cnt < BPS_CNT - 1) beginclk_cnt <= clk_cnt + 1'b1;rx_cnt <= rx_cnt;endelse beginclk_cnt <= 16'd0; //對系統時鐘計數達一個波特率周期后清零rx_cnt <= rx_cnt + 1'b1; //此時接收數據計數器加1endendelse begin //接收過程結束,計數器清零clk_cnt <= 16'd0;rx_cnt <= 4'd0;end end//根據接收數據計數器來寄存uart接收端口數據 always @(posedge sys_clk or negedge sys_rst_n) begin if ( !sys_rst_n) rxdata <= 8'd0; else if(rx_flag) //系統處于接收過程if (clk_cnt == BPS_CNT/2) begin //判斷系統時鐘計數器計數到數據位中間case ( rx_cnt )4'd1 : rxdata[0] <= uart_rxd_d1; //寄存數據位最低位4'd2 : rxdata[1] <= uart_rxd_d1;4'd3 : rxdata[2] <= uart_rxd_d1;4'd4 : rxdata[3] <= uart_rxd_d1;4'd5 : rxdata[4] <= uart_rxd_d1;4'd6 : rxdata[5] <= uart_rxd_d1;4'd7 : rxdata[6] <= uart_rxd_d1;4'd8 : rxdata[7] <= uart_rxd_d1; //寄存數據位最高位default:; endcaseendelse rxdata <= rxdata;elserxdata <= 8'd0; end//數據接收完畢后給出標志信號并寄存輸出接收到的數據 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) beginuart_data <= 8'd0; uart_done <= 1'b0;endelse if(rx_cnt == 4'd9) begin //接收數據計數器計數到停止位時 uart_data <= rxdata; //寄存輸出接收到的數據uart_done <= 1'b1; //并將接收完成標志位拉高endelse beginuart_data <= 8'd0; uart_done <= 1'b0; end endendmodule發送模塊
module uart_send(input sys_clk, //系統時鐘input sys_rst_n, //系統復位,低電平有效input uart_en, //發送使能信號input [7:0] uart_din, //待發送數據output reg uart_txd //UART發送端口);//parameter define parameter CLK_FREQ = 50000000; //系統時鐘頻率 parameter UART_BPS = 9600; //串口波特率 localparam BPS_CNT = CLK_FREQ/UART_BPS; //為得到指定波特率,對系統時鐘計數BPS_CNT次//reg define reg uart_en_d0; reg uart_en_d1; reg [15:0] clk_cnt; //系統時鐘計數器 reg [ 3:0] tx_cnt; //發送數據計數器 reg tx_flag; //發送過程標志信號 reg [ 7:0] tx_data; //寄存發送數據//wire define wire en_flag;//***************************************************** //** main code //***************************************************** //捕獲uart_en上升沿,得到一個時鐘周期的脈沖信號 assign en_flag = (~uart_en_d1) & uart_en_d0;//對發送使能信號uart_en延遲兩個時鐘周期 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) beginuart_en_d0 <= 1'b0; uart_en_d1 <= 1'b0;end else begin uart_en_d0 <= uart_en; uart_en_d1 <= uart_en_d0; end end//當脈沖信號en_flag到達時,寄存待發送的數據,并進入發送過程 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) begin tx_flag <= 1'b0;tx_data <= 8'd0;end else if (en_flag) begin //檢測到發送使能上升沿 tx_flag <= 1'b1; //進入發送過程,標志位tx_flag拉高tx_data <= uart_din; //寄存待發送的數據endelse if ((tx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))begin //計數到停止位中間時,停止發送過程tx_flag <= 1'b0; //發送過程結束,標志位tx_flag拉低tx_data <= 8'd0;endelse begintx_flag <= tx_flag;tx_data <= tx_data;end end//進入發送過程后,啟動系統時鐘計數器與發送數據計數器 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) begin clk_cnt <= 16'd0; tx_cnt <= 4'd0;end else if (tx_flag) begin //處于發送過程if (clk_cnt < BPS_CNT - 1) beginclk_cnt <= clk_cnt + 1'b1;tx_cnt <= tx_cnt;endelse beginclk_cnt <= 16'd0; //對系統時鐘計數達一個波特率周期后清零tx_cnt <= tx_cnt + 1'b1; //此時發送數據計數器加1endendelse begin //發送過程結束clk_cnt <= 16'd0;tx_cnt <= 4'd0;end end//根據發送數據計數器來給uart發送端口賦值 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) uart_txd <= 1'b1; else if (tx_flag)case(tx_cnt)4'd0: uart_txd <= 1'b0; //起始位 4'd1: uart_txd <= tx_data[0]; //數據位最低位4'd2: uart_txd <= tx_data[1];4'd3: uart_txd <= tx_data[2];4'd4: uart_txd <= tx_data[3];4'd5: uart_txd <= tx_data[4];4'd6: uart_txd <= tx_data[5];4'd7: uart_txd <= tx_data[6];4'd8: uart_txd <= tx_data[7]; //數據位最高位4'd9: uart_txd <= 1'b1; //停止位default: ;endcaseelse uart_txd <= 1'b1; //空閑時發送端口為高電平 endendmodule?頂層模塊
?
module uart_top(input sys_clk, //外部50M時鐘input sys_rst_n, //外部復位信號,低有效//uart接口input uart_rxd, //UART接收端口output uart_txd //UART發送端口);//parameter define parameter CLK_FREQ = 50000000; //定義系統時鐘頻率 parameter UART_BPS = 115200; //定義串口波特率//wire define wire uart_en_w; //UART發送使能 wire [7:0] uart_data_w; //UART發送數據 wire clk_1m_w; //1MHz時鐘,用于仿真調試//***************************************************** //** main code //***************************************************** pll_clk u_pll( //時鐘分頻模塊,用于調試.inclk0 (sys_clk),.c0 (clk_1m_w) );uart_recv #( //串口接收模塊.CLK_FREQ (CLK_FREQ), //設置系統時鐘頻率.UART_BPS (UART_BPS)) //設置串口接收波特率 u_uart_recv( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n),.uart_rxd (uart_rxd),.uart_done (uart_en_w),.uart_data (uart_data_w));uart_send #( //串口發送模塊.CLK_FREQ (CLK_FREQ), //設置系統時鐘頻率.UART_BPS (UART_BPS)) //設置串口發送波特率 u_uart_send( .sys_clk (sys_clk),.sys_rst_n (sys_rst_n),.uart_en (uart_en_w),.uart_din (uart_data_w),.uart_txd (uart_txd));endmodule仿真模塊
?
`timescale 1 ns/ 1 ns module uart_top_tb();parameter T = 20; reg sys_clk; reg sys_rst_n; reg uart_rxd;wire uart_txd;wire uart_en_w; //UART發送使能 wire [7:0] uart_data_w; //UART發送數據 wire clk_1m_w; reg uart_en; //發送使能信號 reg [7:0] uart_din; //待發送數據reg uart_done; //接收一幀數據完成標志信號 reg [7:0] uart_data; initial begin sys_clk = 1'b0;sys_rst_n = 1'b0;uart_rxd = 1;#200 sys_rst_n = 1'b1;//模擬發送一幀數據#200 uart_rxd = 0; //起始位#110000 uart_rxd = 0; #110000 uart_rxd = 1; #110000 uart_rxd = 1; #110000 uart_rxd = 0; #110000 uart_rxd = 0; #110000 uart_rxd = 1; #110000 uart_rxd = 0; #110000 uart_rxd = 1; //停止位#1500000 $stop;end always #(T/2) sys_clk = ~sys_clk; pll_clk u_pll( //時鐘分頻模塊,用于調試.inclk0 (sys_clk),.c0 (clk_1m_w) );u_uart_recv( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n),.uart_rxd (uart_rxd),.uart_done (uart_en_w),.uart_data (uart_data_w));u_uart_send( .sys_clk (sys_clk),.sys_rst_n (sys_rst_n),.uart_en (uart_en_w),.uart_din (uart_data_w),.uart_txd (uart_txd)); endmodulertl圖:
總結
- 上一篇: STM32F051 触摸按键功能
- 下一篇: 图片怎么去底色变透明?怎么把图片变透明背